文章插图
计算的思路如下
首先,我们要获取当前时刻(即 SR 包生成时刻)的 NTP 时间 。这个直接从传过来的参数 ctx 中就可以获得:
文章插图
其次,我们要计算当前时刻,应该对应的 RTP 的时间戳是多少 。根据最后一个发送的 RTP 包的时间戳 last_rtp_timestamp_ 和它的采集时刻的系统时间
last_frame_capture_time_ms_,和当前媒体流的时间戳的每 ms 增长速率 rtp_rate,以及从 last_frame_capture_time_ms_ 到当前时刻的时间流逝,就可以算出来 。注意,last_rtp_timestamp_ 是媒体流的原始时间戳,不是经过随机偏移的 RTP 包时间戳,所以最后又累加了偏移量 timestamp_offset_ 。其中最后一个发送的 RTP 包的时间信息是通过下面的函数进行更新的:
文章插图
5、音视频同步的计算
因为同一台机器上音频流和视频流的本地系统时间是一样的,也就是系统时间对应的 NTP 格式的时间也是一样的,是在同一个坐标系上的,所以可以把 NTP 时间作为横轴 X,单位是 ms,而把 RTP 时间戳的值作为纵轴 Y,画在一起 。下图展示了计算音视频同步的原理和方法,其实很简单,就是使用最近的两个 SR 点,两点确定一条直线,之后给任意一个 RTP 时间戳,都可以求出对应的 NTP 时间,又因为视频和音频的 NTP 时间是在同一基准上的,所以就可以算出两者的差值 。
文章插图
上图以音频的两个 SR 包为例,确定出了 RTP 和 NTP 对应关系的直线,然后给任意一个 rtp_a,就算出了其对应的 NTP_a,同理也可以求任意视频包 rtp_v 对应的 NTP_v 的时间点,两个的差值就是时间差 。
【WebRTC 音视频同步原理与实现】下面是 WebRTC 里面计算直线对应的系数 rate 和偏移 offset 的代码:
文章插图
在 WebRTC 中计算的是最新收到的音频 RTP 包和最新收到的视频 RTP 包的对应的 NTP 时间,作为网络传输引入的不同步时长,然后又根据当前音频和视频的 JitterBuffer 和播放缓冲区的大小,得到了播放引入的不同步时长,根据两个不同步时长,得到了最终的音视频不同步时长,计算过程在
StreamSynchronization::ComputeRelativeDelay() 函数中,之后又经过了 StreamSynchronization::ComputeDelays() 函数对其进行了指数平滑等一系列的处理和判断,得出最终控制音频和视频的最小延时时间,分别通过 syncable_audio_->SetMinimumPlayoutDelay(target_audio_delay_ms) 和 syncable_video_->SetMinimumPlayoutDelay(target_video_delay_ms) 应用到了音视频的播放缓冲区 。
这一系列操作都是由定时器调用
RtpStreamsSynchronizer::Process() 函数来处理的 。
另外需要注意一下,在知道采样率的情况下,是可以通过一个 SR 包来计算的,如果没有 SR 包,是无法进行准确的音视频同步的 。
文章插图
WebRTC 而实现音视频同步的手段就是 SR 包,核心的依据就是 SR 包中的 NTP 时间和 RTP 时间戳 。最后的两张 NTP 时间-RTP 时间戳 坐标图如果你能看明白(其实很简单,就是求解出直线方程来计算 NTP),那么也就真正地理解了 WebRTC 中音视频同步的原理 。如果有什么遗漏或者错误,欢迎大家一起交流!
推荐阅读
- 「Java原理探索」「AQS」教你自定义实现自己的同步器
- 如何使用WebRTC建立一个视频会议App
- 记一次生产环境Redis主从同步异常事故
- 小米手环|超越“毕业作”!小米手环7要来了:将与新机同步亮相
- 一招同步 IDEA 个人配置
- mysql数据库的主从同步,实现读写分离
- 音视频处理之FFmpeg+SDL视频播放器
- lsyncd实时同步搭建指南——取代rsync+inotify
- 音视频算法在淘宝中的应用
- 监控mysql主从同步状态是否异常