WebRTC 的平滑发送

平滑的发送

Paced sender,也常常被称为 “pacer”,它是 WebRTC RTP 栈的一部分,主要用于平缓发送到网络的数据包流。

背景

考虑一个码率为 5Mbps 帧率为 30fps 的视频流。理想情况下,这个视频流的每个帧的大小大约 21kB,每个帧被打包成大约 18 个 RTP 数据包。虽然说每秒中滑动窗口的平均比特率是正确的 5Mbps,但在更短的时间尺度上,它可以被视为每隔 33 毫秒 167 Mbps 的突发传输,然后是 32 毫秒的静默期。此外,相当常见的是,在突然移动的情况下,尤其是在处理屏幕共享时,视频编码器会超出目标帧大小。比理想大小大 10 倍甚至 100 倍的帧是一个非常真实的场景。这些数据包突发可能会导致一些问题,例如网络拥塞和缓冲区膨胀,甚至是数据包丢失。大多数会话都有多个媒体流,比如一个视频轨道和一个音频轨道。如果你一次将一个视频帧发送到网络上,并且这些数据包需要 100 毫秒到达对端 —— 这意味着你现在也阻塞了任何音频数据包及时到达远端。

Paced sender 通过使用一个缓冲区来解决这个问题,媒体数据包先在这个缓冲区里排队,然后使用一个 漏桶 算法将它们平缓地发送到网络上。缓冲区中包含所有媒体轨道的单独的 fifo 流,以便实现,比如音频可以优先于视频 - 并且可以以循环方式发送优先级相等的流,以避免任何一个流阻塞其它流。

由于 pacer 控制在网络上发送的比特率,因此它还用于在需要最小发送速率的情况下生成填充 - 如果使用比特率探测,则生成数据包序列。

数据包的生命周期

当使用 paced sender 时,媒体数据包的典型路径看起来就像这样:

  1. RTPSenderVideoRTPSenderAudio 将媒体数据打包成 RTP 数据包。

  2. 数据包被发送给 [RTPSender] 类进行传输。

  3. pacer 通过 [RtpPacketSender] 接口被调用以将数据包批量入队。

  4. 数据包被放进 pacer 内的队列中,等待适当的时机发送它们。

  5. 在计算好的时间,pacer 调用 PacingController::PacketSender() 回调方法,通常由 [PacketRouter] 类实现。

  6. router 基于数据包的 SSRC 将数据包转发到正确的 RTP 模块,并在其中的 RTPSenderEgress 类中打上最后的时间戳,可能会保存它以进行重传等。

  7. 数据包被发送到底层的 Transport 接口,之后它现在超出了范围。

与此异步进行的是确定估计的可用发送带宽 - 并通过 void SetPacingRates(DataRate pacing_rate, DataRate padding_rate) 方法对 RtpPacketPacker 设置目标发送速率。

数据包优先级

pacer 基于两个标准对数据包进行优先级排序:

  • 数据包类型,优先级从高到低如下:

    1. 音频
    2. 重传
    3. 视频和 FEC
    4. 填充
  • 入队的顺序

入队顺序是在每个流 (SSRC) 的基础上执行的。给定相同的优先级,[RoundRobinPacketQueue] 在媒体流之间交替,以确保没有流不必要地阻塞其它流。

实现

当前 paced sender 有两个实现(尽管它们通过 PacingController 类共享大量的逻辑)。 传统的 [PacedSender] 使用专门的线程以 5ms 的间隔轮询 pacing 控制器,并具有保护内部状态的锁。顾名思义,更新的 [TaskQueuePacedSender] 使用 [TaskQueue] 来保护状态,并调度数据包处理,后者动态地基于实际的发送速率和约束。避免在新的应用中使用传统的 PacedSender,我们已经在计划移除它了。

数据包路由器

一个称为 [PacketRouter] 的相邻组件用于路由从 pacer 出来的数据包,并进入正确的 RTP 模块。它具有以下功能:

  • SendPacket 方法查找具有对应于数据包的 SSRC 的 RTP 模块,以进一步路由到网络。。
  • 如果使用了发送端带宽估计,它会填充传输范围内的序列号扩展。
  • 生成填充。支持基于负载的填充的模块被优先考虑,最后一个发送媒体的模块始终是第一选择。
  • 发送媒体之后返回任何生成的 FEC。
  • 转发 REMB 和/或 TransportFeedback 消息给适当的 RTP 模块。

目前 FEC 是基于每个 SSRC 生成的,因此总是在发送媒体后从 RTP 模块返回。希望有一天,我们将支持使用单个 FlexFEC 流覆盖多个流 - 则数据包路由器是这种 FEC 生成器可能存在的地方。它甚至可以用于 FEC 填充,作为 RTX 的替代方案。

API

这一节概述了与 pacer 的几个不同用例相关的类和方法

数据包发送

要发送数据包,可以使用 RtpPacketSender::EnqueuePackets(std::vector<std::unique_ptr<RtpPacketToSend>> packets)。pacer 接收一个 PacingController::PacketSender 对象作为构造函数的参数,当需要实际发送数据包时使用这个回调。

发送速率

要控制发送速率,则使用 void SetPacingRates(DataRate pacing_rate, DataRate padding_rate)。如果数据包队列变为空,且发送速率掉到 padding_rate 以下,则 pacer 将从 PacketRouter 请求填充包。

要完全挂起/恢复发送数据(比如,由于网络可用性),则使用 Pause()Resume() 方法。

在某些情况下,指定的 pacing 速率可能会被覆盖,例如由于极端的编码器过冲。使用 void SetQueueTimeLimit(TimeDelta limit) 来指定你希望数据包在 pacer 的队列中等待的最长时间(暂停除外)。实际发送速率可能会增加到超过 pacing_rate,以尝试使 平均 排队时间小于请求的限制。这样做的理由是,如果发送队列长于三秒,最好冒丢包的风险,然后尝试使用关键帧进行恢复,而不是造成严重的延迟。

带宽估计

如果带宽估计器支持带宽探测,它可能会请求以指定速率发送一组数据包,以判断这是否会导致网络延迟/丢失增加。使用 void CreateProbeCluster(DataRate bitrate, int cluster_id) 方法 - 通过这个 PacketRouter 发送的数据包将在附加的 PacedPacketInfo 结构中用相应的 cluster_id 进行标记。

如果使用拥塞窗口 pushback,则可以使用 SetCongestionWindow()UpdateOutstandingData() 更新状态。

还有一些方法可以帮我们控制如何 pace:

  • SetAccountForAudioPackets() 确定音频数据包是否计入带宽消耗。
  • SetIncludeOverhead() 确定是否把完整 RTP 数据包大小计入带宽使用(否则只计算媒体载荷)。
  • SetTransportOverhead() 设置每个数据包消耗的额外数据大小,表示比如 UDP/IP 头部。

统计数据

有几种方法用于在 pacer 状态中收集统计信息:

  • OldestPacketWaitTime(),自添加进队列中的最早的数据包被添加进队列以来的时间。
  • QueueSizeData(),当前在队列中的总字节数。
  • FirstSentPacketTime(),发送第一个数据包的绝对时间。
  • ExpectedQueueTime(),队列中的总字节数除以发送速率。

RTPSender
RtpPacketSender
RtpPacketPacer
PacketRouter
PacedSender
TaskQueuePacedSender
RoundRobinPacketQueue

原文

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容