FFmpeg的H 264解码器源代码简单分析 宏块解码(Decode)部分-帧内宏块(Intra)

本文详细分析了FFmpeg H.264解码器的宏块解码过程,特别是帧内宏块的解码。通过decode_slice()函数,深入探讨了hl_decode_mb()和hl_decode_mb_predict_luma()等关键步骤,包括帧内预测模式、汇编函数初始化以及4x4帧内预测函数的实现。
摘要由CSDN通过智能技术生成
               

=====================================================

H.264源代码分析文章列表:

【编码 - x264】

x264源代码简单分析:概述

x264源代码简单分析:x264命令行工具(x264.exe)

x264源代码简单分析:编码器主干部分-1

x264源代码简单分析:编码器主干部分-2

x264源代码简单分析:x264_slice_write()

x264源代码简单分析:滤波(Filter)部分

x264源代码简单分析:宏块分析(Analysis)部分-帧内宏块(Intra)

x264源代码简单分析:宏块分析(Analysis)部分-帧间宏块(Inter)

x264源代码简单分析:宏块编码(Encode)部分

x264源代码简单分析:熵编码(Entropy Encoding)部分

FFmpeg与libx264接口源代码简单分析

【解码 - libavcodec H.264 解码器

FFmpeg的H.264解码器源代码简单分析:概述

FFmpeg的H.264解码器源代码简单分析:解析器(Parser)部分

FFmpeg的H.264解码器源代码简单分析:解码器主干部分

FFmpeg的H.264解码器源代码简单分析:熵解码(EntropyDecoding)部分

FFmpeg的H.264解码器源代码简单分析:宏块解码(Decode)部分-帧内宏块(Intra)

FFmpeg的H.264解码器源代码简单分析:宏块解码(Decode)部分-帧间宏块(Inter)

FFmpeg的H.264解码器源代码简单分析:环路滤波(Loop Filter)部分

=====================================================


 本文分析FFmpeg的H.264解码器的宏块解码(Decode)部分的源代码。FFmpeg的H.264解码器调用decode_slice()函数完成了解码工作。这些解码工作可以大体上分为3个步骤:熵解码,宏块解码以及环路滤波。本文分析这3个步骤中的第2个步骤。由于宏块解码部分的内容比较多,因此将本部分内容拆分成两篇文章:一篇文章记录帧内预测宏块(Intra)的宏块解码,另一篇文章记录帧间预测宏块(Inter)的宏块解码。



函数调用关系图

宏块解码(Decode)部分的源代码在整个H.264解码器中的位置如下图所示。


单击查看更清晰的图片


宏块解码(Decode)部分的源代码的调用关系如下图所示。


单击查看更清晰的图片


 宏块解码函数(Decode)通过帧内预测、帧间预测、DCT反变换等方法解码压缩数据。解码函数是ff_h264_hl_decode_mb()。其中跟宏块类型的不同,会调用几个不同的函数,最常见的就是调用hl_decode_mb_simple_8()。
 hl_decode_mb_simple_8()的定义是无法在源代码中直接找到的,这是因为它实际代码的函数名称是使用宏的方式写的。hl_decode_mb_simple_8()的源代码实际上就是FUNC(hl_decode_mb)()函数的源代码。
 从函数调用图中可以看出,FUNC(hl_decode_mb)()根据宏块类型的不同作不同的处理:如果帧内预测宏块(INTRA),就会调用hl_decode_mb_predict_luma()进行帧内预测;如果是帧间预测宏块(INTER),就会调用FUNC(hl_motion_422)()或者FUNC(hl_motion_420)()进行四分之一像素运动补偿。
 经过帧内预测或者帧间预测步骤之后,就得到了预测数据。随后FUNC(hl_decode_mb)()会调用hl_decode_mb_idct_luma()等几个函数对残差数据进行DCT反变换工作,并将变换后的数据叠加到预测数据上,形成解码后的图像数据。
 由于帧内预测宏块和帧间预测宏块的解码工作都比较复杂,因此分成两篇文章记录这两部分的源代码。本文记录帧内预测宏块解码时候的源代码。
 下面首先回顾一下decode_slice()函数。



decode_slice()

decode_slice()用于解码H.264的Slice。该函数完成了“熵解码”、“宏块解码”、“环路滤波”的功能。它的定义位于libavcodec\h264_slice.c,如下所示。
//解码slice//三个主要步骤://1.熵解码(CAVLC/CABAC)//2.宏块解码//3.环路滤波//此外还包含了错误隐藏代码static int decode_slice(struct AVCodecContext *avctx, void *arg){    H264Context *h = *(void **)arg;    int lf_x_start = h->mb_x;    h->mb_skip_run = -1;    av_assert0(h->block_offset[15] == (4 * ((scan8[15] - scan8[0]) & 7) << h->pixel_shift) + 4 * h->linesize * ((scan8[15] - scan8[0]) >> 3));    h->is_complex = FRAME_MBAFF(h) || h->picture_structure != PICT_FRAME ||                    avctx->codec_id != AV_CODEC_ID_H264 ||                    (CONFIG_GRAY && (h->flags & CODEC_FLAG_GRAY));    if (!(h->avctx->active_thread_type & FF_THREAD_SLICE) && h->picture_structure == PICT_FRAME && h->er.error_status_table) {        const int start_i  = av_clip(h->resync_mb_x + h->resync_mb_y * h->mb_width, 0, h->mb_num - 1);        if (start_i) {            int prev_status = h->er.error_status_table[h->er.mb_index2xy[start_i - 1]];            prev_status &= ~ VP_START;            if (prev_status != (ER_MV_END | ER_DC_END | ER_AC_END))                h->er.error_occurred = 1;        }    }    //CABAC情况    if (h->pps.cabac) {        /* realign */        align_get_bits(&h->gb);        /* init cabac */        //初始化CABAC解码器        ff_init_cabac_decoder(&h->cabac,                              h->gb.buffer + get_bits_count(&h->gb) / 8,                              (get_bits_left(&h->gb) + 7) / 8);        ff_h264_init_cabac_states(h);        //循环处理每个宏块        for (;;) {            // START_TIMER         //解码CABAC数据            int ret = ff_h264_decode_mb_cabac(h);            int eos;            // STOP_TIMER("decode_mb_cabac")            //解码宏块            if (ret >= 0)                ff_h264_hl_decode_mb(h);            // FIXME optimal? or let mb_decode decode 16x32 ?            //宏块级帧场自适应。很少接触            if (ret >= 0 && FRAME_MBAFF(h)) {                h->mb_y++;                ret = ff_h264_decode_mb_cabac(h);                //解码宏块                if (ret >= 0)                    ff_h264_hl_decode_mb(h);                h->mb_y--;            }            eos = get_cabac_terminate(&h->cabac);            if ((h->workaround_bugs & FF_BUG_TRUNCATED) &&                h->cabac.bytestream > h->cabac.bytestream_end + 2) {             //错误隐藏                er_add_slice(h, h->resync_mb_x, h->resync_mb_y, h->mb_x - 1,                             h->mb_y, ER_MB_END);                if (h->mb_x >= lf_x_start)                    loop_filter(h, lf_x_start, h->mb_x + 1);                return 0;            }            if (h->cabac.bytestream > h->cabac.bytestream_end + 2 )                av_log(h->avctx, AV_LOG_DEBUG, "bytestream overread %"PTRDIFF_SPECIFIER"\n", h->cabac.bytestream_end - h->cabac.bytestream);            if (ret < 0 || h->cabac.bytestream > h->cabac.bytestream_end + 4) {                av_log(h->avctx, AV_LOG_ERROR,                       "error while decoding MB %d %d, bytestream %"PTRDIFF_SPECIFIER"\n",                       h->mb_x, h->mb_y,                       h->cabac.bytestream_end - h->cabac.bytestream);                er_add_slice(h, h->resync_mb_x, h->resync_mb_y, h->mb_x,                             h->mb_y, ER_MB_ERROR);                return AVERROR_INVALIDDATA;            }            //mb_x自增            //如果自增后超过了一行的mb个数            if (++h->mb_x >= h->mb_width) {             //环路滤波                loop_filter(h, lf_x_start, h->mb_x);                h->mb_x = lf_x_start = 0;                decode_finish_row(h);                //mb_y自增(处理下一行)                ++h->mb_y;                //宏块级帧场自适应,暂不考虑                if (FIELD_OR_MBAFF_PICTURE(h)) {                    ++h->mb_y;                    if (FRAME_MBAFF(h) && h->mb_y < h->mb_height)                        predict_field_decoding_flag(h);                }            }            //如果mb_y超过了mb的行数            if (eos || h->mb_y >= h->mb_height) {                tprintf(h->avctx, "slice end %d %d\n",                        get_bits_count(&h->gb), h->gb.size_in_bits);                er_add_slice(h, h->resync_mb_x, h->resync_mb_y, h->mb_x - 1,                             h->mb_y, ER_MB_END);                if (h->mb_x > lf_x_start)                    loop_filter(h, lf_x_start, h->mb_x);                return 0;            }        }    } else {     //CAVLC情况     //循环处理每个宏块        for (;;) {         //解码宏块的CAVLC            int ret = ff_h264_decode_mb_cavlc(h);            //解码宏块            if (ret >= 0)                ff_h264_hl_decode_mb(h);            // FIXME optimal? or let mb_decode decode 16x32 ?            if (ret >= 0 && FRAME_MBAFF(h)) {                h->mb_y++;                ret = ff_h264_decode_mb_cavlc(h);                if (ret >= 0)                    ff_h264_hl_decode_mb(h);                h->mb_y--;            }            if (ret < 0) {                av_log(h->avctx, AV_LOG_ERROR,                       "error while decoding MB %d %d\n", h->mb_x, h->mb_y);                er_add_slice(h, h->resync_mb_x, h->resync_mb_y, h->mb_x,                             h->mb_y, ER_MB_ERROR);                return ret;            }            if (++h->mb_x >= h->mb_width) {             //环路滤波                loop_filter(h, lf_x_start, h->mb_x);                h->mb_x = lf_x_start = 0;                decode_finish_row(h);                ++h->mb_y;                if (FIELD_OR_MBAFF_PICTURE(h)) {                    ++h->mb_y;                    if (FRAME_MBAFF(h) && h->mb_y < h->mb_height)                        predict_field_decoding_flag(h);                }                if (h->mb_y >= h->mb_height) {                    tprintf(h->avctx, "slice end %d %d\n",                            get_bits_count(&h->gb), h->gb.size_in_bits);                    if (   get_bits_left(&h->gb) == 0                        || get_bits_left(&h->gb) > 0 && !(h->avctx->err_recognition & AV_EF_AGGRESSIVE)) {                     //错误隐藏                        er_add_slice(h, h->resync_mb_x, h->resync_mb_y,                                     h->mb_x - 1, h->mb_y, ER_MB_END);                        return 0;                    } else {                        er_add_slice(h, h->resync_mb_x, h->resync_mb_y,                                     h->mb_x, h->mb_y, ER_MB_END);                        return AVERROR_INVALIDDATA;                    }                }            }            if (get_bits_left(&h->gb) <= 0 && h->mb_skip_run <= 0) {                tprintf(h->avctx, "slice end %d %d\n",                        get_bits_count(&h->gb), h->gb.size_in_bits);                if (get_bits_left(&h->gb) == 0) {                    er_add_slice(h, h->resync_mb_x, h->resync_mb_y,                                 h->mb_x - 1, h->mb_y, ER_MB_END);                    if (h->mb_x > lf_x_start)                        loop_filter(h, lf_x_start, h->mb_x);                    return 0;                } else {                    er_add_slice(h, h->resync_mb_x, h->resync_mb_y, h->mb_x,                                 h->mb_y, ER_MB_ERROR);                    return AVERROR_INVALIDDATA;                }            }        }    }}

重复记录一下decode_slice()的流程:
(1)判断H.264码流是CABAC编码还是CAVLC编码,进入不同的处理循环。
(2)如果是CABAC编码,首先调用ff_init_cabac_decoder()初始化CABAC解码器。然后进入一个循环,依次对每个宏块进行以下处理:
a)调用ff_h264_decode_mb_cabac()进行CABAC熵解码
b)调用ff_h264_hl_decode_mb()进行宏块解码
c)解码一行宏块之后调用loop_filter()进行环路滤波
d)此外还有可能调用er_add_slice()进行错误隐藏处理
(3)如果是CABAC编码,直接进入一个循环,依次对每个宏块进行以下处理:
a)调用ff_h264_decode_mb_cavlc()进行CAVLC熵解码
b)调用ff_h264_hl_decode_mb()进行宏块解码
c)解码一行宏块之后调用loop_filter()进行环路滤波
d)此外还有可能调用er_add_slice()进行错误隐藏处理
可以看出,宏块解码函数是ff_h264_hl_decode_mb()。下面看一下这个函数。



ff_h264_hl_decode_mb()

ff_h264_hl_decode_mb()完成了宏块解码的工作。“宏块解码”就是根据前一步骤“熵解码”得到的宏块类型、运动矢量、参考帧、DCT残差数据等信息恢复图像数据的过程。该函数的定义位于libavcodec\h264_mb.c,如下所示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值