• (转)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 " );
            }
            else
            {
                x264_log( h, X264_LOG_INFO, "using SAR=%d/%d ", 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 ",
                 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 " );
                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 ", 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 );//写入一帧数据
  • 相关阅读:
    周杰伦理片,吴奇隆胸店,邓紫棋牌室,黄家驹留所,金秀贤鸭蛋,郭富城管员,苍井空调店,齐达内衣店,乔布斯袜店……能接下去么?
    华彬庄园_百度百科
    9月19号-9月21号丰宁坝上草原行
    寻找INTERIGHT衬衫男神! [复制链接]
    新公司,新挑战
    docker~通过vs2017的Dockerfile来生成镜像
    EF架构~codeFirst从初始化到数据库迁移
    干货~powershell与bash和docker在项目中怎么用
    docker~Dockerfile优化程序的部署
    代码混淆 GSON完满解决
  • 原文地址:https://www.cnblogs.com/lihaiping/p/4220453.html
Copyright © 2020-2023  润新知