如何在不将所有文件加载到内存中的情况下搜索ogg vorbis文件?

时间:2014-08-12 10:17:32

标签: audio codec ogg seek vorbis

我正试图找到一种方法移动到轨道中的所需位置,而不将所有文件加载到内存中。并且没有使用vorbisfile,因为该文件存储在远程服务器中。我阅读了关于寻求但无法理解的文档中的段落。

2 个答案:

答案 0 :(得分:2)

如果远程服务器允许您使用带有Range标头的HTTP GET,则可以"假"通过发送大量不同部分的请求来访问文件,就像对本地文件一样......

ASSUMING:文件是Ogg封装的,只有Vorbis流...

  1. 执行HTTP HEAD请求以获取文件的总长度
  2. 获取文件的前4KB和" sync" Vorbis标题。您可能需要获取更多数据才能完成此任务。
  3. 获取文件的最后4KB和" sync"最后一个Ogg页面标题以获取总样本数
  4. 执行规范描述的双节搜索,用HTTP GET替换w / Range代替fseek / fread
  5. 如果你做得对,在大多数情况下,寻求转移应少于100KB。

    更新:

    双节搜索有点不直观......想法是在文件中跳转寻找正确的页面,但每次跳转都是"通知"通过之前的跳转和当前页面...一个例子可能是最好的:

    试图在一个包含1,000,000个样本的文件中抽样300,000(我假设我们在上面的第4步):

    1. 将物理文件搜索到{fileStream.Length * .3}
    2. 向前看,直至找到Ogg Page
    3. 检查该网页是否为有问题的Vorbis信息流的一部分
    4. 如果没有,请转到下一个Ogg Page&转到第3步
    5. 检查颗粒位置
    6. 如果不是正确的页面,请查找物理文件到{当前位置+((300000 - 颗粒位置)/ 1000000)* fileStream.Length}&转到第2步
    7. 您找到了正确的页面,但可能需要移回页面才能获得&#34; pre-roll&#34; ... Vorbis要求在所需数据包之前解码1个数据包。< / LI>

      可能有更好的算法,但这是基本的想法。

      请记住,颗粒位置是页面END处的样本计数,因此当您找到正确的页面时,其颗粒位置略微大于目标。

答案 1 :(得分:0)

寻找ogg文件非常困难。

要了解的事情列表

  1. 当ogg_page_packets(&og)> 0时,ogg_page是结束页
  2. 当ogg_page_granulepos(&og)> 0时,页面具有数据包流中的最后一个时间戳
  3. 当ogg_page_peek(&oy,&og)== 1时,会弹出一个完整的页面。
  4. 当ogg_page_peek(&oy,&og)== 0时,页面不完整
  5. 当ogg_page_peek(&oy,&og)== -1时,libogg函数找不到页面的开头
  6. vorbis_granule_time(&vd,ogg_page_granulepos(&og))以秒为单位打印时间

您将需要此功能

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

https://xiph.org/vorbis/doc/libvorbis/reference.html

https://xkcd.com/979/

https://xiph.org/oggz/doc/group__basics.html