1. 程式人生 > >FFMPEG.H264解碼解析-轉自雷神

FFMPEG.H264解碼解析-轉自雷神

本文分析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,如下所示。
  1. AVCodec ff_h264_decoder = {  
  2.     .name                  = "h264",  
  3.     .long_name             = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),  
  4.     .type                  = AVMEDIA_TYPE_VIDEO,  
  5.     .id                    = AV_CODEC_ID_H264,  
  6.     .priv_data_size        = sizeof(H264Context),  
  7.     .init                  = ff_h264_decode_init,  
  8.     .close                 = h264_decode_end,  
  9.     .decode                = h264_decode_frame,  
  10.     .capabilities          = /*CODEC_CAP_DRAW_HORIZ_BAND |*/ CODEC_CAP_DR1 |  
  11.                              CODEC_CAP_DELAY | CODEC_CAP_SLICE_THREADS |  
  12.                              CODEC_CAP_FRAME_THREADS,  
  13.     .flush                 = flush_dpb,  
  14.     .init_thread_copy      = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy),  
  15.     .update_thread_context = ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context),  
  16.     .profiles              = NULL_IF_CONFIG_SMALL(profiles),  
  17.     .priv_class            = &h264_class,  
  18. };  

從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,如下所示。
  1. //H.264解碼器初始化函式
  2. av_cold int ff_h264_decode_init(AVCodecContext *avctx)  
  3. {  
  4.     H264Context *h = avctx->priv_data;  
  5.     int i;  
  6.     int ret;  
  7.     h->avctx = avctx;  
  8.     //8顏色位深8bit
  9.     h->bit_depth_luma    = 8;  
  10.     //1代表是YUV420P
  11.     h->chroma_format_idc = 1;  
  12.     h->avctx->bits_per_raw_sample = 8;  
  13.     h->cur_chroma_format_idc = 1;  
  14.     //初始化DSP相關的彙編函式。包含了IDCT、環路濾波函式等
  15.     ff_h264dsp_init(&h->h264dsp, 8, 1);  
  16.     av_assert0(h->sps.bit_depth_chroma == 0);  
  17.     ff_h264chroma_init(&h->h264chroma, h->sps.bit_depth_chroma);  
  18.     //初始化四分之一畫素運動補償相關的彙編函式
  19.     ff_h264qpel_init(&h->h264qpel, 8);  
  20.     //初始化幀內預測相關的彙編函式
  21.     ff_h264_pred_init(&h->hpc, h->avctx->codec_id, 8, 1);  
  22.     h->dequant_coeff_pps = -1;  
  23.     h->current_sps_id = -1;  
  24.     /* needed so that IDCT permutation is known early */
  25.     if (CONFIG_ERROR_RESILIENCE)  
  26.         ff_me_cmp_init(&h->mecc, h->avctx);  
  27.     ff_videodsp_init(&h->vdsp, 8);  
  28.     memset(h->pps.scaling_matrix4, 16, 6 * 16 * sizeof(uint8_t));  
  29.     memset(h->pps.scaling_matrix8, 16, 2 * 64 * sizeof(uint8_t));  
  30.     h->picture_structure   = PICT_FRAME;  
  31.     h->slice_context_count = 1;  
  32.     h->workaround_bugs     = avctx->workaround_bugs;  
  33.     h->flags               = avctx->flags;  
  34.     /* set defaults */
  35.     // s->decode_mb = ff_h263_decode_mb;
  36.     if (!avctx->has_b_frames)  
  37.         h->low_delay = 1;  
  38.     avctx->chroma_sample_location = AVCHROMA_LOC_LEFT;  
  39.     //初始化熵解碼器
  40.     //CAVLC
  41.     ff_h264_decode_init_vlc();  
  42.     //CABAC
  43.     ff_init_cabac_states();  
  44.     //8-bit H264取0, 大於 8-bit H264取1
  45.     h->pixel_shift        = 0;  
  46.     h->sps.bit_depth_luma = avctx->bits_per_raw_sample = 8;  
  47.     h->thread_context[0] = h;  
  48.     h->outputed_poc      = h->next_outputed_poc = INT_MIN;  
  49.     for (i = 0; i < MAX_DELAYED_PIC_COUNT; i++)  
  50.         h->last_pocs[i] = INT_MIN;  
  51.     h->prev_poc_msb = 1 << 16;  
  52.     h->prev_frame_num = -1;  
  53.     h->x264_build   = -1;  
  54.     h->sei_fpa.frame_packing_arrangement_cancel_flag = -1;  
  55.     ff_h264_reset_sei(h);  
  56.     if (avctx->codec_id == AV_CODEC_ID_H264) {  
  57.         if (avctx->ticks_per_frame == 1) {  
  58.             if(h->avctx->time_base.den < INT_MAX/2) {  
  59.                 h->avctx->time_base.den *= 2;  
  60.             } else
  61.                 h->avctx->time_base.num /= 2;  
  62.         }  
  63.         avctx->ticks_per_frame = 2;  
  64.     }  
  65.     //AVCodecContext中是否包含extradata?包含的話,則解析之
  66.     if (avctx->extradata_size > 0 && avctx->extradata) {  
  67.         ret = ff_h264_decode_extradata(h, avctx->extradata, avctx->extradata_size);  
  68.         if (ret < 0) {  
  69.             ff_h264_free_context(h);  
  70.             return ret;  
  71.         }  
  72.     }  
  73.     if (h->sps.bitstream_restriction_flag &&  
  74.         h->avctx->has_b_frames < h->sps.num_reorder_frames) {  
  75.         h->avctx->has_b_frames = h->sps.num_reorder_frames;  
  76.         h->low_delay           = 0;  
  77.     }  
  78.     avctx->internal->allocate_progress = 1;  
  79.     ff_h264_flush_change(h);  
  80.     return 0;  
  81. }  

從函式定義中可以看出,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,如下所示。
  1. /** 
  2.  * Set the intra prediction function pointers. 
  3.  */
  4. //初始化幀內預測相關的彙編函式
  5. av_cold void ff_h264_pred_init(H264PredContext *h, int codec_id,  
  6.                                constint bit_depth,  
  7.                                int chroma_format_idc)  
  8. {  
  9. #undef FUNC
  10. #undef FUNCC
  11. #define FUNC(a, depth) a ## _ ## depth
  12. #define FUNCC(a, depth) a ## _ ## depth ## _c