x264代码阅读笔记——非常详细

来源:互联网 发布:win7 软件 乱码 编辑:程序博客网 时间:2024/05/16 15:01

(一)common/set.h的注释

#ifndef _SET_H
#define _SET_H 1

enum profile_e
{
    PROFILE_BASELINE = 66,
    PROFILE_MAIN     = 77,
    PROFILE_EXTENTED = 88,
    PROFILE_HIGH    = 100,
    PROFILE_HIGH10  = 110,
    PROFILE_HIGH422 = 122,
    PROFILE_HIGH444 = 144
};

enum cqm4_e
{
    CQM_4IY = 0,
    CQM_4PY = 1,
    CQM_4IC = 2,
    CQM_4PC = 3
};
enum cqm8_e
{
    CQM_8IY = 0,
    CQM_8PY = 1
};

 


typedef struct//profile:外形,评测,概况,剖面
{
    int i_id; //?????

    int i_profile_idc; //指明所用profile
    int i_level_idc; //指明所用level

    int b_constraint_set0;//[毕厚杰书 160 页,constraint_set0_flag],等于1时,表时必须遵从A.2.1 ...所指明的所有制约条件,等于0时表示不必遵从所有条件
    int b_constraint_set1;//[毕厚杰书 160 页,constraint_set0_flag],等于1时,表时必须遵从A.2.2 ...所指明的所有制约条件,等于0时表示不必遵从所有条件
    int b_constraint_set2;//[毕厚杰书 160 页,constraint_set0_flag],等于1时,表时必须遵从A.2.3 ...所指明的所有制约条件,等于0时表示不必遵从所有条件//constraint:约束;限制;强迫;强制
       //注意:当constraint_set0_flag、中有两个以上等于1时,A.2中的所有制约条件都要被遵从
    int i_log2_max_frame_num;//表示图像解码顺序的最大取值
       //[毕 厚杰书 160 页,log2_max_frame_num_minus4],这个句法元素主要是为读取另一个句法元素frame_num服务的,frame_num是最 重要的句法元素之一,它标识所属图像的解码顺序。可以在句法表中看到,frame_num的解码函数是ue(v),函数中的v在这里指 定,v=log2_max_frame_num_minus + 4
    int i_poc_type;   //[毕厚杰书 160 页,pic_order_cnt_type],指明了POC(Picture Order Count)的编码方法、POC标识图像的播放顺序。由于H.264使用了B帧预测,使得图像的解码顺序不一定等于播放顺序,但它们之间存在一定的映射关 系。POC可以由frame_num通过映射关系计算得来,也可以索性由编码器显式地传送。H.264一共定义了3种POC的编码方法,这个句法元素就是 用来通知解码器该用哪种方法来计算POC。pic_order_cnt_tye的取值范围是[0,2]...
   
    int i_log2_max_poc_lsb; //[毕厚杰书 160 页,log2_max_pic_order_cnt_lsb_minus4],指明了变量MaxPicOrderCntLsb的值。
   
    int b_delta_pic_order_always_zero;//[毕厚杰书 161 页,delta_pic_order_always_zero_flag],其值等于1时句法元素delta_pic_order_cnt[0]和 delta_pic_order_cnt[1]不再片头出现,且他们的默认值都为0。为0时上述则出现。
    int i_offset_for_non_ref_pic;//[毕厚杰书 161 页,offset_for_non_ref_pic],用来计算非参考帧或场的picture order count ,其值应在[-2e31,2e31-1]
    int i_offset_for_top_to_bottom_field;//[毕厚杰书 161 页,offset_for_top_to_bottom_field],用来计算帧的底场的picture order count 其值应在[-2e31,2e31-1]
    int i_num_ref_frames_in_poc_cycle;//[毕厚杰书 161 页,num_ref_frames_in_pic_order_cnt_cycle],用来解码picture order count 取值应在[0,255]之间
    int i_offset_for_ref_frame[256];//[毕厚杰书 161 页,offset_for_ref_frame[i]],当picture order count type=1时用来解码poc,这句语法对循环num_ref_frames_in_poc_cycle中的每一个元素指定了一个偏移

    int i_num_ref_frames;//[毕厚杰书 161 页,num_ref_frames],指定参考帧队列的最大长度,h264规定最多可为16个参考帧,本句法元素的值最大为16。值得注意的是这个长度以 帧为单位,如果在场模式下,应该相应地扩展一倍。
    int b_gaps_in_frame_num_value_allowed;//[毕厚杰书 161 页,gaps_in_frame_num_value_allowed_flag],为1时表示允许句法frame_num可以不连续。当传输信道堵塞严 重时,编码器来不及将编码后的图像全部发出,这时允许丢弃若干帧图像。在正常情况下每一帧图像都有依次连续的frame_num值,解码器检查到如果 frame_num不连续,便能确定有图像被编码器丢弃。这时,解码器必须启动错误掩藏机制来近似地恢复这些图像,因为这些图像有可能被后续图像用作参考 帧。
         //当这个句法元素=0时,表示不允许frame_num不连续,即编码器在任何情况下都不能丢弃图像。这时,H.264 允许解码器可以不去检查frame_num的连续性以减少计算量。这种情况下如果依然发生frame_num不连续,表示在传输中发生丢包,解码器会通过 其他机制检测到丢包的发生,然后启动错误掩藏的恢复图像。
    int i_mb_width;//[毕厚杰书 161 页,pic_width_in_mbs_minus1],本句法元素加1后指明图像宽度,以宏块为单位。
    int i_mb_height;//[毕厚杰书 161 页,pic_height_in_map_units_minus1],本句法元素加1后指明图像的高度。
    int b_frame_mbs_only;//[毕厚杰书 161 页,frame_mbs_only_flag],本句法元素=1时,表示本序列中所有图像的编码模式都是帧,没有其他编码模式存在;本句法元素=0时,表 示本序列中图像的编码模式可能是帧,也可能是场或帧场自适应,某个图像具体是哪一种要由其他句法元素决定。
    int b_mb_adaptive_frame_field;//[毕厚杰书 161 页,mb_adaptive_frame_field_flag],指明本序列是否属于帧场自适应模式。=1时,表明在本序列
    int b_direct8x8_inference;//指明b片的直接和skip模式下运动矢量的预测方法

    int b_crop;//crop:剪裁 [毕厚杰书 162 页,frame_cropping_flag],用于指明解码器是否要将图像裁剪后输出,如果是的话,后面紧跟着的四个句法元素分别指出左、右、上、下裁剪的宽度。
    struct
    {
        int i_left;  //[毕厚杰书 162 页,frame_crop_left_offset],左
        int i_right; //[毕厚杰书 162 页,frame_crop_left_offset],右
        int i_top;  //[毕厚杰书 162 页,frame_crop_left_offset],上
        int i_bottom; //[毕厚杰书 162 页,frame_crop_left_offset],下
    } crop;//图像剪裁后输出的参数;crop:剪裁,剪切,剪辑

    int b_vui; ////[毕厚杰书 162 页,vui_parameters_present_flag],指明vui子结构是否出现在码流中,vui的码流结构在...附录指明,用以表征视频格式等额外信息
    struct
    {
        int b_aspect_ratio_info_present;
        int i_sar_width;
        int i_sar_height;
       
        int b_overscan_info_present;
        int b_overscan_info;

        int b_signal_type_present;
        int i_vidformat;
        int b_fullrange;
        int b_color_description_present;
        int i_colorprim;
        int i_transfer;
        int i_colmatrix;

        int b_chroma_loc_info_present;
        int i_chroma_loc_top;
        int i_chroma_loc_bottom;

        int b_timing_info_present;
        int i_num_units_in_tick;
        int i_time_scale;
        int b_fixed_frame_rate;

        int b_bitstream_restriction;
        int b_motion_vectors_over_pic_boundaries;
        int i_max_bytes_per_pic_denom;
        int i_max_bits_per_mb_denom;
        int i_log2_max_mv_length_horizontal;
        int i_log2_max_mv_length_vertical;
        int i_num_reorder_frames;
        int i_max_dec_frame_buffering;

       
    } vui;

    int b_qpprime_y_zero_transform_bypass;

} x264_sps_t;//x264_sps_t定义序列参考队列的参数以及初始化

typedef struct
{
    int i_id;//[毕厚杰:P146 pic_parameter_set_id] 本参数集的序号,在各片的片头被引用
    int i_sps_id;//[毕厚杰:P146 seq_parameter_set_id] 指明本图像参数集所引用的序列参数集的序号

    int b_cabac;//[毕厚杰:P146 entropy_coding_mode_flag] 指明熵编码的选择:0时使用cavlc,1时使用cabac

    int b_pic_order;//[毕厚杰:P147 pic_order_present_flag] poc的三种计算方法在片层还各需要用一些句法元素作为参数;当等于时,表示在片头会有句句法元素指明这些参数;当不为时,表示片头不会给出这些参数
    int i_num_slice_groups;//[毕厚杰:P147 num_slice_groups_minus1] 加一表示图像中片组的个数

    int i_num_ref_idx_l0_active;//[毕厚杰:P147 num_ref_idx_10_active_minus1] 指明目前参考帧队列的长度,即有多少个参考帧(短期和长期),用于list0
    int i_num_ref_idx_l1_active;//[毕厚杰:P147 num_ref_idx_11_active_minus1] 指明目前参考帧队列的长度,即有多少个参考帧(短期和长期),用于list1

    int b_weighted_pred;//[毕厚杰:P147 weighted_pred_flag] 指明是否允许p和sp片的加权预测,如果允许,在片头会出现用以计算加权预测的句法元素
    int b_weighted_bipred;//[毕厚杰:P147 weighted_bipred_idc] 指明是否允许b片的加权预测

    int i_pic_init_qp;//[毕厚杰:P147 pic_init_qp_minus26] 亮度分量的量化参数的初始值
    int i_pic_init_qs;//[毕厚杰:P147 pic_init_qs_minus26] 亮度分量的量化参数的初始值,用于SP和SI

    int i_chroma_qp_index_offset;//[毕厚杰:P147 chroma_qp_index_offset] 色度分量的量化参数是根据亮度分量的量化参数计算出来的,本句法元素用以指明计算时用到的参数表示为在 QPC 值的表格中寻找 Cb色度分量而应加到参数 QPY 和 QSY 上的偏移,chroma_qp_index_offset 的值应在-12 到 +12范围内(包括边界值)


    int b_deblocking_filter_control;//[毕厚杰:P147 deblocking_filter_control_present_flag] 编码器可以通过句法元素显式地控制去块滤波的强度
    int b_constrained_intra_pred;//[毕厚杰:P147 constrained_intra_pred_flag] 在p和b片中,帧内编码的宏块的邻近宏块可能是采用的帧间编码
    int b_redundant_pic_cnt;//[毕厚杰:P147 redundant_pic_cnt_present_flag] redundant_pic_cnt 对于那些属于基本编码图像的条带和条带数据分割块应等于0。在冗余编码图像中的编码条带和编码条带数据分割块的 redundant_pic_cnt 的值应大于 0。当redundant_pic_cnt 不存在时,默认其值为 0。redundant_pic_cnt的值应该在 0到 127范围内(包括 0和127)

    int b_transform_8x8_mode;//?????????????

    int i_cqm_preset;//cqm:外部量化矩阵的设置
    const uint8_t *scaling_list[6];

} x264_pps_t;//图像参数集[毕厚杰,P146 ]



static const uint8_t x264_cqm_jvt4i[16] =
{
      6,13,20,28,
     13,20,28,32,
     20,28,32,37,
     28,32,37,42
};

static const uint8_t x264_cqm_jvt4p[16] =
{
    10,14,20,24,
    14,20,24,27,
    20,24,27,30,
    24,27,30,34
};

static const uint8_t x264_cqm_jvt8i[64] =
{
     6,10,13,16,18,23,25,27,
    10,11,16,18,23,25,27,29,
    13,16,18,23,25,27,29,31,
    16,18,23,25,27,29,31,33,
    18,23,25,27,29,31,33,36,
    23,25,27,29,31,33,36,38,
    25,27,29,31,33,36,38,40,
    27,29,31,33,36,38,40,42
};

static const uint8_t x264_cqm_jvt8p[64] =
{
     9,13,15,17,19,21,22,24,
    13,13,17,19,21,22,24,25,
    15,17,19,21,22,24,25,27,
    17,19,21,22,24,25,27,28,
    19,21,22,24,25,27,28,30,
    21,22,24,25,27,28,30,32,
    22,24,25,27,28,30,32,33,
    24,25,27,28,30,32,33,35
};


static const uint8_t x264_cqm_flat16[64] =
{
    16,16,16,16,16,16,16,16,
    16,16,16,16,16,16,16,16,
    16,16,16,16,16,16,16,16,
    16,16,16,16,16,16,16,16,
    16,16,16,16,16,16,16,16,
    16,16,16,16,16,16,16,16,
    16,16,16,16,16,16,16,16,
    16,16,16,16,16,16,16,16
};
static const uint8_t * const x264_cqm_jvt[6] =
{
    x264_cqm_jvt4i, x264_cqm_jvt4p,
    x264_cqm_jvt4i, x264_cqm_jvt4p,
    x264_cqm_jvt8i, x264_cqm_jvt8p
};

void x264_cqm_init( x264_t *h );
void x264_cqm_delete( x264_t *h );
int  x264_cqm_parse_file( x264_t *h, const char *filename );

#endif


(二)

1. if( p_set_outfile_param( opt->hout, param ) )  // p_set_outfile_param = set_param_bsf 判断输出文件
    {
        fprintf( stderr, "can't set outfile param\n" );
        p_close_infile( opt->hin );
        p_close_outfile( opt->hout );
        return -1;
    }

 p_set_outfile_param = set_param_bsf , 在muxers.c中,函数原型为:

int set_param_bsf( hnd_t handle, x264_param_t *p_param )
{
    return 0;
}

2.  x264_picture_alloc( &pic, X264_CSP_I420, param->i_width, param->i_height );

                                                                   //构造一个图像帧的初始化空间,在common.c中,函数原型为:

void x264_picture_alloc( x264_picture_t *pic, int i_csp, int i_width, int i_height )
{
    pic->i_type = X264_TYPE_AUTO;
    pic->i_qpplus1 = 0;
    pic->img.i_csp = i_csp;
    switch( i_csp & X264_CSP_MASK )
    {
        case X264_CSP_I420:
        case X264_CSP_YV12:
            pic->img.i_plane = 3;
            pic->img.plane[0] = x264_malloc( 3 * i_width * i_height / 2 );
            pic->img.plane[1] = pic->img.plane[0] + i_width * i_height;
            pic->img.plane[2] = pic->img.plane[1] + i_width * i_height / 4;
            pic->img.i_stride[0] = i_width;
            pic->img.i_stride[1] = i_width / 2;
            pic->img.i_stride[2] = i_width / 2;
            break;

        case X264_CSP_I422:
            ...

    }
}

3. i_start = x264_mdate();  //用于编码用时的计算,设定起始时间,在 mdate.c中,函数原型为:

int64_t x264_mdate( void )
{
#if !(defined(_MSC_VER) || defined(__MINGW32__))
    struct timeval tv_date;

    gettimeofday( &tv_date, NULL );
    return( (int64_t) tv_date.tv_sec * 1000000 + (int64_t) tv_date.tv_usec );
#else
    struct _timeb tb;
    _ftime(&tb);
    return ((int64_t)tb.time * (1000) + (int64_t)tb.millitm) * (1000);
#endif
  

4.  进入编码帧
    for( i_frame = 0, i_file = 0, i_progress = 0;
         b_ctrl_c == 0 && (i_frame < i_frame_total || i_frame_total == 0); )
    {
        if( p_read_frame( &pic, opt->hin, i_frame + opt->i_seek ) )//读取
            break;

       //p_read_frame() 按照h->hin提供的输入文件的地址,读入图像的内容到&pic提供的存储区的首地址

        pic.i_pts = (int64_t)i_frame * param->i_fps_den;

        i_file += Encode_frame( h, opt->hout, &pic );//编码并保存,
                                               //Encode_frame( h, opt->hout, &pic )实现编码,是x264的核心部分

        i_frame++;

       
        if( opt->b_progress && param->i_log_level < X264_LOG_DEBUG &&
            ( i_frame_total ? i_frame * 1000 / i_frame_total > i_progress
                            : i_frame % 10 == 0 ) )
        {
            int64_t i_elapsed = x264_mdate() - i_start;
            double fps = i_elapsed > 0 ? i_frame * 1000000. / i_elapsed : 0;
            if( i_frame_total )
            {
                int eta = i_elapsed * (i_frame_total - i_frame) / ((int64_t)i_frame * 1000000);
                i_progress = i_frame * 1000 / i_frame_total;
                fprintf( stderr, "encoded frames: %d/%d (%.1f%%), %.2f fps, eta %d:d:d  \r",
                         i_frame, i_frame_total, (float)i_progress / 10, fps,
                         eta/3600, (eta/60)`, eta` );
            }
            else
                fprintf( stderr, "encoded frames: %d, %.2f fps   \r", i_frame, fps );
            fflush( stderr ); // needed in windows
        }
    }

      注1: //在本文环境下,p_read_frame=read_frame_yuv,read_frame_yuv()定义在muxers.c中,原型为:

int read_frame_yuv( x264_picture_t *p_pic, hnd_t handle, int i_frame )
{
    yuv_input_t *h = handle;

    if( i_frame != h->next_frame )
        if( fseek( h->fh, (uint64_t)i_frame * h->width * h->height * 3 / 2, SEEK_SET ) )
            return -1;

    if( fread( p_pic->img.plane[0], 1, h->width * h->height, h->fh ) <= 0
            || fread( p_pic->img.plane[1], 1, h->width * h->height / 4, h->fh ) <= 0
            || fread( p_pic->img.plane[2], 1, h->width * h->height / 4, h->fh ) <= 0 )
        return -1;

    h->next_frame = i_frame+1;

    return 0;
}

从 文件中分别读取288*352(亮度信息),144*176(Cr),144*176(Cb)的数据放入 p_pic->img.plane[0],p_pic->img.plane[1],p_pic->img.plane[2],如果成 功则继续执行一下程序;如果不成功则打断程序,返回-1.

注2: i_file += Encode_frame( h, opt->hout, &pic );//编码并保存,
                      //Encode_frame( h, opt->hout, &pic )实现编码,是x264的核心部分,在X264.c中,

              //这个函数主要是调用了 x264_encoder_encode( h, &nal, &i_nal, pic, &pic_out ) 来实现编码。

// 原型为:

static int  Encode_frame( x264_t *h, hnd_t hout, x264_picture_t *pic )
{
    x264_picture_t pic_out;
    x264_nal_t *nal;
    int i_nal, i;
    int i_file = 0;

   
    if( pic )
    {
        pic->i_type = X264_TYPE_AUTO;
        pic->i_qpplus1 = 0;
    }
    if( x264_encoder_encode( h, &nal, &i_nal, pic, &pic_out ) < 0 )
    {
        fprintf( stderr, "x264_encoder_encode failed\n" );
    }

    for( i = 0; i < i_nal; i++ )
    {
        int i_size;
        int i_data;

        i_data = DATA_MAX;
        if( ( i_size = x264_nal_encode( data, &i_data, 1, &nal[i] ) ) > 0 )
        {
            i_file += p_write_nalu( hout, data, i_size );
        }
        else if( i_size < 0 )
        {
            fprintf( stderr, "need to increase buffer size (size=%d)\n", -i_size );
        }
    }
    if (i_nal)
        p_set_eop( hout, &pic_out );

    return i_file;
}


(三)

1. x264_encoder_encode( x264_t *h,  x264_nal_t **pp_nal, int *pi_nal,

                                           x264_picture_t *pic_in,   x264_picture_t *pic_out )          //encoder.c中

  (1)x264_frame_t   *frame_psnr = h->fdec; // just to keep the current decoded frame for psnr calculation

    (2)  x264_frame_t *fenc = x264_frame_get( h->frames.unused );//返回h->frames.unused[0]的值,即得到了一幀图像给编码器fenc, x264_frame_get函数在encoder.c中,原型为:

static x264_frame_t *x264_frame_get( x264_frame_t *list[X264_BFRAME_MAX+1] )
{
    x264_frame_t *frame = list[0];
    int i;
    for( i = 0; list[i]; i++ )
        list[i] = list[i+1];
    return frame;
}

    (3)  x264_frame_copy_picture( h, fenc, pic_in );   //  pic_in复制到 fenc ,把要编码的帧存放到 fenc

该函数在frame.c中,原型为:

void x264_frame_copy_picture( x264_t *h, x264_frame_t *dst, x264_picture_t *src )
{
    dst->i_type     = src->i_type;
    dst->i_qpplus1  = src->i_qpplus1;
    dst->i_pts      = src->i_pts;

    switch( src->img.i_csp & X264_CSP_MASK )
    {
        case X264_CSP_I420:
            h->csp.i420( dst, &src->img, h->param.i_width, h->param.i_height );
            break;
        ....

        default:
            x264_log( h, X264_LOG_ERROR, "Arg invalid CSP\n" );
            break;
    }
}

(4) if( h->param.i_width % 16 || h->param.i_height % 16 )      x264_frame_expand_border_mod16( h, fenc );

      fenc->i_frame = h->frames.i_input++;

//若图像宽或高不是16的整数倍,调用 x264_frame_expand_border_mod16( h, fenc );

x264_frame_put( h->frames.next, fenc );//将fenc拷贝给h->frames.next数组中第一个不为0的位置,该函数在encoder.c中,原型为:

static void x264_frame_put( x264_frame_t *list[X264_BFRAME_MAX], x264_frame_t *frame )
{
    int i = 0;
    while( list[i] ) i++;
    list[i] = frame;
}

(5)x264_slicetype_decide( h );      //判断slice的类型,在slicetype_decision.c中,如下:

void x264_slicetype_decide( x264_t *h )
{
    ...

    if( h->param.rc.b_stat_read )
    {
       
        for( i = 0; h->frames.next[i] != NULL; i++ )
            h->frames.next[i]->i_type =
                x264_ratecontrol_slice_type( h, h->frames.next[i]->i_frame );
    }
    else if( h->param.i_bframe && h->param.b_bframe_adaptive )
        x264_slicetype_analyse( h );//x264_slicetype_analyse函数在slicetype_decision.c中。

        //这里要注意默认的x264里面是没有B帧的,如果需要用到B帧可以在最初的参数设置的时候,

        //用语句“--frames 数字”来定义,x264_slicetype_analyse 也只有在定义了“--frames 数字”时候,

        //才会执行。

    ...
}

(6)   

       while( IS_X264_TYPE_B( h->frames.next[bframes]->i_type ) )
            bframes++;
        x264_frame_put( h->frames.current, x264_frame_get( &h->frames.next[bframes] ) );

//将当前要编码的帧h->frames.current送给编码器,然后开始编码

(7)  h->fenc = x264_frame_get( h->frames.current );//将当前要编码的帧给编码器

(8)   if( h->fenc->i_type == X264_TYPE_IDR )
    {
        h->frames.i_last_idr = h->fenc->i_frame;
    }

TIMER_START( i_mtime_encode_frame );

// 如果是解码即时刷新片,则h->frames.i_last_idr = h->fenc->i_frame;然后计时开

(9)if( h->fenc->i_type == X264_TYPE_IDR )
    {
       
        x264_reference_reset( h );

        i_nal_type    = NAL_SLICE_IDR;
        i_nal_ref_idc = NAL_PRIORITY_HIGHEST;
        i_slice_type = SLICE_TYPE_I;
    }
    else if( h->fenc->i_type == X264_TYPE_I )
    {
        i_nal_type    = NAL_SLICE;
        i_nal_ref_idc = NAL_PRIORITY_HIGH;
        i_slice_type = SLICE_TYPE_I;
    }
    else if( h->fenc->i_type == X264_TYPE_P )
    {
        i_nal_type    = NAL_SLICE;
        i_nal_ref_idc = NAL_PRIORITY_HIGH;
        i_slice_type = SLICE_TYPE_P;
    }
    else if( h->fenc->i_type == X264_TYPE_BREF )
    {
        i_nal_type    = NAL_SLICE;
        i_nal_ref_idc = NAL_PRIORITY_HIGH;
        i_slice_type = SLICE_TYPE_B;
    }
    else   
    {
        i_nal_type    = NAL_SLICE;
        i_nal_ref_idc = NAL_PRIORITY_DISPOSABLE;
        i_slice_type = SLICE_TYPE_B;
    }

//根据帧的类型初始化数据。总共有五种片的类型IDR,I,P,BREF,B 。如果是解码即时刷新片(idr),则要对参考帧重新设置




(四)

(1)
          x264_reference_build_list( h, h->fdec->i_poc, i_slice_type );

//创建list0和list1(ref0和ref1),ref0从大到小,ref1从小到大

//x264_reference_build_list( h, h->fdec->i_poc, i_slice_type )定义在encoder.c中,如下:

static inline void x264_reference_build_list( x264_t *h, int i_poc, int i_slice_type )
{
    int i;
    int b_ok;

   
    ...

}

(2) 
    x264_ratecontrol_start( h, i_slice_type, h->fenc->i_qpplus1 );
    i_global_qp = x264_ratecontrol_qp( h );

    pic_out->i_qpplus1 = h->fdec->i_qpplus1 = i_global_qp + 1;

    // 初始化速率和量化步长

(3) x264_slice_init( h, i_nal_type, i_slice_type, i_global_qp );

// 创建片头,片头初始化;

(4)
    if( i_nal_type == NAL_SLICE_IDR && h->param.b_repeat_headers )
    {
        if( h->fenc->i_frame == 0 )
        {
            x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
            x264_sei_version_write( h, &h->out.bs );
            x264_nal_end( h );
        }

       
        x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST );
        x264_sps_write( &h->out.bs, h->sps );
        x264_nal_end( h );

       
        x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST );
        x264_pps_write( &h->out.bs, h->pps );
        x264_nal_end( h );
    }

(5)
    i_frame_size = x264_slices_write( h );

// encoder.c中,原型:

static inline int x264_slices_write( x264_t *h )
{
    ...
}

 关键部分为x264_slice_write( h );该函数定义在encoder.c中,实现一帧的编码。

注1:for( mb_xy = h->sh.i_first_mb, i_skip = 0; mb_xy < h->sh.i_last_mb; mb_xy++ )

//循环22*18=396次对396个宏块进行编码,mb_xy是16*16宏块序号0~395,

//其赋值过程在x264_encoder_encode 中的x264_slice_init里。

 const int i_mb_y = mb_xy / h->sps->i_mb_width; 

//计算当前宏块垂直坐标(行坐标,以宏块为单位)  i_mb_width=22       

const int i_mb_x = mb_xy % h->sps->i_mb_width;

//计算当前宏块水平坐标(列坐标,以宏块为单位) 

int mb_spos = bs_pos(&h->out.bs);

//在bs.h中,静态内联函数,如下:

static inline int bs_pos( bs_t *s )
       {
         return( 8 * ( s->p - s->p_start ) + 8 - s->i_left );
       }

x264_macroblock_cache_load( h, i_mb_x, i_mb_y );

 //它是将要编码的宏块的周围宏块的值读进来。要想得到当前块的预测值,要先知道上面,左面的预测值。

// 分析参数选择合适的块编码模式,加载相邻块的信息。

x264_macroblock_analyse( h );

//对一个16*16块进行预测模式选择,通过比较得出最佳预测模式。

// 定义在analyse.c 中,计算sad值分析是否要对16*16的宏块进行分割和采用哪种分割方式合适。

x264_macroblock_encode( h );

// 根据前面分析得到的h->mb.i_type的类型,对一个16*16块进行编码。




(五)

X264代码主要的部分分为三个步骤,即数据的读入与存放,视频编码层(VCL)的视频编码和网络提取层(Network Abstraction Layer,NAL)单元输出。  

数 据的读入与存放:X264开辟了unused、next、current、refrence等区域分别保存未处理原始图片序列、即将编码帧序列、当前编码 帧和参考帧序列,同时还开辟了fenc和fdec区域用于存放已编码帧和重构帧 。程序按以下顺序读入数据:首先,从YUV数据文件中读取数据存到临时变量pic_in,同时为unused开辟存储空间,并用fenc指针指向这个空 间。接着,将pic_in中的图片数据拷贝到fenc所指向的区域,并在拷贝完成后对图片大小进行判断,如果长宽不为16的整数倍则进行像素扩展;将处理 后的fenc区域数据放入next区域。之后,如果存在B帧,则从next区域取出B帧以后的P帧放到current区域中,也就是说先编码I、P帧再编 码之间的B帧;否则,直接从next区域取出一帧存入current区域。此时current区域中存放的就是已经过预处理的即将要编码的帧数据了。最 后,由于fenc区域是编码的直接对象,再将current区域中的内容拷贝到fenc中正式开始编码 。

视频编码层(VCL)的视频编码:输入文件foreman.yuv格式是CIF,即352*288。在对一帧图像进行处理的过程中以宏块(16*16)为单位,一帧图像处理步骤如下:

定位当前处理的16*16宏块的位置;

X264_macroblock_chche_load(h,i_mb_x,I,mb_y) 

X264_macroblock_analyse(h);X264_macroblock_encode(h) ;

根据h->mb.i_type的类型进行操作, 如果是I帧,则x264_macroblock_write_caclc(h,&h->out.bs);

X264_macroblock_chche_save(h) ;

计算mb stats.














原创粉丝点击