转自: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 );  //初始化码流结构
  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 );//写入一帧数据

(转)x264代码详细阅读之x264.c,common.c,encoder.c的更多相关文章

  1. x264代码剖析(十三):核心算法之帧间预測函数x264_mb_analyse_inter_*()

    x264代码剖析(十三):核心算法之帧间预測函数x264_mb_analyse_inter_*() 帧间预測是指利用视频时间域相关性,使用临近已编码图像像素预測当前图像的像素,以达到有效去除视频时域冗 ...

  2. x264代码剖析(八):encode()函数之x264_encoder_close()函数

    x264代码剖析(八):encode()函数之x264_encoder_close()函数 encode()函数是x264的主干函数.主要包含x264_encoder_open()函数.x264_en ...

  3. x264代码剖析(三):主函数main()、解析函数parse()与编码函数encode()

    x264代码剖析(三):主函数main().解析函数parse()与编码函数encode() x264的入口函数为main().main()函数首先调用parse()解析输入的參数,然后调用encod ...

  4. x264代码剖析(十五):核心算法之宏块编码中的变换编码

    x264代码剖析(十五):核心算法之宏块编码中的变换编码 为了进一步节省图像的传输码率.须要对图像进行压缩,通常採用变换编码及量化来消除图像中的相关性以降低图像编码的动态范围.本文主要介绍变换编码的相 ...

  5. x264源代码简单分析:x264命令行工具(x264.exe)

    ===================================================== H.264源代码分析文章列表: [编码 - x264] x264源代码简单分析:概述 x26 ...

  6. Python 1行代码实现文本分类(实战笔记),含代码详细说明及运行结果

    Python 1行代码实现文本分类(实战笔记),含代码详细说明及运行结果 一.详细说明及代码 tc.py =============================================== ...

  7. Promise入门到精通(初级篇)-附代码详细讲解

    Promise入门到精通(初级篇)-附代码详细讲解 ​     Promise,中文翻译为承诺,约定,契约,从字面意思来看,这应该是类似某种协议,规定了什么事件发生的条件和触发方法. ​     Pr ...

  8. (原)x264代码中的码流控制学习

    (本文主要是自己的学习笔记,如果有误,请留言,一起讨论和更正.)这里采用x264的代码进行走读的方式,来学习qp在码流控制中过程. 在ABR模式下,当我们设置一个bitrate的平均码率以后,x264 ...

  9. SAP CRM BOL编程基础,代码+详细注释

    网络上可以找到一些使用BOL查询.维护数据的DEMO,但几乎都是单纯的代码,缺乏说明,难以理解.本文除了代码外,还给出了详细的注释,有助于理解BOL编程中的一些基本概念. 这是一篇翻译的文章,你可能会 ...

随机推荐

  1. C 字符串常量和字符串变量定义和区别

    字符串常量 定义:在一个双引号""内的字符序列或者转义字符序列称为字符串常量 例如:"HA HA!"  "abc"  "\n\t& ...

  2. spring framework 4 源代码阅读(2)---从ClassPathXmlApplicationContext開始

    Application初始化日志 15:23:12.790 [main] DEBUG o.s.core.env.StandardEnvironment - Adding [systemProperti ...

  3. Multiple Regression

    Multiple Regression What is multiple regression? Multiple regression is regression analysis with mor ...

  4. 了不起的 “filter(NULL IS NOT NULL)”

    经常会在执行计划中看到很奇怪的"FILTER"操作,然后看对应的执行信息是"filter(NULL IS NOT NULL)".  其实这是优化器非常聪明的“短 ...

  5. Oracle PLSQL Demo - 07.LOOP循环,以EXIT WHEN退出[EXIT in LOOP]

    declare v_sal ; begin loop v_sal :; dbms_output.put_line(v_sal); ; end loop; end;

  6. Oracle PLSQL Demo - 04.数字FOR LOOP循环[NUMBERABLE (FOR) LOOP]

    declare v_display ); begin .. loop .. loop dbms_output.put_line(i || ' - ' || j); end loop; end loop ...

  7. Maven工程pom.xml文件秒变gradle工程的命令

    下面是一个maven工程,我想把它转成gradle项目,怎么办? 打开cmd命令行窗口,切换到你的maven工程的pom.xml文件所在目录,然后执行如下命令: gradle init --type ...

  8. js添加收藏夹

    <a href="JavaScript:window.external.AddFavorite('http://baidu.com','百度')"> 添加到收藏夹 &l ...

  9. CentOS 7 开放防火墙端口 命令

    iptables防火墙 1.基本操作 # 查看防火墙状态 service iptables status   # 停止防火墙 service iptables stop   # 启动防火墙 servi ...

  10. LeetCode: Max Points on a Line 解题报告

    Max Points on a Line Given n points on a 2D plane, find the maximum number of points that lie on the ...