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

               

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

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解码器的主干部分。“主干部分”是相对于“熵解码”、“宏块解码”、“环路滤波”这些细节部分而言的。它包含了H.264解码器直到decode_slice()前面的函数调用关系(decode_slice()后面就是H.264解码器的细节部分,主要包含了“熵解码”、“宏块解码”、“环路滤波”3个部分)。


函数调用关系图

解码器主干部分的源代码在整个H.264解码器中的位置如下图所示。


单击查看更清晰的图片

解码器主干部分的源代码的调用关系如下图所示。


单击查看更清晰的图片

从图中可以看出,H.264解码器(Decoder)在初始化的时候调用了ff_h264_decode_init(),ff_h264_decode_init()又调用了下面几个函数进行解码器汇编函数的初始化工作(仅举了几个例子):
ff_h264dsp_init():初始化DSP相关的汇编函数。包含了IDCT、环路滤波函数等。
ff_h264qpel_init():初始化四分之一像素运动补偿相关的汇编函数。
ff_h264_pred_init():初始化帧内预测相关的汇编函数。
H.264解码器在关闭的时候调用了h264_decode_end(),h264_decode_end()又调用了ff_h264_remove_all_refs(),ff_h264_free_context()等几个函数进行清理工作。
H.264解码器在解码图像帧的时候调用了h264_decode_frame(),h264_decode_frame()调用了decode_nal_units(),decode_nal_units()调用了两类函数——解析函数和解码函数,如下所示。
(1)解析函数(获取信息):
ff_h264_decode_nal():解析NALU Header。
ff_h264_decode_seq_parameter_set():解析SPS。
ff_h264_decode_picture_parameter_set():解析PPS。
ff_h264_decode_sei():解析SEI。
ff_h264_decode_slice_header():解析Slice Header。
(2)解码函数(解码获得图像):
ff_h264_execute_decode_slices():解码Slice。
其中ff_h264_execute_decode_slices()调用了decode_slice(),而decode_slice()中调用了解码器中细节处理的函数(暂不详细分析):
ff_h264_decode_mb_cabac():CABAC熵解码函数。
ff_h264_decode_mb_cavlc():CAVLC熵解码函数。
ff_h264_hl_decode_mb():宏块解码函数。
loop_filter():环路滤波函数。
本文针对H.264解码器decode_slice()前面的函数调用关系进行分析。

ff_h264_decoder

ff_h264_decoder是FFmpeg的H.264解码器对应的AVCodec结构体。它的定义位于libavcodec\h264.c,如下所示。
AVCodec ff_h264_decoder = {    .name                  = "h264",    .long_name             = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),    .type                  = AVMEDIA_TYPE_VIDEO,    .id                    = AV_CODEC_ID_H264,    .priv_data_size        = sizeof(H264Context),    .init                  = ff_h264_decode_init,    .close                 = h264_decode_end,    .decode                = h264_decode_frame,    .capabilities          = /*CODEC_CAP_DRAW_HORIZ_BAND |*/ CODEC_CAP_DR1 |                             CODEC_CAP_DELAY | CODEC_CAP_SLICE_THREADS |                             CODEC_CAP_FRAME_THREADS,    .flush                 = flush_dpb,    .init_thread_copy      = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy),    .update_thread_context = ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context),    .profiles              = NULL_IF_CONFIG_SMALL(profiles),    .priv_class            = &h264_class,};

从ff_h264_decoder的定义可以看出:解码器初始化的函数指针init()指向ff_h264_decode_init()函数,解码的函数指针decode()指向h264_decode_frame()函数,解码器关闭的函数指针close()指向h264_decode_end()函数。

ff_h264_decode_init()

ff_h264_decode_init()用于FFmpeg H.264解码器的初始化。该函数的定义位于libavcodec\h264.c,如下所示。
//H.264解码器初始化函数av_cold int ff_h264_decode_init(AVCodecContext *avctx){    H264Context *h = avctx->priv_data;    int i;    int ret;    h->avctx = avctx;    //8颜色位深8bit    h->bit_depth_luma    = 8;    //1代表是YUV420P    h->chroma_format_idc = 1;    h->avctx->bits_per_raw_sample = 8;    h->cur_chroma_format_idc = 1;    //初始化DSP相关的汇编函数。包含了IDCT、环路滤波函数等    ff_h264dsp_init(&h->h264dsp, 8, 1);    av_assert0(h->sps.bit_depth_chroma == 0);    ff_h264chroma_init(&h->h264chroma, h->sps.bit_depth_chroma);    //初始化四分之一像素运动补偿相关的汇编函数    ff_h264qpel_init(&h->h264qpel, 8);    //初始化帧内预测相关的汇编函数    ff_h264_pred_init(&h->hpc, h->avctx->codec_id, 8, 1);    h->dequant_coeff_pps = -1;    h->current_sps_id = -1;    /* needed so that IDCT permutation is known early */    if (CONFIG_ERROR_RESILIENCE)        ff_me_cmp_init(&h->mecc, h->avctx);    ff_videodsp_init(&h->vdsp, 8);    memset(h->pps.scaling_matrix4, 16, 6 * 16 * sizeof(uint8_t));    memset(h->pps.scaling_matrix8, 16, 2 * 64 * sizeof(uint8_t));    h->picture_structure   = PICT_FRAME;    h->slice_context_count = 1;    h->workaround_bugs     = avctx->workaround_bugs;    h->flags               = avctx->flags;    /* set defaults */    // s->decode_mb = ff_h263_decode_mb;    if (!avctx->has_b_frames)        h->low_delay = 1;    avctx->chroma_sample_location = AVCHROMA_LOC_LEFT;    //初始化熵解码器    //CAVLC    ff_h264_decode_init_vlc();    //CABAC    ff_init_cabac_states();    //8-bit H264取0, 大于 8-bit H264取1    h->pixel_shift        = 0;    h->sps.bit_depth_luma = avctx->bits_per_raw_sample = 8;    h->thread_context[0] = h;    h->outputed_poc      = h->next_outputed_poc = INT_MIN;    for (i = 0; i < MAX_DELAYED_PIC_COUNT; i++)        h->last_pocs[i] = INT_MIN;    h->prev_poc_msb = 1 << 16;    h->prev_frame_num = -1;    h->x264_build   = -1;    h->sei_fpa.frame_packing_arrangement_cancel_flag = -1;    ff_h264_reset_sei(h);    if (avctx->codec_id == AV_CODEC_ID_H264) {        if (avctx->ticks_per_frame == 1) {            if(h->avctx->time_base.den < INT_MAX/2) {                h->avctx->time_base.den *= 2;            } else                h->avctx->time_base.num /= 2;        }        avctx->ticks_per_frame = 2;    }    //AVCodecContext中是否包含extradata?包含的话,则解析之    if (avctx->extradata_size > 0 && avctx->extradata) {        ret = ff_h264_decode_extradata(h, avctx->extradata, avctx->extradata_size);        if (ret < 0) {            ff_h264_free_context(h);            return ret;        }    }    if (h->sps.bitstream_restriction_flag &&        h->avctx->has_b_frames < h->sps.num_reorder_frames) {        h->avctx->has_b_frames = h->sps.num_reorder_frames;        h->low_delay           = 0;    }    avctx->internal->allocate_progress = 1;    ff_h264_flush_change(h);    return 0;}

从函数定义中可以看出,ff_h264_decode_init()一方面给H.264 解码器中一些变量(例如bit_depth_luma、chroma_format_idc等)设定了初始值,另一方面调用了一系列汇编函数的初始化函数(初始化函数的具体内容在后续文章中完成)。初始化汇编函数的的步骤是:首先将C语言版本函数赋值给相应模块的函数指针;然后检测平台的特性,如果不支持汇编优化(ARM、X86等),则不再做任何处理,如果支持汇编优化,则将相应的汇编优化函数赋值给相应模块的函数指针(替换掉C语言版本的效率较低的函数)。下面几个函数初始化了几个不同模块的汇编优化函数:

ff_h264dsp_init():初始化DSP相关的汇编函数。包含了IDCT、环路滤波函数等。
ff_h264qpel_init():初始化四分之一像素运动补偿相关的汇编函数。
ff_h264_pred_init():初始化帧内预测相关的汇编函数。

可以举例看一下个ff_h264_pred_init()的代码。


ff_h264_pred_init()

函数用于初始化帧内预测相关的汇编函数,定位于libavcodec\h264pred.c,如下所示。
/** * Set the intra prediction function pointers. *///初始化帧内预测相关的汇编函数av_cold void ff_h264_pred_init(H264PredContext *h, int codec_id,                               const int bit_depth,                               int chroma_format_idc){
   #undef FUNC#undef FUNCC#define FUNC(a, depth) a ## _ ## depth#define FUNCC(a, depth) a ## _ ## depth ## _c#define FUNCD(a) a ## _c//好长的宏定义...(这种很长的宏定义在H.264解码器中似乎很普遍!)//该宏用于给帧内预测模块的函数指针赋值//注意参数为颜色位深度#define H264_PRED(depth) \    if(codec_id != AV_CODEC_ID_RV40){\        if (codec_id == AV_CODEC_ID_VP7 || codec_id == AV_CODEC_ID_VP8) {\            h->pred4x4[VERT_PRED       ]= FUNCD(pred4x4_vertical_vp8);\            h->pred4x4[HOR_PRED        ]= FUNCD(pred4x4_horizontal_vp8);\        } else {\            h->pred4x4[VERT_PRED       ]= FUNCC(pred4x4_vertical          , depth);\            h->pred4x4[HOR_PRED        ]= FUNCC(pred4x4_horizontal        , depth);\        }\        h->pred4x4[DC_PRED             ]= FUNCC(pred4x4_dc                , depth);\        if(codec_id == AV_CODEC_ID_SVQ3)\            h->pred4x4[DIAG_DOWN_LEFT_PRED ]= FUNCD(pred4x4_down_left_svq3);\        else\            h->pred4x4[DIAG_DOWN_LEFT_PRED ]= FUNCC(pred4x4_down_left     , depth);\        h->pred4x4[DIAG_DOWN_RIGHT_PRED]= FUNCC(pred4x4_down_right        , depth);\        h->pred4x4[VERT_RIGHT_PRED     ]= FUNCC(pred4x4_vertical_right    , depth);\        h->pred4x4[HOR_DOWN_PRED       ]= FUNCC(pred4x4_horizontal_down   , depth);\        if (codec_id == AV_CODEC_ID_VP7 || codec_id == AV_CODEC_ID_VP8) {\            h->pred4x4[VERT_LEFT_PRED  ]= FUNCD(pred4x4_vertical_left_vp8);\        } else\            h->pred4x4[VERT_LEFT_PRED  ]= FUNCC(pred4x4_vertical_left     , depth);\        h->pred4x4[HOR_UP_PRED         ]= FUNCC(pred4x4_horizontal_up     , depth);\        if (codec_id != AV_CODEC_ID_VP7 && codec_id != AV_CODEC_ID_VP8) {\            h->pred4x4[LEFT_DC_PRED    ]= FUNCC(pred4x4_left_dc           , depth);\            h->pred4x4[TOP_DC_PRED     ]= FUNCC(pred4x4_top_dc            , depth);\        } else {\            h->pred4x4[TM_VP8_PRED     ]= FUNCD(pred4x4_tm_vp8);\            h->pred4x4[DC_127_PRED     ]= FUNCC(pred4x4_127_dc            , depth);\            h->pred4x4[DC_129_PRED     ]= FUNCC(pred4x4_129_dc            , depth);\            h->pred4x4[VERT_VP8_PRED   ]= FUNCC(pred4x4_vertical          , depth);\            h->pred4x4[HOR_VP8_PRED    ]= FUNCC(pred4x4_horizontal        , depth);\        }\        if (codec_id != AV_CODEC_ID_VP8)\            h->pred4x4[DC_128_PRED     ]= FUNCC(pred4x4_128_dc            , depth);\    }else{\        h->pred4x4[VERT_PRED           ]= FUNCC(pred4x4_vertical          , depth);\        h->pred4x4[HOR_PRED            ]= FUNCC(pred4x4_horizontal        , depth);\        h->pred4x4[DC_PRED             ]= FUNCC(pred4x4_dc                , depth);\        h->pred4x4[DIAG_DOWN_LEFT_PRED ]= FUNCD(pred4x4_down_left_rv40);\        h->pred4x4[DIAG_DOWN_RIGHT_PRED]= FUNCC(pred4x4_down_right        , depth);\        h->pred4x4[VERT_RIGHT_PRED     ]= FUNCC(pred4x4_vertical_right    , depth);\        h->pred4x4[HOR_DOWN_PRED       ]= FUNCC(pred4x4_horizontal_down   , depth);\        h->pred4x4[VERT_LEFT_PRED      ]= FUNCD(pred4x4_vertical_left_rv40);\        h->pred4x4[HOR_UP_PRED         ]= FUNCD(pred4x4_horizontal_up_rv40);\        h->pred4x4[LEFT_DC_PRED        ]= FUNCC(pred4x4_left_dc           , depth);\        h->pred4x4[TOP_DC_PRED         ]= FUNCC(pred4x4_top_dc            , depth);\        h->pred4x4[DC_128_PRED         ]= FUNCC(pred4x4_128_dc            , depth);\        h->pred4x4[DIAG_DOWN_LEFT_PRED_RV40_NODOWN]= FUNCD(pred4x4_down_left_rv40_nodown);\        h->pred4x4[HOR_UP_PRED_RV40_NODOWN]= FUNCD(pred4x4_horizontal_up_rv40_nodown);\        h->pred4x4[VERT_LEFT_PRED_RV40_NODOWN]= FUNCD(pred4x4_vertical_left_rv40_nodown);\    }\\    h->pred8x8l[VERT_PRED           ]= FUNCC(pred8x8l_vertical            , depth);\    h->pred8x8l[HOR_PRED            ]= FUNCC(pred8x8l_horizontal          , depth);\    h->pred8x8l[DC_PRED             ]= FUNCC(pred8x8l_dc                  , depth);\    h->pred8x8l[DIAG_DOWN_LEFT_PRED ]= FUNCC(pred8x8l_down_le
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值