帧类型决策-x264_slicetype_decide()

/*
	lookahead队列中的帧类型确定

	过程:
		1.计算每一帧的时长duration
		2.对lookahead里的每一帧进行帧类型分析
			·x264_ratecontrol_slice_type()
			·x264_slicetpye_analyse()
		3.对一段BBB...BBB(IDR/I/P)
			1.检查BREF是否合规
			2.检查Keyframe,设置为openGOP?I:IDR
			3.检查是否超过了最大关键帧距离,控制GOPsize,将当前帧设置为openGOP?I:IDR
			4.检查I帧,openGOP?I:IDR
			5.检查IDR帧,将前一帧AUTO/B改为P
			6.检查是否超出两参考帧间最大允许的连续B帧数量,或是否lookahead最后一帧,AUTO/B=>P
			7.检查剩下的AUTO,AUTO=>B
		8.检查是否可以使用BREF,将最中间的帧B=>BREF
		9.计算每个B帧的帧开销,计算最后一帧的intra cost,若最后一帧为P,还要计算其inter cost
		10.若使用P权重??不知道是啥
		11.按照帧类型重新组织这些帧,播放顺序=>编码顺序,即原BBB BREF BBB (IDR/I/P) => (IDR/I/P) BREF BBBBBB
		12.进行calculate_duration(),还不知道计算啥
*/
void x264_slicetype_decide( x264_t *h )
{
    x264_frame_t *frames[X264_BFRAME_MAX+2];
    x264_frame_t *frm;
    int bframes;
    int brefs;

    if( !h->lookahead->next.i_size )	//若不用lookahead,则return
        return;

    int lookahead_size = h->lookahead->next.i_size;

	/****************************
		计算每一帧的时长duration
	*****************************/
    for( int i = 0; i < h->lookahead->next.i_size; i++ )	//遍历next列表
    {
        if( h->param.b_vfr_input )	//variable framerate 
        {
            if( lookahead_size-- > 1 )	//不是最后一帧,则 当前帧时长 = 2 * (后帧pts - 当前帧pts)
                h->lookahead->next.list[i]->i_duration = 2 * (h->lookahead->next.list[i+1]->i_pts - h->lookahead->next.list[i]->i_pts);
            else	// 最后一帧时长 = 前一帧时长
                h->lookahead->next.list[i]->i_duration = h->i_prev_duration;
        }
        else
			/*	根据frame的结构来得到duration,其中i_pic_struct取值:
				PIC_STRUCT_AUTO              = 0, // automatically decide (default)
				PIC_STRUCT_PROGRESSIVE       = 1, // progressive frame
				// "TOP" and "BOTTOM" are not supported in x264 (PAFF only)
				PIC_STRUCT_TOP_BOTTOM        = 4, // top field followed by bottom
				PIC_STRUCT_BOTTOM_TOP        = 5, // bottom field followed by top
				PIC_STRUCT_TOP_BOTTOM_TOP    = 6, // top field, bottom field, top field repeated
				PIC_STRUCT_BOTTOM_TOP_BOTTOM = 7, // bottom field, top field, bottom field repeated
				PIC_STRUCT_DOUBLE            = 8, // double frame
				PIC_STRUCT_TRIPLE            = 9, // triple frame	*/
            h->lookahead->next.list[i]->i_duration = delta_tfi_divisor[h->lookahead->next.list[i]->i_pic_struct];

		//将上一个duration更新为当前帧的duration
        h->i_prev_duration = h->lookahead->next.list[i]->i_duration;

		/* 通过i_duration和sps里的i_time_scale计算f_duration,
		   f_duration是以秒为单位的float,
		   i_duration是以sps里面time_scale为单位的int */
        h->lookahead->next.list[i]->f_duration = (double)h->lookahead->next.list[i]->i_duration
                                               * h->sps->vui.i_num_units_in_tick
                                               / h->sps->vui.i_time_scale;

		//计算Presentation field count
        if( h->lookahead->next.list[i]->i_frame > h->i_disp_fields_last_frame && lookahead_size > 0 )
        {
            h->lookahead->next.list[i]->i_field_cnt = h->i_disp_fields;
            h->i_disp_fields += h->lookahead->next.list[i]->i_duration;
            h->i_disp_fields_last_frame = h->lookahead->next.list[i]->i_frame;
        }
        else if( lookahead_size == 0 )
        {
            h->lookahead->next.list[i]->i_field_cnt = h->i_disp_fields;
            h->lookahead->next.list[i]->i_duration = h->i_prev_duration;
        }

    }	//end of for( int i = 0; i < h->lookahead->next.i_size; i++ )

	/*****************************************
		对lookahead里的每一帧进行帧类型分析
	******************************************/
    if( h->param.rc.b_stat_read )	//若stat_read读取编码配置文件
    {
        /* Use the frame types from the first pass */
		//将lookahead队列中next的所有帧进行码率控制帧类型选择
        for( int i = 0; i < h->lookahead->next.i_size; i++ )
            h->lookahead->next.list[i]->i_type =
                x264_ratecontrol_slice_type( h, h->lookahead->next.list[i]->i_frame );
    }
	//允许B帧 && B帧自适应 || 使用场景转换 || 使用mb-tree || 使用vbv&&码率控制
    else if( (h->param.i_bframe && h->param.i_bframe_adaptive)	
             || h->param.i_scenecut_threshold
             || h->param.rc.b_mb_tree
             || (h->param.rc.i_vbv_buffer_size && h->param.rc.i_lookahead) )
        x264_slicetype_analyse( h, 0 );	//进行帧类型分析,得到每个frame的i_type

	/******************************************************************
		逐个检查各帧的帧类型是否合理
		边遍历边统计B帧个数,直到出现非B帧为止,即BBB...BBB(IDR/I/P)
	*******************************************************************/
    for( bframes = 0, brefs = 0;; bframes++ )
    {
        frm = h->lookahead->next.list[bframes];	//取lookahead中next中的帧

        if( frm->i_forced_type != X264_TYPE_AUTO && frm->i_type != frm->i_forced_type &&
            !(frm->i_forced_type == X264_TYPE_KEYFRAME && IS_X264_TYPE_I( frm->i_type )) )
        {	//输出log,表明该帧的forced类型被改成某类型
            x264_log( h, X264_LOG_WARNING, "forced frame type (%d) at %d was changed to frame type (%d)\n",
                      frm->i_forced_type, frm->i_frame, frm->i_type );
        }

		/* 检查BREF类型是否合规

		   若使用的是X264_B_PYRAMID_NONE(0)或X264_B_PYRAMID_STRICT(1)
		   且brefs == h->param.i_bframe_pyramid??不明白
		   则将当前BREF改为B

		   若使用的是X264_B_PYRAMID_NORMAL(2)
		   且有BREF帧
		   且最大参考帧数量<=BREF帧数量+3
		   则将当前BREF改为B
		   */
        if( frm->i_type == X264_TYPE_BREF && h->param.i_bframe_pyramid < X264_B_PYRAMID_NORMAL &&
            brefs == h->param.i_bframe_pyramid )
        {
            frm->i_type = X264_TYPE_B;
            x264_log( h, X264_LOG_WARNING, "B-ref at frame %d incompatible with B-pyramid %s \n",
                      frm->i_frame, x264_b_pyramid_names[h->param.i_bframe_pyramid] );
        }
        /* pyramid with multiple B-refs needs a big enough dpb that the preceding P-frame stays available.
           smaller dpb could be supported by smart enough use of mmco, but it's easier just to forbid it. */
        else if( frm->i_type == X264_TYPE_BREF && h->param.i_bframe_pyramid == X264_B_PYRAMID_NORMAL &&
            brefs && h->param.i_frame_reference <= (brefs+3) )
        {
            frm->i_type = X264_TYPE_B;
            x264_log( h, X264_LOG_WARNING, "B-ref at frame %d incompatible with B-pyramid %s and %d reference frames\n",
                      frm->i_frame, x264_b_pyramid_names[h->param.i_bframe_pyramid], h->param.i_frame_reference );
        }

		/*检查关键帧*/
        if( frm->i_type == X264_TYPE_KEYFRAME )
            frm->i_type = h->param.b_open_gop ? X264_TYPE_I : X264_TYPE_IDR;

        /* Limit GOP size 限制GOP大小
			检查当前帧距离上一个关键帧的距离>=最大关键帧间距,
			则超出GOP的size,将当前帧改为openGOP ? I : IDR	*/
        if( (!h->param.b_intra_refresh || frm->i_frame == 0) && frm->i_frame - h->lookahead->i_last_keyframe >= h->param.i_keyint_max )
        {	
            if( frm->i_type == X264_TYPE_AUTO || frm->i_type == X264_TYPE_I )	//若是auto或I帧
                frm->i_type = h->param.b_open_gop && h->lookahead->i_last_keyframe >= 0 ? X264_TYPE_I : X264_TYPE_IDR;
            int warn = frm->i_type != X264_TYPE_IDR;	//若当前非IDR,则warn = 1
            if( warn && h->param.b_open_gop )//若warn 且 openGOP
                warn &= frm->i_type != X264_TYPE_I; 
            if( warn )	//警告,GOPsize超标
            {
                x264_log( h, X264_LOG_WARNING, "specified frame type (%d) at %d is not compatible with keyframe interval\n", frm->i_type, frm->i_frame );
                
				//将当前帧类型改为I/IDR
				frm->i_type = h->param.b_open_gop && h->lookahead->i_last_keyframe >= 0 ? X264_TYPE_I : X264_TYPE_IDR;
            }
        }

		/* 检查当前帧是I帧时,且满足距离上一关键帧距离>=最小关键帧距离 */
        if( frm->i_type == X264_TYPE_I && frm->i_frame - h->lookahead->i_last_keyframe >= h->param.i_keyint_min )
        {
            if( h->param.b_open_gop )	//若是openGOP
            {
                h->lookahead->i_last_keyframe = frm->i_frame; // Use display order 更新上一个关键帧为当前帧
                if( h->param.b_bluray_compat )
                    h->lookahead->i_last_keyframe -= bframes; // Use bluray order
                frm->b_keyframe = 1;	//更新当前帧的关键帧标识
            }
            else	//non-openGOP
                frm->i_type = X264_TYPE_IDR;
        }

		/*	检查当前IDR,将其前一帧改为P */
        if( frm->i_type == X264_TYPE_IDR )	//若是IDR
        {
            /* Close GOP */
            h->lookahead->i_last_keyframe = frm->i_frame;	//更新上一个关键帧为当前帧
            frm->b_keyframe = 1;	//设置关键帧标识

			//将前一帧改为P帧,并将B帧计数器-1
            if( bframes > 0 )
            {
                bframes--;
                h->lookahead->next.list[bframes]->i_type = X264_TYPE_P;	//IDR前一帧设置为P帧
            }
        }

		/*	若B帧的数量已经达到了两个参考帧之间所允许的B帧数量上限i_bframe
			或当前帧已是最后一帧
			则当前帧AUTO/B=>P,I/IDR则不变	*/
        if( bframes == h->param.i_bframe ||
            !h->lookahead->next.list[bframes+1] )
        {
            if( IS_X264_TYPE_B( frm->i_type ) )	//若是B帧则打印log
                x264_log( h, X264_LOG_WARNING, "specified frame type is not compatible with max B-frames\n" );
            if( frm->i_type == X264_TYPE_AUTO	//若是AUTO/B帧,则改为P帧
                || IS_X264_TYPE_B( frm->i_type ) )
                frm->i_type = X264_TYPE_P;
        }

		/*若是可BREF,则bref计数器累加*/
        if( frm->i_type == X264_TYPE_BREF )
            brefs++;

		/*若是AUTO,则改为B*/
        if( frm->i_type == X264_TYPE_AUTO )
            frm->i_type = X264_TYPE_B;

		/*若是非B帧则退出循环*/
        else if( !IS_X264_TYPE_B( frm->i_type ) ) break;
    }	//end of for( bframes = 0, brefs = 0;; bframes++ )

    if( bframes )	//如果有B帧
        h->lookahead->next.list[bframes-1]->b_last_minigop_bframe = 1;	//标记最后一个B帧
    h->lookahead->next.list[bframes]->i_bframes = bframes;	//存储两个参考帧之间的B帧数量

    /* insert a bref into the sequence 
		如果允许B帧被参考,且B帧数量>1,且目前可参考B帧数量=0
		则取最中间的那一个B帧作为BREF */
    if( h->param.i_bframe_pyramid && bframes > 1 && !brefs )
    {
        h->lookahead->next.list[(bframes-1)/2]->i_type = X264_TYPE_BREF;	//在B帧最中间插入BREF
        brefs++;	//brefs累加器累加
    }

    /*****************************************************************
		calculate the frame costs ahead of time for x264_rc_analyse_slice while we still have lowres 
	   若码率控制不是CQP方法,则在进行码率控制分析之前计算帧开销 
	   ****************************************************************/
    if( h->param.rc.i_rc_method != X264_RC_CQP )
    {
        x264_mb_analysis_t a;
        int p0, p1, b;
        p1 = b = bframes + 1;	//

        lowres_context_init( h, &a );	//低分辨率上下文初始化

        frames[0] = h->lookahead->last_nonb;	//取上一个非B帧

		//将bframes+1个帧拷贝下来,即BBB...BBB(IDR/I/P)
        memcpy( &frames[1], h->lookahead->next.list, (bframes+1) * sizeof(x264_frame_t*) );

		//若最后一帧是IDR/I帧,则p0 = p1 = b = bframes+1
        if( IS_X264_TYPE_I( h->lookahead->next.list[bframes]->i_type ) )
            p0 = bframes + 1;
        else //最后一帧P帧,p0 = 0,即上一个非B帧,p1 = b = bframes+1
            p0 = 0;

		//计算最后一帧b的开销
        slicetype_frame_cost( h, &a, frames, p0, p1, b );

        if( (p0 != p1 || bframes) && h->param.rc.i_vbv_buffer_size )
			//非I帧 || 有B帧 , && 使用vbv
        {
            /* We need the intra costs for row SATDs. 
			  同时我们还要计算帧b作为I帧时候的cost*/
            slicetype_frame_cost( h, &a, frames, b, b, b );

            /* We need B-frame costs for row SATDs. 
				计算每一个B帧的帧开销 */
            p0 = 0;
            for( b = 1; b <= bframes; b++ )
            {
                if( frames[b]->i_type == X264_TYPE_B )
                    for( p1 = b; frames[p1]->i_type == X264_TYPE_B; )
                        p1++;
                else
                    p1 = bframes + 1;
                slicetype_frame_cost( h, &a, frames, p0, p1, b );
                if( frames[b]->i_type == X264_TYPE_BREF )
                    p0 = b;
            }
        }
    }

    /* Analyse for weighted P frames */
    if( !h->param.rc.b_stat_read && h->lookahead->next.list[bframes]->i_type == X264_TYPE_P
        && h->param.analyse.i_weighted_pred >= X264_WEIGHTP_SIMPLE )
		//不使用stat_read && 最后一帧是P帧 && 
    {
        x264_emms();
        x264_weights_analyse( h, h->lookahead->next.list[bframes], h->lookahead->last_nonb, 0 );
    }

    /* shift sequence to coded order.
       use a small temporary list to avoid shifting the entire next buffer around 
	   将定义好的帧类型序列调整顺序
	   即 播放顺序 => 编码顺序  */
    int i_coded = h->lookahead->next.list[0]->i_frame;
    if( bframes )
    {
        int idx_list[] = { brefs+1, 1 };
        for( int i = 0; i < bframes; i++ )
        {
            int idx = idx_list[h->lookahead->next.list[i]->i_type == X264_TYPE_BREF]++;
            frames[idx] = h->lookahead->next.list[i];
            frames[idx]->i_reordered_pts = h->lookahead->next.list[idx]->i_pts;
        }
        frames[0] = h->lookahead->next.list[bframes];
        frames[0]->i_reordered_pts = h->lookahead->next.list[0]->i_pts;
        memcpy( h->lookahead->next.list, frames, (bframes+1) * sizeof(x264_frame_t*) );
    }

	/*	计算时间?好像跟SPS/时间戳有关 */
    for( int i = 0; i <= bframes; i++ )
    {
        h->lookahead->next.list[i]->i_coded = i_coded++;
        if( i )
        {
            calculate_durations( h, h->lookahead->next.list[i], h->lookahead->next.list[i-1], &h->i_cpb_delay, &h->i_coded_fields );
            h->lookahead->next.list[0]->f_planned_cpb_duration[i-1] = (double)h->lookahead->next.list[i]->i_cpb_duration *
                                                                      h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale;
        }
        else
            calculate_durations( h, h->lookahead->next.list[i], NULL, &h->i_cpb_delay, &h->i_coded_fields );
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值