H.264解码图像顺序的计算方法

图像顺序号用来在解码B条带时决定参考图像的初始图像顺序,在B条带的显式加权预测中以及在解码器的一致性检查中,用来表示时域直接模式下运动矢量推导过程中的帧或场之间的图像序号差别。对每一帧、场(或者由编码场解码得到,或者作为解码帧的一部分)和互补场对都要产生图像顺序号信息。

(1)每一个编码帧有两个图像顺序号,顶场顺序号和底场顺序号。

(2)每一个编码场有一个图像顺序号,顶场顺序号或底场顺序号。

(3)每一个互补参考场对有两个图像顺序号, 顶场顺序号和底场顺序号。

顶场顺序号和底场顺序号是相对于前一个IDR图像的第一个输出场的图像顺序。

pic_order_cnt_type是SPS的语法元素,用来表示解码图像顺序的计算方法,其值可以为0、1和2。包含下列任何情况的一个编码视频序列中pic_order_cnt_type的值都不能为2:

(1)一个包含非参考图像的视频单元紧跟在一个包含非参考帧的访问单元之后;

(2)一个包含非参考图像的视频单元紧跟在分别包含一个由两个场一起组成一个互补的非参考场对的两个访问单元之后;

(3)一个包含非参考场的访问单元之后紧跟在一个包含另一个非参考图像的访问单元(该访问单元没有与两个访问单元中的第一个组成一个互补的非参考场对)。

一、解码图像顺序类型为0

看一下ffmpeg的实现方法:

const int max_poc_lsb = 1 << log2_max_poc_lsb;
if (poc_lsb < prev_poc_lsb && prev_poc_lsb - poc_lsb >= max_poc_lsb / 2) {
    poc_msb = prev_poc_msb + max_poc_lsb;
} else if (poc_lsb > prev_poc_lsb && prev_poc_lsb - poc_lsb < -max_poc_lsb / 2) {
    poc_msb = prev_poc_msb - max_poc_lsb;
} else {
    poc_msb = prev_poc_msb;
}
field_poc[0] = field_poc[1] = poc_msb + poc_lsb;
if (picture_structure == PICT_FRAME) {
    field_poc[1] += delta_poc_bottom;
}

(1)LSB是指最低有效位,MSB是指最高有效位,两者之和即是顶场的解码顺序,加上顶场和底场的解码顺序差值,便可得到底场的解码顺序。最低有效位可以从Slice的header中解析出来,因此,关键是要得到MSB。

(2)log2_max_poc_lsb = log2_max_pic_order_cnt_lsb_minus4 + 4;其中log2_max_pic_order_cnt_lsb_minus4是序列参数集的语法元素。

(3)poc_lsb是slice header的语法元素picture_order_cnt_lsb,表示一个编码帧的顶场或一个编码场的图像顺序数对MaxPicOrderCntLsb的取模。

(4)delta_poc_bottom对应slice header的语法元素delta_pic_order_cnt_bottom,表示一个编码帧的底场和顶场的图像顺序数之间的差。

二、解码图像顺序类型为1

看一下ffmpeg的实现方法:

int abs_frame_num, expected_delta_per_poc_cycle, expectedpoc;
int i;
if (poc_cycle_length != 0) {
    abs_frame_num = frame_num_offset + frame_num;
} else {
    abs_frame_num = 0;
}
if (nal_ref_idc == 0 && abs_frame_num > 0) {
    abs_frame_num--;
}
expected_delta_per_poc_cycle = 0;
for (i = 0; i < sps.poc_cycle_length; i++) {
    expected_delta_per_poc_cycle += offset_for_ref_frame[i];
}
if (abs_frame_num > 0) {
    int poc_cycle_cnt          = (abs_frame_num - 1) / poc_cycle_length;
    int frame_num_in_poc_cycle = (abs_frame_num - 1) % poc_cycle_length;
    expectedpoc = poc_cycle_cnt * expected_delta_per_poc_cycle;
    for (i = 0; i <= frame_num_in_poc_cycle; i++) {
        expectedpoc = expectedpoc + offset_for_ref_frame[i];
    }
} else {
    expectedpoc = 0;
}
if (nal_ref_idc == 0) {
    expectedpoc = expectedpoc + h->sps.offset_for_non_ref_pic;
}
field_poc[0] = expectedpoc + delta_poc[0];
field_poc[1] = field_poc[0] + sps.offset_for_top_to_bottom_field;
if (h->picture_structure == PICT_FRAME) {
    field_poc[1] += delta_poc[1];
}

(1)frame_num和frame_num_offset的获得方法和解码图像顺序类型为2时的获得方法一样。

三、解码图像顺序类型为2

看一下ffmpeg的实现方法:

int poc = 2 * (frame_num_offset + frame_num);
if (!nal_ref_idc) {
    poc--;
}
field_poc[0] = poc;
field_poc[1] = poc;

(1)frame_num是Slice的header的语法元素,用作一个图像标识符。
(2)frame_num可以从Slice的header中解析出来,因此,求得顶场合底场的解码顺序的关键是如何获得frame_num_offset的值。frame_num_offset的值根据下面的过程获得:
const int max_frame_num = 1 << log2_max_frame_num;
frame_num_offset = prev_frame_num_offset;
if (frame_num < prev_frame_num) {
        frame_num_offset += max_frame_num;
}
其中,log2_max_frame_num = log2_max_frame_num_minus4 + 4,而log2_max_frame_num_minus4是SPS的语法元素。

你可能感兴趣的:(音视频编解码)