从内存解码 OggVorbis 小记

搞了一个多小时,终于把这个功能写出来了,后续还需要很多优化的地方要搞,就目前为止,先做一下笔记,以后如果忘记了,可以回头看看笔记。

 

如果是通过 fopen 打开 ogg 文件得到 FILE 句柄之后,可以通过 ov_open 来初始化 OggVorbis_File 结构体,不过,如果 ogg 文件是加载到内存里面的,不是通过 fopen 从磁盘文件打开的,情况就比较复杂了,libvorbis 提供了 ov_open_callbacks 函数来支持从内存初始化 OggVorbis_File 结构体,然而,在这种情况下,libvorbis 需要用户提供四个自定义函数,来操作通过 ov_open_callbacks 传进去的 ogg 文件数据,以辅助 libvorbis 获取 ogg 文件信息,这四个自定义函数的功能等价于标准 C 函数的 fread、fseek、ftell 和 fclose 四个函数,然而在返回值上,libvorbis 有自己的一点点特别需求,总体来讲,难度在于编写这四个用户自定义函数。

 

当你调用 ov_open_callbacks 函数时,ov_open_callbacks 会把用户自定义数据传递到上述四个自定义函数中,让用户手动帮忙从用户自定义数据中读取 ogg 文件数据,这里提到的【用户自定义数据】,就是指调用 ov_open_callbacks 函数时的第一个参数,假设你把 ogg 文件的所有数据都读入到内存区,再通过一个名为 oggFileData 的指针变量来保存指向这块内存区的内存地址,然后,你把 oggFileData 变量作为第一个参数调用 ov_open_callbacks 函数,那么,ov_open_callbacks 函数在运行的过程中,就会原封不动地把 oggFileData 变量传递到四个用户自定义函数中,我们在四个自定义函数中拿到 oggFileData 之后,再按照 libvorbis 提出的需求,从 oggFileData 中拷贝 ogg 文件数据到 libvorbis 提供的输出变量中即可。

 

如果仅仅是想测试一块内存区中保存的数据是不是标准的 ogg 文件数据,可以使用 ov_test_callbacks 函数来替代 ov_open_callbacks 函数。

 

一旦成功初始化 OggVorbis_File 结构体之后,就可以通过 ov_info 函数获取音频信息,比如:音频频道数量、音频取样率,至于每次取样多少位这个数据,目前来讲,OggVorbis 是固定每次取样 16 位数据的,也就是每次取样 2 个字节的数据,这个我在代码里面通过硬编码手段写死了,之后还要看看官方文档推荐我们用户怎么去处理这个数据的才行,拿到音频信息之后,可以通过 ov_time_total 函数拿到整个 ogg 文件的总播放时长,以秒为单位,得到了上面所有信息之后,就可以计算出解码之后的音频数据的大小了。

 

一般来讲,播放 ogg 文件都是采用流式播放的,就是一边读 ogg 数据,一边播放,这样子,但是,出于实验性,我选择了一次性全部解码所有 ogg 数据,所以,我在实验代码中需要事先知道整个 ogg 文件的音频数据解码后有多大。

 

准备好用于保存解码后的音频数据的缓冲区之后,可以通过 ov_read 函数来解码 ogg 音频数据了,这个函数的第 3 个参数填写要解码出多少字节的音频数据,官方手册推荐我们写 4096,我试了一下,就算我填写超过 4096 的数值,ov_read 也只会解码出 4096 字节的音频数据,所以,还是按照官方文档的推荐数值来填吧。

 

通过不断调用 ov_read 函数,把整个 ogg 文件的音频数据都解码完毕之后,就得到了一份符合 PCM 标准的音频数据了,你可以直接将这份 PCM 音频数据交给 DirectSound 或者 OpenAL 来进行播放操作。

 

现在的游戏基本上都用 ogg 作为容器来保存游戏音频数据,wav 由于体积巨大,会导致安装包体积剧增,特别是在 Galgame 游戏中,声音数据非常巨大,用 wav 文件不太可取,而 mp3 又是收费的编码标准,不给钱就拿不到 SDK 来进行解码,不过,网上好像有人编写了开源的 mp3 解码库,好像叫做 mpg123,单纯解码音频文件不会很复杂,复杂的地方在于,需要配合程序的性能表现和用户体验,比方说要通过流式加载来播放音频数据,不然,性能稍微差一点的电脑,都要几秒到十几秒的时间来解码音频数据,估计用户会等的不耐烦。

 

posted @ 2018-05-24 03:22  NekoMasutaDev  阅读(371)  评论(0编辑  收藏  举报