程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> x264源代碼簡單分析:x264命令行工具(x264.exe)

x264源代碼簡單分析:x264命令行工具(x264.exe)

編輯:關於C++

本文簡單分析x264項目中的命令行工具(x264.exe)的源代碼。該命令行工具可以調用libx264將YUV格式像素數據編碼為H.264碼流。

 

 

函數調用關系圖

X264命令行工具的源代碼在x264中的位置如下圖所示。

/

單擊查看更清晰的圖片
X264命令行工具的源代碼的調用關系如下圖所示。
/ 單擊查看更清晰的圖片
從圖中可以看出,X264命令行工具調用了libx264的幾個API完成了H.264編碼工作。使用libx264的API進行編碼可以參考《最簡單的視頻編碼器:基於libx264(編碼YUV為H.264)》,這個流程中最關鍵的API包括:
x264_param_default():設置參數集結構體x264_param_t的缺省值。
x264_encoder_open():打開編碼器。
x264_encoder_headers():輸出SPS,PPS,SEI等信息。
x264_encoder_encode():編碼輸出一幀圖像。
x264_encoder_close():關閉編碼器。

在X264命令行工具中,main()首先調用parse()解析輸入的命令行參數,然後調用encode()進行編碼。parse()首先調用x264_param_default()為存儲參數的結構體x264_param_t賦默認值;然後在一個大循環中調用getopt_long()逐個解析輸入的參數,並作相應的處理;最後調用select_input()和select_output()解析輸入文件格式(例如yuv,y4m…)和輸出文件格式(例如raw,flv,MP4…)。encode()首先調用x264_encoder_open()打開H.264編碼器,然後調用x264_encoder_headers()輸出H.264碼流的頭信息(例如SPS、PPS、SEI),接著進入一個循環並且調用encode_frame()逐幀編碼視頻,最後調用x264_encoder_close()關閉解碼器。其中encode_frame()中又調用了x264_encoder_encode()完成了具體的編碼工作。下文將會對上述流程展開分析。

 

 

main()

main()是x264控制台程序的入口函數,定義如下所示。
//主函數
int main( int argc, char **argv )
{
	//參數集
    x264_param_t param;
    cli_opt_t opt = {0};
    int ret = 0;

    FAIL_IF_ERROR( x264_threading_init(), unable to initialize threading
 )

#ifdef _WIN32
    FAIL_IF_ERROR( !get_argv_utf8( &argc, &argv ), unable to convert command line to UTF-8
 )

    GetConsoleTitleW( org_console_title, CONSOLE_TITLE_SIZE );
    _setmode( _fileno( stdin ),  _O_BINARY );
    _setmode( _fileno( stdout ), _O_BINARY );
    _setmode( _fileno( stderr ), _O_BINARY );
#endif

    /* Parse command line */
    //解析命令行輸入
    if( parse( argc, argv, ¶m, &opt ) < 0 )
        ret = -1;

#ifdef _WIN32
    /* Restore title; it can be changed by input modules */
    SetConsoleTitleW( org_console_title );
#endif

    /* Control-C handler */
    signal( SIGINT, sigint_handler );
    //編碼
    if( !ret )
        ret = encode( ¶m, &opt );

    /* clean up handles */
    if( filter.free )
        filter.free( opt.hin );
    else if( opt.hin )
        cli_input.close_file( opt.hin );
    if( opt.hout )
        cli_output.close_file( opt.hout, 0, 0 );
    if( opt.tcfile_out )
        fclose( opt.tcfile_out );
    if( opt.qpfile )
        fclose( opt.qpfile );

#ifdef _WIN32
    SetConsoleTitleW( org_console_title );
    free( argv );
#endif

    return ret;
}

可以看出main()的定義很簡單,它主要調用了兩個函數:parse()和encode()。main()首先調用parse()解析輸入的命令行參數,然後調用encode()進行編碼。下面分別分析這兩個函數。

 

 

parse()

parse()用於解析命令行輸入的參數(存儲於argv[]中)。它的定義如下所示。
//解析命令行輸入
static int parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt )
{
    char *input_filename = NULL;
    const char *demuxer = demuxer_names[0];
    char *output_filename = NULL;
    const char *muxer = muxer_names[0];
    char *tcfile_name = NULL;
    x264_param_t defaults;
    char *profile = NULL;
    char *vid_filters = NULL;
    int b_thread_input = 0;
    int b_turbo = 1;
    int b_user_ref = 0;
    int b_user_fps = 0;
    int b_user_interlaced = 0;
    cli_input_opt_t input_opt;
    cli_output_opt_t output_opt;
    char *preset = NULL;
    char *tune = NULL;
    //初始化參數默認值
    x264_param_default( &defaults );
    cli_log_level = defaults.i_log_level;

    memset( &input_opt, 0, sizeof(cli_input_opt_t) );
    memset( &output_opt, 0, sizeof(cli_output_opt_t) );
    input_opt.bit_depth = 8;
    input_opt.input_range = input_opt.output_range = param->vui.b_fullrange = RANGE_AUTO;
    int output_csp = defaults.i_csp;
    opt->b_progress = 1;

    /* Presets are applied before all other options. */
    for( optind = 0;; )
    {
        int c = getopt_long( argc, argv, short_options, long_options, NULL );
        if( c == -1 )
            break;
        if( c == OPT_PRESET )
            preset = optarg;
        if( c == OPT_TUNE )
            tune = optarg;
        else if( c == '?' )
            return -1;
    }

    if( preset && !strcasecmp( preset, placebo ) )
        b_turbo = 0;
    //設置preset,tune
    if( x264_param_default_preset( param, preset, tune ) < 0 )
        return -1;

    /* Parse command line options */
    //解析命令行選項
    for( optind = 0;; )
    {
        int b_error = 0;
        int long_options_index = -1;

        int c = getopt_long( argc, argv, short_options, long_options, &long_options_index );

        if( c == -1 )
        {
            break;
        }
        //不同的選項做不同的處理
        switch( c )
        {
            case 'h':
                help( &defaults, 0 );//-h幫助菜單
                exit(0);
            case OPT_LONGHELP:
                help( &defaults, 1 );
                exit(0);
            case OPT_FULLHELP:
                help( &defaults, 2 );
                exit(0);
            case 'V':
                print_version_info();//打印版本信息
                exit(0);
            case OPT_FRAMES:
                param->i_frame_total = X264_MAX( atoi( optarg ), 0 );
                break;
            case OPT_SEEK:
                opt->i_seek = X264_MAX( atoi( optarg ), 0 );
                break;
            case 'o':
                output_filename = optarg;//輸出文件路徑
                break;
            case OPT_MUXER:
                FAIL_IF_ERROR( parse_enum_name( optarg, muxer_names, &muxer ), Unknown muxer `%s'
, optarg )
                break;
            case OPT_DEMUXER:
                FAIL_IF_ERROR( parse_enum_name( optarg, demuxer_names, &demuxer ), Unknown demuxer `%s'
, optarg )
                break;
            case OPT_INDEX:
                input_opt.index_file = optarg;
                break;
            case OPT_QPFILE:
                opt->qpfile = x264_fopen( optarg, rb );
                FAIL_IF_ERROR( !opt->qpfile, can't open qpfile `%s'
, optarg )
                if( !x264_is_regular_file( opt->qpfile ) )
                {
                    x264_cli_log( x264, X264_LOG_ERROR, qpfile incompatible with non-regular file `%s'
, optarg );
                    fclose( opt->qpfile );
                    return -1;
                }
                break;
            case OPT_THREAD_INPUT:
                b_thread_input = 1;
                break;
            case OPT_QUIET:
                cli_log_level = param->i_log_level = X264_LOG_NONE;//設置log級別
                break;
            case 'v':
                cli_log_level = param->i_log_level = X264_LOG_DEBUG;//設置log級別
                break;
            case OPT_LOG_LEVEL:
                if( !parse_enum_value( optarg, log_level_names, &cli_log_level ) )
                    cli_log_level += X264_LOG_NONE;
                else
                    cli_log_level = atoi( optarg );
                param->i_log_level = cli_log_level;//設置log級別
                break;
            case OPT_NOPROGRESS:
                opt->b_progress = 0;
                break;
            case OPT_TUNE:
            case OPT_PRESET:
                break;
            case OPT_PROFILE:
                profile = optarg;
                break;
            case OPT_SLOWFIRSTPASS:
                b_turbo = 0;
                break;
            case 'r':
                b_user_ref = 1;
                goto generic_option;
            case OPT_FPS:
                b_user_fps = 1;
                param->b_vfr_input = 0;
                goto generic_option;
            case OPT_INTERLACED:
                b_user_interlaced = 1;
                goto generic_option;
            case OPT_TCFILE_IN:
                tcfile_name = optarg;
                break;
            case OPT_TCFILE_OUT:
                opt->tcfile_out = x264_fopen( optarg, wb );
                FAIL_IF_ERROR( !opt->tcfile_out, can't open `%s'
, optarg )
                break;
            case OPT_TIMEBASE:
                input_opt.timebase = optarg;
                break;
            case OPT_PULLDOWN:
                FAIL_IF_ERROR( parse_enum_value( optarg, pulldown_names, &opt->i_pulldown ), Unknown pulldown `%s'
, optarg )
                break;
            case OPT_VIDEO_FILTER:
                vid_filters = optarg;
                break;
            case OPT_INPUT_FMT:
                input_opt.format = optarg;//輸入文件格式
                break;
            case OPT_INPUT_RES:
                input_opt.resolution = optarg;//輸入分辨率
                break;
            case OPT_INPUT_CSP:
                input_opt.colorspace = optarg;//輸入色域
                break;
            case OPT_INPUT_DEPTH:
                input_opt.bit_depth = atoi( optarg );//輸入顏色位深
                break;
            case OPT_DTS_COMPRESSION:
                output_opt.use_dts_compress = 1;
                break;
            case OPT_OUTPUT_CSP:
                FAIL_IF_ERROR( parse_enum_value( optarg, output_csp_names, &output_csp ), Unknown output csp `%s'
, optarg )
                // correct the parsed value to the libx264 csp value
#if X264_CHROMA_FORMAT
                static const uint8_t output_csp_fix[] = { X264_CHROMA_FORMAT, X264_CSP_RGB };
#else
                static const uint8_t output_csp_fix[] = { X264_CSP_I420, X264_CSP_I422, X264_CSP_I444, X264_CSP_RGB };
#endif
                param->i_csp = output_csp = output_csp_fix[output_csp];
                break;
            case OPT_INPUT_RANGE:
                FAIL_IF_ERROR( parse_enum_value( optarg, range_names, &input_opt.input_range ), Unknown input range `%s'
, optarg )
                input_opt.input_range += RANGE_AUTO;
                break;
            case OPT_RANGE:
                FAIL_IF_ERROR( parse_enum_value( optarg, range_names, ¶m->vui.b_fullrange ), Unknown range `%s'
, optarg );
                input_opt.output_range = param->vui.b_fullrange += RANGE_AUTO;
                break;
            default:
generic_option:
            {
                if( long_options_index < 0 )
                {
                    for( int i = 0; long_options[i].name; i++ )
                        if( long_options[i].val == c )
                        {
                            long_options_index = i;
                            break;
                        }
                    if( long_options_index < 0 )
                    {
                        /* getopt_long already printed an error message */
                        return -1;
                    }
                }
                //解析以字符串方式輸入的參數
                //即選項名稱和選項值都是字符串
                b_error |= x264_param_parse( param, long_options[long_options_index].name, optarg );
            }
        }

        if( b_error )
        {
            const char *name = long_options_index > 0 ? long_options[long_options_index].name : argv[optind-2];
            x264_cli_log( x264, X264_LOG_ERROR, invalid argument: %s = %s
, name, optarg );
            return -1;
        }
    }

    /* If first pass mode is used, apply faster settings. */
    if( b_turbo )
        x264_param_apply_fastfirstpass( param );

    /* Apply profile restrictions. */
    //設置profile
    if( x264_param_apply_profile( param, profile ) < 0 )
        return -1;

    /* Get the file name */
    FAIL_IF_ERROR( optind > argc - 1 || !output_filename, No %s file. Run x264 --help for a list of options.
,
                   optind > argc - 1 ? input : output )
    //根據文件名的後綴確定輸出的文件格式(raw H264,flv,mp4...)
    if( select_output( muxer, output_filename, param ) )
        return -1;
    FAIL_IF_ERROR( cli_output.open_file( output_filename, &opt->hout, &output_opt ), could not open output file `%s'
, output_filename )
    //輸入文件路徑
    input_filename = argv[optind++];
    video_info_t info = {0};
    char demuxername[5];

    /* set info flags to be overwritten by demuxer as necessary. */
    //設置info結構體
    info.csp        = param->i_csp;
    info.fps_num    = param->i_fps_num;
    info.fps_den    = param->i_fps_den;
    info.fullrange  = input_opt.input_range == RANGE_PC;
    info.interlaced = param->b_interlaced;
    if( param->vui.i_sar_width > 0 && param->vui.i_sar_height > 0 )
    {
        info.sar_width  = param->vui.i_sar_width;
        info.sar_height = param->vui.i_sar_height;
    }
    info.tff        = param->b_tff;
    info.vfr        = param->b_vfr_input;

    input_opt.seek = opt->i_seek;
    input_opt.progress = opt->b_progress;
    input_opt.output_csp = output_csp;
    //設置輸入文件的格式(yuv,y4m...)
    if( select_input( demuxer, demuxername, input_filename, &opt->hin, &info, &input_opt ) )
        return -1;

    FAIL_IF_ERROR( !opt->hin && cli_input.open_file( input_filename, &opt->hin, &info, &input_opt ),
                   could not open input file `%s'
, input_filename )

    x264_reduce_fraction( &info.sar_width, &info.sar_height );
    x264_reduce_fraction( &info.fps_num, &info.fps_den );
    x264_cli_log( demuxername, X264_LOG_INFO, %dx%d%c %u:%u @ %u/%u fps (%cfr)
, info.width,
                  info.height, info.interlaced ? 'i' : 'p', info.sar_width, info.sar_height,
                  info.fps_num, info.fps_den, info.vfr ? 'v' : 'c' );

    if( tcfile_name )
    {
        FAIL_IF_ERROR( b_user_fps, --fps + --tcfile-in is incompatible.
 )
        FAIL_IF_ERROR( timecode_input.open_file( tcfile_name, &opt->hin, &info, &input_opt ), timecode input failed
 )
        cli_input = timecode_input;
    }
    else FAIL_IF_ERROR( !info.vfr && input_opt.timebase, --timebase is incompatible with cfr input
 )

    /* init threaded input while the information about the input video is unaltered by filtering */
#if HAVE_THREAD
    if( info.thread_safe && (b_thread_input || param->i_threads > 1
        || (param->i_threads == X264_THREADS_AUTO && x264_cpu_num_processors() > 1)) )
    {
        if( thread_input.open_file( NULL, &opt->hin, &info, NULL ) )
        {
            fprintf( stderr, x264 [error]: threaded input failed
 );
            return -1;
        }
        cli_input = thread_input;
    }
#endif

    /* override detected values by those specified by the user */
    if( param->vui.i_sar_width > 0 && param->vui.i_sar_height > 0 )
    {
        info.sar_width  = param->vui.i_sar_width;
        info.sar_height = param->vui.i_sar_height;
    }
    if( b_user_fps )
    {
        info.fps_num = param->i_fps_num;
        info.fps_den = param->i_fps_den;
    }
    if( !info.vfr )
    {
        info.timebase_num = info.fps_den;
        info.timebase_den = info.fps_num;
    }
    if( !tcfile_name && input_opt.timebase )
    {
        uint64_t i_user_timebase_num;
        uint64_t i_user_timebase_den;
        int ret = sscanf( input_opt.timebase, %SCNu64/%SCNu64, &i_user_timebase_num, &i_user_timebase_den );
        FAIL_IF_ERROR( !ret, invalid argument: timebase = %s
, input_opt.timebase )
        else if( ret == 1 )
        {
            i_user_timebase_num = info.timebase_num;
            i_user_timebase_den = strtoul( input_opt.timebase, NULL, 10 );
        }
        FAIL_IF_ERROR( i_user_timebase_num > UINT32_MAX || i_user_timebase_den > UINT32_MAX,
                       timebase you specified exceeds H.264 maximum
 )
        opt->timebase_convert_multiplier = ((double)i_user_timebase_den / info.timebase_den)
                                         * ((double)info.timebase_num / i_user_timebase_num);
        info.timebase_num = i_user_timebase_num;
        info.timebase_den = i_user_timebase_den;
        info.vfr = 1;
    }
    if( b_user_interlaced )
    {
        info.interlaced = param->b_interlaced;
        info.tff = param->b_tff;
    }
    if( input_opt.input_range != RANGE_AUTO )
        info.fullrange = input_opt.input_range;

    //初始化濾鏡filter
    //filter可以認為是一種“擴展”了的輸入源
    if( init_vid_filters( vid_filters, &opt->hin, &info, param, output_csp ) )
        return -1;

    /* set param flags from the post-filtered video */
    param->b_vfr_input = info.vfr;
    param->i_fps_num = info.fps_num;
    param->i_fps_den = info.fps_den;
    param->i_timebase_num = info.timebase_num;
    param->i_timebase_den = info.timebase_den;
    param->vui.i_sar_width  = info.sar_width;
    param->vui.i_sar_height = info.sar_height;

    info.num_frames = X264_MAX( info.num_frames - opt->i_seek, 0 );
    if( (!info.num_frames || param->i_frame_total < info.num_frames)
        && param->i_frame_total > 0 )
        info.num_frames = param->i_frame_total;
    param->i_frame_total = info.num_frames;

    if( !b_user_interlaced && info.interlaced )
    {
#if HAVE_INTERLACED
        x264_cli_log( x264, X264_LOG_WARNING, input appears to be interlaced, enabling %cff interlaced mode.

                                      If you want otherwise, use --no-interlaced or --%cff
,
                      info.tff ? 't' : 'b', info.tff ? 'b' : 't' );
        param->b_interlaced = 1;
        param->b_tff = !!info.tff;
#else
        x264_cli_log( x264, X264_LOG_WARNING, input appears to be interlaced, but not compiled with interlaced support
 );
#endif
    }
    /* if the user never specified the output range and the input is now rgb, default it to pc */
    int csp = param->i_csp & X264_CSP_MASK;
    if( csp >= X264_CSP_BGR && csp <= X264_CSP_RGB )
    {
        if( input_opt.output_range == RANGE_AUTO )
            param->vui.b_fullrange = RANGE_PC;
        /* otherwise fail if they specified tv */
        FAIL_IF_ERROR( !param->vui.b_fullrange, RGB must be PC range )
    }

    /* Automatically reduce reference frame count to match the user's target level
     * if the user didn't explicitly set a reference frame count. */
    if( !b_user_ref )
    {
        int mbs = (((param->i_width)+15)>>4) * (((param->i_height)+15)>>4);
        for( int i = 0; x264_levels[i].level_idc != 0; i++ )
            if( param->i_level_idc == x264_levels[i].level_idc )
            {
                while( mbs * param->i_frame_reference > x264_levels[i].dpb && param->i_frame_reference > 1 )
                    param->i_frame_reference--;
                break;
            }
    }


    return 0;
}

下面簡單梳理parse()的流程:
(1)調用x264_param_default()為存儲參數的結構體x264_param_t賦默認值
(2)調用x264_param_default_preset()為x264_param_t賦值
(3)在一個大循環中調用getopt_long()逐個解析輸入的參數,並作相應的處理。舉幾個例子:
a)“-h”:調用help()打開幫助菜單。
b)“-V”調用print_version_info()打印版本信息。
c)對於長選項,調用x264_param_parse()進行處理。
(4)調用select_input()解析輸出文件格式(例如raw,flv,MP4…)
(5)調用select_output()解析輸入文件格式(例如yuv,y4m…)

下文按照順序記錄parse()中涉及到的函數:

x264_param_default()
x264_param_default_preset()
help()
print_version_info()
x264_param_parse()
select_input()
select_output()

x264_param_default()

x264_param_default()是一個x264的API。該函數用於設置x264中x264_param_t結構體的默認值。函數的聲明如下所示。
/* x264_param_default:
 *      fill x264_param_t with default values and do CPU detection */
void    x264_param_default( x264_param_t * );
x264_param_default()的定義如下所示。
/****************************************************************************
 * x264_param_default:
 ****************************************************************************/
//初始化參數默認值
void x264_param_default( x264_param_t *param )
{
    /* */
    memset( param, 0, sizeof( x264_param_t ) );

    /* CPU autodetect */
    param->cpu = x264_cpu_detect();
    param->i_threads = X264_THREADS_AUTO;
    param->i_lookahead_threads = X264_THREADS_AUTO;
    param->b_deterministic = 1;
    param->i_sync_lookahead = X264_SYNC_LOOKAHEAD_AUTO;

    /* Video properties */
    param->i_csp           = X264_CHROMA_FORMAT ? X264_CHROMA_FORMAT : X264_CSP_I420;
    param->i_width         = 0;
    param->i_height        = 0;
    param->vui.i_sar_width = 0;
    param->vui.i_sar_height= 0;
    param->vui.i_overscan  = 0;  /* undef */
    param->vui.i_vidformat = 5;  /* undef */
    param->vui.b_fullrange = -1; /* default depends on input */
    param->vui.i_colorprim = 2;  /* undef */
    param->vui.i_transfer  = 2;  /* undef */
    param->vui.i_colmatrix = -1; /* default depends on input */
    param->vui.i_chroma_loc= 0;  /* left center */
    param->i_fps_num       = 25;
    param->i_fps_den       = 1;
    param->i_level_idc     = -1;
    param->i_slice_max_size = 0;
    param->i_slice_max_mbs = 0;
    param->i_slice_count = 0;

    /* Encoder parameters */
    //編碼參數--最常見
    param->i_frame_reference = 3;
    param->i_keyint_max = 250;
    param->i_keyint_min = X264_KEYINT_MIN_AUTO;
    param->i_bframe = 3;
    param->i_scenecut_threshold = 40;
    param->i_bframe_adaptive = X264_B_ADAPT_FAST;
    param->i_bframe_bias = 0;
    param->i_bframe_pyramid = X264_B_PYRAMID_NORMAL;
    param->b_interlaced = 0;
    param->b_constrained_intra = 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;
    //碼率控制模塊 Rate Control
    param->rc.i_rc_method = X264_RC_CRF;
    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 = 23 + QP_BD_OFFSET;
    param->rc.f_rf_constant = 23;
    param->rc.i_qp_min = 0;
    param->rc.i_qp_max = QP_MAX;
    param->rc.i_qp_step = 4;
    param->rc.f_ip_factor = 1.4;
    param->rc.f_pb_factor = 1.3;
    param->rc.i_aq_mode = X264_AQ_VARIANCE;
    param->rc.f_aq_strength = 1.0;
    param->rc.i_lookahead = 40;

    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.f_qcompress = 0.6;
    param->rc.f_qblur = 0.5;
    param->rc.f_complexity_blur = 20;
    param->rc.i_zones = 0;
    param->rc.b_mb_tree = 1;

    /* Log */
    //日志模塊
    param->pf_log = x264_log_default;
    param->p_log_private = NULL;
    param->i_log_level = X264_LOG_INFO;

    /* */
    //分析模塊 Analysis
    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.f_psy_rd = 1.0;
    param->analyse.b_psy = 1;
    param->analyse.f_psy_trellis = 0;
    param->analyse.i_me_range = 16;
    param->analyse.i_subpel_refine = 7;
    param->analyse.b_mixed_references = 1;
    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_chroma_qp_offset = 0;
    param->analyse.b_fast_pskip = 1;
    param->analyse.b_weighted_bipred = 1;
    param->analyse.i_weighted_pred = X264_WEIGHTP_SMART;
    param->analyse.b_dct_decimate = 1;
    param->analyse.b_transform_8x8 = 1;
    param->analyse.i_trellis = 1;
    param->analyse.i_luma_deadzone[0] = 21;
    param->analyse.i_luma_deadzone[1] = 11;
    param->analyse.b_psnr = 0;
    param->analyse.b_ssim = 0;

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

    param->b_repeat_headers = 1;
    param->b_annexb = 1;
    param->b_aud = 0;
    param->b_vfr_input = 1;
    param->i_nal_hrd = X264_NAL_HRD_NONE;
    param->b_tff = 1;
    param->b_pic_struct = 0;
    param->b_fake_interlaced = 0;
    param->i_frame_packing = -1;
    param->b_opencl = 0;
    param->i_opencl_device = 0;
    param->opencl_device_id = NULL;
    param->psz_clbin_file = NULL;
}

從源代碼可以看出,x264_param_default()對輸入的存儲參數的結構體x264_param_t的成員變量進行了賦值工作。

x264_param_default_preset()

x264_param_default_preset()是一個libx264的API,用於設置x264的preset和tune。該函數的聲明如下所示。
/*      Multiple tunings can be used if separated by a delimiter in ,./-+,
 *      however multiple psy tunings cannot be used.
 *      film, animation, grain, stillimage, psnr, and ssim are psy tunings.
 *
 *      returns 0 on success, negative on failure (e.g. invalid preset/tune name). */
int     x264_param_default_preset( x264_param_t *, const char *preset, const char *tune );
x264_param_default_preset()的定義如下所示。
//設置preset,tune
int x264_param_default_preset( x264_param_t *param, const char *preset, const char *tune )
{
    x264_param_default( param );

    //設置preset
    if( preset && x264_param_apply_preset( param, preset ) < 0 )
        return -1;

    //設置tune
    if( tune && x264_param_apply_tune( param, tune ) < 0 )
        return -1;
    return 0;
}

從源代碼可以看出,x264_param_default_preset()調用x264_param_apply_preset()設置preset,調用x264_param_apply_tune()設置tune。記錄一下這兩個函數。

x264_param_apply_preset()
x264_param_apply_preset()用於設置preset。該函數的定義如下所示。
//設置preset
static int x264_param_apply_preset( x264_param_t *param, const char *preset )
{
    char *end;
    int i = strtol( preset, &end, 10 );
    if( *end == 0 && i >= 0 && i < sizeof(x264_preset_names)/sizeof(*x264_preset_names)-1 )
        preset = x264_preset_names[i];

    //幾種不同的preset設置不同的參數
    if( !strcasecmp( preset, ultrafast ) )
    {
        param->i_frame_reference = 1;
        param->i_scenecut_threshold = 0;
        param->b_deblocking_filter = 0;//不使用去塊濾波
        param->b_cabac = 0;//不使用CABAC
        param->i_bframe = 0;//不使用B幀
        param->analyse.intra = 0;
        param->analyse.inter = 0;
        param->analyse.b_transform_8x8 = 0;//不使用8x8DCT
        param->analyse.i_me_method = X264_ME_DIA;//運動搜索方法使用“Diamond”
        param->analyse.i_subpel_refine = 0;
        param->rc.i_aq_mode = 0;
        param->analyse.b_mixed_references = 0;
        param->analyse.i_trellis = 0;
        param->i_bframe_adaptive = X264_B_ADAPT_NONE;
        param->rc.b_mb_tree = 0;
        param->analyse.i_weighted_pred = X264_WEIGHTP_NONE;//不使用加權
        param->analyse.b_weighted_bipred = 0;
        param->rc.i_lookahead = 0;
    }
    else if( !strcasecmp( preset, superfast ) )
    {
        param->analyse.inter = X264_ANALYSE_I8x8|X264_ANALYSE_I4x4;
        param->analyse.i_me_method = X264_ME_DIA;//鑽石模板
        param->analyse.i_subpel_refine = 1;//亞像素運動估計質量為1
        param->i_frame_reference = 1;
        param->analyse.b_mixed_references = 0;
        param->analyse.i_trellis = 0;
        param->rc.b_mb_tree = 0;
        param->analyse.i_weighted_pred = X264_WEIGHTP_SIMPLE;
        param->rc.i_lookahead = 0;
    }
    else if( !strcasecmp( preset, veryfast ) )
    {
        param->analyse.i_me_method = X264_ME_HEX;//六邊形模板
        param->analyse.i_subpel_refine = 2;
        param->i_frame_reference = 1;
        param->analyse.b_mixed_references = 0;
        param->analyse.i_trellis = 0;
        param->analyse.i_weighted_pred = X264_WEIGHTP_SIMPLE;
        param->rc.i_lookahead = 10;
    }
    else if( !strcasecmp( preset, faster ) )
    {
        param->analyse.b_mixed_references = 0;
        param->i_frame_reference = 2;
        param->analyse.i_subpel_refine = 4;
        param->analyse.i_weighted_pred = X264_WEIGHTP_SIMPLE;
        param->rc.i_lookahead = 20;
    }
    else if( !strcasecmp( preset, fast ) )
    {
        param->i_frame_reference = 2;
        param->analyse.i_subpel_refine = 6;
        param->analyse.i_weighted_pred = X264_WEIGHTP_SIMPLE;
        param->rc.i_lookahead = 30;
    }
    else if( !strcasecmp( preset, medium ) )
    {
        /* Default is medium */
    }
    else if( !strcasecmp( preset, slow ) )
    {
        param->analyse.i_me_method = X264_ME_UMH;//UMH相對復雜
        param->analyse.i_subpel_refine = 8;//亞像素運動估計質量為8
        param->i_frame_reference = 5;
        param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;
        param->analyse.i_direct_mv_pred = X264_DIRECT_PRED_AUTO;
        param->rc.i_lookahead = 50;
    }
    else if( !strcasecmp( preset, slower ) )
    {
        param->analyse.i_me_method = X264_ME_UMH;
        param->analyse.i_subpel_refine = 9;
        param->i_frame_reference = 8;
        param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;
        param->analyse.i_direct_mv_pred = X264_DIRECT_PRED_AUTO;
        param->analyse.inter |= X264_ANALYSE_PSUB8x8;
        param->analyse.i_trellis = 2;
        param->rc.i_lookahead = 60;
    }
    else if( !strcasecmp( preset, veryslow ) )
    {
        param->analyse.i_me_method = X264_ME_UMH;
        param->analyse.i_subpel_refine = 10;
        param->analyse.i_me_range = 24;
        param->i_frame_reference = 16;
        param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;
        param->analyse.i_direct_mv_pred = X264_DIRECT_PRED_AUTO;
        param->analyse.inter |= X264_ANALYSE_PSUB8x8;
        param->analyse.i_trellis = 2;
        param->i_bframe = 8;
        param->rc.i_lookahead = 60;
    }
    else if( !strcasecmp( preset, placebo ) )
    {
        param->analyse.i_me_method = X264_ME_TESA;//TESA很慢
        param->analyse.i_subpel_refine = 11;
        param->analyse.i_me_range = 24;
        param->i_frame_reference = 16;
        param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;
        param->analyse.i_direct_mv_pred = X264_DIRECT_PRED_AUTO;
        param->analyse.inter |= X264_ANALYSE_PSUB8x8;
        param->analyse.b_fast_pskip = 0;
        param->analyse.i_trellis = 2;
        param->i_bframe = 16;
        param->rc.i_lookahead = 60;
    }
    else
    {
        x264_log( NULL, X264_LOG_ERROR, invalid preset '%s'
, preset );
        return -1;
    }
    return 0;
}

可以看出x264_param_apply_preset()通過strcasecmp()比較字符串的方法得到輸入的preset類型;然後根據preset類型,設定 x264_param_t中相應的參數。

x264_param_apply_tune()
x264_param_apply_tune()用於設置tune。該函數的定義如下所示。
//設置tune
static int x264_param_apply_tune( x264_param_t *param, const char *tune )
{
    char *tmp = x264_malloc( strlen( tune ) + 1 );
    if( !tmp )
        return -1;
    tmp = strcpy( tmp, tune );
    //分解一個字符串為一個字符串數組。第2個參數為分隔符
    char *s = strtok( tmp, ,./-+ );
    int psy_tuning_used = 0;

    //設置
    //這裡是循環的,可以設置多次
    while( s )
    {
        if( !strncasecmp( s, film, 4 ) )
        {
            if( psy_tuning_used++ ) goto psy_failure;
            param->i_deblocking_filter_alphac0 = -1;
            param->i_deblocking_filter_beta = -1;
            param->analyse.f_psy_trellis = 0.15;
        }
        else if( !strncasecmp( s, animation, 9 ) )
        {
            if( psy_tuning_used++ ) goto psy_failure;
            param->i_frame_reference = param->i_frame_reference > 1 ? param->i_frame_reference*2 : 1;
            param->i_deblocking_filter_alphac0 = 1;
            param->i_deblocking_filter_beta = 1;
            param->analyse.f_psy_rd = 0.4;
            param->rc.f_aq_strength = 0.6;
            param->i_bframe += 2;
        }
        else if( !strncasecmp( s, grain, 5 ) )
        {
            if( psy_tuning_used++ ) goto psy_failure;
            param->i_deblocking_filter_alphac0 = -2;
            param->i_deblocking_filter_beta = -2;
            param->analyse.f_psy_trellis = 0.25;
            param->analyse.b_dct_decimate = 0;
            param->rc.f_pb_factor = 1.1;
            param->rc.f_ip_factor = 1.1;
            param->rc.f_aq_strength = 0.5;
            param->analyse.i_luma_deadzone[0] = 6;
            param->analyse.i_luma_deadzone[1] = 6;
            param->rc.f_qcompress = 0.8;
        }
        else if( !strncasecmp( s, stillimage, 10 ) )
        {
            if( psy_tuning_used++ ) goto psy_failure;
            param->i_deblocking_filter_alphac0 = -3;
            param->i_deblocking_filter_beta = -3;
            param->analyse.f_psy_rd = 2.0;
            param->analyse.f_psy_trellis = 0.7;
            param->rc.f_aq_strength = 1.2;
        }
        else if( !strncasecmp( s, psnr, 4 ) )
        {
            if( psy_tuning_used++ ) goto psy_failure;
            param->rc.i_aq_mode = X264_AQ_NONE;
            param->analyse.b_psy = 0;
        }
        else if( !strncasecmp( s, ssim, 4 ) )
        {
            if( psy_tuning_used++ ) goto psy_failure;
            param->rc.i_aq_mode = X264_AQ_AUTOVARIANCE;
            param->analyse.b_psy = 0;
        }
        else if( !strncasecmp( s, fastdecode, 10 ) )
        {
            param->b_deblocking_filter = 0;
            param->b_cabac = 0;
            param->analyse.b_weighted_bipred = 0;
            param->analyse.i_weighted_pred = X264_WEIGHTP_NONE;
        }
        else if( !strncasecmp( s, zerolatency, 11 ) )
        {
        	//zerolatency速度快
            param->rc.i_lookahead = 0;
            param->i_sync_lookahead = 0;
            param->i_bframe = 0;//不使用B幀
            param->b_sliced_threads = 1;
            param->b_vfr_input = 0;
            param->rc.b_mb_tree = 0;
        }
        else if( !strncasecmp( s, touhou, 6 ) )
        {
            if( psy_tuning_used++ ) goto psy_failure;
            param->i_frame_reference = param->i_frame_reference > 1 ? param->i_frame_reference*2 : 1;
            param->i_deblocking_filter_alphac0 = -1;
            param->i_deblocking_filter_beta = -1;
            param->analyse.f_psy_trellis = 0.2;
            param->rc.f_aq_strength = 1.3;
            if( param->analyse.inter & X264_ANALYSE_PSUB16x16 )
                param->analyse.inter |= X264_ANALYSE_PSUB8x8;
        }
        else
        {
            x264_log( NULL, X264_LOG_ERROR, invalid tune '%s'
, s );
            x264_free( tmp );
            return -1;
        }
        if( 0 )
        {
    psy_failure:
            x264_log( NULL, X264_LOG_WARNING, only 1 psy tuning can be used: ignoring tune %s
, s );
        }
        s = strtok( NULL, ,./-+ );
    }
    x264_free( tmp );
    return 0;
}

可以看出x264_param_apply_tune()首先通過strtok()得到存儲tune[]數組;然後通過strncasecmp()比較字符串的方法判斷當前的tune類型;最後根據tune類型,設定 x264_param_t中相應的參數。

help()

help()用於打印幫助菜單。在x264命令行程序中添加“-h”參數後會調用該函數。該函數的定義如下所示。
//幫助菜單
//longhelp標識是否展開更長的幫助菜單
static void help( x264_param_t *defaults, int longhelp )
{
    char buf[50];
    //H0(),H1(),H2()都是printf()
    //H1(),H2()只有“長幫助菜單”的情況下才會調用printf()
#define H0 printf
#define H1 if(longhelp>=1) printf
#define H2 if(longhelp==2) printf
    H0( x264 core:%d%s

        Syntax: x264 [options] -o outfile infile

        

        Infile can be raw (in which case resolution is required),

          or YUV4MPEG (*.y4m),

          or Avisynth if compiled with support (%s).

          or libav* formats if compiled with lavf support (%s) or ffms support (%s).

        Outfile type is selected by filename:

         .264 -> Raw bytestream

         .mkv -> Matroska

         .flv -> Flash Video

         .mp4 -> MP4 if compiled with GPAC or L-SMASH support (%s)

        Output bit depth: %d (configured at compile time)

        

        Options:

        

          -h, --help                  List basic options

              --longhelp              List more options

              --fullhelp              List all options

        
,
        X264_BUILD, X264_VERSION,
#if HAVE_AVS
        yes,
#else
        no,
#endif
#if HAVE_LAVF
        yes,
#else
        no,
#endif
#if HAVE_FFMS
        yes,
#else
        no,
#endif
#if HAVE_GPAC
        gpac,
#elif HAVE_LSMASH
        lsmash,
#else
        no,
#endif
        x264_bit_depth
      );
    H0( Example usage:
 );
    H0( 
 );
    H0(       Constant quality mode:
 );
    H0(             x264 --crf 24 -o 
); H0( ); H0( Two-pass with a bitrate of 1000kbps: ); H0( x264 --pass 1 --bitrate 1000 -o ); H0( x264 --pass 2 --bitrate 1000 -o ); H0( ); H0( Lossless: ); H0( x264 --qp 0 -o ); H0( ); H0( Maximum PSNR at the cost of speed and visual quality: ); H0( x264 --preset placebo --tune psnr -o ); H0( ); H0( Constant bitrate at 1000kbps with a 2 second-buffer: ); H0( x264 --vbv-bufsize 2000 --bitrate 1000 -o ); H0( ); H0( Presets: ); H0( ); H0( --profile Force the limits of an H.264 profile Overrides all settings. ); H2( #if X264_CHROMA_FORMAT <= X264_CSP_I420 #if BIT_DEPTH==8 - baseline: --no-8x8dct --bframes 0 --no-cabac --cqm flat --weightp 0 No interlaced. No lossless. - main: --no-8x8dct --cqm flat No lossless. - high: No lossless. #endif - high10: No lossless. Support for bit depth 8-10. #endif #if X264_CHROMA_FORMAT <= X264_CSP_I422 - high422: No lossless. Support for bit depth 8-10. Support for 4:2:0/4:2:2 chroma subsampling. #endif - high444: Support for bit depth 8-10. Support for 4:2:0/4:2:2/4:4:4 chroma subsampling. ); else H0( - #if X264_CHROMA_FORMAT <= X264_CSP_I420 #if BIT_DEPTH==8 baseline,main,high, #endif high10, #endif #if X264_CHROMA_FORMAT <= X264_CSP_I422 high422, #endif high444 ); H0( --preset Use a preset to select encoding settings [medium] Overridden by user settings. ); H2( - ultrafast: --no-8x8dct --aq-mode 0 --b-adapt 0 --bframes 0 --no-cabac --no-deblock --no-mbtree --me dia --no-mixed-refs --partitions none --rc-lookahead 0 --ref 1 --scenecut 0 --subme 0 --trellis 0 --no-weightb --weightp 0 - superfast: --no-mbtree --me dia --no-mixed-refs --partitions i8x8,i4x4 --rc-lookahead 0 --ref 1 --subme 1 --trellis 0 --weightp 1 - veryfast: --no-mixed-refs --rc-lookahead 10 --ref 1 --subme 2 --trellis 0 --weightp 1 - faster: --no-mixed-refs --rc-lookahead 20 --ref 2 --subme 4 --weightp 1 - fast: --rc-lookahead 30 --ref 2 --subme 6 --weightp 1 - medium: Default settings apply. - slow: --b-adapt 2 --direct auto --me umh --rc-lookahead 50 --ref 5 --subme 8 - slower: --b-adapt 2 --direct auto --me umh --partitions all --rc-lookahead 60 --ref 8 --subme 9 --trellis 2 - veryslow: --b-adapt 2 --bframes 8 --direct auto --me umh --merange 24 --partitions all --ref 16 --subme 10 --trellis 2 --rc-lookahead 60 - placebo: --bframes 16 --b-adapt 2 --direct auto --slow-firstpass --no-fast-pskip --me tesa --merange 24 --partitions all --rc-lookahead 60 --ref 16 --subme 11 --trellis 2 ); else H0( - ultrafast,superfast,veryfast,faster,fast - medium,slow,slower,veryslow,placebo ); H0( --tune Tune the settings for a particular type of source or situation Overridden by user settings. Multiple tunings are separated by commas. Only one psy tuning can be used at a time. ); H2( - film (psy tuning): --deblock -1:-1 --psy-rd :0.15 - animation (psy tuning): --bframes {+2} --deblock 1:1 --psy-rd 0.4: --aq-strength 0.6 --ref {Double if >1 else 1} - grain (psy tuning): --aq-strength 0.5 --no-dct-decimate --deadzone-inter 6 --deadzone-intra 6 --deblock -2:-2 --ipratio 1.1 --pbratio 1.1 --psy-rd :0.25 --qcomp 0.8 - stillimage (psy tuning): --aq-strength 1.2 --deblock -3:-3 --psy-rd 2.0:0.7 - psnr (psy tuning): --aq-mode 0 --no-psy - ssim (psy tuning): --aq-mode 2 --no-psy - fastdecode: --no-cabac --no-deblock --no-weightb --weightp 0 - zerolatency: --bframes 0 --force-cfr --no-mbtree --sync-lookahead 0 --sliced-threads --rc-lookahead 0 ); else H0( - psy tunings: film,animation,grain, stillimage,psnr,ssim - other tunings: fastdecode,zerolatency ); H2( --slow-firstpass Don't force these faster settings with --pass 1: --no-8x8dct --me dia --partitions none --ref 1 --subme {2 if >2 else unchanged} --trellis 0 --fast-pskip ); else H1( --slow-firstpass Don't force faster settings with --pass 1 ); H0( ); H0( Frame-type options: ); H0( ); H0( -I, --keyint Maximum GOP size [%d] , defaults->i_keyint_max ); H2( -i, --min-keyint Minimum GOP size [auto] ); H2( --no-scenecut Disable adaptive I-frame decision ); H2( --scenecut How aggressively to insert extra I-frames [%d] , defaults->i_scenecut_threshold ); H2( --intra-refresh Use Periodic Intra Refresh instead of IDR frames ); H1( -b, --bframes Number of B-frames between I and P [%d] , defaults->i_bframe ); H1( --b-adapt Adaptive B-frame decision method [%d] Higher values may lower threading efficiency. - 0: Disabled - 1: Fast - 2: Optimal (slow with high --bframes) , defaults->i_bframe_adaptive ); H2( --b-bias Influences how often B-frames are used [%d] , defaults->i_bframe_bias ); H1( --b-pyramid Keep some B-frames as references [%s] - none: Disabled - strict: Strictly hierarchical pyramid - normal: Non-strict (not Blu-ray compatible) , strtable_lookup( x264_b_pyramid_names, defaults->i_bframe_pyramid ) ); H1( --open-gop Use recovery points to close GOPs Only available with b-frames ); H1( --no-cabac Disable CABAC ); H1( -r, --ref Number of reference frames [%d] , defaults->i_frame_reference ); H1( --no-deblock Disable loop filter ); H1( -f, --deblock Loop filter parameters [%d:%d] , defaults->i_deblocking_filter_alphac0, defaults->i_deblocking_filter_beta ); H2( --slices Number of slices per frame; forces rectangular slices and is overridden by other slicing options ); else H1( --slices Number of slices per frame ); H2( --slices-max Absolute maximum slices per frame; overrides slice-max-size/slice-max-mbs when necessary ); H2( --slice-max-size Limit the size of each slice in bytes ); H2( --slice-max-mbs Limit the size of each slice in macroblocks (max) ); H2( --slice-min-mbs Limit the size of each slice in macroblocks (min) ); H0( --tff Enable interlaced mode (top field first) ); H0( --bff Enable interlaced mode (bottom field first) ); H2( --constrained-intra Enable constrained intra prediction. ); H0( --pulldown Use soft pulldown to change frame rate - none, 22, 32, 64, double, triple, euro (requires cfr input) ); H2( --fake-interlaced Flag stream as interlaced but encode progressive. Makes it possible to encode 25p and 30p Blu-Ray streams. Ignored in interlaced mode. ); H2( --frame-packing For stereoscopic videos define frame arrangement - 0: checkerboard - pixels are alternatively from L and R - 1: column alternation - L and R are interlaced by column - 2: row alternation - L and R are interlaced by row - 3: side by side - L is on the left, R on the right - 4: top bottom - L is on top, R on bottom - 5: frame alternation - one view per frame ); H0( ); H0( Ratecontrol: ); H0( ); H1( -q, --qp Force constant QP (0-%d, 0=lossless) , QP_MAX ); H0( -B, --bitrate Set bitrate (kbit/s) ); H0( --crf Quality-based VBR (%d-51) [%.1f] , 51 - QP_MAX_SPEC, defaults->rc.f_rf_constant ); H1( --rc-lookahead Number of frames for frametype lookahead [%d] , defaults->rc.i_lookahead ); H0( --vbv-maxrate Max local bitrate (kbit/s) [%d] , defaults->rc.i_vbv_max_bitrate ); H0( --vbv-bufsize Set size of the VBV buffer (kbit) [%d] , defaults->rc.i_vbv_buffer_size ); H2( --vbv-init Initial VBV buffer occupancy [%.1f] , defaults->rc.f_vbv_buffer_init ); H2( --crf-max With CRF+VBV, limit RF to this value May cause VBV underflows! ); H2( --qpmin Set min QP [%d] , defaults->rc.i_qp_min ); H2( --qpmax Set max QP [%d] , defaults->rc.i_qp_max ); H2( --qpstep Set max QP step [%d] , defaults->rc.i_qp_step ); H2( --ratetol Tolerance of ABR ratecontrol and VBV [%.1f] , defaults->rc.f_rate_tolerance ); H2( --ipratio QP factor between I and P [%.2f] , defaults->rc.f_ip_factor ); H2( --pbratio QP factor between P and B [%.2f] , defaults->rc.f_pb_factor ); H2( --chroma-qp-offset QP difference between chroma and luma [%d] , defaults->analyse.i_chroma_qp_offset ); H2( --aq-mode AQ method [%d] - 0: Disabled - 1: Variance AQ (complexity mask) - 2: Auto-variance AQ (experimental) , defaults->rc.i_aq_mode ); H1( --aq-strength Reduces blocking and blurring in flat and textured areas. [%.1f] , defaults->rc.f_aq_strength ); H1( ); H0( -p, --pass Enable multipass ratecontrol - 1: First pass, creates stats file - 2: Last pass, does not overwrite stats file ); H2( - 3: Nth pass, overwrites stats file ); H1( --stats Filename for 2 pass stats [%s] , defaults->rc.psz_stat_out ); H2( --no-mbtree Disable mb-tree ratecontrol. ); H2( --qcomp QP curve compression [%.2f] , defaults->rc.f_qcompress ); H2( --cplxblur Reduce fluctuations in QP (before curve compression) [%.1f] , defaults->rc.f_complexity_blur ); H2( --qblur Reduce fluctuations in QP (after curve compression) [%.1f] , defaults->rc.f_qblur ); H2( --zones //... Tweak the bitrate of regions of the video ); H2( Each zone is of the form ,,
help()中主要有3個宏定義:H0(),H1()和H2()。這三個宏定義實質上都是printf()。它們之間的區別在於:H0()無論如何都會調用print();H1()在longhelp大於等於1的時候才會調用print();而H2()在longhelp等於2時候才會調用print()。

print_version_info()

print_version_info()用於打印x264的版本信息。在x264命令行程序中添加“-V”參數後會調用該函數。該函數的定義如下所示。
//打印版本信息
static void print_version_info( void )
{
#ifdef X264_POINTVER
    printf( x264 X264_POINTVER
 );
#else
    printf( x264 0.%d.X
, X264_BUILD );
#endif
#if HAVE_SWSCALE
    printf( (libswscale %d.%d.%d)
, LIBSWSCALE_VERSION_MAJOR, LIBSWSCALE_VERSION_MINOR, LIBSWSCALE_VERSION_MICRO );
#endif
#if HAVE_LAVF
    printf( (libavformat %d.%d.%d)
, LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO );
#endif
#if HAVE_FFMS
    printf( (ffmpegsource %d.%d.%d.%d)
, FFMS_VERSION >> 24, (FFMS_VERSION & 0xff0000) >> 16, (FFMS_VERSION & 0xff00) >> 8, FFMS_VERSION & 0xff );
#endif
    printf( built on  __DATE__ ,  );
#ifdef __INTEL_COMPILER
    printf( intel: %.2f (%d)
, __INTEL_COMPILER / 100.f, __INTEL_COMPILER_BUILD_DATE );
#elif defined(__GNUC__)
    printf( gcc:  __VERSION__ 
 );
#elif defined(_MSC_FULL_VER)
    printf( msvc: %.2f (%u)
, _MSC_VER / 100.f, _MSC_FULL_VER );
#else
    printf( using an unknown compiler
 );
#endif
    printf( configuration: --bit-depth=%d --chroma-format=%s
, x264_bit_depth, X264_CHROMA_FORMAT ? (output_csp_names[0]+1) : all );
    printf( x264 license:  );
#if HAVE_GPL
    printf( GPL version 2 or later
 );
#else
    printf( Non-GPL commercial
 );
#endif
#if HAVE_SWSCALE
    const char *license = swscale_license();
    printf( libswscale%s%s license: %s
, HAVE_LAVF ? /libavformat : , HAVE_FFMS ? /ffmpegsource :  , license );
    if( !strcmp( license, nonfree and unredistributable ) ||
       (!HAVE_GPL && (!strcmp( license, GPL version 2 or later )
                  ||  !strcmp( license, GPL version 3 or later ))))
        printf( WARNING: This binary is unredistributable!
 );
#endif
}

該函數定義比較淺顯易懂,不再詳細記錄。

x264_param_parse()

x264_param_parse()是一個x264的API。該函數以字符串鍵值對的方式設置x264_param_t結構體的一個成員變量。該函數的聲明如下所示。
/* x264_param_parse:
 *  set one parameter by name.
 *  returns 0 on success, or returns one of the following errors.
 *  note: BAD_VALUE occurs only if it can't even parse the value,
 *  numerical range is not checked until x264_encoder_open() or
 *  x264_encoder_reconfig().
 *  value=NULL means true for boolean options, but is a BAD_VALUE for non-booleans. */
int x264_param_parse( x264_param_t *, const char *name, const char *value );
x264_param_parse()的定義如下所示。
//解析以字符串方式輸入的參數
//即選項名稱和選項值都是字符串
//實質就是通過strcmp()方法
int x264_param_parse( x264_param_t *p, const char *name, const char *value )
{
    char *name_buf = NULL;
    int b_error = 0;
    int name_was_bool;
    int value_was_null = !value;
    int i;

    if( !name )
        return X264_PARAM_BAD_NAME;
    if( !value )
        value = true;

    if( value[0] == '=' )
        value++;

    if( strchr( name, '_' ) ) // s/_/-/g
    {
        char *c;
        name_buf = strdup(name);
        while( (c = strchr( name_buf, '_' )) )
            *c = '-';
        name = name_buf;
    }

    if( (!strncmp( name, no-, 3 ) && (i = 3)) ||
        (!strncmp( name, no, 2 ) && (i = 2)) )
    {
        name += i;
        value = atobool(value) ? false : true;
    }
    name_was_bool = 0;

#define OPT(STR) else if( !strcmp( name, STR ) )
#define OPT2(STR0, STR1) else if( !strcmp( name, STR0 ) || !strcmp( name, STR1 ) )
    if(0);
    //OPT()實際上就是strcmp()
    OPT(asm)
    {
        p->cpu = isdigit(value[0]) ? atoi(value) :
                 !strcasecmp(value, auto) || atobool(value) ? x264_cpu_detect() : 0;
        if( b_error )
        {
            char *buf = strdup(value);
            char *tok, UNUSED *saveptr=NULL, *init;
            b_error = 0;
            p->cpu = 0;
            for( init=buf; (tok=strtok_r(init, ,, &saveptr)); init=NULL )
            {
                for( i=0; x264_cpu_names[i].flags && strcasecmp(tok, x264_cpu_names[i].name); i++ );
                p->cpu |= x264_cpu_names[i].flags;
                if( !x264_cpu_names[i].flags )
                    b_error = 1;
            }
            free( buf );
            if( (p->cpu&X264_CPU_SSSE3) && !(p->cpu&X264_CPU_SSE2_IS_SLOW) )
                p->cpu |= X264_CPU_SSE2_IS_FAST;
        }
    }
    OPT(threads)
    {
        if( !strcasecmp(value, auto) )
            p->i_threads = X264_THREADS_AUTO;
        else
            p->i_threads = atoi(value);
    }
    OPT(lookahead-threads)
    {
        if( !strcasecmp(value, auto) )
            p->i_lookahead_threads = X264_THREADS_AUTO;
        else
            p->i_lookahead_threads = atoi(value);
    }
    OPT(sliced-threads)
        p->b_sliced_threads = atobool(value);
    OPT(sync-lookahead)
    {
        if( !strcasecmp(value, auto) )
            p->i_sync_lookahead = X264_SYNC_LOOKAHEAD_AUTO;
        else
            p->i_sync_lookahead = atoi(value);
    }
    OPT2(deterministic, n-deterministic)
        p->b_deterministic = atobool(value);
    OPT(cpu-independent)
        p->b_cpu_independent = atobool(value);
    OPT2(level, level-idc)
    {
        if( !strcmp(value, 1b) )
            p->i_level_idc = 9;
        else if( atof(value) < 6 )
            p->i_level_idc = (int)(10*atof(value)+.5);
        else
            p->i_level_idc = atoi(value);
    }
    OPT(bluray-compat)
        p->b_bluray_compat = atobool(value);
    OPT(avcintra-class)
        p->i_avcintra_class = atoi(value);
    OPT(sar)
    {
        b_error = ( 2 != sscanf( value, %d:%d, &p->vui.i_sar_width, &p->vui.i_sar_height ) &&
                    2 != sscanf( value, %d/%d, &p->vui.i_sar_width, &p->vui.i_sar_height ) );
    }
    OPT(overscan)
        b_error |= parse_enum( value, x264_overscan_names, &p->vui.i_overscan );
    OPT(videoformat)
        b_error |= parse_enum( value, x264_vidformat_names, &p->vui.i_vidformat );
    OPT(fullrange)
        b_error |= parse_enum( value, x264_fullrange_names, &p->vui.b_fullrange );
    OPT(colorprim)
        b_error |= parse_enum( value, x264_colorprim_names, &p->vui.i_colorprim );
    OPT(transfer)
        b_error |= parse_enum( value, x264_transfer_names, &p->vui.i_transfer );
    OPT(colormatrix)
        b_error |= parse_enum( value, x264_colmatrix_names, &p->vui.i_colmatrix );
    OPT(chromaloc)
    {
        p->vui.i_chroma_loc = atoi(value);
        b_error = ( p->vui.i_chroma_loc < 0 || p->vui.i_chroma_loc > 5 );
    }
    OPT(fps)
    {
        if( sscanf( value, %u/%u, &p->i_fps_num, &p->i_fps_den ) == 2 )
            ;
        else
        {
            float fps = atof(value);
            if( fps > 0 && fps <= INT_MAX/1000 )
            {
                p->i_fps_num = (int)(fps * 1000 + .5);
                p->i_fps_den = 1000;
            }
            else
            {
                p->i_fps_num = atoi(value);
                p->i_fps_den = 1;
            }
        }
    }
    OPT2(ref, frameref)
        p->i_frame_reference = atoi(value);
    OPT(dpb-size)
        p->i_dpb_size = atoi(value);
    OPT(keyint)
    {
        if( strstr( value, infinite ) )
            p->i_keyint_max = X264_KEYINT_MAX_INFINITE;
        else
            p->i_keyint_max = atoi(value);
    }
    OPT2(min-keyint, keyint-min)
    {
        p->i_keyint_min = atoi(value);
        if( p->i_keyint_max < p->i_keyint_min )
            p->i_keyint_max = p->i_keyint_min;
    }
    OPT(scenecut)
    {
        p->i_scenecut_threshold = atobool(value);
        if( b_error || p->i_scenecut_threshold )
        {
            b_error = 0;
            p->i_scenecut_threshold = atoi(value);
        }
    }
    OPT(intra-refresh)
        p->b_intra_refresh = atobool(value);
    OPT(bframes)
        p->i_bframe = atoi(value);
    OPT(b-adapt)
    {
        p->i_bframe_adaptive = atobool(value);
        if( b_error )
        {
            b_error = 0;
            p->i_bframe_adaptive = atoi(value);
        }
    }
    OPT(b-bias)
        p->i_bframe_bias = atoi(value);
    OPT(b-pyramid)
    {
        b_error |= parse_enum( value, x264_b_pyramid_names, &p->i_bframe_pyramid );
        if( b_error )
        {
            b_error = 0;
            p->i_bframe_pyramid = atoi(value);
        }
    }
    OPT(open-gop)
        p->b_open_gop = atobool(value);
    OPT(nf)
        p->b_deblocking_filter = !atobool(value);
    OPT2(filter, deblock)
    {
        if( 2 == sscanf( value, %d:%d, &p->i_deblocking_filter_alphac0, &p->i_deblocking_filter_beta ) ||
            2 == sscanf( value, %d,%d, &p->i_deblocking_filter_alphac0, &p->i_deblocking_filter_beta ) )
        {
            p->b_deblocking_filter = 1;
        }
        else if( sscanf( value, %d, &p->i_deblocking_filter_alphac0 ) )
        {
            p->b_deblocking_filter = 1;
            p->i_deblocking_filter_beta = p->i_deblocking_filter_alphac0;
        }
        else
            p->b_deblocking_filter = atobool(value);
    }
    OPT(slice-max-size)
        p->i_slice_max_size = atoi(value);
    OPT(slice-max-mbs)
        p->i_slice_max_mbs = atoi(value);
    OPT(slice-min-mbs)
        p->i_slice_min_mbs = atoi(value);
    OPT(slices)
        p->i_slice_count = atoi(value);
    OPT(slices-max)
        p->i_slice_count_max = atoi(value);
    OPT(cabac)
        p->b_cabac = atobool(value);
    OPT(cabac-idc)
        p->i_cabac_init_idc = atoi(value);
    OPT(interlaced)
        p->b_interlaced = atobool(value);
    OPT(tff)
        p->b_interlaced = p->b_tff = atobool(value);
    OPT(bff)
    {
        p->b_interlaced = atobool(value);
        p->b_tff = !p->b_interlaced;
    }
    OPT(constrained-intra)
        p->b_constrained_intra = atobool(value);
    OPT(cqm)
    {
        if( strstr( value, flat ) )
            p->i_cqm_preset = X264_CQM_FLAT;
        else if( strstr( value, jvt ) )
            p->i_cqm_preset = X264_CQM_JVT;
        else
            p->psz_cqm_file = strdup(value);
    }
    OPT(cqmfile)
        p->psz_cqm_file = strdup(value);
    OPT(cqm4)
    {
        p->i_cqm_preset = X264_CQM_CUSTOM;
        b_error |= parse_cqm( value, p->cqm_4iy, 16 );
        b_error |= parse_cqm( value, p->cqm_4py, 16 );
        b_error |= parse_cqm( value, p->cqm_4ic, 16 );
        b_error |= parse_cqm( value, p->cqm_4pc, 16 );
    }
    OPT(cqm8)
    {
        p->i_cqm_preset = X264_CQM_CUSTOM;
        b_error |= parse_cqm( value, p->cqm_8iy, 64 );
        b_error |= parse_cqm( value, p->cqm_8py, 64 );
        b_error |= parse_cqm( value, p->cqm_8ic, 64 );
        b_error |= parse_cqm( value, p->cqm_8pc, 64 );
    }
    OPT(cqm4i)
    {
        p->i_cqm_preset = X264_CQM_CUSTOM;
        b_error |= parse_cqm( value, p->cqm_4iy, 16 );
        b_error |= parse_cqm( value, p->cqm_4ic, 16 );
    }
    OPT(cqm4p)
    {
        p->i_cqm_preset = X264_CQM_CUSTOM;
        b_error |= parse_cqm( value, p->cqm_4py, 16 );
        b_error |= parse_cqm( value, p->cqm_4pc, 16 );
    }
    OPT(cqm4iy)
    {
        p->i_cqm_preset = X264_CQM_CUSTOM;
        b_error |= parse_cqm( value, p->cqm_4iy, 16 );
    }
    OPT(cqm4ic)
    {
        p->i_cqm_preset = X264_CQM_CUSTOM;
        b_error |= parse_cqm( value, p->cqm_4ic, 16 );
    }
    OPT(cqm4py)
    {
        p->i_cqm_preset = X264_CQM_CUSTOM;
        b_error |= parse_cqm( value, p->cqm_4py, 16 );
    }
    OPT(cqm4pc)
    {
        p->i_cqm_preset = X264_CQM_CUSTOM;
        b_error |= parse_cqm( value, p->cqm_4pc, 16 );
    }
    OPT(cqm8i)
    {
        p->i_cqm_preset = X264_CQM_CUSTOM;
        b_error |= parse_cqm( value, p->cqm_8iy, 64 );
        b_error |= parse_cqm( value, p->cqm_8ic, 64 );
    }
    OPT(cqm8p)
    {
        p->i_cqm_preset = X264_CQM_CUSTOM;
        b_error |= parse_cqm( value, p->cqm_8py, 64 );
        b_error |= parse_cqm( value, p->cqm_8pc, 64 );
    }
    OPT(log)
        p->i_log_level = atoi(value);
    OPT(dump-yuv)
        p->psz_dump_yuv = strdup(value);
    OPT2(analyse, partitions)
    {
        p->analyse.inter = 0;
        if( strstr( value, none ) )  p->analyse.inter =  0;
        if( strstr( value, all ) )   p->analyse.inter = ~0;

        if( strstr( value, i4x4 ) )  p->analyse.inter |= X264_ANALYSE_I4x4;
        if( strstr( value, i8x8 ) )  p->analyse.inter |= X264_ANALYSE_I8x8;
        if( strstr( value, p8x8 ) )  p->analyse.inter |= X264_ANALYSE_PSUB16x16;
        if( strstr( value, p4x4 ) )  p->analyse.inter |= X264_ANALYSE_PSUB8x8;
        if( strstr( value, b8x8 ) )  p->analyse.inter |= X264_ANALYSE_BSUB16x16;
    }
    OPT(8x8dct)
        p->analyse.b_transform_8x8 = atobool(value);
    OPT2(weightb, weight-b)
        p->analyse.b_weighted_bipred = atobool(value);
    OPT(weightp)
        p->analyse.i_weighted_pred = atoi(value);
    OPT2(direct, direct-pred)
        b_error |= parse_enum( value, x264_direct_pred_names, &p->analyse.i_direct_mv_pred );
    OPT(chroma-qp-offset)
        p->analyse.i_chroma_qp_offset = atoi(value);
    OPT(me)
        b_error |= parse_enum( value, x264_motion_est_names, &p->analyse.i_me_method );
    OPT2(merange, me-range)
        p->analyse.i_me_range = atoi(value);
    OPT2(mvrange, mv-range)
        p->analyse.i_mv_range = atoi(value);
    OPT2(mvrange-thread, mv-range-thread)
        p->analyse.i_mv_range_thread = atoi(value);
    OPT2(subme, subq)
        p->analyse.i_subpel_refine = atoi(value);
    OPT(psy-rd)
    {
        if( 2 == sscanf( value, %f:%f, &p->analyse.f_psy_rd, &p->analyse.f_psy_trellis ) ||
            2 == sscanf( value, %f,%f, &p->analyse.f_psy_rd, &p->analyse.f_psy_trellis ) ||
            2 == sscanf( value, %f|%f, &p->analyse.f_psy_rd, &p->analyse.f_psy_trellis ))
        { }
        else if( sscanf( value, %f, &p->analyse.f_psy_rd ) )
        {
            p->analyse.f_psy_trellis = 0;
        }
        else
        {
            p->analyse.f_psy_rd = 0;
            p->analyse.f_psy_trellis = 0;
        }
    }
    OPT(psy)
        p->analyse.b_psy = atobool(value);
    OPT(chroma-me)
        p->analyse.b_chroma_me = atobool(value);
    OPT(mixed-refs)
        p->analyse.b_mixed_references = atobool(value);
    OPT(trellis)
        p->analyse.i_trellis = atoi(value);
    OPT(fast-pskip)
        p->analyse.b_fast_pskip = atobool(value);
    OPT(dct-decimate)
        p->analyse.b_dct_decimate = atobool(value);
    OPT(deadzone-inter)
        p->analyse.i_luma_deadzone[0] = atoi(value);
    OPT(deadzone-intra)
        p->analyse.i_luma_deadzone[1] = atoi(value);
    OPT(nr)
        p->analyse.i_noise_reduction = atoi(value);
    OPT(bitrate)
    {
        p->rc.i_bitrate = atoi(value);
        p->rc.i_rc_method = X264_RC_ABR;
    }
    OPT2(qp, qp_constant)
    {
        p->rc.i_qp_constant = atoi(value);
        p->rc.i_rc_method = X264_RC_CQP;
    }
    OPT(crf)
    {
        p->rc.f_rf_constant = atof(value);
        p->rc.i_rc_method = X264_RC_CRF;
    }
    OPT(crf-max)
        p->rc.f_rf_constant_max = atof(value);
    OPT(rc-lookahead)
        p->rc.i_lookahead = atoi(value);
    OPT2(qpmin, qp-min)
        p->rc.i_qp_min = atoi(value);
    OPT2(qpmax, qp-max)
        p->rc.i_qp_max = atoi(value);
    OPT2(qpstep, qp-step)
        p->rc.i_qp_step = atoi(value);
    OPT(ratetol)
        p->rc.f_rate_tolerance = !strncmp(inf, value, 3) ? 1e9 : atof(value);
    OPT(vbv-maxrate)
        p->rc.i_vbv_max_bitrate = atoi(value);
    OPT(vbv-bufsize)
        p->rc.i_vbv_buffer_size = atoi(value);
    OPT(vbv-init)
        p->rc.f_vbv_buffer_init = atof(value);
    OPT2(ipratio, ip-factor)
        p->rc.f_ip_factor = atof(value);
    OPT2(pbratio, pb-factor)
        p->rc.f_pb_factor = atof(value);
    OPT(aq-mode)
        p->rc.i_aq_mode = atoi(value);
    OPT(aq-strength)
        p->rc.f_aq_strength = atof(value);
    OPT(pass)
    {
        int pass = x264_clip3( atoi(value), 0, 3 );
        p->rc.b_stat_write = pass & 1;
        p->rc.b_stat_read = pass & 2;
    }
    OPT(stats)
    {
        p->rc.psz_stat_in = strdup(value);
        p->rc.psz_stat_out = strdup(value);
    }
    OPT(qcomp)
        p->rc.f_qcompress = atof(value);
    OPT(mbtree)
        p->rc.b_mb_tree = atobool(value);
    OPT(qblur)
        p->rc.f_qblur = atof(value);
    OPT2(cplxblur, cplx-blur)
        p->rc.f_complexity_blur = atof(value);
    OPT(zones)
        p->rc.psz_zones = strdup(value);
    OPT(crop-rect)
        b_error |= sscanf( value, %u,%u,%u,%u, &p->crop_rect.i_left, &p->crop_rect.i_top,
                                                 &p->crop_rect.i_right, &p->crop_rect.i_bottom ) != 4;
    OPT(psnr)
        p->analyse.b_psnr = atobool(value);
    OPT(ssim)
        p->analyse.b_ssim = atobool(value);
    OPT(aud)
        p->b_aud = atobool(value);
    OPT(sps-id)
        p->i_sps_id = atoi(value);
    OPT(global-header)
        p->b_repeat_headers = !atobool(value);
    OPT(repeat-headers)
        p->b_repeat_headers = atobool(value);
    OPT(annexb)
        p->b_annexb = atobool(value);
    OPT(force-cfr)
        p->b_vfr_input = !atobool(value);
    OPT(nal-hrd)
        b_error |= parse_enum( value, x264_nal_hrd_names, &p->i_nal_hrd );
    OPT(filler)
        p->rc.b_filler = atobool(value);
    OPT(pic-struct)
        p->b_pic_struct = atobool(value);
    OPT(fake-interlaced)
        p->b_fake_interlaced = atobool(value);
    OPT(frame-packing)
        p->i_frame_packing = atoi(value);
    OPT(stitchable)
        p->b_stitchable = atobool(value);
    OPT(opencl)
        p->b_opencl = atobool( value );
    OPT(opencl-clbin)
        p->psz_clbin_file = strdup( value );
    OPT(opencl-device)
        p->i_opencl_device = atoi( value );
    else
        return X264_PARAM_BAD_NAME;
#undef OPT
#undef OPT2
#undef atobool
#undef atoi
#undef atof

    if( name_buf )
        free( name_buf );

    b_error |= value_was_null && !name_was_bool;
    return b_error ? X264_PARAM_BAD_VALUE : 0;
}

x264_param_parse()中判斷參數的宏OPT()和OPT2()實質上就是strcmp()。由此可見該函數的流程首先是調用strcmp()判斷當前輸入參數的名稱name,然後再調用atoi(),atof(),或者atobool()等將當前輸入參數值value轉換成相應類型的值並賦值給對應的參數。

x264_param_apply_profile()

x264_param_apply_profile()是一個x264的API。該函數用於設置x264的profile,它的聲明如下所示。
/*      (can be NULL, in which case the function will do nothing)
 *
 *      Does NOT guarantee that the given profile will be used: if the restrictions
 *      of High are applied to settings that are already Baseline-compatible, the
 *      stream will remain baseline.  In short, it does not increase settings, only
 *      decrease them.
 *
 *      returns 0 on success, negative on failure (e.g. invalid profile name). */
int     x264_param_apply_profile( x264_param_t *, const char *profile );
x264_param_apply_profile()的定義如下所示。
//設置profile
int x264_param_apply_profile( x264_param_t *param, const char *profile )
{
    if( !profile )
        return 0;
    //字符串到整型
    int p = profile_string_to_int( profile );
    //檢查profile設置是否正確
    if( p < 0 )
    {
        x264_log( NULL, X264_LOG_ERROR, invalid profile: %s
, profile );
        return -1;
    }
    if( p < PROFILE_HIGH444_PREDICTIVE && ((param->rc.i_rc_method == X264_RC_CQP && param->rc.i_qp_constant <= 0) ||
        (param->rc.i_rc_method == X264_RC_CRF && (int)(param->rc.f_rf_constant + QP_BD_OFFSET) <= 0)) )
    {
        x264_log( NULL, X264_LOG_ERROR, %s profile doesn't support lossless
, profile );
        return -1;
    }
    if( p < PROFILE_HIGH444_PREDICTIVE && (param->i_csp & X264_CSP_MASK) >= X264_CSP_I444 )
    {
        x264_log( NULL, X264_LOG_ERROR, %s profile doesn't support 4:4:4
, profile );
        return -1;
    }
    if( p < PROFILE_HIGH422 && (param->i_csp & X264_CSP_MASK) >= X264_CSP_I422 )
    {
        x264_log( NULL, X264_LOG_ERROR, %s profile doesn't support 4:2:2
, profile );
        return -1;
    }
    if( p < PROFILE_HIGH10 && BIT_DEPTH > 8 )
    {
        x264_log( NULL, X264_LOG_ERROR, %s profile doesn't support a bit depth of %d
, profile, BIT_DEPTH );
        return -1;
    }
    //根據不同的Profile做設置
    //Baseline基本型
    if( p == PROFILE_BASELINE )
    {
    	//不支持DCT8x8
        param->analyse.b_transform_8x8 = 0;
        //不使用CABAC
        param->b_cabac = 0;
        param->i_cqm_preset = X264_CQM_FLAT;
        param->psz_cqm_file = NULL;
        //沒有B幀
        param->i_bframe = 0;
        //沒有加權
        param->analyse.i_weighted_pred = X264_WEIGHTP_NONE;
        //不支持隔行掃描
        if( param->b_interlaced )
        {
            x264_log( NULL, X264_LOG_ERROR, baseline profile doesn't support interlacing
 );
            return -1;
        }
        if( param->b_fake_interlaced )
        {
            x264_log( NULL, X264_LOG_ERROR, baseline profile doesn't support fake interlacing
 );
            return -1;
        }
    }
    //Main主型
    else if( p == PROFILE_MAIN )
    {
    	//不支持DCT8x8
        param->analyse.b_transform_8x8 = 0;
        param->i_cqm_preset = X264_CQM_FLAT;
        param->psz_cqm_file = NULL;
    }
    return 0;
}

從定義可以看出,x264_param_apply_profile()首先調用了一個函數profile_string_to_int()將輸入的profile字符串轉換為int類型的profile;然後會檢查該profile的設置是否合理;最後會根據profile對x264_param_t中的參數進行相應的設置。
該函數中調用的profile_string_to_int()的定義如下。

static int profile_string_to_int( const char *str )
{
    if( !strcasecmp( str, baseline ) )
        return PROFILE_BASELINE;
    if( !strcasecmp( str, main ) )
        return PROFILE_MAIN;
    if( !strcasecmp( str, high ) )
        return PROFILE_HIGH;
    if( !strcasecmp( str, high10 ) )
        return PROFILE_HIGH10;
    if( !strcasecmp( str, high422 ) )
        return PROFILE_HIGH422;
    if( !strcasecmp( str, high444 ) )
        return PROFILE_HIGH444_PREDICTIVE;
    return -1;
}
從定義可以看出profile_string_to_int()根據輸入的字符串str返回不同的整型變量。

select_output()

select_output()用於設定輸出的文件格式。該函數的定義如下所示。
//根據文件名的後綴確定輸出的文件格式(raw H264,flv,mp4...)
static int select_output( const char *muxer, char *filename, x264_param_t *param )
{
    //從文件路徑字符串中解析出擴展名,存入ext
    //解析的方式就是反向搜索字符“.”
    const char *ext = get_filename_extension( filename );

    //strcasecmp(char *s1, char *s2)用於忽略大小寫比較字符串.
    //參數s1和s2字符串相等則返回0。s1大於s2則返回大於0 的值,s1 小於s2 則返回小於0的值。

    if( !strcmp( filename, - ) || strcasecmp( muxer, auto ) )
        ext = muxer;
    //後綴為“mp4”
    if( !strcasecmp( ext, mp4 ) )
    {
#if HAVE_GPAC || HAVE_LSMASH
        cli_output = mp4_output;
        param->b_annexb = 0;
        param->b_repeat_headers = 0;
        if( param->i_nal_hrd == X264_NAL_HRD_CBR )
        {
            x264_cli_log( x264, X264_LOG_WARNING, cbr nal-hrd is not compatible with mp4
 );
            param->i_nal_hrd = X264_NAL_HRD_VBR;
        }
#else
        x264_cli_log( x264, X264_LOG_ERROR, not compiled with MP4 output support
 );
        return -1;
#endif
    }
    else if( !strcasecmp( ext, mkv ) )
    {
    	//設定cli_output_t
        cli_output = mkv_output;
        //不加起始碼0x00000001
        param->b_annexb = 0;
        //不再每個Keyframe前面加SPS和PPS
        param->b_repeat_headers = 0;
    }
    else if( !strcasecmp( ext, flv ) )
    {
        cli_output = flv_output;
        param->b_annexb = 0;
        param->b_repeat_headers = 0;
    }
    else
        cli_output = raw_output;//不符合上述後綴,則輸出裸流
    return 0;
}

從函數定義可以看出,select_output()首先調用get_filename_extension()從輸入文件路徑的字符串中提取出了擴展名,然後根據不同的擴展名設定不同的輸出格式。其中get_filename_extension()是一個提取擴展名的函數,定義如下所示。
//根據“.”確定文件後綴
static inline char *get_filename_extension( char *filename )
{
    char *ext = filename + strlen( filename );
    while( *ext != '.' && ext > filename )
        ext--;
    ext += *ext == '.';
    return ext;
}
可以看出get_filename_extension()從字符串的末尾開始向前搜索點符號“.”,並且將“.”後面的內容作為提取出來的擴展名。

select_input()

select_input()用於設定輸入的文件格式。該函數的定義如下所示。
//設置輸入文件的格式(yuv,y4m...)
static int select_input( const char *demuxer, char *used_demuxer, char *filename,
                         hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
{
    int b_auto = !strcasecmp( demuxer, auto );
    //從文件路徑字符串中解析出擴展名,存入ext
    //解析的方式就是反向搜索字符“.”
    const char *ext = b_auto ? get_filename_extension( filename ) : ;
    int b_regular = strcmp( filename, - );
    if( !b_regular && b_auto )
        ext = raw;
    b_regular = b_regular && x264_is_regular_file_path( filename );
    if( b_regular )
    {
        FILE *f = x264_fopen( filename, r );
        if( f )
        {
            b_regular = x264_is_regular_file( f );
            fclose( f );
        }
    }
    const char *module = b_auto ? ext : demuxer;

    //strcasecmp(char *s1, char *s2)用於忽略大小寫比較字符串.
    //參數s1和s2字符串相等則返回0。s1大於s2則返回大於0 的值,s1 小於s2 則返回小於0的值。

    if( !strcasecmp( module, avs ) || !strcasecmp( ext, d2v ) || !strcasecmp( ext, dga ) )
    {
#if HAVE_AVS
        cli_input = avs_input;
        module = avs;
#else
        x264_cli_log( x264, X264_LOG_ERROR, not compiled with AVS input support
 );
        return -1;
#endif
    }
    else if( !strcasecmp( module, y4m ) )
        cli_input = y4m_input;
    else if( !strcasecmp( module, raw ) || !strcasecmp( ext, yuv ) )
        cli_input = raw_input;
    else
    {
#if HAVE_FFMS
        if( b_regular && (b_auto || !strcasecmp( demuxer, ffms )) &&
            !ffms_input.open_file( filename, p_handle, info, opt ) )
        {
            module = ffms;
            b_auto = 0;
            cli_input = ffms_input;
        }
#endif
#if HAVE_LAVF
        if( (b_auto || !strcasecmp( demuxer, lavf )) &&
            !lavf_input.open_file( filename, p_handle, info, opt ) )
        {
            module = lavf;
            b_auto = 0;
            cli_input = lavf_input;
        }
#endif
#if HAVE_AVS
        if( b_regular && (b_auto || !strcasecmp( demuxer, avs )) &&
            !avs_input.open_file( filename, p_handle, info, opt ) )
        {
            module = avs;
            b_auto = 0;
            cli_input = avs_input;
        }
#endif
        if( b_auto && !raw_input.open_file( filename, p_handle, info, opt ) )
        {
            module = raw;
            b_auto = 0;
            cli_input = raw_input;
        }

        FAIL_IF_ERROR( !(*p_handle), could not open input file `%s' via any method!
, filename )
    }
    strcpy( used_demuxer, module );

    return 0;
}

從源代碼中可以看出,select_input()首先調用get_filename_extension()獲取輸入文件名的擴展名;然後根據擴展名設置不同的輸入格式。

至此x264命令行程序main()函數調用的parse()函數就分析完畢了。下面分析main()函數調用的另一個函數encode()。



encode()

encode()編碼YUV為H.264碼流,該函數的定義如下所示。
//編碼(在內部有一個循環用於一幀一幀編碼)
static int encode( x264_param_t *param, cli_opt_t *opt )
{
    x264_t *h = NULL;
    x264_picture_t pic;
    cli_pic_t cli_pic;
    const cli_pulldown_t *pulldown = NULL; // shut up gcc

    int     i_frame = 0;
    int     i_frame_output = 0;
    int64_t i_end, i_previous = 0, i_start = 0;
    int64_t i_file = 0;
    int     i_frame_size;
    int64_t last_dts = 0;
    int64_t prev_dts = 0;
    int64_t first_dts = 0;
#   define  MAX_PTS_WARNING 3 /* arbitrary */
    int     pts_warning_cnt = 0;
    int64_t largest_pts = -1;
    int64_t second_largest_pts = -1;
    int64_t ticks_per_frame;
    double  duration;
    double  pulldown_pts = 0;
    int     retval = 0;

    opt->b_progress &= param->i_log_level < X264_LOG_DEBUG;

    /* set up pulldown */
    if( opt->i_pulldown && !param->b_vfr_input )
    {
        param->b_pulldown = 1;
        param->b_pic_struct = 1;
        pulldown = &pulldown_values[opt->i_pulldown];
        param->i_timebase_num = param->i_fps_den;
        FAIL_IF_ERROR2( fmod( param->i_fps_num * pulldown->fps_factor, 1 ),
                        unsupported framerate for chosen pulldown
 )
        param->i_timebase_den = param->i_fps_num * pulldown->fps_factor;
    }
    //打開編碼器
    h = x264_encoder_open( param );
    FAIL_IF_ERROR2( !h, x264_encoder_open failed
 );
    //獲得參數
    x264_encoder_parameters( h, param );
    //一些不是裸流的封轉格式(FLV,MP4等)需要一些參數,例如寬高等等
    //cli_output_t是代表輸出媒體文件的結構體
    FAIL_IF_ERROR2( cli_output.set_param( opt->hout, param ), can't set outfile param
 );
    //計時
    i_start = x264_mdate();

    /* ticks/frame = ticks/second / frames/second */
    ticks_per_frame = (int64_t)param->i_timebase_den * param->i_fps_den / param->i_timebase_num / param->i_fps_num;
    FAIL_IF_ERROR2( ticks_per_frame < 1 && !param->b_vfr_input, ticks_per_frame invalid: %PRId64
, ticks_per_frame )
    ticks_per_frame = X264_MAX( ticks_per_frame, 1 );

    //如果不是在每個keyframe前面都增加SPS/PPS/SEI的話,就在整個碼流前面加SPS/PPS/SEI
    //Header指的就是SPS/PPS/SEI
    if( !param->b_repeat_headers )
    {
        // Write SPS/PPS/SEI
        x264_nal_t *headers;
        int i_nal;
        //獲得文件頭(SPS、PPS、SEI)
        FAIL_IF_ERROR2( x264_encoder_headers( h, &headers, &i_nal ) < 0, x264_encoder_headers failed
 )
        //把文件頭寫入輸出文件
        FAIL_IF_ERROR2( (i_file = cli_output.write_headers( opt->hout, headers )) < 0, error writing headers to output file
 );
    }

    if( opt->tcfile_out )
        fprintf( opt->tcfile_out, # timecode format v2
 );

    /* Encode frames */
    //循環進行編碼
    for( ; !b_ctrl_c && (i_frame < param->i_frame_total || !param->i_frame_total); i_frame++ )
    {
    	//從輸入源中獲取1幀YUV數據,存於cli_pic
    	//cli_vid_filter_t可以認為是x264一種“擴展”後的輸入源,可以在像素域對圖像進行拉伸裁剪等工作。
    	//原本代表輸入源的結構體是cli_input_t
        if( filter.get_frame( opt->hin, &cli_pic, i_frame + opt->i_seek ) )
            break;
        //初始化x264_picture_t結構體pic
        x264_picture_init( &pic );
        //cli_pic到pic
        convert_cli_to_lib_pic( &pic, &cli_pic );

        if( !param->b_vfr_input )
            pic.i_pts = i_frame;

        if( opt->i_pulldown && !param->b_vfr_input )
        {
            pic.i_pic_struct = pulldown->pattern[ i_frame % pulldown->mod ];
            pic.i_pts = (int64_t)( pulldown_pts + 0.5 );
            pulldown_pts += pulldown_frame_duration[pic.i_pic_struct];
        }
        else if( opt->timebase_convert_multiplier )
            pic.i_pts = (int64_t)( pic.i_pts * opt->timebase_convert_multiplier + 0.5 );

        if( pic.i_pts <= largest_pts )
        {
            if( cli_log_level >= X264_LOG_DEBUG || pts_warning_cnt < MAX_PTS_WARNING )
                x264_cli_log( x264, X264_LOG_WARNING, non-strictly-monotonic pts at frame %d (%PRId64 <= %PRId64)
,
                             i_frame, pic.i_pts, largest_pts );
            else if( pts_warning_cnt == MAX_PTS_WARNING )
                x264_cli_log( x264, X264_LOG_WARNING, too many nonmonotonic pts warnings, suppressing further ones
 );
            pts_warning_cnt++;
            pic.i_pts = largest_pts + ticks_per_frame;
        }

        second_largest_pts = largest_pts;
        largest_pts = pic.i_pts;
        if( opt->tcfile_out )
            fprintf( opt->tcfile_out, %.6f
, pic.i_pts * ((double)param->i_timebase_num / param->i_timebase_den) * 1e3 );

        if( opt->qpfile )
            parse_qpfile( opt, &pic, i_frame + opt->i_seek );

        prev_dts = last_dts;
        //編碼pic中存儲的1幀YUV數據
        i_frame_size = encode_frame( h, opt->hout, &pic, &last_dts );
        if( i_frame_size < 0 )
        {
            b_ctrl_c = 1; /* lie to exit the loop */
            retval = -1;
        }
        else if( i_frame_size )
        {
            i_file += i_frame_size;
            i_frame_output++;
            if( i_frame_output == 1 )
                first_dts = prev_dts = last_dts;
        }
        //釋放處理完的YUV數據
        if( filter.release_frame( opt->hin, &cli_pic, i_frame + opt->i_seek ) )
            break;

        /* update status line (up to 1000 times per input file) */
        if( opt->b_progress && i_frame_output )
            i_previous = print_status( i_start, i_previous, i_frame_output, param->i_frame_total, i_file, param, 2 * last_dts - prev_dts - first_dts );
    }
    /* Flush delayed frames */
    //輸出編碼器中剩余的幀
    //x264_encoder_delayed_frames()返回剩余的幀的個數
    while( !b_ctrl_c && x264_encoder_delayed_frames( h ) )
    {
        prev_dts = last_dts;
        //編碼
        //注意第3個參數為NULL
        i_frame_size = encode_frame( h, opt->hout, NULL, &last_dts );
        if( i_frame_size < 0 )
        {
            b_ctrl_c = 1; /* lie to exit the loop */
            retval = -1;
        }
        else if( i_frame_size )
        {
            i_file += i_frame_size;
            i_frame_output++;
            if( i_frame_output == 1 )
                first_dts = prev_dts = last_dts;
        }
        //輸出一些統計信息
        if( opt->b_progress && i_frame_output )
            i_previous = print_status( i_start, i_previous, i_frame_output, param->i_frame_total, i_file, param, 2 * last_dts - prev_dts - first_dts );
    }
fail:
    if( pts_warning_cnt >= MAX_PTS_WARNING && cli_log_level < X264_LOG_DEBUG )
        x264_cli_log( x264, X264_LOG_WARNING, %d suppressed nonmonotonic pts warnings
, pts_warning_cnt-MAX_PTS_WARNING );

    /* duration algorithm fails when only 1 frame is output */
    if( i_frame_output == 1 )
        duration = (double)param->i_fps_den / param->i_fps_num;
    else if( b_ctrl_c )
        duration = (double)(2 * last_dts - prev_dts - first_dts) * param->i_timebase_num / param->i_timebase_den;
    else
        duration = (double)(2 * largest_pts - second_largest_pts) * param->i_timebase_num / param->i_timebase_den;
    //計時
    i_end = x264_mdate();
    /* Erase progress indicator before printing encoding stats. */
    if( opt->b_progress )
        fprintf( stderr,                                                                                
 );
    //關閉編碼器
    if( h )
        x264_encoder_close( h );
    fprintf( stderr, 
 );

    if( b_ctrl_c )
        fprintf( stderr, aborted at input frame %d, output frame %d
, opt->i_seek + i_frame, i_frame_output );
    //關閉輸出文件
    cli_output.close_file( opt->hout, largest_pts, second_largest_pts );
    opt->hout = NULL;

    if( i_frame_output > 0 )
    {
        double fps = (double)i_frame_output * (double)1000000 /
                     (double)( i_end - i_start );

        fprintf( stderr, encoded %d frames, %.2f fps, %.2f kb/s
, i_frame_output, fps,
                 (double) i_file * 8 / ( 1000 * duration ) );
    }

    return retval;
}

從源代碼可以梳理出來encode()的流程:
(1)調用x264_encoder_open()打開H.264編碼器。
(2)調用x264_encoder_parameters()獲得當前的參數集x264_param_t,用於後續步驟中的一些配置。
(3)調用輸出格式(H.264裸流、FLV、mp4等)對應cli_output_t結構體的set_param()方法,為輸出格式的封裝器設定參數。其中參數源自於上一步驟得到的x264_param_t。
(4)如果不是在每個keyframe前面都增加SPS/PPS/SEI的話,就調用x264_encoder_headers()在整個碼流前面加SPS/PPS/SEI。
(5)進入一個循環中進行一幀一幀的將YUV編碼為H.264:
a)調用輸入格式(YUV、Y4M等)對應的cli_vid_filter_t結構體get_frame()方法,獲取一幀YUV數據。
b)調用encode_frame()編碼該幀YUV數據為H.264數據,並且輸出出來。該函數內部調用x264_encoder_encode()完成編碼工作,調用輸出格式對應cli_output_t結構體的write_frame()完成了輸出工作。
c)調用輸入格式(YUV、Y4M等)對應的cli_vid_filter_t結構體release_frame()方法,釋放剛才獲取的YUV數據。
d)調用print_status()輸出一些統計信息。
(6)編碼即將結束的時候,進入另一個循環,輸出編碼器中緩存的視頻幀:
a)不再傳遞新的YUV數據,直接調用encode_frame(),將編碼器中緩存的剩余幾幀數據編碼輸出出來。
b)調用print_status()輸出一些統計信息。
(7)調用x264_encoder_close()關閉H.264編碼器。
 

encode()的流程中涉及到libx264的幾個關鍵的API,在這裡暫時不做詳細分析(後續文章中再進行補充):
x264_encoder_open():打開H.264編碼器。
x264_encoder_headers():輸出SPS/PPS/SEI。
x264_encoder_encode():編碼一幀數據。
x264_encoder_close():關閉H.264編碼器。

此外上述流程中涉及到兩個比較簡單的函數:encode_frame()和print_status()。其中encode_frame()用於編碼一幀數據,而print_status()用於輸出一幀數據編碼後的統計信息。下文記錄一下這兩個函數的定義。

encode_frame()

encode_frame()的定義如下。
//編碼1幀
static int encode_frame( x264_t *h, hnd_t hout, x264_picture_t *pic, int64_t *last_dts )
{
    x264_picture_t pic_out;
    x264_nal_t *nal;
    int i_nal;
    int i_frame_size = 0;
    //編碼API
    //編碼x264_picture_t為x264_nal_t
    i_frame_size = x264_encoder_encode( h, &nal, &i_nal, pic, &pic_out );

    FAIL_IF_ERROR( i_frame_size < 0, x264_encoder_encode failed
 );

    if( i_frame_size )
    {
    	//通過cli_output_t中的方法輸出
    	//輸出raw H.264流的話,等同於直接fwrite()
    	//其他封裝格式,則還需進行一定的封裝
        i_frame_size = cli_output.write_frame( hout, nal[0].p_payload, i_frame_size, &pic_out );
        *last_dts = pic_out.i_dts;
    }

    return i_frame_size;
}

從源代碼可以看出,encode_frame()內部調用x264_encoder_encode()完成編碼工作,調用輸出格式對應cli_output_t結構體的write_frame()完成了輸出工作。其中有關cli_output_t結構體的知識將在後文中記錄。

print_status()

print_status()的定義如下。
//打印一些和時間有關的統計信息
static int64_t print_status( int64_t i_start, int64_t i_previous, int i_frame, int i_frame_total, int64_t i_file, x264_param_t *param, int64_t last_ts )
{
    char buf[200];
    int64_t i_time = x264_mdate();
    if( i_previous && i_time - i_previous < UPDATE_INTERVAL )
        return i_previous;
    int64_t i_elapsed = i_time - i_start;
    double fps = i_elapsed > 0 ? i_frame * 1000000. / i_elapsed : 0;
    double bitrate;
    if( last_ts )
        bitrate = (double) i_file * 8 / ( (double) last_ts * 1000 * param->i_timebase_num / param->i_timebase_den );
    else
        bitrate = (double) i_file * 8 / ( (double) 1000 * param->i_fps_den / param->i_fps_num );
    if( i_frame_total )
    {
    	//形成輸出的字符串
        int eta = i_elapsed * (i_frame_total - i_frame) / ((int64_t)i_frame * 1000000);
        sprintf( buf, x264 [%.1f%%] %d/%d frames, %.2f fps, %.2f kb/s, eta %d:%02d:%02d,
                 100. * i_frame / i_frame_total, i_frame, i_frame_total, fps, bitrate,
                 eta/3600, (eta/60)%60, eta%60 );
    }
    else
        sprintf( buf, x264 %d frames: %.2f fps, %.2f kb/s, i_frame, fps, bitrate );
    //輸出到stderr
    fprintf( stderr, %s  
, buf+5 );
    //設置到標題欄?
    x264_cli_set_console_title( buf );
    fflush( stderr ); // needed in windows
    return i_time;
}

print_status()的代碼不再詳細記錄,它的輸出效果如下圖中紅框中的文字。
/

 

 

X264控制台程序中和輸入輸出相關的結構體

在x264控制台程序中有3個和輸入輸出相關的結構體:
cli_output_t:輸出格式對應的結構體。輸出格式一般為H.264裸流、FLV、MP4等。
cli_input_t:輸入格式對應的結構體。輸入格式一般為純YUV像素數據,Y4M格式數據等。
cli_vid_filter_t:輸入格式濾鏡結構體。濾鏡可以對輸入數據做一些簡單的處理,例如拉伸、裁剪等等(當然濾鏡也可以不作任何處理,直接讀取輸入數據)。
在x264的編碼過程中,調用cli_vid_filter_t結構體的get_frame()讀取YUV數據,調用cli_output_t的write_frame()寫入數據。下面簡單分析一下它們之間的關系。

cli_output_t

x264項目中和cli_output_t結構體相關的源代碼都位於根目錄的output文件夾下。cli_output_t的定義位於outputoutput.h,如下所示。
typedef struct
{
    int (*open_file)( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt );
    int (*set_param)( hnd_t handle, x264_param_t *p_param );
    int (*write_headers)( hnd_t handle, x264_nal_t *p_nal );
    int (*write_frame)( hnd_t handle, uint8_t *p_nal, int i_size, x264_picture_t *p_picture );
    int (*close_file)( hnd_t handle, int64_t largest_pts, int64_t second_largest_pts );
} cli_output_t;

extern const cli_output_t raw_output;
extern const cli_output_t mkv_output;
extern const cli_output_t mp4_output;
extern const cli_output_t flv_output;

從源代碼中可以看出,cli_output_t中一共包含了open_file(),set_param(),write_headers(),write_frame(),close_file()五個接口。在x264中有raw_output,mkv_output,mp4_output,flv_output這幾個cli_output_t結構體,分別對應H.264裸流,MKV,MP4,FLV格式。下面舉例看兩個結構體:raw_output和flv_output。

raw_output(H.264裸流的cli_output_t結構體)
raw_output的定義位於output aw.c,該文件內容如下所示。

#include output.h

static int open_file( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt )
{
    if( !strcmp( psz_filename, - ) )
        *p_handle = stdout;
    else if( !(*p_handle = x264_fopen( psz_filename, w+b )) )
        return -1;

    return 0;
}

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

static int write_headers( hnd_t handle, x264_nal_t *p_nal )
{
    int size = p_nal[0].i_payload + p_nal[1].i_payload + p_nal[2].i_payload;

    if( fwrite( p_nal[0].p_payload, size, 1, (FILE*)handle ) )
        return size;
    return -1;
}

static int write_frame( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_t *p_picture )
{
    if( fwrite( p_nalu, i_size, 1, (FILE*)handle ) )
        return i_size;
    return -1;
}

static int close_file( hnd_t handle, int64_t largest_pts, int64_t second_largest_pts )
{
    if( !handle || handle == stdout )
        return 0;

    return fclose( (FILE*)handle );
}

const cli_output_t raw_output = { open_file, set_param, write_headers, write_frame, close_file };

可以看出raw_output中的函數定義都比較簡單,只是封裝了fwrite(),fclose()等函數。

flv_output(FLV格式的cli_output_t結構體)
flv_output的定義位於output lv.c,如下所示。

const cli_output_t flv_output = { open_file, set_param, write_headers, write_frame, close_file };
該文件內容比較多,只舉例看一下其中的兩個函數:open_file()和write_frame()。

open_file()
flv_output 中的open_file()的定義如下所示。

static int write_header( flv_buffer *c )
{
    flv_put_tag( c, FLV ); // Signature
    flv_put_byte( c, 1 );    // Version
    flv_put_byte( c, 1 );    // Video Only
    flv_put_be32( c, 9 );    // DataOffset
    flv_put_be32( c, 0 );    // PreviousTagSize0

    return flv_flush_data( c );
}

static int open_file( char *psz_filename, hnd_t *p_handle, cli_output_opt_t *opt )
{
    *p_handle = NULL;
    flv_hnd_t *p_flv = calloc( 1, sizeof(flv_hnd_t) );
    if( !p_flv )
        return -1;

    p_flv->b_dts_compress = opt->use_dts_compress;

    p_flv->c = flv_create_writer( psz_filename );
    if( !p_flv->c )
        return -1;

    CHECK( write_header( p_flv->c ) );
    *p_handle = p_flv;

    return 0;
}
可以看出flv_output 中的open_file()中完成了FLV封裝格式文件頭的創建。

write_frame()
flv_output 中的write_frame()的定義如下所示。

static int write_frame( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_t *p_picture )
{
    flv_hnd_t *p_flv = handle;
    flv_buffer *c = p_flv->c;

#define convert_timebase_ms( timestamp, timebase ) (int64_t)((timestamp) * (timebase) * 1000 + 0.5)

    if( !p_flv->i_framenum )
    {
        p_flv->i_delay_time = p_picture->i_dts * -1;
        if( !p_flv->b_dts_compress && p_flv->i_delay_time )
            x264_cli_log( flv, X264_LOG_INFO, initial delay %PRId64 ms
,
                          convert_timebase_ms( p_picture->i_pts + p_flv->i_delay_time, p_flv->d_timebase ) );
    }

    int64_t dts;
    int64_t cts;
    int64_t offset;

    if( p_flv->b_dts_compress )
    {
        if( p_flv->i_framenum == 1 )
            p_flv->i_init_delta = convert_timebase_ms( p_picture->i_dts + p_flv->i_delay_time, p_flv->d_timebase );
        dts = p_flv->i_framenum > p_flv->i_delay_frames
            ? convert_timebase_ms( p_picture->i_dts, p_flv->d_timebase )
            : p_flv->i_framenum * p_flv->i_init_delta / (p_flv->i_delay_frames + 1);
        cts = convert_timebase_ms( p_picture->i_pts, p_flv->d_timebase );
    }
    else
    {
        dts = convert_timebase_ms( p_picture->i_dts + p_flv->i_delay_time, p_flv->d_timebase );
        cts = convert_timebase_ms( p_picture->i_pts + p_flv->i_delay_time, p_flv->d_timebase );
    }
    offset = cts - dts;

    if( p_flv->i_framenum )
    {
        if( p_flv->i_prev_dts == dts )
            x264_cli_log( flv, X264_LOG_WARNING, duplicate DTS %PRId64 generated by rounding

                                         decoding framerate cannot exceed 1000fps
, dts );
        if( p_flv->i_prev_cts == cts )
            x264_cli_log( flv, X264_LOG_WARNING, duplicate CTS %PRId64 generated by rounding

                                         composition framerate cannot exceed 1000fps
, cts );
    }
    p_flv->i_prev_dts = dts;
    p_flv->i_prev_cts = cts;

    // A new frame - write packet header
    flv_put_byte( c, FLV_TAG_TYPE_VIDEO );
    flv_put_be24( c, 0 ); // calculated later
    flv_put_be24( c, dts );
    flv_put_byte( c, dts >> 24 );
    flv_put_be24( c, 0 );

    p_flv->start = c->d_cur;
    flv_put_byte( c, p_picture->b_keyframe ? FLV_FRAME_KEY : FLV_FRAME_INTER );
    flv_put_byte( c, 1 ); // AVC NALU
    flv_put_be24( c, offset );

    if( p_flv->sei )
    {
        flv_append_data( c, p_flv->sei, p_flv->sei_len );
        free( p_flv->sei );
        p_flv->sei = NULL;
    }
    flv_append_data( c, p_nalu, i_size );

    unsigned length = c->d_cur - p_flv->start;
    flv_rewrite_amf_be24( c, length, p_flv->start - 10 );
    flv_put_be32( c, 11 + length ); // Last tag size
    CHECK( flv_flush_data( c ) );

    p_flv->i_framenum++;

    return i_size;
}

flv_output 中的可以看出write_frame()中完成了FLV封裝格式中一個Tag單元的創建。

cli_input_t

x264項目中和cli_input_t結構體相關的源代碼都位於根目錄的input文件夾下。cli_input_t的定義位於inputinput.h,如下所示。
typedef struct
{
    int (*open_file)( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt );
    int (*picture_alloc)( cli_pic_t *pic, int csp, int width, int height );
    int (*read_frame)( cli_pic_t *pic, hnd_t handle, int i_frame );
    int (*release_frame)( cli_pic_t *pic, hnd_t handle );
    void (*picture_clean)( cli_pic_t *pic );
    int (*close_file)( hnd_t handle );
} cli_input_t;

extern const cli_input_t raw_input;
extern const cli_input_t y4m_input;
extern const cli_input_t avs_input;
extern const cli_input_t lavf_input;
extern const cli_input_t ffms_input;

從源代碼中可以看出,cli_input_t中一共包含了open_file(),picture_alloc(),read_frame(),release_frame(),picture_clean(),close_file()六個接口。在x264中有raw_input,y4m_input,avs_input,lavf_input,ffms_input這幾個cli_output_t結構體,分別對應H.264裸流,Y4M,AVS,LAVF,FFMS格式(後幾種沒有接觸過)。下面舉例看兩個結構體:raw_input和y4m_input。

raw_input(純YUV像素數據的cli_input_t結構體)
raw_input的定義位於input aw.c,該文件內容如下所示。

#include input.h
#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, raw, __VA_ARGS__ )

typedef struct
{
    FILE *fh;
    int next_frame;
    uint64_t plane_size[4];
    uint64_t frame_size;
    int bit_depth;
} raw_hnd_t;

//打開raw YUV格式文件
static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
{
    raw_hnd_t *h = calloc( 1, sizeof(raw_hnd_t) );
    if( !h )
        return -1;

    if( !opt->resolution )
    {
    	//如果沒有設置分辨率
    	//嘗試從文件名中解析分辨率
        /* try to parse the file name */
        for( char *p = psz_filename; *p; p++ )
            if( *p >= '0' && *p <= '9' && sscanf( p, %dx%d, &info->width, &info->height ) == 2 )
                break;
    }
    else
        sscanf( opt->resolution, %dx%d, &info->width, &info->height );
    //沒有分辨率信息的話,會彈出錯誤信息
    FAIL_IF_ERROR( !info->width || !info->height, raw input requires a resolution.
 )
    //設置顏色空間
    if( opt->colorspace )
    {
        for( info->csp = X264_CSP_CLI_MAX-1; info->csp > X264_CSP_NONE; info->csp-- )
        {
            if( x264_cli_csps[info->csp].name && !strcasecmp( x264_cli_csps[info->csp].name, opt->colorspace ) )
                break;
        }
        FAIL_IF_ERROR( info->csp == X264_CSP_NONE, unsupported colorspace `%s'
, opt->colorspace );
    }
    else /* default */
        info->csp = X264_CSP_I420;//默認為YUV420P
    //顏色位深
    h->bit_depth = opt->bit_depth;
    FAIL_IF_ERROR( h->bit_depth < 8 || h->bit_depth > 16, unsupported bit depth `%d'
, h->bit_depth );
    if( h->bit_depth > 8 )
        info->csp |= X264_CSP_HIGH_DEPTH;

    if( !strcmp( psz_filename, - ) )
        h->fh = stdin;  //從管道輸入
    else
        h->fh = x264_fopen( psz_filename, rb );  //打開文件
    if( h->fh == NULL )
        return -1;

    info->thread_safe = 1;
    info->num_frames  = 0;
    info->vfr         = 0;

    const x264_cli_csp_t *csp = x264_cli_get_csp( info->csp );
    for( int i = 0; i < csp->planes; i++ )
    {
        h->plane_size[i] = x264_cli_pic_plane_size( info->csp, info->width, info->height, i );
        h->frame_size += h->plane_size[i];
        /* x264_cli_pic_plane_size returns the size in bytes, we need the value in pixels from here on */
        h->plane_size[i] /= x264_cli_csp_depth_factor( info->csp );
    }

    if( x264_is_regular_file( h->fh ) )
    {
        fseek( h->fh, 0, SEEK_END );
        uint64_t size = ftell( h->fh );
        fseek( h->fh, 0, SEEK_SET );
        info->num_frames = size / h->frame_size;
    }

    *p_handle = h;
    return 0;
}

//讀取一幀數據-內部
static int read_frame_internal( cli_pic_t *pic, raw_hnd_t *h, int bit_depth_uc )
{
    int error = 0;
    int pixel_depth = x264_cli_csp_depth_factor( pic->img.csp );
    //一個分量一個分量讀
    for( int i = 0; i < pic->img.planes && !error; i++ )
    {
    	//fread()讀取
        error |= fread( pic->img.plane[i], pixel_depth, h->plane_size[i], h->fh ) != h->plane_size[i];
        if( bit_depth_uc )
        {
            /* upconvert non 16bit high depth planes to 16bit using the same
             * algorithm as used in the depth filter. */
            uint16_t *plane = (uint16_t*)pic->img.plane[i];
            uint64_t pixel_count = h->plane_size[i];
            int lshift = 16 - h->bit_depth;
            for( uint64_t j = 0; j < pixel_count; j++ )
                plane[j] = plane[j] << lshift;
        }
    }
    return error;
}
//讀取一幀數據
static int read_frame( cli_pic_t *pic, hnd_t handle, int i_frame )
{
    raw_hnd_t *h = handle;

    if( i_frame > h->next_frame )
    {
        if( x264_is_regular_file( h->fh ) )
            fseek( h->fh, i_frame * h->frame_size, SEEK_SET );  //fseek()。偏移量=幀序號*幀大小。
        else
            while( i_frame > h->next_frame )
            {
            	//讀取一幀數據-內部
                if( read_frame_internal( pic, h, 0 ) )
                    return -1;
                h->next_frame++;
            }
    }

    if( read_frame_internal( pic, h, h->bit_depth & 7 ) )
        return -1;

    h->next_frame = i_frame+1;
    return 0;
}

//關閉文件
static int close_file( hnd_t handle )
{
    raw_hnd_t *h = handle;
    if( !h || !h->fh )
        return 0;
    //fclose()關閉文件
    fclose( h->fh );
    free( h );
    return 0;
}
//raw格式對應的數組
const cli_input_t raw_input = { open_file, x264_cli_pic_alloc, read_frame, NULL, x264_cli_pic_clean, close_file };

從源代碼中可以看出,raw_input 中的open_file()函數在打開YUV像素數據的時候,會首先判斷是否設置了寬和高(YUV是純像素數據,沒有寬和高信息),如果沒有設置,則會嘗試從文件路徑中解析寬和高信息。如果成功完成上述步驟,open_file()就會調用x264_fopen()打開輸入文件。其他的函數在源代碼中都寫了注釋,就不再重復記錄了。

 

y4m_input(Y4M格式的cli_input_t結構體)
y4m_input的定義位於inputy4m.c,如下所示。

const cli_input_t y4m_input = { open_file, x264_cli_pic_alloc, read_frame, NULL, x264_cli_pic_clean, close_file };
該文件內容較多,不再進行詳細分析。在這裡看一個打開文件的函數open_file()。該函數的定義如下所示。
typedef struct
{
    FILE *fh;
    int next_frame;
    int seq_header_len;
    int frame_header_len;
    uint64_t frame_size;
    uint64_t plane_size[3];
    int bit_depth;
} y4m_hnd_t;

#define Y4M_MAGIC YUV4MPEG2
#define MAX_YUV4_HEADER 80
#define Y4M_FRAME_MAGIC FRAME
#define MAX_FRAME_HEADER 80

static int parse_csp_and_depth( char *csp_name, int *bit_depth )
{
    int csp    = X264_CSP_MAX;

    /* Set colorspace from known variants */
    if( !strncmp( 420, csp_name, 3 ) )
        csp = X264_CSP_I420;
    else if( !strncmp( 422, csp_name, 3 ) )
        csp = X264_CSP_I422;
    else if( !strncmp( 444, csp_name, 3 ) && strncmp( 444alpha, csp_name, 8 ) ) // only accept alphaless 4:4:4
        csp = X264_CSP_I444;

    /* Set high bit depth from known extensions */
    if( sscanf( csp_name, %*d%*[pP]%d, bit_depth ) != 1 )
        *bit_depth = 8;

    return csp;
}

static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
{
    y4m_hnd_t *h = malloc( sizeof(y4m_hnd_t) );
    int i;
    uint32_t n, d;
    char header[MAX_YUV4_HEADER+10];
    char *tokend, *header_end;
    int colorspace = X264_CSP_NONE;
    int alt_colorspace = X264_CSP_NONE;
    int alt_bit_depth  = 8;
    if( !h )
        return -1;

    h->next_frame = 0;
    info->vfr = 0;

    if( !strcmp( psz_filename, - ) )
        h->fh = stdin;
    else
        h->fh = x264_fopen(psz_filename, rb);
    if( h->fh == NULL )
        return -1;

    h->frame_header_len = strlen( Y4M_FRAME_MAGIC )+1;

    /* Read header */
    //解析Y4M格式的文件頭
    for( i = 0; i < MAX_YUV4_HEADER; i++ )
    {
        header[i] = fgetc( h->fh );
        if( header[i] == '
' )www.2cto.com
        {
            /* Add a space after last option. Makes parsing 444 vs
               444alpha easier. */
            header[i+1] = 0x20;
            header[i+2] = 0;
            break;
        }
    }
    if( i == MAX_YUV4_HEADER || strncmp( header, Y4M_MAGIC, strlen( Y4M_MAGIC ) ) )
        return -1;

    /* Scan properties */
    header_end = &header[i+1]; /* Include space */
    h->seq_header_len = i+1;
    for( char *tokstart = &header[strlen( Y4M_MAGIC )+1]; tokstart < header_end; tokstart++ )
    {
        if( *tokstart == 0x20 )
            continue;
        switch( *tokstart++ )
        {
            case 'W': /* Width. Required. */
                info->width = strtol( tokstart, &tokend, 10 );
                tokstart=tokend;
                break;
            case 'H': /* Height. Required. */
                info->height = strtol( tokstart, &tokend, 10 );
                tokstart=tokend;
                break;
            case 'C': /* Color space */
                colorspace = parse_csp_and_depth( tokstart, &h->bit_depth );
                tokstart = strchr( tokstart, 0x20 );
                break;
            case 'I': /* Interlace type */
                switch( *tokstart++ )
                {
                    case 't':
                        info->interlaced = 1;
                        info->tff = 1;
                        break;
                    case 'b':
                        info->interlaced = 1;
                        info->tff = 0;
                        break;
                    case 'm':
                        info->interlaced = 1;
                        break;
                    //case '?':
                    //case 'p':
                    default:
                        break;
                }
                break;
            case 'F': /* Frame rate - 0:0 if unknown */
                if( sscanf( tokstart, %u:%u, &n, &d ) == 2 && n && d )
                {
                    x264_reduce_fraction( &n, &d );
                    info->fps_num = n;
                    info->fps_den = d;
                }
                tokstart = strchr( tokstart, 0x20 );
                break;
            case 'A': /* Pixel aspect - 0:0 if unknown */
                /* Don't override the aspect ratio if sar has been explicitly set on the commandline. */
                if( sscanf( tokstart, %u:%u, &n, &d ) == 2 && n && d )
                {
                    x264_reduce_fraction( &n, &d );
                    info->sar_width  = n;
                    info->sar_height = d;
                }
                tokstart = strchr( tokstart, 0x20 );
                break;
            case 'X': /* Vendor extensions */
                if( !strncmp( YSCSS=, tokstart, 6 ) )
                {
                    /* Older nonstandard pixel format representation */
                    tokstart += 6;
                    alt_colorspace = parse_csp_and_depth( tokstart, &alt_bit_depth );
                }
                tokstart = strchr( tokstart, 0x20 );
                break;
        }
    }

    if( colorspace == X264_CSP_NONE )
    {
        colorspace   = alt_colorspace;
        h->bit_depth = alt_bit_depth;
    }

    // default to 8bit 4:2:0 if nothing is specified
    if( colorspace == X264_CSP_NONE )
    {
        colorspace    = X264_CSP_I420;
        h->bit_depth  = 8;
    }

    FAIL_IF_ERROR( colorspace <= X264_CSP_NONE || colorspace >= X264_CSP_MAX, colorspace unhandled
 )
    FAIL_IF_ERROR( h->bit_depth < 8 || h->bit_depth > 16, unsupported bit depth `%d'
, h->bit_depth );

    info->thread_safe = 1;
    info->num_frames  = 0;
    info->csp         = colorspace;
    h->frame_size     = h->frame_header_len;

    if( h->bit_depth > 8 )
        info->csp |= X264_CSP_HIGH_DEPTH;

    const x264_cli_csp_t *csp = x264_cli_get_csp( info->csp );

    for( i = 0; i < csp->planes; i++ )
    {
        h->plane_size[i] = x264_cli_pic_plane_size( info->csp, info->width, info->height, i );
        h->frame_size += h->plane_size[i];
        /* x264_cli_pic_plane_size returns the size in bytes, we need the value in pixels from here on */
        h->plane_size[i] /= x264_cli_csp_depth_factor( info->csp );
    }

    /* Most common case: frame_header = FRAME */
    if( x264_is_regular_file( h->fh ) )
    {
        uint64_t init_pos = ftell( h->fh );
        fseek( h->fh, 0, SEEK_END );
        uint64_t i_size = ftell( h->fh );
        fseek( h->fh, init_pos, SEEK_SET );
        info->num_frames = (i_size - h->seq_header_len) / h->frame_size;
    }

    *p_handle = h;
    return 0;
}

從源代碼可以看出,y4m_input中的open_file()完成了Y4M文件的打開和文件頭解析的功能。

cli_vid_filter_t

x264項目中和cli_vid_filter_t結構體相關的源代碼都位於根目錄的filters文件夾下。cli_vid_filter_t的定義位於filters ideo ideo.h,如下所示。
struct cli_vid_filter_t
{
    /* name of the filter */
    const char *name;
    /* help: a short message on what the filter does and how to use it.
     * this should only be implemented by filters directly accessible by the user */
    void (*help)( int longhelp );
    /* init: initializes the filter given the input clip properties and parameter to adjust them as necessary
     * with the given options provided by the user.
     * returns 0 on success, nonzero on error. */
    int (*init)( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string );
    /* get_frame: given the storage for the output frame and desired frame number, generate the frame accordingly.
     * the image data returned by get_frame should be treated as const and not be altered.
     * returns 0 on success, nonzero on error. */
    int (*get_frame)( hnd_t handle, cli_pic_t *output, int frame );
    /* release_frame: frame is done being used and is signaled for cleanup.
     * returns 0 on succeess, nonzero on error. */
    int (*release_frame)( hnd_t handle, cli_pic_t *pic, int frame );
    /* free: run filter cleanup procedures. */
    void (*free)( hnd_t handle );
    /* next registered filter, unused by filters themselves */
    cli_vid_filter_t *next;
};

從源代碼中可以看出,cli_input_t中一共包含了help(),init(),get_frame(),release_frame(),free()幾個接口。下面舉例看兩個Filter結構體:
source_filter:不作任何處理。
resize_filter:拉伸。

source_filter(沒有功能的cli_vid_filter_t結構體)
source_filter的定義位於filters ideosource.c,該文件內容如下所示。

#include video.h

/* This filter converts the demuxer API into the filtering API for video frames.
 * Backseeking is prohibited here as not all demuxers are capable of doing so. */

typedef struct
{
    cli_pic_t pic;
    hnd_t hin;
    int cur_frame;
} source_hnd_t;

cli_vid_filter_t source_filter;

static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
{
    source_hnd_t *h = calloc( 1, sizeof(source_hnd_t) );
    if( !h )
        return -1;
    h->cur_frame = -1;

    if( cli_input.picture_alloc( &h->pic, info->csp, info->width, info->height ) )
        return -1;

    h->hin = *handle;
    *handle = h;
    *filter = source_filter;

    return 0;
}

static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
{
    source_hnd_t *h = handle;
    /* do not allow requesting of frames from before the current position */
    if( frame <= h->cur_frame || cli_input.read_frame( &h->pic, h->hin, frame ) )
        return -1;
    h->cur_frame = frame;
    *output = h->pic;
    return 0;
}

static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
{
    source_hnd_t *h = handle;
    if( cli_input.release_frame && cli_input.release_frame( &h->pic, h->hin ) )
        return -1;
    return 0;
}

static void free_filter( hnd_t handle )
{
    source_hnd_t *h = handle;
    cli_input.picture_clean( &h->pic );
    cli_input.close_file( h->hin );
    free( h );
}

cli_vid_filter_t source_filter = { source, NULL, init, get_frame, release_frame, free_filter, NULL };

從源代碼中可以看出,source_filter的get_frame()直接調用了cli_input_t的read_frame();而它的release_frame()也是直接調用了cli_input_t的release_frame()。簡而言之,source_filter相當於是一個cli_input_t。

resize_filter(拉伸功能對應的cli_vid_filter_t結構體)
resize_filter的定義位於filters ideo esize.c,該結構體定義如下。

cli_vid_filter_t resize_filter = { NAME, help, init, get_frame, release_frame, free_filter, NULL };
由於resize_filter涉及到的代碼比較多,在這裡僅看一下它的get_frame()的定義。
static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
{
    resizer_hnd_t *h = handle;
    if( h->prev_filter.get_frame( h->prev_hnd, output, frame ) )
        return -1;
    if( h->variable_input && check_resizer( h, output ) )
        return -1;
    h->working = 1;
    if( h->pre_swap_chroma )
        XCHG( uint8_t*, output->img.plane[1], output->img.plane[2] );
    if( h->ctx )
    {
        sws_scale( h->ctx, (const uint8_t* const*)output->img.plane, output->img.stride,
                   0, output->img.height, h->buffer.img.plane, h->buffer.img.stride );
        output->img = h->buffer.img; /* copy img data */
    }
    else
        output->img.csp = h->dst_csp;
    if( h->post_swap_chroma )
        XCHG( uint8_t*, output->img.plane[1], output->img.plane[2] );

    return 0;
}

可以看出resize_filter中調用了libswscale類庫中的sws_scale()對圖像完成了拉伸工作。
注:拉伸濾鏡需要libswscale類庫的支持。


至此cli_output_t,cli_input_t,cli_vid_filter_t這3個在x264中與輸入輸出有關的結構體的源代碼就分析完畢了。有關x264命令行工具的源代碼分析工作也就做完了。下一篇文章開始對libx264內部的源代碼進行分析。


 




  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved