**求职者:**如果被问到放正心态,能回答多少是多少。如果你看了这篇文章肯定是可以回答上的
在这个流程中,“计算上一帧显示时长”这一步骤至关重要。先来看下代码:
这里与系统时刻的对比,引入了另一个概念——frame_timer。可以理解为帧显示时刻,如更新前,是上一帧的显示时刻;对于更新后(is-frame_timer+=delay),则为当前帧显示时刻。
上一帧显示时刻加上delay(还应显示多久(含帧本身时长))即为上一帧应结束显示的时刻。具体原理看如下示意图:
这里给出了3种情况的示意图:
time1:系统时刻小于lastvp结束显示的时刻(frame_timer+dealy),即虚线圆圈位置。此时应该继续显示lastvp
time2:系统时刻大于lastvp的结束显示时刻,但小于vp的结束显示时刻(vp的显示时间开始于虚线圆圈,结束于黑色圆圈)。此时既不重复显示lastvp,也不丢弃vp,即应显示vp
time3:系统时刻大于vp结束显示时刻(黑色圆圈位置,也是nextvp预计的开始显示时刻)。此时应该丢弃vp。
那么接下来就要看最关键的lastvp的显示时长delay是如何计算的。
这在函数compute_target_delay中实现:
staticdoublecompute_target_delay(doubledelay,VideoState*is){doublesync_threshold,diff=0;/*updatedelaytofollowmastersynchronisationsource*/if(get_master_sync_type(is)!=AV_SYNC_VIDEO_MASTER){/*ifvideoisslave,wetrytocorrectbigdelaysbyduplicatingordeletingaframe*/diff=get_clock(is-vidclk)-get_master_clock(is);/*'tknowifitisthebestguess*/sync_threshold=FFMAX(AV_SYNC_THRESHOLD_MIN,FFMIN(AV_SYNC_THRESHOLD_MAX,delay));if(!isnan(diff)fabs(diff)is-max_frame_duration){if(diff=-sync_threshold)delay=FFMAX(0,delay+diff);elseif(diff=sync_thresholddelayAV_SYNC_FRAMEDUP_THRESHOLD)delay=delay+diff;elseif(diff=sync_threshold)delay=2*delay;}}av_log(NULL,AV_LOG_TRACE,"video:delay=%0.3fA-V=%f\n",delay,-diff);returndelay;}上面代码中的注释全部是源码的注释,代码不长,注释占了快一半,可见这段代码重要性。
这段代码中最难理解的是sync_threshold,画个图帮助理解:
图中坐标轴是diff值大小,diff为0表示videoclock与audioclock完全相同,完美同步。图纸下方色块,表示要返回的值,色块值的delay指传入参数,结合上一节代码,即lastvp的显示时长。
从图上可以看出来sync_threshold是建立一块区域,在这块区域内无需调整lastvp的显示时长,直接返回delay即可。也就是在这块区域内认为是准同步的。
如果不仅大于sync_threshold,而且超过了AV_SYNC_FRAMEDUP_THRESHOLD,那么返回delay+diff,由具体diff决定还要显示多久(这里不是很明白代码意图,按我理解,统一处理为返回2*delay,或者delay+diff即可,没有区分的必要)
这一策略的实现方式是:引入frame_timer概念,标记帧的显示时刻和应结束显示的时刻,再与系统时刻对比,决定重复还是丢帧。
lastvp的应结束显示的时刻,除了考虑这一帧本身的显示时长,还应考虑了videoclock与audioclock的差值。
并不是每时每刻都在同步,而是有一个“准同步”的差值区域。