最近遇到一个问题:一类H264码流解码出来的yuv数据出错。初步分析是带mbaff特性的P/B slice解码结果都有问题,而mbaff的I slice结果正常。因此猜测是mbaff帧间预测有问题。 基于JM做中间结果比对:
- 1.保存的残差数据与JM保存结果一致;
- 2.保存的deblocking前数据不一致,可以判断是帧间预测出了问题;
- 3.保存了一个B宏块的前向和后向预测结果,确认后向预测数据与JM保存结果不一致,而mv数据是一致的;可以猜测参考帧用错了;
- 4.确定参考帧索引号确实不一样。
最终发现参考帧索引被错误的容错条件修改,从而导致参考帧用错。
为更好的掌握mbaff参考帧管理,结合JM代码做了以下分析。
编码mbaff的目的是什么
原始视频数据根据编码需要,可以以不同方式进行扫描,逐行(progressive)或隔行(interlace)。逐行和隔行有不同的编码特性,因此产生了帧模式和场模式编码。一般的,遂行帧进行帧编码,隔行帧可在帧编码和场编码间选取。 对于隔行帧整个画面不一定都是运动区域,静止区域宏块使用帧编码压缩率更高,而运动区域宏块仍使用场编码。这也就是基于宏块的帧场自适应编码(mbaff)。
码流中的元素表示
mbaff特性在码流中由以下元素共同确定:
-
1.sps中的frame_mbs_only_flag
=1,表示编码图像是帧编码(后面的元素都不应存在)
=0,表示可以是帧编码或场编码 -
2 .sps中的mb_adaptive_frame_field_flag
=1,表示一个slice中的宏块可以帧编码或场编码;
=0,表示一个slice中不能切换帧编码和场编码 -
3.slice header中的field_pic_flag,bottom_field_flag
field_pic_flag=0,表示该slice是帧编码
field_pic_flag=1 && bottom_field_flag=0,表示该slice是场编码并且是顶场
field_pic_flag=1 && bottom_field_flag=1,表示该slice是场编码并且是底场 -
4.slice data中宏块数据前 mb_field_decoding_flag
=0,表示该宏块对是帧宏块对
=1,表示该宏块对是场宏块对
mbaff参考帧队列管理
基于JM分析mbaff参考帧队列管理:
1.slice结构体中定义了listX参考帧队列指针数组以及listXsize数组表示参考帧队列里帧个数,其中数组固定为6.
listX[0]表示前向参考帧队列;
listX[1]表示后向参考帧队列;(B slice用到)
listX[2]表示前向参考顶场队列;
listX[3]表示后向参考顶场队列;
listX[4]表示前向参考底场队列;
listX[5]表示后向参考底场队列;
其中,数组中后四个队列只有mbaff才会用到。
2.Macroblock结构体中由一个变量list_offset,用于表示该宏块需要参考队列listX索引值的偏移量,其赋值过程如下:
list_offset = (mb_adaptive_frame_field_flag && mb_field_decoding_flag) ? ((currMB->mbAddrX&01) ? 4: 2): 0
// mb_adaptive_frame_field_flag 表示是否mbaff
// mb_field_decoding_flag表示是否场宏块对
// (currMB->mbAddrX&01) 表示该宏块是顶场还是底场
3.在调用帧间预测时,会根据list_offset选择对应参考帧队列
list0 = currSlice->listX[LIST0 + list_offset][l0_refframe]
参考JM代码 mc_prediction.c