Encode函数学习(大神写的好详细)

x264.c之static int  Encode( x264_param_t *param, cli_opt_t *opt )

函数的原型声明:

  1. static int  Encode( x264_param_t *param, cli_opt_t *opt )
复制代码


static,是静态函数,网上搜了一段介绍:

在C中static有了第二种含义:用来表示不能被其它文件访问的全局变量和函数。, 但为了限制全局变量/函数的作用域, 函数或变量前加static使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。注意此时, 对于外部(全局)变量, 不论是否有static限制, 它的存储区域都是在静态存储区, 生存期都是全局的. 此时的static只是起作用域限制作用, 限定作用域在本模块(文件)内部.
使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。


函数的返回类型是int,在函数最后,返回了0:
  1. return 0;
复制代码


函数接受两个参数:
  1. x264_param_t *param, cli_opt_t *opt
复制代码

都是自定义结构体的指针,对于这类比较庞大的变量来说,一般都是按地址传递的,所以用了指针类型。
x264_param_t在x264.h里定义,cli_opt_t在x264.c里定义,因为前一个结构体太长,不往这儿放了,只放出第二个来

  1. typedef struct
  2. {
  3.     /* (CPU 标志位)*/
  4.     unsigned int cpu;
  5.     int         i_threads;  
  6.     /* 视频属性 */
  7.     int         i_width;  /* 宽度*/
  8.     int         i_height;  /* 高度*/
  9.     int         i_csp;   /* 色彩空间参数 */
  10.     int         i_level_idc; /* level值的设置 */
  11.     int         i_frame_total; /* 编码帧的总数, 默认 0 */
  12.     ... ...


  13.     /* Muxing parameters */
  14.     int b_aud;                 
  15.     int b_repeat_headers;       /*  */
  16.     int i_sps_id;               /* SPS and PPS id number */
  17. } x264_param_t;


  18. typedef struct {
  19.     int b_progress;  //用来控制是否显示编码进度的一个东西。取值为0,1
  20.     int i_seek;   //表示开始从哪一帧编码
  21.     hnd_t hin;   //
  22.     hnd_t hout;   // 
  23.     FILE *qpfile;  //
  24. } cli_opt_t; /* 此结构体是记录一些与编码关系较小的设置信息的 */
复制代码





函数体的开始,定义了一些变量,有内置类型,也有自定义的结构体对象


  1.     x264_t *h;
  2.     x264_picture_t pic;
  3.     int     i_frame, i_frame_total;
  4.     int64_t i_start, i_end; //从i_start开始编码,直到i_end结束
  5.     int64_t i_file;
  6.     int     i_frame_size;
  7.     int     i_progress;
复制代码


x264_t也是一个很庞大的结构体:
在common.h里定义:

  1. struct x264_t
  2. {
  3. /* encoder parameters ( 编码器参数 )*/
  4. x264_param_t param;

  5. x264_t *thread[X264_SLICE_MAX];

  6. ... ...

  7.     /* vlc table for decoding purpose only */
  8.     x264_vlc_table_t *x264_coeff_token_lookup[5];
  9.     x264_vlc_table_t *x264_level_prefix_lookup;
  10.     x264_vlc_table_t *x264_total_zeros_lookup[15];
  11.     x264_vlc_table_t *x264_total_zeros_dc_lookup[3];
  12.     x264_vlc_table_t *x264_run_before_lookup[7];
  13.     #if VISUALIZE
  14.     struct visualize_t *visualize;
  15.     #endif
  16. };
复制代码


x264_picture_t的定义相对来说简单些(在x264.h):
  1. typedef struct
  2. {
  3. int i_type;
  4. int i_qpplus1;
  5. int64_t i_pts;

  6. x264_image_t img;
  7. } x264_picture_t;
  8. 这里前三个都是普通的类型,第4个字段是结构体对象x264_image_t

  9. typedef struct
  10. {
  11.     int     i_csp;
  12.     int     i_plane;
  13.     int     i_stride[4];
  14.     uint8_t *plane[4];
  15. } x264_image_t;

  16. 我们也可以把它合起来看,效果是一样的,把它看成是有7个字段的结构体:

  17. typedef struct
  18. {
  19. int i_type;
  20. int i_qpplus1;
  21. int64_t i_pts;

  22. int     i_csp;
  23. int     i_plane;
  24. int     i_stride[4];
  25. uint8_t *plane[4];
  26. } x264_picture_t;

  27. 或者换一种方法来描述:

  28. typedef struct
  29. {
  30. int i_type;
  31. int i_qpplus1;
  32. int64_t i_pts;

  33. //x264_image_t img;
  34.     typedef struct
  35.     {
  36.         int     i_csp;
  37.         int     i_plane;
  38.         int     i_stride[4];
  39.         uint8_t *plane[4];
  40.     } x264_image_t;

  41. } x264_picture_t;

  42. 这种好象叫结构体的嵌套,当然原来的那种和这两种意思一样,但是实际效果在某些方面是有区别的。具体怎么样我也说不大清。
复制代码


开始的其它几个变量就都是普通的类型了,具体的作用,从变量的名称上能看出个大概来。
i_frame--统计已编码的总帧数
i_frame_total-----总共有多少帧要编码(可能会小于视频文件的总帧数)
i_start------起始帧(比如,从第101帧开始编码)
i_end------结束帧(比如,比如,从第101帧开始编码,到第200帧结束,文件可能一共有几万帧)


  1. i_frame_total = p_get_frame_total( opt->hin );
复制代码


赋值号左边是变量,i_frame_total,右边是函数p_get_frame_total,提供的参数是opt->hin,而opt 是调用Encode函数必须提供的第2个参数,
根据一般的命名规则看,这个hin应该是一个句柄,实际上它是指向一个YUV文件的指针,也就不难理解,赋值号右侧是提供一个文件句柄,它就可以获取到这个文件一共有多少帧。
实际上,x264是要编码,我们需要的是编码多少帧,并不在意文件总共有多少帧,即使用到,也是一个过渡。
上面提到,i_frame_total----是总共有多少帧要编码,当然还没有确认,只是大概的判断是这么个意思。

至于p_get_frame_total的具体实现,定位到这段代码:
  1. int (*p_open_infile)( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param );
复制代码

怎么看都觉得别扭,先不深究了

  1. i_frame_total -= opt->i_seek;
复制代码


这个是用到了C++的复合运算符,改写成最普通的式子:


  1. i_frame_total = i_frame_total - opt->i_seek;
复制代码


int i_seek;   //表示开始从哪一帧编码,文件的总帧数 - 开始编码的起始帧数,这个结果还不是要编码的总帧数,因为要编码的最后一帧不一定是文件的最后一帧。



  1.     if( ( i_frame_total == 0 || param->i_frame_total < i_frame_total ) && param->i_frame_total > 0 )
  2.         i_frame_total = param->i_frame_total;
复制代码


这句进行了一个判断,用到了逻辑或(||)和逻辑与(&&)操作符,判断的结果如果为真(在C++中非0即为真,比如2也代表真)
那么就进行赋值操作,说白了,就是为了保证这个i_frame_total是一个合情合理的数,不要整出个没意义的来。

但是这几个i_frame_total让人有点迷糊,因为跨度太远了,前面的这个param->i_frame_total 是怎么得到的有点忘了,只是记的处理命令行参数时,有个解析命令行的过程,估计是那个时候填充这个字段的,还得回去翻翻。

在main函数中,调用了函数x264_param_default( &param );进行参数初始化,但是在这个函数里并没有针对i_frame_total初始化或者赋值(应该叫赋值吧,定义后再指定值应该是叫赋值)。
也就是说,调用x264_param_default( &param );后,i_frame_total实际是int型的默认值:0;
随后还有一个命令行的解析,调用的语句为:

  1. Parse( argc, argv, &param, &opt )
复制代码


在Parse里涉及到了i_frame_total:



  1. ... ...

  2.   #define OPT_FRAMES 256
  3. ... ...
  4.             case OPT_FRAMES:
  5.                 param->i_frame_total = atoi( optarg );
  6.                 break;
复制代码


从这儿也只是看出进行了字段填充,也看不出什么来,还得涉及到命令行的格式和解析,我记的命令行参数的实现,是把参数作为一个字符串来处理,收到后再用代码进行分析,从而实现命令的分情况处理。

在源码中,Parse函数内部调用了下面一条语句:
  1.         int c = getopt_long( argc, argv, "8A:B:b:f:hI:i:m:o:p:q:r:t:Vvw", long_options, &long_options_index);
复制代码

这个getopt_long的实现在“extras/getopt.c”文件中,

  1. /*
  2. * getopt_long --
  3. * Parse argc/argv argument vector.
  4. */
  5. int
  6. getopt_long(nargc, nargv, options, long_options, idx)
  7. int nargc;
  8. char * const *nargv;
  9. const char *options;
  10. const struct option *long_options;
  11. int *idx;
  12. {
  13. int retval;
  14. _DIAGASSERT(nargv != NULL);
  15. _DIAGASSERT(options != NULL);
  16. _DIAGASSERT(long_options != NULL);
  17. /* idx may be NULL */
  18. if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
  19.   char *current_argv, *has_equal;
  20.   size_t current_argv_len;
  21.   int i, match;
  22.   current_argv = place;
  23.   match = -1;
  24.   optind++;
  25.   place = EMSG;
  26.   if (*current_argv == '\0') {  /* found "--" */
  27.    /*
  28.     * We found an option (--), so if we skipped
  29.     * non-options, we have to permute.
  30.     */
  31.    if (nonopt_end != -1) {
  32.     permute_args(nonopt_start, nonopt_end,
  33.         optind, nargv);
  34.     optind -= nonopt_end - nonopt_start;
  35.    }
  36.    nonopt_start = nonopt_end = -1;
  37.    return -1;
  38.   }
  39.   if ((has_equal = strchr(current_argv, '=')) != NULL) {
  40.    /* argument found (--option=arg) */
  41.    current_argv_len = has_equal - current_argv;
  42.    has_equal++;
  43.   } else
  44.    current_argv_len = strlen(current_argv);
  45.   for (i = 0; long_options[i].name; i++) {
  46.    /* find matching long option */
  47.    if (strncmp(current_argv, long_options[i].name,
  48.        current_argv_len))
  49.     continue;
  50.    if (strlen(long_options[i].name) ==
  51.        (unsigned)current_argv_len) {
  52.     /* exact match */
  53.     match = i;
  54.     break;
  55.    }
  56.    if (match == -1)  /* partial match */
  57.     match = i;
  58.    else {
  59.     /* ambiguous abbreviation */
  60.     if (PRINT_ERROR)
  61.      warnx(ambig, (int)current_argv_len,
  62.           current_argv);
  63.     optopt = 0;
  64.     return BADCH;
  65.    }
  66.   }
  67.   if (match != -1) {   /* option found */
  68.    if (long_options[match].has_arg == no_argument
  69.        && has_equal) {
  70.     if (PRINT_ERROR)
  71.      warnx(noarg, (int)current_argv_len,
  72.           current_argv);
  73.     /*
  74.      * XXX: GNU sets optopt to val regardless of
  75.      * flag
  76.      */
  77.     if (long_options[match].flag == NULL)
  78.      optopt = long_options[match].val;
  79.     else
  80.      optopt = 0;
  81.     return BADARG;
  82.    }
  83.    if (long_options[match].has_arg == required_argument ||
  84.        long_options[match].has_arg == optional_argument) {
  85.     if (has_equal)
  86.      optarg = has_equal;
  87.     else if (long_options[match].has_arg ==
  88.         required_argument) {
  89.      /*
  90.       * optional argument doesn't use
  91.       * next nargv
  92.       */
  93.      optarg = nargv[optind++];
  94.     }
  95.    }
  96.    if ((long_options[match].has_arg == required_argument)
  97.        && (optarg == NULL)) {
  98.     /*
  99.      * Missing argument; leading ':'
  100.      * indicates no error should be generated
  101.      */
  102.     if (PRINT_ERROR)
  103.      warnx(recargstring, current_argv);
  104.     /*
  105.      * XXX: GNU sets optopt to val regardless
  106.      * of flag
  107.      */
  108.     if (long_options[match].flag == NULL)
  109.      optopt = long_options[match].val;
  110.     else
  111.      optopt = 0;
  112.     --optind;
  113.     return BADARG;
  114.    }
  115.   } else {   /* unknown option */
  116.    if (PRINT_ERROR)
  117.     warnx(illoptstring, current_argv);
  118.    optopt = 0;
  119.    return BADCH;
  120.   }
  121.   if (long_options[match].flag) {
  122.    *long_options[match].flag = long_options[match].val;
  123.    retval = 0;
  124.   } else
  125.    retval = long_options[match].val;
  126.   if (idx)
  127.    *idx = match;
  128. }
  129. return retval;
  130. }
复制代码

当然,上面这个函数有点扯远了,因为这段代码隐藏的比较深,所以先记在这儿,在代码中定位的时候只能定位到一个extern声明。
因为在Linux下,这个函数好象是系统带的,网上说windows下没有这个函数,得自己实现,所以多看了一眼。

大概的可以判断,param->i_frame_total最开始存的是命令行里输入的要编码的总帧数,比如我只编码视频中间的一截,可以指定编码100帧,而一个视频文件可能够播一个小时,当然远远不止这100帧。
当然,也有可能一个文件只有100帧,但是命令行中输出了500帧,这就是错误的了,指定编的总帧数超出了实际的物理帧数。

就需要在代码中判断,如果超范围了,就指定为一个最大的物理范围,即文件的总帧数,更确切的说,是文件的总帧数减去起始的帧余下的帧范围。

这样折腾只是为了多了解一下代码,其实不看代码也能猜想到该是什么意思。

在命令行输入从哪帧开始编码,一共编码多少帧解析命令行时,把用户的输入保存下来进行判断,确定要编码的总帧数是一个合理的范围 文件的总帧数-开始帧数,就是这个范围的上限,如果超出这个上限,就用这个上限,如果没超出,就用命令行的输入值 当然还有个情况,开始帧可能也会超出文件的总帧数,也需要判断







  1. if( ( h = x264_encoder_open( param ) ) == NULL )
复制代码


打开一个编码器,同时判断是否打开成功,左边是赋值语句,接收打开编码器返回的值,右边是比较判断,如果为NULL,说明打开失败

该函数的实现在“encoder/encoder.c”中:

  1. /****************************************************************************
  2. * x264_encoder_open:
  3. ****************************************************************************/
  4. x264_t *x264_encoder_open   ( x264_param_t *param )
  5. {
  6.     x264_t *h = x264_malloc( sizeof( x264_t ) ); /* 分配空间并进行初始化,x264_malloc( )在common.c中 */
  7.     int i;
  8.     memset( h, 0, sizeof( x264_t ) ); /*  */
  9.     /* Create a copy of param */
  10.     memcpy( &h->param, param, sizeof( x264_param_t ) ); /*  */
  11.     if( x264_validate_parameters( h ) < 0 ) /* 函数x264_validate_parameters( h )在encoder.c中,功能为判断参数是否有效,并对不合适的参数进行修改 */
  12.     {
  13.         x264_free( h ); /*  */
  14.         return NULL;
  15.     }
  16.     if( h->param.psz_cqm_file ) /*  */
  17.         if( x264_cqm_parse_file( h, h->param.psz_cqm_file ) < 0 ) /*  */
  18.         {
  19.             x264_free( h );
  20.             return NULL;
  21.         }
  22.     if( h->param.rc.psz_stat_out ) /*  */
  23.         h->param.rc.psz_stat_out = strdup( h->param.rc.psz_stat_out ); /*  */
  24.     if( h->param.rc.psz_stat_in ) /*  */
  25.         h->param.rc.psz_stat_in = strdup( h->param.rc.psz_stat_in ); /*  */
  26.     if( h->param.rc.psz_rc_eq ) /*  */
  27.         h->param.rc.psz_rc_eq = strdup( h->param.rc.psz_rc_eq ); /*  */
  28.     /* VUI */
  29.     if( h->param.vui.i_sar_width > 0 && h->param.vui.i_sar_height > 0 ) /*  */
  30.     {
  31.         int i_w = param->vui.i_sar_width; /*  */
  32.         int i_h = param->vui.i_sar_height;
  33.         x264_reduce_fraction( &i_w, &i_h ); /*  */
  34.         while( i_w > 65535 || i_h > 65535 )
  35.         {
  36.             i_w /= 2;
  37.             i_h /= 2;
  38.         }
  39.         h->param.vui.i_sar_width = 0;
  40.         h->param.vui.i_sar_height = 0;
  41.         if( i_w == 0 || i_h == 0 )
  42.         {
  43.             x264_log( h, X264_LOG_WARNING, "cannot create valid sample aspect ratio\n" ); /*  */
  44.         }
  45.         else
  46.         {
  47.             x264_log( h, X264_LOG_INFO, "using SAR=%d/%d\n", i_w, i_h ); /*  */
  48.             h->param.vui.i_sar_width = i_w;
  49.             h->param.vui.i_sar_height = i_h;
  50.         }
  51.     }
  52.     x264_reduce_fraction( &h->param.i_fps_num, &h->param.i_fps_den ); /*  */

  53.     ... ...
  54.     return h;
  55. }
复制代码


这个函数很复杂,先不理会它的内部了,从后续的过程看,它只是进行了一些准备工作,并没有实际进行编码,因为后面有一个是对每一帧编码的循环过程。

  1. if( ( h = x264_encoder_open( param ) ) == NULL ) 
  2. {
  3. fprintf( stderr, "x264 [error]: x264_encoder_open failed\n" ); 
  4. p_close_infile( opt->hin ); 
  5. p_close_outfile( opt->hout ); 
  6. return -1;
  7. }
复制代码


判断创建编码器失败的话就关闭打开的输入和输出文件,并返回-1

  1. if( p_set_outfile_param( opt->hout, param ) ) 
  2. {
  3. fprintf( stderr, "x264 [error]: can't set outfile param\n" );
  4. p_close_infile( opt->hin ); 
  5. p_close_outfile( opt->hout ); 
  6. return -1;
  7. }
复制代码


设置输出文件参数,如果失败的话,仍然是输出出错提示并关闭输入文件和输出文件,来看看这三个函数
p_set_outfile_param
p_close_infile
p_close_outfile


  1. [code]static int (*p_open_outfile)( char *psz_filename, hnd_t *p_handle );
  2. static int (*p_set_outfile_param)( hnd_t handle, x264_param_t *p_param );
  3. static int (*p_close_outfile)( hnd_t handle );
复制代码


int (*p_open_infile)( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param );
int (*p_close_infile)( hnd_t handle );
[/code]

这是在x264.c里声明的函数原型。
奇怪的是怎么也搜不到具体的实现代码。


  1. i_start = x264_mdate();
复制代码

该函数的声明是在common/common.h中
  1. int64_t x264_mdate( void );
复制代码

该函数的实现代码是在common/mdate.c中

  1. int64_t x264_mdate( void )
  2. {
  3. #if !(defined(_MSC_VER) || defined(__MINGW32__))
  4.     struct timeval tv_date;
  5.     gettimeofday( &tv_date, NULL );
  6.     return( (int64_t) tv_date.tv_sec * 1000000 + (int64_t) tv_date.tv_usec );
  7. #else
  8.     struct _timeb tb;
  9.     _ftime(&tb);
  10.     return ((int64_t)tb.time * (1000) + (int64_t)tb.millitm) * (1000);
  11. #endif
  12. }
复制代码


大略的意思是返回当前时间,用毫秒表示的,不是很清楚,反正是开始编码的时间,后面会再取到一个结束时间,然后求差,来计算编码速度。就这么个意思。
上面这个函数呢,没看懂,只能大略的这样理解了。

接下来,是个for循环,实质性的编码就在这儿展开,它循环的取每一帧,然后调用某个函数进行编码,如此反复,直到完成工作或者出错。


  1.     /* (循环编码每一帧) Encode frames */
  2.     for( i_frame = 0, i_file = 0, i_progress = 0; b_ctrl_c == 0 && (i_frame < i_frame_total || i_frame_total == 0); )
  3.     {
  4.         if( p_read_frame( &pic, opt->hin, i_frame + opt->i_seek ) )/* 读入第i_frame帧,从起始帧算,而不是视频的第0帧 */
  5.             break;
  6.         pic.i_pts = (int64_t)i_frame * param->i_fps_den;
  7.         if( opt->qpfile )
  8.             parse_qpfile( opt, &pic, i_frame + opt->i_seek );
  9.         else
  10.         {
  11.             /* Do not force any parameters */
  12.             pic.i_type = X264_TYPE_AUTO;
  13.             pic.i_qpplus1 = 0;
  14.         }
  15.         i_file += Encode_frame( h, opt->hout, &pic );
  16.         i_frame++;//已编码的帧数统计(递增1)
  17.         /* 更新状态提示 update status line (up to 1000 times per input file) */
  18.         if( opt->b_progress && param->i_log_level < X264_LOG_DEBUG && 
  19.             ( i_frame_total ? i_frame * 1000 / i_frame_total > i_progress
  20.                             : i_frame % 10 == 0 ) )
  21.         {
  22.             int64_t i_elapsed = x264_mdate() - i_start;
  23.             double fps = i_elapsed > 0 ? i_frame * 1000000. / i_elapsed : 0;
  24.             if( i_frame_total )
  25.             {
  26.                 int eta = i_elapsed * (i_frame_total - i_frame) / ((int64_t)i_frame * 1000000);
  27.                 i_progress = i_frame * 1000 / i_frame_total;
  28.                 fprintf( stderr, "encoded frames: %d/%d (%.1f%%), %.2f fps, eta %d:%02d:%02d  \r", /* 状态提示 */
  29.                          i_frame, i_frame_total, (float)i_progress / 10, fps,
  30.                          eta/3600, (eta/60)%60, eta%60 );
  31.             }
  32.             else
  33.                 fprintf( stderr, "encoded frames: %d, %.2f fps   \r", i_frame, fps ); /* 状态提示,共编码了... */
  34.             fflush( stderr ); // needed in windows
  35.        }
  36.     }
复制代码


一开始就试过命令行执行x264.exe,给定一个输入文件和一个输出文件,最后编码输出,把编码的结果会输出到给定的输出文件。
所以在这个循环中,某一步中肯定会有一个写文件的动作,当然,可能不是在这段中直接用到,可能是调用的某个函数中也是有可能的。


  1. for( i_frame = 0, i_file = 0, i_progress = 0; b_ctrl_c == 0 && (i_frame < i_frame_total || i_frame_total == 0); )
复制代码

平常见的都是

  1. for (int i = 0; i<100;i++)
  2. {
  3. ... ...
  4. }
复制代码

这样的循环语句,这儿怎么是这样子。

  1. for( i_frame = 0, i_file = 0, i_progress = 0; b_ctrl_c == 0 && (i_frame < i_frame_total || i_frame_total == 0); )
复制代码


这个语句的意思是:

进入for循环时,
第一步:执行三个变量的赋值, i_frame = 0, i_file = 0, i_progress = 0;
第二步:判断是否退出循环的条件为b_ctrl_c == 0 && (i_frame < i_frame_total || i_frame_total == 0); 
第三步:平时的递增省略了,由for函数体内的语句人为的进行递增操作。


  1.         if( p_read_frame( &pic, opt->hin, i_frame + opt->i_seek ) ) /* 读入第i_frame帧,从起始帧算,而不是视频的第0帧 */
  2.             break;
复制代码


该函数的声明在x264.c
  1. int (*p_read_frame)( x264_picture_t *p_pic, hnd_t handle, int i_frame );
复制代码

第1个参数为结构体的指针,读到的像素值将填充它,第2个参数是句柄,指定读哪个文件,第3个参数指定读哪一帧,其中第3个参数帧序号是递增的,递增的实现在后面的一句语句:
  1. i_frame++;
复制代码

至于if后的break;因为p_read_frame的类型是int,当返回值非0时退出for循环,所以当读取帧内容正确的时候,它返回的应该是0,因为现在还没找到它的具体的实现代码,所以这样判断一下了。


  1. pic.i_pts = (int64_t)i_frame * param->i_fps_den;
复制代码


这句是什么意思啊????


  1. if( opt->qpfile )
复制代码


如果opt->qpfile非0,那么执行后续的语句,这个qpfile是结构体cli_opt_t的字段之一:
  1. typedef struct {
  2.     int b_progress;  //用来控制是否显示编码进度的一个东西。取值为0,1
  3.     int i_seek;   //表示开始从哪一帧编码
  4.     hnd_t hin;   //Hin 指向输入yuv文件的指针 //void *在C语言里空指针是有几个特性的,他是一个一般化指针,可以指向任何一种类型,但却不能解引用,需要解引用的时候,需要进行强制转换。采用空指针的策略,应该是为了声明变量的简便和统一。
  5.     hnd_t hout;   //Hout 指向编码过后生成的文件的指针
  6.     FILE *qpfile;  //Qpfile 是一个指向文件类型的指针,他是文本文件,其每一行的格式是framenum frametype QP,用于强制指定某些帧或者全部帧的帧类型和QP(quant param量化参数)的值
  7. } cli_opt_t; /* 此结构体是记录一些与编码关系较小的设置信息的 */
复制代码

这个qpfile指的这个文件是什么意思,不是说那个yuv文件吗,另外还有一个文件????


  1.             pic.i_type = X264_TYPE_AUTO;
  2.             pic.i_qpplus1 = 0;//I_qpplus1 :此参数减1代表当前画面的量化参数值
复制代码


其中的i_qpplus1,此参数减1代表当前画面的量化参数值,那么当前画面的量化参数值是-1?真的是负数?


  1.         i_file += Encode_frame( h, opt->hout, &pic );
复制代码


这是真正的调用这个函数对这一帧进行编码,该函数的第二个参数,看样子是指向输出文件,第三个参数里,存储着待编码的原始数据,当然还有其它一些相关的字段,第一个参数是一个结构。

该函数的原型声明及实现:

  1. static int  Encode_frame( x264_t *h, hnd_t hout, x264_picture_t *pic )
  2. {
  3.     x264_picture_t pic_out;
  4.     x264_nal_t *nal;
  5.     int i_nal, i;
  6.     int i_file = 0;
  7.     if( x264_encoder_encode( h, &nal, &i_nal, pic, &pic_out ) < 0 )
  8.     {
  9.         fprintf( stderr, "x264 [error]: x264_encoder_encode failed\n" );
  10.     }
  11.     for( i = 0; i < i_nal; i++ )
  12.     {
  13.         int i_size;
  14.         int i_data;
  15.         i_data = DATA_MAX;
  16.         if( ( i_size = x264_nal_encode( data, &i_data, 1, &nal[i] ) ) > 0 )
  17.         {
  18.             i_file += p_write_nalu( hout, data, i_size );
  19.         }
  20.         else if( i_size < 0 )
  21.         {
  22.             fprintf( stderr, "x264 [error]: need to increase buffer size (size=%d)\n", -i_size );
  23.         }
  24.     }
  25.     if (i_nal)
  26.         p_set_eop( hout, &pic_out );
  27.     return i_file;
  28. }
复制代码

这个先不深究,因为大概看了一下,看一明白,继续

  1. /* Flush delayed(延迟) B-frames */
  2. do {
  3. i_file +=
  4. i_frame_size = Encode_frame( h, opt->hout, NULL );//上面调用的是i_file += Encode_frame( h, opt->hout, &pic );
  5. } while( i_frame_size );
复制代码


这里面又循环调用了 Encode_frame( h, opt->hout, NULL );,前面刚刚循环过,两次的差别只是最后一个参数
  1. i_file += Encode_frame( h, opt->hout, &pic );

  2. i_file +=
  3.         i_frame_size = Encode_frame( h, opt->hout, NULL );
复制代码

第1个一开始认为是对帧进行了编码,那第二次调呢,又是做什么,糊涂了。

至于后面的代码,就是进行一些状态的提示。并关闭输入和输出文件。因为这是main函数调用的最后一步了。
这个函数里有好几个地方不懂,用特殊颜色标出来了,以后慢慢找答案吧。

再去转一个文件,看看状态提示什么的。

TM截图未命名.jpg 



x264_picture_clean的实现代码在common/common.c中

  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. }
复制代码

memset是把一块内存设为某个值,这里全设置成了0。这是为了防止下次再用这块内存的时候忘了初始化,上次的结果会带到下次。不会描述了,就那么个意思,比如下次定义了一个变量,本来应该是NULL或者0,但是未初始化它,本来不应该是其它值的,结果这块内存上次用完未还原,结果新定义的变量可能就不是NULL或者为0,而是一个意想不到的值,这个值和上次使用完时的状态有关。是个不确定的值。
  1. void x264_free( void *p )
  2. {
  3. if( p )
  4. {
  5. #if defined( HAVE_MALLOC_H ) || defined( SYS_MACOSX )
  6. free( p );
  7. #else
  8. free( *( ( ( void **) p ) - 1 ) );
  9. #endif
  10. }
  11. }
复制代码

  1. x264_encoder_close( h );
复制代码

关闭编码器,前面有个对应的函数调用,是打开编码器


  1. if( ( h = x264_encoder_open( param ) ) == NULL )
  2. ... ...
  3. x264_encoder_close( h );
复制代码


x264_encoder_close在encoder/encoder.c中实现,也是非常的复杂。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值