x264源代码帧级编码分支encode_frame( h, opt->hout, NULL, &last_dts )

来源:互联网 发布:java获取当年的第一天 编辑:程序博客网 时间:2024/05/01 23:17

最近看x264源代码,帧级编码函数static int encode( x264_param_t *param, cli_opt_t *opt )中有两个循环

  for( ; !b_ctrl_c && (i_frame < param->i_frame_total || !param->i_frame_total); i_frame++ )
    {
        if( filter.get_frame( opt->hin, &cli_pic, i_frame + opt->i_seek ) )
            break;
        x264_picture_init( &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")\n",
                             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\n" );
            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\n", 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;
        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;
        }


        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 */
    while( !b_ctrl_c && x264_encoder_delayed_frames( h ) )
    {
        prev_dts = last_dts;
        i_frame_size = encode_frame( h, opt->hout, NULL, &last_dts );//进入MBtree模块
        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 );
    }

其中encode_frame出现了两次,不知道各自有什么作用。

假设视频100帧,

当开启mbtree时,第一个encode_frame函数执行65次后,开始在此函数编码第1帧,在此循环内共编100-65=35帧,其余的65帧在第二个循环内编码。就相当于,前65帧为预处理,在预处理时,把读入的帧放入了缓存内,总共放入65帧缓存。

当关闭mbtree时,第一个encode_frame函数执行17次后,开始在此函数编码第1帧,在此循环内共编100-17=83帧,其余的17帧在第二个循环内编码。就相当于,前17帧为预处理,在预处理时,把读入的帧放入了缓存内,总共放入17帧缓存。

当视频输入帧数较少时,比如少于10帧,则在第一个循环内进行10帧的预处理,在第二个循环内进行这10帧的编码。


具体分析x264为什么这么做呢?

可以这么理解,第一个循环主要是为了读入全部的帧,并进行预处理;预处理窗口满了之后进行编码。这个编码帧应该比读入的帧延迟n帧,n为预处理窗口的大小。

第二个循环主要是用来对剩余在缓存器里的,也就是预处理窗口剩下的帧进行编码;因为已经输入了,所以不用再输入图像了,所以第二个encode_frame输入的参数有一个为NUL。

缓存器的大小为z,那么 z = max(rc-lookahed+6,18),自己实验测得的。

0 0