惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tenable Blog
H
Heimdal Security Blog
K
Kaspersky official blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
S
Schneier on Security
G
GRAHAM CLULEY
U
Unit 42
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
C
CERT Recently Published Vulnerability Notes
Google DeepMind News
Google DeepMind News
罗磊的独立博客
Stack Overflow Blog
Stack Overflow Blog
阮一峰的网络日志
阮一峰的网络日志
Simon Willison's Weblog
Simon Willison's Weblog
C
Cisco Blogs
Cyberwarzone
Cyberwarzone
T
The Exploit Database - CXSecurity.com
Project Zero
Project Zero
Security Archives - TechRepublic
Security Archives - TechRepublic
www.infosecurity-magazine.com
www.infosecurity-magazine.com
博客园 - 司徒正美
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
V
Visual Studio Blog
博客园 - Franky
Engineering at Meta
Engineering at Meta
WordPress大学
WordPress大学
Jina AI
Jina AI
P
Proofpoint News Feed
P
Proofpoint News Feed
有赞技术团队
有赞技术团队
L
LINUX DO - 最新话题
宝玉的分享
宝玉的分享
N
News and Events Feed by Topic
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
博客园 - 聂微东
T
The Blog of Author Tim Ferriss
Spread Privacy
Spread Privacy
Application and Cybersecurity Blog
Application and Cybersecurity Blog
IT之家
IT之家
S
Security Affairs
博客园 - 叶小钗
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
小众软件
小众软件
N
News | PayPal Newsroom
Cloudbric
Cloudbric
AWS News Blog
AWS News Blog
W
WeLiveSecurity
The Last Watchdog
The Last Watchdog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
NISL@THU
NISL@THU

WebRTC

WebSocket 假连接与 gRPC Gateway 自动发现:复杂局域网 WebRTC 的恢复设计 有用 LiveKit 开发过视频语音会议系统的嘛? - V2EX 在 window 如何处理音频 aec? - V2EX 有在生产环境用过的 livekit 的吗?体验怎么样? - V2EX 安卓 WebRTC 如何使用自定义的音频数据? - V2EX 请推荐一个支持在线会议(仅限于音频)的 SFU - V2EX 基于声网 SDK 实现 iOS 端的一对一视频通话 - V2EX webrtc 有什么封装比较好的 c/c++库吗 - V2EX android google 原生 webrtc setVideoProcessor 如何消费 VideoFrame - V2EX 阿里云 RTC QoS 弱网对抗之变分辨率编码 - V2EX I need help froma software engineer who can work with Janus gateway (WebRTC server technologies ) system . - V2EX WebRTC 如何实现美颜功能 - V2EX 白话解读 WebRTC 音频 NetEQ 及优化实践 - V2EX light-rtc: 理念与实践 - V2EX 求助: serverless-webrtc-chat 在 Firefox 下失败 - V2EX 求助关于 WebRTC 只能在局域网建立成功问题 - V2EX 正在做一个开源 WebRTC 项目,有感兴趣的小伙伴吗。 sharedrop P2P 文件分享 - V2EX webrtc 不是才有的技术,应用领悟主要是网页端,现在安卓版本更新很快,为什么内置浏览器不支持这种几年前就有的新技术,这个不合理 - V2EX Webtorrent 在内网环境进行播种的想法 - V2EX 能否用 WebRTC 做一个局域网的视频播放服务? - V2EX 求助各位大神,国内目前主流浏览器支持 MediaRecorder 吗? - V2EX 公司要测试 webrtc 是不是买个国内阿里云什么的? - V2EX kurento room 服务端 Python 适配时 iceCandidate 内容怎么设置 - V2EX kurento 的 Python 改造问题 - V2EX [ 北京] WebRTC Meetup, 1 月 8 日 node-party @ 微软大厦,报名从速 ! - V2EX 有没有好用的语音直播服务推荐? - V2EX 编译了 WebRTC 和 Janus Gateway 的 API 文档 - V2EX 编译了 iOS 和 Android 平台上最新的 webrtc 需要的可以来用 - V2EX 关于 WEB 在线语音聊天系统 - V2EX awesome-webrtc: http://openrtc.io/ 有用过 WebRTC 的吗?想了解一下开发难度 - V2EX WebRTC 1.0 WebRTC 技术介绍 - V2EX 怎样禁用浏览器的WebRTC?这东西居然能读取本地IP!! - V2EX webtorrent - BitTorrent over WebRTC - V2EX 用 webrtc 写个类似 btsync 的工具可行不? - V2EX http://lynckia.com/licode/ 一套webrtc开源的类库 - V2EX 一个基于 WebRTC 技术的网络电话服务 - V2EX WebRTC World - V2EX PeerCDN - V2EX WebRTC 的一系列技术真的是一个特别好的创业方向 搭建自己的 TURN Server - V2EX WebRTC Book - V2EX SimpleWebRTC - V2EX Sharefest - V2EX PeerJS P2P Chat Demo - V2EX PeerJS - V2EX WebRTC.io - V2EX WebRTCStats - V2EX
WebRTC 音视频同步原理与实现 - V2EX
VideoCloudTech · 2021-03-09 · via WebRTC

所有的基于网络传输的音视频采集播放系统都会存在音视频同步的问题,作为现代互联网实时音视频通信系统的代表,WebRTC 也不例外。本文将对音视频同步的原理以及 WebRTC 的实现做深入分析。

时间戳 (timestamp)

同步问题就是快慢的问题,就会牵扯到时间跟音视频流媒体的对应关系,就有了时间戳的概念。

时间戳用来定义媒体负载数据的采样时刻,从单调线性递增的时钟中获取,时钟的精度由 RTP 负载数据的采样频率决定。音频和视频的采样频率是不一样的,一般音频的采样频率有 16KHz 、44.1KHz 、48KHz 等,而视频反映在采样帧率上,一般帧率有 25fps 、29.97fps 、30fps 等。

习惯上音频的时间戳的增速就是其采样率,比如 16KHz 采样,每 10ms 采集一帧,则下一帧的时间戳,比上一帧的时间戳,从数值上多 16 x10=160,即音频时间戳增速为 16/ms 。而视频的采样频率习惯上是按照 90KHz 来计算的,就是每秒 90K 个时钟 tick,之所以用 90K 是因为它正好是上面所说的视频帧率的倍数,所以就采用了 90K 。所以视频帧的时间戳的增长速率就是 90/ms 。

时间戳的生成

音频帧时间戳的生成

WebRTC 的音频帧的时间戳,从第一个包为 0,开始累加,每一帧增加 = 编码帧长 (ms) x 采样率 / 1000,如果采样率 16KHz,编码帧长 20ms,则每个音频帧的时间戳递增 20 x 16000/1000 = 320 。这里只是说的未打包之前的音频帧的时间戳,而封装到 RTP 包里面的时候,会将这个音频帧的时间戳再累加上一个随机偏移量(构造函数里生成),然后作为此 RTP 包的时间戳,发送出去,如下面代码所示,注意,这个逻辑同样适用于视频包。

视频帧时间戳的生成

WebRTC 的视频帧,生成机制跟音频帧完全不同。视频帧的时间戳来源于系统时钟,采集完成后至编码之前的某个时刻(这个传递链路非常长,不同配置的视频帧,走不同的逻辑,会有不同的获取位置),获取当前系统的时间 timestamp_us_ ,然后算出此系统时间对应的 ntp_time_ms_ ,再根据此 ntp 时间算出原始视频帧的时间戳 timestamp_rtp_ ,参看下面的代码,计算逻辑也在 OnFrame 这个函数中。 为什么视频帧采用了跟音频帧不同的时间戳计算机制呢?我的理解,一般情况音频的采集设备的采样间隔和时钟精度更加准确,10ms 一帧,每秒是 100 帧,一般不会出现大的抖动,而视频帧的帧间隔时间较大采集精度,每秒 25 帧的话,就是 40ms 一帧。如果还采用音频的按照采样率来递增的话,可能会出现跟实际时钟对不齐的情况,所以就直接每取一帧,按照取出时刻的系统时钟算出一个时间戳,这样可以再现真实视频帧跟实际时间的对应关系。

跟上面音频一样,在封装到 RTP 包的时候,会将原始视频帧的时间戳累加上一个随机偏移量(此偏移量跟音频的并不是同一个值),作为此 RTP 包的时间戳发送出去。值得注意的是,这里计算的 NTP 时间戳根本就不会随着 RTP 数据包一起发送出去,因为 RTP 包的包头里面没有 NTP 字段,即使是扩展字段里,我们也没有放这个值,如下面视频的时间相关的扩展字段。

音视频同步核心依据

从上面可以看出,RTP 包里面只包含每个流的独立的、单调递增的时间戳信息,也就是说音频和视频两个时间戳完全是独立的,没有关系的,无法只根据这个信息来进行同步,因为无法对两个流的时间进行关联,我们需要一种映射关系,将两个独立的时间戳关联起来。

这个时候 RTCP 包里面的一种发送端报告分组 SR (SenderReport) 包就上场了,详情请参考 RFC3550 SR 包的其中一个作用就是来告诉我们每个流的 RTP 包的时间戳和 NTP 时间的对应关系的。靠的就是上边图片中标出的 NTP 时间戳和 RTP 时间戳,通过 RFC3550 的描述,我们知道这两个时间戳对应的是同一个时刻,这个时刻表示此 SR 包生成的时刻。这就是我们对音视频进行同步的最核心的依据,所有的其它计算都是围绕这个核心依据来展开的。

SR 包的生成

由上面论述可知,NTP 时间和 RTP 时间戳是同一时刻的不同表示,只是精度和单位不一样。NTP 时间是绝对时间,以毫秒为单位,而 RTP 时间戳则和媒体的采样频率有关,是一个单调递增数值。生成 SR 包的过程在 RTCPSender::BuildSR(const RtcpContext& ctx) 函数里面,老版本里面有 bug,写死了采样率为 8K,新版本已经修复,下面截图是老版本的代码:

计算的思路如下

首先,我们要获取当前时刻(即 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 包的时间信息是通过下面的函数进行更新的:

音视频同步的计算

因为同一台机器上音频流和视频流的本地系统时间是一样的,也就是系统时间对应的 NTP 格式的时间也是一样的,是在同一个坐标系上的,所以可以把 NTP 时间作为横轴 X,单位是 ms,而把 RTP 时间戳的值作为纵轴 Y,画在一起。下图展示了计算音视频同步的原理和方法,其实很简单,就是使用最近的两个 SR 点,两点确定一条直线,之后给任意一个 RTP 时间戳,都可以求出对应的 NTP 时间,又因为视频和音频的 NTP 时间是在同一基准上的,所以就可以算出两者的差值。 上图以音频的两个 SR 包为例,确定出了 RTP 和 NTP 对应关系的直线,然后给任意一个 rtp_a,就算出了其对应的 NTP_a,同理也可以求任意视频包 rtp_v 对应的 NTP_v 的时间点,两个的差值就是时间差。

下面是 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 中音视频同步的原理。如果有什么遗漏或者错误,欢迎大家一起交流!