(转)x264代码详细阅读之x264.c,common.c,encoder.c

转自:http://alphamailpost.blog.163.com/blog/static/201118081201281103931932/

x264代码详细阅读第一之x264.c
http://www.usr.cc/thread-52097-1-2.html
x264代码详细阅读第二之common.c
http://www.usr.cc/thread-52098-1-2.html
x264代码详细阅读第三之encoder.c
http://www.usr.cc/thread-52100-1-2.html

1.x264代码详细阅读第一之x264.c
一个x264的图像分两个层,一个是视频编码层(VCL),一个是网络提取层(NAL)。
对图像一个帧进行VCL编码,用的是x264_encoder_encode函数,这个函数输入一个pic结构,这个结构包含了输入数像的大小得信息,还一个缓冲区指针,指向一个存有一帧图像的缓冲。函数会对这个缓冲区中的数据编码,编码后数据存入nal数组.
然后再对得到的nal缓冲区再进行NAL编码,这样得到的数据即可在网上传,也可以存为文件。
下面是编码一帧,并输出一帧数据的代码。要编码的数据存入inBufs->bufs[0]指向的缓冲区中,大小是176*144*1.5字节。输入 存入outBufs->bufs[0]指向缓冲区。其中i_nal是指VCL编码一帧后产生的nal层数据包的个数。请参考附件中的6.3.3节

资源:
115网盘附件下载:
davinci.pdf (5.00MB)

附件是x264的协议标准。因为读代码经常用到,所以加上了附件。

  1. char *temp=outBufs->bufs[0];
  2. pic.img.plane[0] = (uint8_t *)inBufs->bufs[0]; 
  3. pic.img.plane[1] = pic.img.plane[0] + 176* 144; 
  4. pic.img.plane[2] = pic.img.plane[1] + 176 *144 / 4; 
  5. x264_encoder_encode( h, &nal, &i_nal, &pic, &pic_out );
  6. for( i = 0; i < i_nal; i++ )
  7.         {
  8.                 int i_size;
  9.                 if( mux_buffer_size < nal[i].i_payload * 3/2 + 4 )
  10.                 {
  11.                     mux_buffer_size = nal[i].i_payload * 2 + 4;
  12.                     x264_free( mux_buffer );
  13.                     mux_buffer = x264_malloc( mux_buffer_size );
  14.                 }
  15.                 i_size = mux_buffer_size;
  16.                 x264_nal_encode( mux_buffer, &i_size, 1, &nal[i] );
  17.                 memcpy(temp, mux_buffer,i_size);
  18.                 temp+=i_size;
  19.                 bufSize+=i_size;
  20.                 i_file +=i_size;
  21.         }

 

mdate函数定义于mdate.c中,这个函数只在x264.c中调用了,在开始编码时获得一次,结束时再获得一次,两次时间取差,得到编码用的总时 间,用于打印编译效率。并非编码算法的核心。移植为dsp算法时也用不到,所以删掉mdate.c和相应与mdate相关的变量及打印语句。

mdate.c中的全部内容:


  1. /*****************************************************************************
  2. * mdate.c: h264 encoder
  3. *****************************************************************************
  4. * Copyright (C) 2003 Laurent Aimar
  5. * $Id: mdate.c,v 1.1 2004/06/03 19:27:07 fenrir Exp $
  6. *
  7. * Authors: Laurent Aimar <fenrir@via.ecp.fr>
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 2 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
  22. *****************************************************************************/
  23. #if !(defined(_MSC_VER) || defined(__MINGW32__))
  24. //#include <sys/time.h>
  25. #else
  26. #include <sys/types.h>
  27. #include <sys/timeb.h>
  28. #endif
  29. #include <time.h>
  30. #include "osdep.h"
  31. far int64_t x264_mdate( void )
  32. {
  33. #if !(defined(_MSC_VER) || defined(__MINGW32__))
  34. //     struct timeval tv_date;
  35. // 
  36. //     gettimeofday( &tv_date, NULL );
  37. //     return( (int64_t) tv_date.tv_sec * 1000000 + (int64_t) tv_date.tv_usec );
  38. #else
  39.     struct _timeb tb;
  40.     _ftime(&tb);
  41.     return ((int64_t)tb.time * (1000) + (int64_t)tb.millitm) * (1000);
  42. #endif
  43. }



再次调用的代码:
i_start=x264_mdate();
i_end = x264_mdate();

x264中开始编码到结束的流程

首先
这三个结构的添充要完成:
x264_param_t param;  
cli_opt_t opt;
x264_picture_t pic;

填充param首先给各个域一个默认值,调用:x264_param_default( &param );
然后自己相应的做一些改变,最常用的有:


  1.     param.rc.i_qp_constant = 30;//帧率
  2.     param.rc.i_rc_method = X264_RC_CQP ;//rate control method
  3.     param.i_width=176;//测试文件图像大小。
  4.     param.i_height=144;



这四个改了其他的不用变,然后就可以用这个param来打开一个encoder了,打开方法:
h = x264_encoder_open( &param ) ;

然后是编码,编码之前要添充好pic:


  1. pic.i_type = X264_TYPE_AUTO;
  2.         pic.i_qpplus1 = 0;
  3.         pic.img.i_csp =X264_CSP_I420;
  4.         pic.img.i_plane = 3;
  5.         pic.img.i_stride[0] = 176;
  6.         pic.img.i_stride[1] = 176 / 2;
  7.         pic.img.i_stride[2] = 176 / 2;
  8.         pic.i_type = X264_TYPE_AUTO;
  9.         pic.i_qpplus1 = 0;


这些指胆图象的大小,格式。
还要指明输入一帧数据所有的缓冲区:


  1.         pic.img.plane[0] = (uint8_t *)inBufs->bufs[0];
  2.         pic.img.plane[1] = pic.img.plane[0] + 176* 144;
  3.         pic.img.plane[2] = pic.img.plane[1] + 176 *144 / 4;


一个图像通常有三个plane,对于4:2:0的图片来说:第一个plane是Y信息,176x144大小。另两个分别是U和V,各是四分之一个Y信息的大小。

opt是用来解析命令行传入的选项的,如果测试阶段,都在文件中指定,用不到。

另外,编译时还要初始化一个pic_out和nal结构,pic_out用于encoder_encode的参数。nal则用于承载VCL编码后的数,编成多个nal帧。显然nal是一个数组,i_nal是数组下标的最大值,即数组边界。
看nal编码的代码就知道了:


  1. for( i = 0; i < i_nal; i++ )
  2.         {
  3.                 int i_size;
  4.                 if( mux_buffer_size < nal[i].i_payload * 3/2 + 4 )
  5.                 {
  6.                     mux_buffer_size = nal[i].i_payload * 2 + 4;
  7.                     x264_free( mux_buffer );
  8.                     mux_buffer = x264_malloc( mux_buffer_size );
  9.                 }
  10.                 i_size = mux_buffer_size;
  11.                 x264_nal_encode( mux_buffer, &i_size, 1, &nal[i] );
  12.                 memcpy(temp, mux_buffer,i_size);
  13.                 temp+=i_size;
  14.                 bufSize+=i_size;
  15.                 i_file +=i_size;
  16.         }

 

编码完成后要释放pic中的缓冲区:

  1. void x264_picture_clean( x264_picture_t *pic )
  2. {
  3.     x264_free( pic->img.plane[0] );
  4.     /* just to be safe */
  5.     memset( pic, 0, sizeof( x264_picture_t ) );
  6. }


并关闭encoder:
  x264_encoder_close( h );
 






2.x264代码详细阅读第二之common.c

x264.c中向外调用的函数只有六个,三个在common.c中,三个在encoder.c中:

  • x264_param_default( &param );
  • x264_encoder_open( &param ) ;  encoder.c
  • x264_encoder_encode( h, &nal, &i_nal, &pic, &pic_out );  encoder.c
  • x264_nal_encode( mux_buffer, &i_size, 1, &nal[i] );
  • x264_picture_clean( &pic );
  • x264_encoder_close( h ); encoder.c

x264_param_default函数

这个函数将param结构所有成员赋了一次初值,可真够难为它的了....
要了解param各个域的意思,非要对x264及视频格式有点了解才行,没有的可以借这个机会了解:

void    x264_param_default( x264_param_t *param )
{
    /* */
    memset( param, 0, sizeof( x264_param_t ) );  //清空,这个谁都懂。

    /* CPU autodetect */
    param->cpu = x264_cpu_detect(); //cpu是什么,这个函数在cpu.c中,
//这个文件中就是这个函数的各种实现,主要是判断CPU有没有特殊指令的支持,
//大家看一下它的可选的值就知道了,cpu域是一个掩码,对davinci,我们没优化的情况下,返回0。
    param->i_threads = 1; //支持的线程数
    param->b_deterministic = 1;//多线程的情况下是否支持非确定性优化.

    /* Video properties 视频属性*/
    param->i_csp           = X264_CSP_I420; //格式
    param->i_width         = 0;
    param->i_height        = 0;
    param->vui.i_sar_width = 0;//视频可用性信息(Video Usablity Info)中的SAR宽度
    param->vui.i_sar_height= 0;
    param->vui.i_overscan  = 0;  /* undef */
    param->vui.i_vidformat = 5;  /* undef */
    param->vui.b_fullrange = 0;  /* off */
    param->vui.i_colorprim = 2;  /* undef */
    param->vui.i_transfer  = 2;  /* undef */
    param->vui.i_colmatrix = 2;  /* undef */
    param->vui.i_chroma_loc= 0;  /* left center */
    param->i_fps_num       = 25;
    param->i_fps_den       = 1;
    param->i_level_idc     = 51; /* as close to "unrestricted" as we can get */

    /* Encoder parameters */
    param->i_frame_reference = 1;
    param->i_keyint_max = 250;
    param->i_keyint_min = 25;
    param->i_bframe = 0;
    param->i_scenecut_threshold = 40;
    param->b_bframe_adaptive = 1;
    param->i_bframe_bias = 0;
    param->b_bframe_pyramid = 0;

    param->b_deblocking_filter = 1;
    param->i_deblocking_filter_alphac0 = 0;
    param->i_deblocking_filter_beta = 0;

    param->b_cabac = 1;
    param->i_cabac_init_idc = 0;

    param->rc.i_rc_method = X264_RC_NONE;
    param->rc.i_bitrate = 0;
    param->rc.f_rate_tolerance = 1.0;
    param->rc.i_vbv_max_bitrate = 0;
    param->rc.i_vbv_buffer_size = 0;
    param->rc.f_vbv_buffer_init = 0.9;
    param->rc.i_qp_constant = 26;
    param->rc.f_rf_constant = 0;
    param->rc.i_qp_min = 10;
    param->rc.i_qp_max = 51;
    param->rc.i_qp_step = 4;
    param->rc.f_ip_factor = 1.4;
    param->rc.f_pb_factor = 1.3;

    param->rc.b_stat_write = 0;
    param->rc.psz_stat_out = "x264_2pass.log";
    param->rc.b_stat_read = 0;
    param->rc.psz_stat_in = "x264_2pass.log";
    param->rc.psz_rc_eq = "blurCplx^(1-qComp)";
    param->rc.f_qcompress = 0.6;
    param->rc.f_qblur = 0.5;
    param->rc.f_complexity_blur = 20;
    param->rc.i_zones = 0;

    /* Log */
    param->pf_log = x264_log_default;
    param->p_log_private = NULL;
    param->i_log_level = X264_LOG_INFO;

    /* */
    param->analyse.intra = X264_ANALYSE_I4x4 | X264_ANALYSE_I8x8;
    param->analyse.inter = X264_ANALYSE_I4x4 | X264_ANALYSE_I8x8
                         | X264_ANALYSE_PSUB16x16 | X264_ANALYSE_BSUB16x16;
    param->analyse.i_direct_mv_pred = X264_DIRECT_PRED_SPATIAL;
    param->analyse.i_me_method = X264_ME_HEX;
    param->analyse.i_me_range = 16;
    param->analyse.i_subpel_refine = 5;
    param->analyse.b_chroma_me = 1;
    param->analyse.i_mv_range_thread = -1;
    param->analyse.i_mv_range = -1; // set from level_idc
    param->analyse.i_direct_8x8_inference = -1; // set from level_idc
    param->analyse.i_chroma_qp_offset = 0;
    param->analyse.b_fast_pskip = 1;
    param->analyse.b_dct_decimate = 1;
    param->analyse.i_luma_deadzone[0] = 21;
    param->analyse.i_luma_deadzone[1] = 11;
    param->analyse.b_psnr = 1;
    param->analyse.b_ssim = 1;

    param->i_cqm_preset = X264_CQM_FLAT;
    memset( param->cqm_4iy, 16, 16 );
    memset( param->cqm_4ic, 16, 16 );
    memset( param->cqm_4py, 16, 16 );
    memset( param->cqm_4pc, 16, 16 );
    memset( param->cqm_8iy, 16, 64 );
    memset( param->cqm_8py, 16, 64 );

    param->b_repeat_headers = 1;
    param->b_aud = 0;
}


x264_nal_encode

这个函数运行于x264_encoder_encode之后,encoder_encode指缓冲区中的帧编译后,变成了i_nal个片段,这些片段被加上一个nal头,就变成一个nalu,即nal单元.nal头是一个字节的数据.


  1. int x264_nal_encode( void *p_data, int *pi_data, int b_annexeb, x264_nal_t *nal )
  2. {
  3.     uint8_t *dst = p_data;
  4.     uint8_t *src = nal->p_payload;
  5.     uint8_t *end = &nal->p_payload[nal->i_payload];
  6.     int i_count = 0;
  7.     /* FIXME this code doesn't check overflow */
  8.     if( b_annexeb )
  9.     {
  10.         /* long nal start code (we always use long ones)*/ //加起始码四字节.
  11.         *dst++ = 0x00;
  12.         *dst++ = 0x00;
  13.         *dst++ = 0x00;
  14.         *dst++ = 0x01;
  15.     }
  16.     /* nal header */
  17.     *dst++ = ( 0x00 << 7 ) | ( nal->i_ref_idc << 5 ) | nal->i_type;  //加头一字节
  18.     while( src < end )
  19.     {
  20.         if( i_count == 2 && *src <= 0x03 )
  21.         {
  22.             *dst++ = 0x03;
  23.             i_count = 0;
  24.         }
  25.         if( *src == 0 )
  26.         {
  27.             i_count++;
  28.         }
  29.         else
  30.         {
  31.             i_count = 0;
  32.         }
  33.         *dst++ = *src++;
  34.     }
  35.     *pi_data = dst - (uint8_t*)p_data;
  36.     return *pi_data;
  37. }

x264_picture_clean( &pic );
这个就是release掉pic->plane[0]中指向的缓冲区

 

 

 

 

x264代码详细阅读第三之encoder.c

上篇 x264代码详细阅读第二之common.c 讲到x264.c中的六个函数,还有三个函数定义在encoder.c中。



一楼看x264_encoder_open函数:
先是创建一个x264_t的结构,给指针h,并且0初始化
x264_t *h = x264_malloc( sizeof( x264_t ) );
memset( h, 0, sizeof( x264_t ) );


将x264_param_t的结构复制到x264_t中的param域中:
memcpy( &h->param, param, sizeof( x264_param_t ) );


然后验证h中的各个域,这个函数其实验证的全是param中的参数,它不仅验证,还要做修正,就是把可能出错的参数根据猜测做一些修改,提供一些容错能力。
x264_validate_parameters( h ) 

x264_cqm_parse_file( h, h->param.psz_cqm_file ) //这个函数首先把文件内容读到内存中,并根据文件内容,获得一个量化矩阵,本来是有一个jvt的默认量化矩阵的,因此这个函数所做的事情其实可以不做, 移植到DSP的话也不可能让DSP自己读取文件,所以是一定要注释掉的。

嘿嘿,这三个参数的意思我搞了好久才明白个大概的,视频编码有个2pass的变码率压缩技术,这个技术要对影片的数据过两次,第一次分析数据,看看应该怎么压缩,然后再过一遍,这一遍才真正的压缩,而第一次分析得到的数据就存在上面的三个结构里。
    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 );//这个是传入状态数据
    if( h->param.rc.psz_rc_eq )
        h->param.rc.psz_rc_eq = strdup( h->param.rc.psz_rc_eq );//这个是rate control 方程

上面这一段移植的时候是要修改的,因为strdup函数在DSP的编译器cgtools中的定义与标准C的不一样的,不好用,所以我们在文件头部定义一个strdup1函数:
char * strdup1(const char *s)
{
        char *t=NULL;
        if(s&&(t=(char *)malloc(strlen(s)+1)))
                strcpy(t,s);
        return t;
}



    /* VUI */
    if( h->param.vui.i_sar_width > 0 && h->param.vui.i_sar_height > 0 )
    {
这个sar_width和sar_height是用来表示宽高比的,不是表示正常的宽高像素值.
        int i_w = param->vui.i_sar_width;
        int i_h = param->vui.i_sar_height;


        x264_reduce_fraction( &i_w, &i_h );//这个函数是把两个数字变得互质,也就是说把16:12变成4:3

        while( i_w > 65535 || i_h > 65535 )//如果都转人成互质的数了,还这么大,那就没有办法了,只能以牺牲精度的方式不断除2了.
        {
            i_w /= 2;
            i_h /= 2;
        }

        h->param.vui.i_sar_width = 0;//原来的数清零,以备把i_w,i_h的值传回来.
        h->param.vui.i_sar_height = 0;
        if( i_w == 0 || i_h == 0 )
        {
            x264_log( h, X264_LOG_WARNING, "cannot create valid sample aspect ratio\n" );
        }
        else
        {
            x264_log( h, X264_LOG_INFO, "using SAR=%d/%d\n", i_w, i_h );
            h->param.vui.i_sar_width = i_w; //这里传回改变后的宽高比值
            h->param.vui.i_sar_height = i_h;
        }
    }

    x264_reduce_fraction( &h->param.i_fps_num, &h->param.i_fps_den );//对fps_num和fps_den也转成互质,i_fps_num是fps分子,i_fps_den是fps分母,这两个数合起来组成fps,即fps=i_fps_num/i_fps_den. 


    /* Init x264_t */
    h->i_frame = 0;
    h->i_frame_num = 0;
    h->i_idr_pic_id = 0;
初始化SPS PPS的数据结构,关于SPS和PPS可以参考:
http://www.usr.cc/blog-16-3091.html
http://www.usr.cc/blog-16-3090.html

   h->sps = &h->sps_array[0];
    x264_sps_init( h->sps, h->param.i_sps_id, &h->param );

    h->pps = &h->pps_array[0];
    x264_pps_init( h->pps, h->param.i_sps_id, &h->param, h->sps);


    x264_validate_levels( h );这个函数用于验证一些参数是否大于某个level. 这些参数包括:frame size(帧大小), DPB size(Decoded Picture Buffer)解码后图像缓冲区大小(关于DPB参考这个附件:H.264 DPB summery.pdf ), VBV bitrate, VBV  buffer(参考这里),MV range(参考),MB bitrate.


    if( x264_cqm_init( h ) < 0 )
    {
        x264_free( h );
        return NULL;
    }

初始化量化矩阵。
    
    h->mb.i_mb_count = h->sps->i_mb_width * h->sps->i_mb_height;//mb是宏块,macroblock

    /* Init frames. 初始化帧相关的内容 */
    h->frames.i_delay = h->param.i_bframe + h->param.i_threads - 1;
    h->frames.i_max_ref0 = h->param.i_frame_reference;
    h->frames.i_max_ref1 = h->sps->vui.i_num_reorder_frames;
    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.b_bframe_adaptive
          || h->param.b_pre_scenecut );

    h->frames.i_last_idr = - h->param.i_keyint_max;
    h->frames.i_input    = 0;
    h->frames.last_nonb  = NULL;


    h->i_ref0 = 0;
    h->i_ref1 = 0;

    x264_rdo_init( );//预计算abs_level_m1编码代价

    /* init CPU functions 初始化CPU功能*/
    x264_predict_16x16_init( h->param.cpu, h->predict_16x16 );
    x264_predict_8x8c_init( h->param.cpu, h->predict_8x8c );
    x264_predict_8x8_init( h->param.cpu, h->predict_8x8 );
    x264_predict_4x4_init( h->param.cpu, h->predict_4x4 );

这里实际上四个函数调用中的第二个参数都是一个数组,即h->predict_NxN是一个数组,数组元素是函数指针。这四个函数初始的正这些指针的值。这些指针指向的函数都是完成相应大小的宏块的预测所用的函数。这些函数内容类似于:
    pf[I_PRED_16x16_V ]     = predict_16x16_v; 分别是七种预测模式的预测函数。
    pf[I_PRED_16x16_H ]     = predict_16x16_h;
    pf[I_PRED_16x16_DC]     = predict_16x16_dc;
    pf[I_PRED_16x16_P ]     = predict_16x16_p;
    pf[I_PRED_16x16_DC_LEFT]= predict_16x16_dc_left;
    pf[I_PRED_16x16_DC_TOP ]= predict_16x16_dc_top;
    pf[I_PRED_16x16_DC_128 ]= predict_16x16_dc_128;

    x264_pixel_init( h->param.cpu, &h->pixf );
初始化像素预测数的指针。
   x264_dct_init( h->param.cpu, &h->dctf );
初始化离散余弦变换的函数指针。
   x264_zigzag_init( h->param.cpu, &h->zigzagf, h->param.b_interlaced );
初始化Z形扫描函数指针
  
x264_mc_init( h->param.cpu, &h->mc );
初始化运动补偿相应函数指针
    x264_csp_init( h->param.cpu, h->param.i_csp, &h->csp );
初始化YUV格式转换的一些函数指针
    x264_quant_init( h, h->param.cpu, &h->quantf );//初始化量化器函数指针
    x264_deblock_init( h->param.cpu, &h->loopf );//不知道做什么的
    x264_dct_init_weights();//dct权值

    mbcmp_init( h );

    x264_log( h, X264_LOG_INFO, "using cpu capabilities: %s%s%s%s%s%s%s%s\n",
             param->cpu&X264_CPU_MMX ? "MMX " : "",
             param->cpu&X264_CPU_MMXEXT ? "MMXEXT " : "",
             param->cpu&X264_CPU_SSE ? "SSE " : "",
             param->cpu&X264_CPU_SSE2 ? "SSE2 " : "",
             param->cpu&X264_CPU_SSSE3 ? "SSSE3 " : "",
             param->cpu&X264_CPU_3DNOW ? "3DNow! " : "",
             param->cpu&X264_CPU_ALTIVEC ? "Altivec " : "",
             param->cpu ? "" : "none!" );

    h->out.i_nal = 0;//i_nal,即nal段的数目初始化为0
    h->out.i_bitstream = X264_MAX( 1000000, h->param.i_width * h->param.i_height * 4
        * ( h->param.rc.i_rc_method == X264_RC_ABR ? pow( 0.95, h->param.rc.i_qp_min )
          : pow( 0.95, h->param.rc.i_qp_constant ) * X264_MAX( 1, h->param.rc.f_ip_factor )));

    h->thread[0] = h;//多线程方面的东西
    h->i_thread_num = 0;
    for( i = 1; i < h->param.i_threads; i++ )
        h->thread[i] = x264_malloc( sizeof(x264_t) );

    for( i = 0; i < h->param.i_threads; i++ )
    {
        if( i > 0 )
            *h->thread[i] = *h;
        h->thread[i]->fdec = x264_frame_pop_unused( h );
        h->thread[i]->out.p_bitstream = x264_malloc( h->out.i_bitstream );
        if( x264_macroblock_cache_init( h->thread[i] ) < 0 )
            return NULL;
    }

    if( x264_ratecontrol_new( h ) < 0 )
        return NULL;

#ifdef DEBUG_DUMP_FRAME
    {
        /* create or truncate the reconstructed video file */
        FILE *f = fopen( "fdec.yuv", "w" );
        if( f )
            fclose( f );
        else
        {
            x264_log( h, X264_LOG_ERROR, "can't write to fdec.yuv\n" );
            x264_free( h );
            return NULL;
        }
    }
#endif

    return h;


[tr][td]资源:[/td][/tr]
[tr][td]115网盘附件下载:[/td][/tr]
[tr][td]H.264 DPB summery.pdf (120.48KB) [/td][/tr]
[/table]



x264_encoder_encode函数

x264_t *thread_current, *thread_prev, *thread_oldest;//这三个指针记录是哪一个线程,单线程用不到。
    int     i_nal_type;
    int     i_nal_ref_idc;

    int   i_global_qp;

    if( h->param.i_threads > 1) //如果是多线程
    {
        int i = ++h->i_thread_phase;
        int t = h->param.i_threads;
        thread_current = h->thread[ i%t ];
        thread_prev    = h->thread[ (i-1)%t ];
        thread_oldest  = h->thread[ (i+1)%t ];
        x264_thread_sync_context( thread_current, thread_prev );
        x264_thread_sync_ratecontrol( thread_current, thread_prev, thread_oldest );
        h = thread_current;
//      fprintf(stderr, "current: %p  prev: %p  oldest: %p \n", thread_current, thread_prev, thread_oldest);
    }
    else
    {
        thread_current =
        thread_prev    =
        thread_oldest  = h;
    }

这一段显然只要留一句话就行了。

// ok to call this before encoding any frames, since the initial values of fdec have b_kept_as_ref=0
    x264_reference_update( h );
    h->fdec->i_lines_completed = -1;
更新引用数:
static inline void x264_reference_update( x264_t *h )
{
    int i;

    if( h->fdec->i_frame >= 0 )//fdec指向正在被重建的帧,i_frame是其序号
        h->i_frame++;  //i_frame为当然帧的序号

    if( !h->fdec->b_kept_as_ref )//正在被重建的帧不被kept as ref,即不被用作参考帧
    {
        if( h->param.i_threads > 1 )//如果是多线程的话
        {
            x264_frame_push_unused( h, h->fdec );//将己经不用了的帧加入到unused数组中,x264_t中的有个frames结构,里面有三个数组,unused,current,next,分别是未被使用的(用完了在这里回收),正要被编码的,和没有决定是什么类型(待编码的)。这是一条龙的。
            h->fdec = x264_frame_pop_unused( h );//之所以要先push再pop,是就是为了多线程的同步。
        }
        return;
    }

    /* move lowres copy of the image to the ref frame */
    for( i = 0; i < 4; i++)
    {
        XCHG( uint8_t*, h->fdec->lowres[i], h->fenc->lowres[i] );
        XCHG( uint8_t*, h->fdec->buffer_lowres[i], h->fenc->buffer_lowres[i] );
    }

    /* adaptive B decision needs a pointer, since it can't use the ref lists */
    if( h->sh.i_type != SLICE_TYPE_B )//片的类型不是B帧
        h->frames.last_nonb = h->fdec;//上一个不是B的帧就是这个帧

    /* move frame in the buffer */
    x264_frame_push( h->frames.reference, h->fdec );//将上一个重建帧加入到参考帧列表。
    if( h->frames.reference[h->frames.i_max_dpb] )//i_max_dpb是解码图片缓冲区的数目,这句成立代表解码缓冲区満。
        x264_frame_push_unused( h, x264_frame_shift( h->frames.reference ) );//就把参考数组中最低的拿出来放到不用的数组里面。
    h->fdec = x264_frame_pop_unused( h );//从未被使用的帧中拿出一个来用,放到重建帧指针上。
}


    /* no data out */ 因为还没解码中数据来,
    *pi_nal = 0; //nal数据段数为0
    *pp_nal = NULL; //nal数组第一个元素为NULL

    TIMER_START( i_mtime_encode_frame );//这个要定义了DEBUGBENCHMARK才要用,否则这句没用

if( pic_in != NULL )
    {//对输入的图片进行处理
        /* 1: Copy the picture to a frame and move it to a buffer */
        x264_frame_t *fenc = x264_frame_pop_unused( h );//首先从未被使用的帧中拿出一个来,用于编码帧
        x264_frame_copy_picture( h, fenc, pic_in );//从pic结构中复制图像数据到fenc中
        if( h->param.i_width != 16 * h->sps->i_mb_width ||
            h->param.i_height != 16 * h->sps->i_mb_height )
            x264_frame_expand_border_mod16( h, fenc );

        fenc->i_frame = h->frames.i_input++;//输入的帧的数目即为编码帧的数目加一

        x264_frame_push( h->frames.next, fenc );//把这个编码帧加入到next,即将要编码的帧的数组中

        if( h->frames.b_have_lowres )
            x264_frame_init_lowres( h->param.cpu, fenc );

        if( h->frames.i_input <= h->frames.i_delay + 1 - h->param.i_threads )
        {
            /* Nothing yet to encode */
            /* waiting for filling bframe buffer */
            pic_out->i_type = X264_TYPE_AUTO;
            return 0;
        }
    }



[quote] if( h->frames.current[0] == NULL )//如果当前编码帧队列己经空了,这时要添加新的帧了
    {
        int bframes = 0;
        /* 2: Select frame types */
        if( h->frames.next[0] == NULL )//如果待编码帧队列为空,则当前帧是最后一个帧,用encoder_frame_end来编码
        {
            x264_encoder_frame_end( thread_oldest, thread_current, pp_nal, pi_nal, pic_out );
            return 0;
        }

        x264_slicetype_decide( h );//决定片的类型

        /* 3: move some B-frames and 1 non-B to encode queue 本段引用中以下所有代码都是为了向h->frames.current中添加一个非B帧和多个B帧*/
        while( IS_X264_TYPE_B( h->frames.next[bframes]->i_type ) )//测试待编码帧队列,遇到B帧时,测试的是连续的B帧,遇到第一非B帧时停止
            bframes++;//b帧计数加一
        x264_frame_push( h->frames.current, x264_frame_shift( &h->frames.next[bframes] ) );
//向当前编码帧队列中添加一个非B帧
        /* FIXME: when max B-frames > 3, BREF may no longer be centered after GOP closing */
//下面的if中是向待编码帧队列中乱取一个帧,加入当前编码帧队列,并且标记为参考帧,BREF:B帧,且为参考帧
        if( h->param.b_bframe_pyramid && bframes > 1 )//B帧

  1.     /* ------------------------ Create slice header  ----------------------- */
  2.     x264_slice_init( h, i_nal_type, i_global_qp );//初始化片,创建片头,一个片包含一个或多个宏块,片头要包含这些信息
  3.     if( i_nal_ref_idc != NAL_PRIORITY_DISPOSABLE )
  4.         h->i_frame_num++;//帧数增加



  1.     /* ---------------------- Write the bitstream -------------------------- */
  2.     /* Init bitstream context */
  3. //写入比特流,初始化比特流环境
  4.     h->out.i_nal = 0;//输出的nal个数为0
  5.     bs_init( &h->out.bs, h->out.p_bitstream, h->out.i_bitstream );  //初始化码流结构
  6.   



  1. typedef struct bs_s
  2. {
  3.     uint8_t *p_start;
  4.     uint8_t *p;
  5.     uint8_t *p_end;
  6.     int     i_left;    /* i_count number of available bits */
  7.     int     i_bits_encoded; /* RD only */
  8. } bs_t;
  9. static inline void bs_init( bs_t *s, void *p_data, int i_data )
  10. {
  11.     s->p_start = p_data;//将p_start指上输出码流的缓冲区首部
  12.     s->p       = p_data;//p指向数据本身
  13.     s->p_end   = s->p + i_data;//p_end指向数据的底部,i_data应该是数据的个数.
  14.     s->i_left  = 8;//i_left是编码的比特数为8,也就是一个字节.
  15. }

 

  1. if(h->param.b_aud){
  2.         int pic_type;
  3.         if(h->sh.i_type == SLICE_TYPE_I)  //片的类型是I片,即采用的是帧内编码,里面全是I宏块.
  4.             pic_type = 0;
  5.         else if(h->sh.i_type == SLICE_TYPE_P) //片的类型是P,里面是I宏块或P宏块,采用帧内或单向的帧间编码
  6.             pic_type = 1;
  7.         else if(h->sh.i_type == SLICE_TYPE_B) //片的类型是B,里面是B宏块,双向帧间编码
  8.             pic_type = 2;
  9.         else
  10.             pic_type = 7; //其他片类型
  11.         x264_nal_start(h, NAL_AUD, NAL_PRIORITY_DISPOSABLE);//开始写一个nal数据段
  12.         bs_write(&h->out.bs, 3, pic_type);//写入
  13.         bs_rbsp_trailing(&h->out.bs);//加上RBSP尾部
  14.         x264_nal_end(h);//结束一个数据段
  15.     }



  1. static inline void bs_write(struct bs_s * s, int i_count, uint32_t i_bits )
  2. {
  3.     if( s->p >= s->p_end - 4 )
  4.         return;
  5.     while( i_count > 0 )
  6.     {
  7.         if( i_count < 32 )
  8.             i_bits &= (1<<i_count)-1;
  9.         if( i_count < s->i_left )
  10.         {
  11.             *s->p = (*s->p << i_count) | i_bits;//写入i_count个位,这些位的值为i_bits
  12.             s->i_left -= i_count;
  13.             break;
  14.         }
  15.         else
  16.         {
  17.             *s->p = (*s->p << s->i_left) | (i_bits >> (i_count - s->i_left));
  18.             i_count -= s->i_left;
  19.             s->p++;
  20.             s->i_left = 8;
  21.         }
  22.     }
  23. }
  1. /* Write SPS and PPS 写入序列参数集和图像参数集*/
  2.     if( i_nal_type == NAL_SLICE_IDR && h->param.b_repeat_headers )
  3.     {
  4.         if( h->fenc->i_frame == 0 )
  5.         {
  6.             /* identify ourself */
  7.             x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
  8.             x264_sei_version_write( h, &h->out.bs );
  9.             x264_nal_end( h );
  10.         }
  11.         /* generate sequence parameters */
  12.         x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST );
  13.         x264_sps_write( &h->out.bs, h->sps );//写入序列参数集
  14.         x264_nal_end( h );
  15.         /* generate picture parameters */
  16.         x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST );
  17.         x264_pps_write( &h->out.bs, h->pps );//写入图像参数集
  18.         x264_nal_end( h );
  19.     }



  1. /* Write frame */
  2.     if( h->param.i_threads > 1 )
  3.     {
  4.         x264_pthread_create( &h->thread_handle, NULL, (void*)x264_slices_write, h );//创建一个线程来写入一帧数据
  5.         h->b_thread_active = 1;
  6.     }
  7.     else
  8.         x264_slices_write( h );//写入一帧数据
posted @ 2015-01-13 09:38  lihaiping  阅读(984)  评论(0编辑  收藏  举报