ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

WebRTC 音视频同步分析

2022-01-09 20:32:55  阅读:265  来源: 互联网

标签:同步 SR ntp SFU 音视频 timestamp WebRTC


文中提到的代码引用自 libwebrtc M96 版本 https://github.com/aggresss/libwebrtc/tree/M96

0x00 前言

WebRTC 音频和视频分别通过不同 RTP stream 传输,而 RFC 3550 Section 5.1 中明确说明 “The initial value of the timestamp SHOULD be random”,即两个不同的 RTP stream 之间不能直接通过 RTP 的 timestamp 进行同步。所以 RFC 3550 Section 6.4.1 中的 RTCP SR 维护了 RTP timestamp 与 NTP Time 的映射关系,在接收端通过与该 RTP Stream 关联的 RTCP SR 估算出墙上时钟 (wall clock) 后再进行音频和视频的同步。本文通过对 libwebrtc M96 中音频和视频同步的实现进行分析,进而讨论经过 SFU 转发后的音视频同步需要考量的因素。


0x01 libwebrtc 音视频同步原理

1.1 墙上时钟估算

RTCP SR 定义中与 NTP Timestamp 和 RTP Timestamp 相关的结构如下所示:

        0                   1                   2                   3
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
header |V=2|P|    RC   |   PT=SR=200   |             length            |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                         SSRC of sender                        |
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
sender |              NTP timestamp, most significant word             |
info   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |             NTP timestamp, least significant word             |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                         RTP timestamp                         |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                     sender's packet count                     |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                      sender's octet count                     |
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
       ...

libwebrtc 在 modules/rtp_rtcp/source/rtcp_sender.ccRTCPSender::BuildSR 实现了 NTP timestamp 与 RTP timestamp 的计算过程,可通过伪代码表示为:

ntp_timestamp = now_ms;
rtp_timestamp = last_send_rtp_timestamp + (now_ms - last_send_ntp_timestamp) * clock_rate / 1000;

通过分析RTCP SR 中 rtp 与 ntp 时间戳的生成规则可以看出,两者之间在理想情况下是线性关系,如下图所示:
rtp_ntp_timestamp
可以看出,只要两个不同时间的 SR 就能估算出每个 rtp timestamp 对应的 ntp timstamp, 但是收到网络抖动等影响,通过更多的 SR 进行线性拟合后会让 ntp timestamp 的估算更加精确。libwebrtc 中 system_wrappers/source/rtp_to_ntp_estimator.cc 实现了 ntp timestamp 的估算。

1.2 视频流与音频流同步关联

参考标准 WebRTC 1.0: Real-Time Communication Between BrowsersMedia Capture and Streams ,在 MediaStream API 中找到了关于音视频同步的说明:

Each MediaStream can contain zero or more MediaStreamTrack objects. All tracks in a MediaStream are intended to be synchronized when rendered. This is not a hard requirement, since it might not be possible to synchronize tracks from sources that have different clocks. Different MediaStream objects do not need to be synchronized.

就是说在浏览器接口中一个 MediaStream 对象包含 0 或多个 MediaStreamTrack 对象,MediaStream 中的所有可同步的 MediaStreamTrack 对象在渲染时需要进行同步,不同的 MediaStream 之间不需要同步。
所有以上接口在 libwebrtc 中以 SDP 的形式被解析,通过分析 RFC 8830 可以看出,每个 m-line 中通过 a=msid 来标识 MediaStream 信息,格式为:

  // a=msid:<stream id> <track id>
  // msid-value = msid-id [ SP msid-appdata ]
  // msid-id = 1*64token-char ; see RFC 4566
  // msid-appdata = 1*64token-char  ; see RFC 4566

例如:

# First MediaStream - id is 47017fee-b6c1-4162-929c-a25110252400
m=audio 56500 UDP/TLS/RTP/SAVPF 96 0 8 97 98
a=msid:47017fee-b6c1-4162-929c-a25110252400 f83006c5-a0ff-4e0a-9ed9-d3e6747be7d9
m=video 56502 UDP/TLS/RTP/SAVPF 100 101
a=msid:47017fee-b6c1-4162-929c-a25110252400 b47bdb4a-5db8-49b5-bcdc-e0c9a23172e0
# Second MediaStream - id is 61317484-2ed4-49d7-9eb7-1414322a7aae
m=audio 56503 UDP/TLS/RTP/SAVPF 96 0 8 97 98
a=msid:61317484-2ed4-49d7-9eb7-1414322a7aae b94006c5-cade-4e0a-9ed9-d3e6747be7d9
m=video 56504 UDP/TLS/RTP/SAVPF 100 101
a=msid:61317484-2ed4-49d7-9eb7-1414322a7aae f30bdb4a-1497-49b5-3198-e0c9a23172e0

SDP 的解析过程详见 pc/webrtc_sdp.cc

在 libwebrtc 中需要同步的 stream 之间通过 sync_group 来关联,sync_group 的来源为 stream_id,config.sync_group = stream_ids[0];

call/call.cc 中的 Call::ConfigureSync 中实现了 audio_stream 和 video_stream 的关联逻辑。

1.3 音视频同步执行过程

音视频同步操作通常是视频同步到音频,即视频计算出渲染偏移时间同步到音频。
在 libwebrtc 中,音视频同步的基本操作对象是 AudioReceiveStreamVideoReceiveStream,两者都继承自 Syncable
通过 call/syncable.h 可以看到,Syncable 是一个纯虚类,以下是三个与音视频同步相关的纯虚函数:

  virtual bool GetPlayoutRtpTimestamp(uint32_t* rtp_timestamp, int64_t* time_ms) const = 0;
  virtual bool SetMinimumPlayoutDelay(int delay_ms) = 0;
  virtual void SetEstimatedPlayoutNtpTimestampMs(int64_t ntp_timestamp_ms, int64_t time_ms) = 0;

负责音视频同步的线程是 call 模块的 module_process_thread_,主要处理文件是 video/rtp_streams_synchronizer.cc
RtpStreamsSynchronizer 类包含以下成员:

  • StreamSynchronization
  • audio 和 video 的 Measurements
  • AudioReceiveStreamVideoReceiveStream 的指针:syncable_audio_syncable_video_

处理过程详见 RtpStreamsSynchronizer::Process()


0x02 SFU 转发后的音视频同步

2.1 SFU 中 RTCP SR 的时间戳管理

在 P2P 场景中,只有 Sender 和 Receiver,RTP 和 RTCP 都是从 Sender 直接传输到 Receiver ,而在 SFU 场景中,SFU为了适配一对多的转发需求,通常会增加一个中间层,即 Receive Stream 与 Send Stream 隔离,所以每个 Send Stream 的 Sequence 和 Timestamp 都会重新随机,RTCP 也会被隔离,Send Stream 根据发送情况重新生成 RTCP SR。

通常,SFU 会以本地时间为基准进行 NTP timestamp 与 RTP timestamp 计算,可通过伪代码表示为:

ntp_timestamp = now_ms;
rtp_timestamp = last_send_rtp_timestamp + (now_ms - last_send_ntp_timestamp) * clock_rate / 1000;

这种计算方式在简单传输的场景下,通常不会出现异常,如下图所示:

sfu_a
因为 audio 与 video 在同一个 PC 中,PC 的网络抖动同时作用在 audio 和 video 上,产生的延迟也相对同步,所以 Receiver A 在接收后通过 SFU 的 RTCP SR 计算的视频延迟偏移与 P2P 情况下的误差并不大。

下图演示了一个增加了一次转发,并且音频和视频在不同的传输通道中二次转发:

sfu_b
当音视频在不同的传输通道时,网络抖动与延迟的不同步会导致 Receiver 只以 SFU 到 Receiver 之间的 RTCP SR 计算视频延迟偏移产生较大误差,所以需要对上面 SFU 中生成 RTCP SR 的算法做一些改进,改进的思路为以 Sender 的 NTP timestamp 作为时间基准,每一级 SFU 以上一级的 SFU 作为时间基准,这样 Receiver 接收到的 RTCP SR 仍然是以 Sender 为时间基准,从而剪掉了 SFU 转发过程中每一级 SFU 独立生成 RTCP SR 而增加的误差。计算步骤如下:

  • ① SFU 接收到 Receive Stream 的 RTCP SR 后通知与其关联的 Send Stream;
  • ② Send Stream 接收到上游 Receive Stream 的 STCP SR 通知后,记录
    • last_sync_rtp_ts
    • last_sync_remote_ntp_ms
    • last_sync_local_ntp_ms
  • ③ rtp_timestamp 和 ntp_timestamp 计算伪代码:
    rtp_timestamp = last_sync_rtp_ts + (now_ms - last_sync_local_ntp_ms) * clock_rate() / 1000;
    ntp_timestamp = now_ms - last_sync_local_ntp_ms + last_sync_remote_ntp_ms;
    

注意:因为修改了 RTCP SR 的时间基准,所以 Send Stream 接收到 RTCP RR 时通过 LSR 计算 RTT 时也需要做相应的修改。

2.2 SFU 中音视频同步关联管理

下图为 SFU 的经典场景,一个 Receiver 通过一个 PC 可以订阅多个 Sender 的音视频:
sfu_c
通常 Receiver 的 Remote Description 会由 Receiver 或者 SFU 将多个 Sender 的 SDP 进行合成,在 Receiver 侧的表现为一个 PC 中包含多个 MediaStream,需要根据 msid 对属于不同 MediaStream 的 MediaStreamTrack 进行描述,以保证每个视频都能关联到正确的音频。


参考文档

  1. WebRTC音视频同步 · 言剑
  2. WebRTC 音视频同步原理与实现 · 阿里云视频云
  3. WebRTC音视频同步机制实现分析 · weizhenwei
  4. WebRTC笔记(三)音视频同步 · jiayayao
  5. WebRTC音视频同步分析 · lincai2018
  6. WebRTC研究:MediaStream概念以及定义 · 剑痴乎

标签:同步,SR,ntp,SFU,音视频,timestamp,WebRTC
来源: https://blog.csdn.net/aggresss/article/details/122363410

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有