一、介绍
不论是音频数据还是视频数据,我都为MPlayer项目开发过一些开源的解码器。因此我个人认为我有资格写一篇文档来介绍如何开发新的编解码器。
学习如何添加一个新的编解码器的最好方法通常是学习大量的已有代码。本文档仅仅是对代码的一个补充,给出一些技巧、关键点和一般的路线图。
术语介绍:“Codec"表示编码器/解码器(如果你愿意,也可以称为压缩器/解压缩器),它表示一个模块既可以对数据进行编码,也可以进行解码。然而本文档主要是针对解码器,而术语“decoder”和“codec”通常也被一起使用。
二、必要的资源
当你决定要为MPlayer实现一个新的解码器时,那么下面这些应该是你需要的:
(1)实现编解码器的知识
你需要知道MPlayer传给你的数据格式,知道如何把其中的数据结构拆开,在重组这些原始的媒体数据时,需要知道对这些数据的算法操作。
(2)媒体样本
MPlayer在解码时需要确切地知道如何解析存储在媒体文件中经编码后的数据格式(包括AVI,ASF,MOV,RM,VIVO等等)。如果MPlayer无法识别媒体文件中的编码数据,那么你就必须把这种格式转换为能识别的格式,或者为MPlayer写你自己的文件解复用器来处理这些数据。如何写解复用器已经超出了本文档的论述范围,此处不做论述。
尽力获得支持所有可能模式的解码器的媒体样本。如果一个音频的编解码器同时支持单声道和立体声,那么就要找到相应的媒体样本来测试。如果一个视频编解码器能够同时工作在7种不同的比特率,那么你也要想尽办法去获得相应7种比特率编码的媒体样本。
(3)最新的CVS快照
为最新的MPlayer开发版本来开发编解码器同样也是很重要的,因此要经常用CVS更新你的版本。
(4)普通的编程知识,能在Linux环境下开发
这通常是不用说的,但有时候你却不得不说。
三、开发流程
(1)建立基本的开发环境
首先,修改你的本地配置文件codecs.conf,它可能是系统共享的或在你主目录下。为你的编解码器添加一个新的条目,如果是开源的编解码器,那么最好把它放在其他开源的编解码器一起。当你确信把条目添加正确后,一定要把它添加到你的工作目录的etc/codecs.conf中。想要了解这个配置文件的详细信息,可以查看codecs.conf.txt。创建一个新的包含相关信息,格式,输出格式,特定驱动名称的音频编解码或视频编解码块,并记住驱动的名称。
创建一个新的源文件,它应该包含解码的主函数以便MPlayer调用来进行解码数据。最终你可能会有多个文件组成你的解码器,举一个简单的例子如下:
对于一个音频解码器,比如ad_sample.c。针对视频解码器,可以命名为vd_*.c。
接下来,需要修改Makefile文件使得新的编解码器被编译进去。当然,你还需要把解码器加入到ad.c(对音频来说)或vd.c(对视频来说)的数组中。
编译整个工程,看看到目前为止是否有错误。
为了把你的解码函数放在首要位置,你需要确保你的编码数据。这听起来像是一个微小的练习,但却可能发生很多错误。在你解码函数的开始,加入下面的代码:
int i;
for (i = 0; i < 16; i++)
printf ("%02X ", input[i]);
printf ("/n");
当你编译并运行MPlayer时,你的解码函数将会打印它所接收到的每个数据块的前16个字节。用16进制编辑器打开媒体样本,看看你屏幕上的输出和打开文件所看到的,如果解码器打印的与文件中的一致,那么说明你已经准备好进入第二步了,否则你就要找出为什么没有得到正确的数据,是因为解码器没有被触发吗?为什么?
(2)开发解码器
记住首先要让它能工作,其次才是工作的快。
解码器支持什么输出格式?任何都可以。通常YUV输出要比RGB输出受欢迎。如果一个编码器采用YUV数据作为它的源数据,那么就需要能解码一个YUV数据的帧。如果编码器想很多老的视频编码器一样用RGB数据作为输入,那么支持YUV输出就没有意义了,尽可能的输出RGB格式就可以了。
最受欢迎的视频数据输出格式是YV12,因为MPlayer支持大量的硬件设备来对这些数据直接显示、缩放和过滤。MPlayer同样也有很多优化的转换函数能把YV12数据转换为其他数据输出格式。
如果你确实采用RGB作为输出,你必须意识到MPlayer事实上打包RGB为BGR,如果你解码成BGR24数据缓存,那么输出如下:
B G R B G R B G R B ...
如果你解码成BGR32,那么在每个BGR后面需要一个额外的字节:
B G R - B G R - B G ...
很好地利用检查方法。在解码器的开始包含头文件mp_msg.h,你可以使用mp_msg()函数作为你的printf()语句。一旦你的解码器发现奇怪的数据或情况,打印如下:
mp_msg(MSGT_DECVIDEO, MSGL_WARN, "Odd data encountered: %d/n", data);
显然,为了你好,你应该使得消息更加清晰。MSGL_WARN对这种类型的信息来说是一个好的消息级别。mp_msg.h中定义了所有的错误级别,你甚至可以用MSGL_FATAL来使得MPlayer完全退出,但这不是解码数据需要的级别。
什么情况下应该触发一个警告呢?任何超出普通的情况。许多压缩的视频数据包含的头里有宽、高和大小这些数据,把这些域作为参数传给解码函数,这些数据应该是匹配的,否则应该给出一个警告并决定应该使用哪个数据。如果头部信息里应该有一个Magic Number,或是从头部数据计算出来的,但却不正确,则应该给出警告。
无论检查边界索引有多枯燥,你都必须确保没有超出理论上的内存范围,访问超出范围的内存会导致段错误,永远不要相信传递给你的所有数据都是正确的。一旦索引超出了范围,就需要给出警告并退出解码过程(而不是整个应用程序)。
写出所有可能产生警告的情况看似无聊的,但如果不写的话,万一在解码过程中出现错误,你却不知道是为什么以及在哪里出了错误。
(3)调试和测试解码器
如果你超级幸运,解码器第一次运行就成功了;或是你很幸运,通过修改了几个程序错误以后,你的解码器也能工作了。现实来说,你需要查看很多次,修改很多明显和不明显的程序错误,还需要通过调试来解决一个很小的功能。
提示:请求所有的警告信息。例如gcc的-Wall选项。开发时使用调试模式是很有用的,为了在调试模式下编译你的MPlayer,使用--enable-debug选项。注意所有的警告并消除所有警告。
(4)把解码器贡献到代码库
用"diff -u"来创建一个补丁,然后给MPlayer开发组发邮件获得许可,你需要diff下列文件:
- Makefile
- etc/codecs.conf
- ad.c or vd.c
当然,你应该包含你新创建的文件:vd_<name>.c 或 ad_<name>.c。如果你贡献够多的话,MPlayer开发组或许会授予你修改CVS的权利。
(5)等待BUG报告并跟进
当你发布你的编解码器后,你也许认为你已经完成了,但前提是你足够幸运。但往往却事与愿违,所以你需要查看BUG报告并跟进修改BUG。
一、介绍
不论是音频数据还是视频数据,我都为MPlayer项目开发过一些开源的解码器。因此我个人认为我有资格写一篇文档来介绍如何开发新的编解码器。
学习如何添加一个新的编解码器的最好方法通常是学习大量的已有代码。本文档仅仅是对代码的一个补充,给出一些技巧、关键点和一般的路线图。
术语介绍:“Codec"表示编码器/解码器(如果你愿意,也可以称为压缩器/解压缩器),它表示一个模块既可以对数据进行编码,也可以进行解码。然而本文档主要是针对解码器,而术语“decoder”和“codec”通常也被一起使用。
二、必要的资源
当你决定要为MPlayer实现一个新的解码器时,那么下面这些应该是你需要的:
(1)实现编解码器的知识
你需要知道MPlayer传给你的数据格式,知道如何把其中的数据结构拆开,在重组这些原始的媒体数据时,需要知道对这些数据的算法操作。
(2)媒体样本
MPlayer在解码时需要确切地知道如何解析存储在媒体文件中经编码后的数据格式(包括AVI,ASF,MOV,RM,VIVO等等)。如果MPlayer无法识别媒体文件中的编码数据,那么你就必须把这种格式转换为能识别的格式,或者为MPlayer写你自己的文件解复用器来处理这些数据。如何写解复用器已经超出了本文档的论述范围,此处不做论述。
尽力获得支持所有可能模式的解码器的媒体样本。如果一个音频的编解码器同时支持单声道和立体声,那么就要找到相应的媒体样本来测试。如果一个视频编解码器能够同时工作在7种不同的比特率,那么你也要想尽办法去获得相应7种比特率编码的媒体样本。
(3)最新的CVS快照
为最新的MPlayer开发版本来开发编解码器同样也是很重要的,因此要经常用CVS更新你的版本。
(4)普通的编程知识,能在Linux环境下开发
这通常是不用说的,但有时候你却不得不说。
三、开发流程
(1)建立基本的开发环境
首先,修改你的本地配置文件codecs.conf,它可能是系统共享的或在你主目录下。为你的编解码器添加一个新的条目,如果是开源的编解码器,那么最好把它放在其他开源的编解码器一起。当你确信把条目添加正确后,一定要把它添加到你的工作目录的etc/codecs.conf中。想要了解这个配置文件的详细信息,可以查看codecs.conf.txt。创建一个新的包含相关信息,格式,输出格式,特定驱动名称的音频编解码或视频编解码块,并记住驱动的名称。
创建一个新的源文件,它应该包含解码的主函数以便MPlayer调用来进行解码数据。最终你可能会有多个文件组成你的解码器,举一个简单的例子如下:
对于一个音频解码器,比如ad_sample.c。针对视频解码器,可以命名为vd_*.c。
接下来,需要修改Makefile文件使得新的编解码器被编译进去。当然,你还需要把解码器加入到ad.c(对音频来说)或vd.c(对视频来说)的数组中。
编译整个工程,看看到目前为止是否有错误。
为了把你的解码函数放在首要位置,你需要确保你的编码数据。这听起来像是一个微小的练习,但却可能发生很多错误。在你解码函数的开始,加入下面的代码:
int i;
for (i = 0; i < 16; i++)
printf ("%02X ", input[i]);
printf ("/n");
当你编译并运行MPlayer时,你的解码函数将会打印它所接收到的每个数据块的前16个字节。用16进制编辑器打开媒体样本,看看你屏幕上的输出和打开文件所看到的,如果解码器打印的与文件中的一致,那么说明你已经准备好进入第二步了,否则你就要找出为什么没有得到正确的数据,是因为解码器没有被触发吗?为什么?
(2)开发解码器
记住首先要让它能工作,其次才是工作的快。
解码器支持什么输出格式?任何都可以。通常YUV输出要比RGB输出受欢迎。如果一个编码器采用YUV数据作为它的源数据,那么就需要能解码一个YUV数据的帧。如果编码器想很多老的视频编码器一样用RGB数据作为输入,那么支持YUV输出就没有意义了,尽可能的输出RGB格式就可以了。
最受欢迎的视频数据输出格式是YV12,因为MPlayer支持大量的硬件设备来对这些数据直接显示、缩放和过滤。MPlayer同样也有很多优化的转换函数能把YV12数据转换为其他数据输出格式。
如果你确实采用RGB作为输出,你必须意识到MPlayer事实上打包RGB为BGR,如果你解码成BGR24数据缓存,那么输出如下:
B G R B G R B G R B ...
如果你解码成BGR32,那么在每个BGR后面需要一个额外的字节:
B G R - B G R - B G ...
很好地利用检查方法。在解码器的开始包含头文件mp_msg.h,你可以使用mp_msg()函数作为你的printf()语句。一旦你的解码器发现奇怪的数据或情况,打印如下:
mp_msg(MSGT_DECVIDEO, MSGL_WARN, "Odd data encountered: %d/n", data);
显然,为了你好,你应该使得消息更加清晰。MSGL_WARN对这种类型的信息来说是一个好的消息级别。mp_msg.h中定义了所有的错误级别,你甚至可以用MSGL_FATAL来使得MPlayer完全退出,但这不是解码数据需要的级别。
什么情况下应该触发一个警告呢?任何超出普通的情况。许多压缩的视频数据包含的头里有宽、高和大小这些数据,把这些域作为参数传给解码函数,这些数据应该是匹配的,否则应该给出一个警告并决定应该使用哪个数据。如果头部信息里应该有一个Magic Number,或是从头部数据计算出来的,但却不正确,则应该给出警告。
无论检查边界索引有多枯燥,你都必须确保没有超出理论上的内存范围,访问超出范围的内存会导致段错误,永远不要相信传递给你的所有数据都是正确的。一旦索引超出了范围,就需要给出警告并退出解码过程(而不是整个应用程序)。
写出所有可能产生警告的情况看似无聊的,但如果不写的话,万一在解码过程中出现错误,你却不知道是为什么以及在哪里出了错误。
(3)调试和测试解码器
如果你超级幸运,解码器第一次运行就成功了;或是你很幸运,通过修改了几个程序错误以后,你的解码器也能工作了。现实来说,你需要查看很多次,修改很多明显和不明显的程序错误,还需要通过调试来解决一个很小的功能。
提示:请求所有的警告信息。例如gcc的-Wall选项。开发时使用调试模式是很有用的,为了在调试模式下编译你的MPlayer,使用--enable-debug选项。注意所有的警告并消除所有警告。
(4)把解码器贡献到代码库
用"diff -u"来创建一个补丁,然后给MPlayer开发组发邮件获得许可,你需要diff下列文件:
- Makefile
- etc/codecs.conf
- ad.c or vd.c
当然,你应该包含你新创建的文件:vd_<name>.c 或 ad_<name>.c。如果你贡献够多的话,MPlayer开发组或许会授予你修改CVS的权利。
(5)等待BUG报告并跟进
当你发布你的编解码器后,你也许认为你已经完成了,但前提是你足够幸运。但往往却事与愿违,所以你需要查看BUG报告并跟进修改BUG。