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

本文主要分析x264编码器的x264_encoder_open()函数,该函数初始化编码所需变量,包括x264_sps_init(), x264_pps_init()和x264_predict_16x16_init()等。x264_predict_16x16_init()涉及帧内预测方法,如x264_predict_16x16_v_c()等,用于根据边界像素值推算宏块内部像素值。" 111887220,10535627,Java连接MQTT服务TCP实战教程,"['java解析mqtt', 'MQTT连接', 'TCP通信']
摘要由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)部分

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


 本文分析x264编码器主干部分的源代码。“主干部分”指的就是libx264中最核心的接口函数——x264_encoder_encode(),以及相关的几个接口函数x264_encoder_open(),x264_encoder_headers(),和x264_encoder_close()。这一部分源代码比较复杂,现在看了半天依然感觉很多地方不太清晰,暂且把已经理解的地方整理出来,以后再慢慢补充还不太清晰的地方。由于主干部分内容比较多,因此打算分成两篇文章来记录:第一篇文章记录x264_encoder_open(),x264_encoder_headers(),和x264_encoder_close()这三个函数,第二篇文章记录x264_encoder_encode()函数。



函数调用关系图

X264编码器主干部分的源代码在整个x264中的位置如下图所示。

X264编码器主干部分的函数调用关系如下图所示。

 

 从图中可以看出,x264主干部分最复杂的函数就是x264_encoder_encode(),该函数完成了编码一帧YUV为H.264码流的工作。与之配合的还有打开编码器的函数x264_encoder_open(),关闭编码器的函数x264_encoder_close(),以及输出SPS/PPS/SEI这样的头信息的x264_encoder_headers()。

x264_encoder_open()用于打开编码器,其中初始化了libx264编码所需要的各种变量。它调用了下面的函数:
x264_validate_parameters():检查输入参数(例如输入图像的宽高是否为正数)。
x264_predict_16x16_init():初始化Intra16x16帧内预测汇编函数。
x264_predict_4x4_init():初始化Intra4x4帧内预测汇编函数。
x264_pixel_init():初始化像素值计算相关的汇编函数(包括SAD、SATD、SSD等)。
x264_dct_init():初始化DCT变换和DCT反变换相关的汇编函数。
x264_mc_init():初始化运动补偿相关的汇编函数。
x264_quant_init():初始化量化和反量化相关的汇编函数。
x264_deblock_init():初始化去块效应滤波器相关的汇编函数。
x264_lookahead_init():初始化Lookahead相关的变量。
x264_ratecontrol_new():初始化码率控制相关的变量。

x264_encoder_headers()输出SPS/PPS/SEI这些H.264码流的头信息。它调用了下面的函数:
x264_sps_write():输出SPS
x264_pps_write():输出PPS
x264_sei_version_write():输出SEI

x264_encoder_encode()编码一帧YUV为H.264码流。它调用了下面的函数:
x264_frame_pop_unused():获取1个x264_frame_t类型结构体fenc。如果frames.unused[]队列不为空,就调用x264_frame_pop()从unused[]队列取1个现成的;否则就调用x264_frame_new()创建一个新的。
x264_frame_copy_picture():将输入的图像数据拷贝至fenc。
x264_lookahead_put_frame():将fenc放入lookahead.next.list[]队列,等待确定帧类型。
x264_lookahead_get_frames():通过lookahead分析帧类型。该函数调用了x264_slicetype_decide(),x264_slicetype_analyse()和x264_slicetype_frame_cost()等函数。经过一些列分析之后,最终确定了帧类型信息,并且将帧放入frames.current[]队列。
x264_frame_shift():从frames.current[]队列取出1帧用于编码。
x264_reference_update():更新参考帧列表。
x264_reference_reset():如果为IDR帧,调用该函数清空参考帧列表。
x264_reference_hierarchy_reset():如果是I(非IDR帧)、P帧、B帧(可做为参考帧),调用该函数。
x264_reference_build_list():创建参考帧列表list0和list1。
x264_ratecontrol_start():开启码率控制。
x264_slice_init():创建 Slice Header。
x264_slices_write():编码数据(最关键的步骤)。其中调用了x264_slice_write()完成了编码的工作(注意“x264_slices_write()”和“x264_slice_write()”名字差了一个“s”)。
x264_encoder_frame_end():编码结束后做一些后续处理,例如记录一些统计信息。其中调用了x264_frame_push_unused()将fenc重新放回frames.unused[]队列,并且调用x264_ratecontrol_end()关闭码率控制。

x264_encoder_close()用于关闭解码器,同时输出一些统计信息。它调用了下面的函数:
x264_lookahead_delete():释放Lookahead相关的变量。
x264_ratecontrol_summary():汇总码率控制信息。

x264_ratecontrol_delete():关闭码率控制。


本文将会记录x264_encoder_open(),x264_encoder_headers(),和x264_encoder_close()这三个函数的源代码。下一篇文章记录x264_encoder_encode()函数。



x264_encoder_open()

x264_encoder_open()是一个libx264的API。该函数用于打开编码器,其中初始化了libx264编码所需要的各种变量。该函数的声明如下所示。

/* x264_encoder_open: *      create a new encoder handler, all parameters from x264_param_t are copied */x264_t *x264_encoder_open( x264_param_t * );
x264_encoder_open()的定义位于encoder\encoder.c,如下所示。
/**************************************************************************** * x264_encoder_open:* 注释和处理:雷霄骅* http://blog.csdn.net/leixiaohua1020* leixiaohua1020@126.com ****************************************************************************///打开编码器x264_t *x264_encoder_open( x264_param_t *param ){    x264_t *h;    char buf[1000], *p;    int qp, i_slicetype_length;    CHECKED_MALLOCZERO( h, sizeof(x264_t) );    /* Create a copy of param */    //将参数拷贝进来    memcpy( &h->param, param, sizeof(x264_param_t) );    if( param->param_free )        param->param_free( param );    if( x264_threading_init() )    {        x264_log( h, X264_LOG_ERROR, "unable to initialize threading\n" );        goto fail;    }    //检查输入参数    if( x264_validate_parameters( h, 1 ) < 0 )        goto fail;    if( h->param.psz_cqm_file )        if( x264_cqm_parse_file( h, h->param.psz_cqm_file ) < 0 )            goto fail;    if( h->param.rc.psz_stat_out )        h->param.rc.psz_stat_out = strdup( h->param.rc.psz_stat_out );    if( h->param.rc.psz_stat_in )        h->param.rc.psz_stat_in = strdup( h->param.rc.psz_stat_in );    x264_reduce_fraction( &h->param.i_fps_num, &h->param.i_fps_den );    x264_reduce_fraction( &h->param.i_timebase_num, &h->param.i_timebase_den );    /* Init x264_t */    h->i_frame = -1;    h->i_frame_num = 0;    if( h->param.i_avcintra_class )        h->i_idr_pic_id = 5;    else        h->i_idr_pic_id = 0;    if( (uint64_t)h->param.i_timebase_den * 2 > UINT32_MAX )    {        x264_log( h, X264_LOG_ERROR, "Effective timebase denominator %u exceeds H.264 maximum\n", h->param.i_timebase_den );        goto fail;    }    x264_set_aspect_ratio( h, &h->param, 1 );    //初始化SPS和PPS    x264_sps_init( h->sps, h->param.i_sps_id, &h->param );    x264_pps_init( h->pps, h->param.i_sps_id, &h->param, h->sps );    //检查级Level-通过宏块个数等等    x264_validate_levels( h, 1 );    h->chroma_qp_table = i_chroma_qp_table + 12 + h->pps->i_chroma_qp_index_offset;    if( x264_cqm_init( h ) < 0 )        goto fail;    //各种赋值    h->mb.i_mb_width = h->sps->i_mb_width;    h->mb.i_mb_height = h->sps->i_mb_height;    h->mb.i_mb_count = h->mb.i_mb_width * h->mb.i_mb_height;    h->mb.chroma_h_shift = CHROMA_FORMAT == CHROMA_420 || CHROMA_FORMAT == CHROMA_422;    h->mb.chroma_v_shift = CHROMA_FORMAT == CHROMA_420;    /* Adaptive MBAFF and subme 0 are not supported as we require halving motion     * vectors during prediction, resulting in hpel mvs.     * The chosen solution is to make MBAFF non-adaptive in this case. */    h->mb.b_adaptive_mbaff = PARAM_INTERLACED && h->param.analyse.i_subpel_refine;    /* Init frames. */    if( h->param.i_bframe_adaptive == X264_B_ADAPT_TRELLIS && !h->param.rc.b_stat_read )        h->frames.i_delay = X264_MAX(h->param.i_bframe,3)*4;    else        h->frames.i_delay = h->param.i_bframe;    if( h->param.rc.b_mb_tree || h->param.rc.i_vbv_buffer_size )        h->frames.i_delay = X264_MAX( h->frames.i_delay, h->param.rc.i_lookahead );    i_slicetype_length = h->frames.i_delay;    h->frames.i_delay += h->i_thread_frames - 1;    h->frames.i_delay += h->param.i_sync_lookahead;    h->frames.i_delay += h->param.b_vfr_input;    h->frames.i_bframe_delay = h->param.i_bframe ? (h->param.i_bframe_pyramid ? 2 : 1) : 0;    h->frames.i_max_ref0 = h->param.i_frame_reference;    h->frames.i_max_ref1 = X264_MIN( h->sps->vui.i_num_reorder_frames, h->param.i_frame_reference );    h->frames.i_max_dpb  = h->sps->vui.i_max_dec_frame_buffering;    h->frames.b_have_lowres = !h->param.rc.b_stat_read        && ( h->param.rc.i_rc_method == X264_RC_ABR          || h->param.rc.i_rc_method == X264_RC_CRF          || h->param.i_bframe_adaptive          || h->param.i_scenecut_threshold          || h->param.rc.b_mb_tree          || h->param.analyse.i_weighted_pred );    h->frames.b_have_lowres |= h->param.rc.b_stat_read && h->param.rc.i_vbv_buffer_size > 0;    h->frames.b_have_sub8x8_esa = !!(h->param.analyse.inter & X264_ANALYSE_PSUB8x8);    h->frames.i_last_idr =    h->frames.i_last_keyframe = - h->param.i_keyint_max;    h->frames.i_input    = 0;    h->frames.i_largest_pts = h->frames.i_second_largest_pts = -1;    h->frames.i_poc_last_open_gop = -1;    //CHECKED_MALLOCZERO(var, size)    //调用malloc()分配内存,然后调用memset()置零    CHECKED_MALLOCZERO( h->frames.unused[0], (h->frames.i_delay + 3) * sizeof(x264_frame_t *) );    /* Allocate room for max refs plus a few extra just in case. */    CHECKED_MALLOCZERO( h->frames.unused[1], (h->i_thread_frames + X264_REF_MAX + 4) * sizeof(x264_frame_t *) );    CHECKED_MALLOCZERO( h->frames.current, (h->param.i_sync_lookahead + h->param.i_bframe                        + h->i_thread_frames + 3) * sizeof(x264_frame_t *) );    if( h->param.analyse.i_weighted_pred > 0 )        CHECKED_MALLOCZERO( h->frames.blank_unused, h->i_thread_frames * 4 * sizeof(x264_frame_t *) );    h->i_ref[0] = h->i_ref[1] = 0;    h->i_cpb_delay = h->i_coded_fields = h->i_disp_fields = 0;    h->i_prev_duration = ((uint64_t)h->param.i_fps_den * h->sps->vui.i_time_scale) / ((uint64_t)h->param.i_fps_num * h->sps->vui.i_num_units_in_tick);    h->i_disp_fields_last_frame = -1;    //RDO初始化    x264_rdo_init();    /* init CPU functions */    //初始化包含汇编优化的函数    //帧内预测    x264_predict_16x16_init( h->param.cpu, h->predict_16x16 );    x264_predict_8x8c_init( h->param.cpu, h->predict_8x8c );    x264_predict_8x16c_init( h->param.cpu, h->predict_8x16c );    x264_predict_8x8_init( h->param.cpu, h->predict_8x8, &h->predict_8x8_filter );    x264_predict_4x4_init( h->param.cpu, h->predict_4x4 );    //SAD等和像素计算有关的函数    x264_pixel_init( h->param.cpu, &h->pixf );    //DCT    x264_dct_init( h->param.cpu, &h->dctf );    //“之”字扫描    x264_zigzag_init( h->param.cpu, &h->zigzagf_progressive, &h->zigzagf_interlaced );    memcpy( &h->zigzagf, PARAM_INTERLACED ? &h->zigzagf_interlaced : &h->zigzagf_progressive, sizeof(h->zigzagf) );    //运动补偿    x264_mc_init( h->param.cpu, &h->mc, h->param.b_cpu_independent );    //量化    x264_quant_init( h, h->param.cpu, &h->quantf );    //去块效应滤波    x264_deblock_init( h->par
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值