直播软件源码如何实现音视频同步(三)

tech2023-08-11  100

步骤如下: [1] 根据上一帧 lastvp 的播放时长 duration,校正等到 delay 值,duration 是上一帧理想播放时长,delay 是上一帧实际播放时长,根据delay 值可以计算得到当前帧的播放时刻 [2] 如果当前帧 vp 播放时刻未到,则继续显示上一帧 lastvp,并将延时值 remaining_time 作为输出参数供上级调用函数处理 [3] 如果当前帧 vp 播放时刻已到,则立即显示当前帧,并更新读指针

在 video_refresh() 函数中,调用了 compute_target_delay() 来根据视频时钟与主时钟的差异来调节 delay 值,从而调节视频帧播放的时刻:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 // 根据视频时钟与同步时钟(如音频时钟)的差值,校正delay值,使视频时钟追赶或等待同步时钟 // 输入参数delay是上一帧播放时长,即上一帧播放后应延时多长时间后再播放当前帧,通过调节此值来调节当前帧播放快慢 // 返回值delay是将输入参数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 by duplicating or deleting a frame */ // 视频时钟与同步时钟(如音频时钟)的差异,时钟值是上一帧pts值(实为:上一帧pts + 上一帧至今流逝的时间差) diff = get_clock(&is->vidclk) - get_master_clock(is); // delay是上一帧播放时长:当前帧(待播放的帧)播放时间与上一帧播放时间差理论值 // diff是视频时钟与同步时钟的差值 /* skip or repeat frame. We take into account the delay to compute the threshold. I still don't know if it is the best guess */ // 若delay < AV_SYNC_THRESHOLD_MIN,则同步域值为AV_SYNC_THRESHOLD_MIN // 若delay > AV_SYNC_THRESHOLD_MAX,则同步域值为AV_SYNC_THRESHOLD_MAX // 若AV_SYNC_THRESHOLD_MIN < delay < AV_SYNC_THRESHOLD_MAX,则同步域值为delay 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); // 当前帧播放时刻落后于同步时钟(delay+diff<0)则delay=0(视频追赶,立即播放),否则delay=delay+diff else if (diff >= sync_threshold && delay > AV_SYNC_FRAMEDUP_THRESHOLD) // 视频时钟超前于同步时钟,且超过同步域值,但上一帧播放时长超长 delay = delay + diff; // 仅仅校正为delay=delay+diff,主要是AV_SYNC_FRAMEDUP_THRESHOLD参数的作用,不作同步补偿 else if (diff >= sync_threshold) // 视频时钟超前于同步时钟,且超过同步域值 delay = 2 * delay; // 视频播放要放慢脚步,delay扩大至2倍 } } av_log(NULL, AV_LOG_TRACE, "video: delay=%0.3f A-V=%f\n", delay, -diff); return delay; }

compute_target_delay() 的输入参数 delay 是上一帧理想播放时长 duration,返回值 delay 是经校正后的上一帧实际播放时长。为方便描述,下面我们将输入参数记作 duration(对应函数的输入参数 delay),返回值记作 delay(对应函数返回值 delay)。

本函数实现功能如下: [1] 计算视频时钟与音频时钟(主时钟)的偏差 diff,实际就是视频上一帧 pts 减去音频上一帧 pts。所谓上一帧,就是已经播放的最后一帧,上一帧的 pts 可以标识视频流/音频流的播放时刻(进度)。 [2] 计算同步域值 sync_threshold,同步域值的作用是:若视频时钟与音频时钟差异值小于同步域值,则认为音视频是同步的,不校正 delay;若差异值大于同步域值,则认为音视频不同步,需要校正 delay值。同步域值的计算方法如下: 若 duration < AV_SYNC_THRESHOLD_MIN,则同步域值为 AV_SYNC_THRESHOLD_MIN 若 duration > AV_SYNC_THRESHOLD_MAX,则同步域值为 AV_SYNC_THRESHOLD_MAX 若 AV_SYNC_THRESHOLD_MIN < duration < AV_SYNC_THRESHOLD_MAX,则同步域值为 duration [3] delay 校正策略如下: [3.1] 视频时钟落后于同步时钟且落后值超过同步域值: [3.1.1] 若当前帧播放时刻落后于同步时钟(delay+diff<0),则 delay=0(视频追赶,立即播放); [3.1.2] 否则 delay=duration+diff [3.2] 视频时钟超前于同步时钟且超过同步域值: [3.2.1] 上一帧播放时长过长(超过最大值),仅校正为 delay=duration+diff; [3.2.2] 否则 delay=duration×2,视频播放放慢脚步,等待音频 [3.3] 视频时钟与音频时钟的差异在同步域值内,表明音视频处于同步状态,不校正 delay,则 delay=duration

本文转载自网络,感谢原作者的分享,转载仅为分享干货知识,如有侵权欢迎联系作者进行删除处理

最新回复(0)