想做直播的你,这些热门的音视频如何绝对同步的。你 get 了嘛
心理分析:音视频同步本身比较难,一般使用 ijkplayer 第三方做音视频同步。不排除有视频直播 视频通话需要用音视频同步,可以从三种 音频为准 视频为准 自定义时钟为准三种方式实现音视频同步
**求职者:**如果被问到 放正心态,能回答多少是多少。如果你看了这篇文章肯定是可以回答上的
音视频的直播系统是一个复杂的工程系统,要做到非常低延迟的直播,需要复杂的系统工程优化和对各组件非常熟悉的掌握。下面整理几个简单常用的调优技巧:###以 fflay 来看音视频同步流程 ffplay 中将视频同步到音频的主要方案是,如果视频播放过快,则重复播放上一帧,以等待音频;如果视频播放过慢,则丢帧追赶音频。
这一部分的逻辑实现在视频输出函数 video_refresh 中,分析代码前,我们先来回顾下这个函数的流程图:
在这个流程中,“计算上一帧显示时长”这一步骤至关重要。先来看下代码:
static void video_refresh(void *opaque, double *remaining_time){//……//lastvp 上一帧,vp 当前帧 ,nextvp 下一帧
last_duration = vp_duration(is, lastvp, vp);//计算上一帧的持续时长 delay = compute_target_delay(last_duration, is);//参考 audio clock 计算上一帧真正的持续时长
time= av_gettime_relative()/1000000.0;//取系统时刻 if (time < is->frame_timer + delay) {//如果上一帧显示时长未满,重复显示上一帧*remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);goto display;}
is->frame_timer += delay;//frame_timer 更新为上一帧结束时刻,也是当前帧开始时刻 if (delay > 0 && time - is->frame_timer > AV_SYNC_THRESHOLD_MAX)is->frame_timer = time;//如果与系统时间的偏离太大,则修正为系统时间
//更新 video clock//视频同步音频时没作用 SDL_LockMutex(is->pictq.mutex);if (!isnan(vp->pts))update_video_pts(is, vp->pts, vp->pos, vp->serial);SDL_UnlockMutex(is->pictq.mutex);
//……
//丢帧逻辑 if (frame_queue_nb_remaining(&is->pictq) > 1) {Frame *nextvp = frame_queue_peek_next(&is->pictq);duration = vp_duration(is, vp, nextvp);//当前帧显示时长 if(time > is->frame_timer + duration){//如果系统时间已经大于当前帧,则丢弃当前帧 is->frame_drops_late++;frame_queue_next(&is->pictq);goto retry;//回到函数开始位置,继续重试(这里不能直接 while 丢帧,因为很可能 audio clock 重新对时了,这样 delay 值需要重新计算)}}}
代码只保留了同步相关的部分,完整的代码可以参考 ffmpeg 源码
这段代码的逻辑在上述流程图中有包含。主要思路就是一开始提到的如果视频播放过快,则重复播放上一帧,以等待音频;如果视频播放过慢,则丢帧追赶音频。实现的方式是,参考 audio clock,计算上一帧(在屏幕上的那个画面)还应显示多久(含帧本身时长),然后与系统时刻对比,是否该显示下一帧了。
这里与系统时刻的对比,引入了另一个概念——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。###delay 的计算那么接下来就要看最关键的 lastvp 的显示时长 delay 是如何计算的。
这在函数 compute_target_delay 中实现:
static double compute_target_delay(double delay, VideoState *is){double sync_threshold, diff = 0;
/* update delay to follow master synchronisation source /if (get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER) {/ if video is slave, we try to correct big delays byduplicating or deleting a frame */
评论