记录一个实现编码器的坑。
这大概是十年前遇到的一个问题,十年连人都可以忘记,但这个问题还能记住,所以不得不提。
视频压缩标准中不会显式声明要写startcode,当按照标准完成了编码器之后,解码失败了,crash segment fault等等等等,反正就是不能正常解码,让人摸不着头脑。
之前遇到这个问题的项目是根据AVS+标准,参考GDM,完善xavs功能,支持AVS+编码。
首先要简单、固定的复现问题:
使用一张白纸录制的简单序列,用xavs编码,将编码信息以及相关的ctx存下来,作为GDM-encoder中算术编码器的输入,然后分别用GDM-decoder解码,可以对比xavs-encoder & xavs2GDM-encoder编码过程的log,以及两种情况在GDM-decoder解码过程的log,发现编码端的信息是一致的。但解码端从一开始在解宏块结束符的地方value_s就对不上了,一个是39一个是41,造成value_s不同的代码如下:
value_s = 0;
while (value_t<QUARTER){ int j;
if (–Dbits_to_go < 0) get_byte();
j=(Dbuffer >> Dbits_to_go) & 0x01;
value_t = (value_t << 1) | j;
value_s++; }
进一步打印其中的Dbuffer 等信息,发现Dbuffer不同,而这个是从编码文件中读取的,需要去查encoder端为何在写文件的时候Dbuffer不同了?
最后跟踪AVS+encoder发现,encoder还对伪起始码做了处理,在xavs也需要有对应的function才行。
解决问题:
AVS标准的startcode跟H264等其他的startcode还有一点不同,就是AVS的startcode不是地址对齐的,判断起来就麻烦一些,过程如下:
写入一个字节的第二最低有效位时,检查该位之前写入的 22 位是否都为‘0’,如果是则在该位之前插入字符‘10’,则该位成为下一字节的最高有效位。在解码端应做相应处理:每读入一个字节,都要检查当前字节和前面读入的两个字节,如果这三个字节构成比特串‘0000 0000 0000 0000 0000 0010’,则丢弃当前字节的最低两位。对于序列头、用户数据、序列显示扩展、摄像机参数扩展、版权扩展中的数据不应采用上述方法。
增加了伪起始码处理之后,解码就正常啦!
据说后续的AVS2的伪起始码处理也是如此,AVS3的不太清楚。
如果遇到不明原因的解码失败问题,可以检查一下是不是没有处理伪起始码导致的。