【发布时间】:2014-08-12 10:17:32
【问题描述】:
我正在尝试找到一种方法来移动到轨道中的所需位置,而无需将所有文件加载到内存中。而没有使用vorbisfile,因为文件存储在远程服务器中。我阅读了文档中关于寻求但无法理解的段落。
【问题讨论】:
标签: audio codec ogg seek vorbis
我正在尝试找到一种方法来移动到轨道中的所需位置,而无需将所有文件加载到内存中。而没有使用vorbisfile,因为文件存储在远程服务器中。我阅读了文档中关于寻求但无法理解的段落。
【问题讨论】:
标签: audio codec ogg seek vorbis
如果远程服务器允许您使用带有 Range 标头的 HTTP GET,您可以通过向不同部分发送一堆请求来“伪造”文件访问,就像您对本地文件所做的那样......
假设:该文件是 Ogg 封装的,并且其中只有 Vorbis 流...
如果你做对了,在大多数情况下,寻找应该传输不到 100KB。
更新:
二分搜索有点不直观......想法是在文件中跳转以寻找正确的页面,但每次跳转都是由先前的跳转和当前页面“通知”的......最好举个例子:
在一个包含 1,000,000 个样本的文件中寻找 300,000 个样本(我假设我们在上面的第 4 步):
对此可能有更好的算法,但这是基本思想。
请记住,颗粒位置是页面末尾的样本计数,因此当您找到正确的页面时,其颗粒位置将略比您的目标大。
【讨论】:
寻找 ogg 文件很难。
要理解的事情清单
你需要这个功能
int buffer_data(){
//oy is an ogg_sync_state https://xiph.org/ogg/doc/libogg/ogg_sync_state.html
//in is just a file
char *buffer=ogg_sync_buffer(&oy,4096);
int bytes=fread(buffer,1,4096,&in);
ogg_sync_wrote(&oy,bytes);
return(bytes);
}
在下面的代码中,除了 ogg 页面和 ogg 数据包之外,我将添加另一层,它本质上是文件缓冲区。本质上,我的代码只将每个文件缓冲区的第一个同步结束页一分为二。
当我找不到 ogg_page_sync 时,我的代码会创建第二个块光标来加载下一个 4k 文件缓冲区,直到我找到页面同步或超出边界。
#include <unordered_map>
struct _page_info {
size_t block_number;
double_t time;
ogg_int64_t granulepos;
};
struct _page_info left_page = { .time = 0, .block_number = 0, .granulepos = 0 };
struct _page_info mid_page = { .time = 0, .block_number = 0, .granulepos = 0 };
struct _page_info right_page = { .time = DBL_MAX, .block_number = 0x7FFFFFFFFFFFFFFF, .granulepos = 0x7FFFFFFFFFFFFFFF };
unordered_map<int, double> block_time;
unordered_map<ogg_int64_t, _page_info> page_info_table;
ogg_page og;
while (left <= right) {
//Seek to block
size_t mid_block = left + (right - left) / 2;
int block = mid_block;
if (block_time.has(block)) {
//Check whether this block has been visited
break;
}
//clear the sync state
ogg_sync_reset(&oy);
file.seek(block * buffer_size);
buffer_data();
bool next_midpoint = true;
while (true) {
//keep syncing until a page is found. Buffer is only 4k while ogg pages can be up to 65k in size
int ogg_page_sync_state = ogg_sync_pageout(&oy, &og);
if (ogg_page_sync_state == -1) {
//Give up when the file advances past the right boundary
if (buffer_data() == 0) {
right = mid_block;
break;
} else {
//increment block size we buffered the next block
block++;
}
} else {
if (ogg_page_sync_state == 0) {
//Check if I reached the end of the file
if (buffer_data() == 0) {
right = mid_block;
break;
} else {
block++;
}
} else {
//Only pages with a end packet have granulepos. Check the stream
if (ogg_page_packets(&og) > 0 && ogg_page_serialno(&og) == vo.serialno) {
next_midpoint = false;
break;
}
}
}
}
if (next_midpoint)
continue;
ogg_int64_t granulepos = ogg_page_granulepos(&og);
ogg_int64_t page_number = ogg_page_pageno(&og);
struct _page_info pg_info = { .time = vorbis_granule_time(vd, granulepos), .block_number = mid_block, .granulepos = granulepos };
page_info_table[page_number] = pg_info;
block_time[mid_block] = pg_info.time;
mid_page = pg_info;
//I can finally implement the binary search comparisons
if (abs(p_time - pg_info.time) < .001) {
//The video managed to be equal
right_page = pg_info;
break;
}
if (pg_info.time > p_time) {
if (pg_info.granulepos < right_page.granulepos)
right_page = pg_info;
right = mid_block;
} else {
if (pg_info.granulepos > left_page.granulepos)
left_page = pg_info;
left = mid_block;
}
}
当你完成后,你基本上会回溯 ogg_pages,直到找到所需的 ogg_packet。
这是一个用连续递增的数据包计算时间戳的技巧
while(ogg_sync_pageout(&oy, &og) > 0)
ogg_stream_pagein(&vo, &og);
ogg_int64_t last_granule = ogg_page_granulepos(&og);
ogg_int64_t total_granule = ogg_page_packets(&og));
while(ogg_stream_packetout(&vo, &op) > 0 ) {
double time = vorbis_granule_time(&vd, last_granule - total_granule--);
}
https://xiph.org/ogg/doc/libogg/reference.html
https://github.com/xiph/theora/blob/master/examples/player_example.c
【讨论】: