0. 操作演示
Simulcast就是多播,在多人会议中,有时候大家网络状况并不相同,Simulcast能适配不同用户的网络和终端情况。
举个例子,3个人,有的是1Mbps网络,有的是2Mbps,有的是5Mbps。大家该推多少码率的流?
•如果推2Mbps的流,两个人会很Happy画质清晰很流畅,但是那个网络差的人就挂了,整个会议也开不下去。•如果照顾比较差的网络,大家推1Mbps或更低码率的流,其他网络很好也只能看低码率的流,明明网络很好画质却很差。
Simulcast就是这个问题的解决方案之一,客户端只需要推一路流,但在服务器转发时(不用视频转码),可以出不同的码率的流,客户端根据自己的网络选择合适的流。
•大家全部推3Mbps码率的一路流。•带宽只有1Mbps的客户端,就拉1Mbps的流,虽然画质不清楚,但是很流畅,可以正常开会。•带宽很好的客户端,可以拉3Mbps的流,流畅而且画质很好。
Note: 为了便于理解,我们简单以码率代表画质,实际上并不完全等价。
下面实际操作看看,以OSX为例,先启动SRS:
代码语言:javascript复制git clone -b feature/simulcast https://gitee.com/ossrs/srs &&
cd srs/trunk && ./configure --osx && make &&
./objs/srs -c conf/rtc.conf
推一路Simulcast流,有3个子流,直接在Chrome打开即可:
•http://127.0.0.1:8080/players/rtc_publisher.html?numberOfSimulcastLayers=3&autostart=true
如果网络比较好的客户端,播放高码率的流,即layer=3
的流:
•http://127.0.0.1:8080/players/rtc_player.html?layer=2&autostart=true
网络比较差的客户端,播放低码率的流,即layer=0
的流:
•http://127.0.0.1:8080/players/rtc_player.html?layer=0&autostart=true
Note: 请详细参考#2420[1]的用法和效果。
1. Simulcast 介绍
WebRTC 的 Simulcast , 在一个视频媒体行(MediaLine)中存在多个视频流(RTP Stream), 这些流来自相同的视频采集源, 其差别主要体现在视频编码,分辨率,码率等方面。
在 WebRTC 内部, Simulcast 会为每一个视频流分配一个编码器, 不同的编码器生成不同大小的码流, 这些码流经服务端转发, 最终达到用户播放器。
不过与普通单流转发不同, 因 Simulcast 推送多个码流的特点, 服务器可以为不同用户调度不同的码流。
比如, 考虑到播放端用户的 带宽
和 设备分辨率
的差异, 可以给部分用户推送高清码流, 另一部分推送低清码流.
代价是 推送端 需要更大的带宽, 更强的算力, 应付多码流的输出.
目前 WebRTC 源码为 Simulcast 提供了两种接口的API(参考1)
•SDP munging 风格•RID based 风格
SDP 示例
代码语言:javascript复制// SDP munging 风格
a=ssrc-group:SIM 1390104252 1390104253 1390104254
// RID based 风格
a=rid:high send
a=rid:mid send
a=rid:low send
a=simulcast:send high;mid;low
这里重点讨论如何在 SRS 中支持 SDP munging 风格的 Simulcast, 对于 RID based 风格支持, 我会在下一篇介绍。
2. SDP munging 风格特点
2.1 特点
SDP munging 风格的 Simulcast 接口体现在sdp协商时,其视频媒体行会出现 a=ssrc-group:SIM
字样,其格式为
a=ssrc-group:SIM layer0 layer1 layer2...
其中 SSRC 序列 {layer0 layer1 layer2...} 即为 Simulcast 的多个层级, 序列长度通常不超过3.
它们按照分辨率大小从小到大依次排列; 假定 layer0 的分辨率为 w0xh0, 其他类推, 则:
分辨率满足:
代码语言:javascript复制layer0(w0xh0) < layer1(w1xh1) < layer2(w2xh2) ...
长宽比率满足:
代码语言:javascript复制w0 : w1 : w2 = 1: k: k^2
h0 : h1 : h2 = 1: k: k^2
(通常k = 2, 如果是源码开发,可自行修改)
2.2 示例
一份offer的某个视频媒体的部分片段
代码语言:javascript复制a=ssrc-group:FID 1390104252 2798384649
a=ssrc:1390104252 cname:rsPDymoMnGBuVXTx
a=ssrc:1390104252 msid:ez0lPBqQZbbAN0ImN4ZrrUCbshr2khvkB6ob 99528680-68d8-418b-afc6-205372e9cb6f
a=ssrc:2798384649 cname:rsPDymoMnGBuVXTx
a=ssrc:2798384649 msid:ez0lPBqQZbbAN0ImN4ZrrUCbshr2khvkB6ob 99528680-68d8-418b-afc6-205372e9cb6f
a=ssrc-group:FID 1390104253 2798384650
a=ssrc:1390104253 cname:rsPDymoMnGBuVXTx
a=ssrc:1390104253 msid:ez0lPBqQZbbAN0ImN4ZrrUCbshr2khvkB6ob 99528680-68d8-418b-afc6-205372e9cb6f
a=ssrc:2798384650 cname:rsPDymoMnGBuVXTx
a=ssrc:2798384650 msid:ez0lPBqQZbbAN0ImN4ZrrUCbshr2khvkB6ob 99528680-68d8-418b-afc6-205372e9cb6f
a=ssrc-group:FID 1390104254 2798384651
a=ssrc:1390104254 cname:rsPDymoMnGBuVXTx
a=ssrc:1390104254 msid:ez0lPBqQZbbAN0ImN4ZrrUCbshr2khvkB6ob 99528680-68d8-418b-afc6-205372e9cb6f
a=ssrc:2798384651 cname:rsPDymoMnGBuVXTx
a=ssrc:2798384651 msid:ez0lPBqQZbbAN0ImN4ZrrUCbshr2khvkB6ob 99528680-68d8-418b-afc6-205372e9cb6f
a=ssrc-group:SIM 1390104252 1390104253 1390104254
2.3 解释
根据这个片段可以推测, 这个sdp里面视频媒体行是一个同播3个码流的 Simulcast
代码语言:javascript复制low: 1390104252
mid: 1390104253
high: 1390104254
3. SRS 处理 RTC 基本流程回顾与分析
3.1 普通场景(single stream)
推流端
以 SRS 的测试网页为例, 打开 http://127.0.0.1:8080/players/rtc_publisher.html 点击 开始推流
浏览器在处理 offer 后, 会将 offer 发给 SRS, SRS 处理 offer 后返回 answer给浏览器并就绪等待收流, 浏览器在获得answer,经过后续步骤后最终推流给 SRS
播放端
播放端同样在 SRS 收到浏览器的offer后,回复answer,并准备把推流端的流转发给播放端
3.2 Simulcast 场景 (simulcast stream)
对于支持 Simulcast 场景, 大部分流程与上述分析基本一致, 除了以下几点
•推流端如何生成带有 a=ssrc-group:SIM
的sdp, 以及确定 SSRC 序列 {layer0 layer1 layer2...}
的长度•SRS 如何识别推流端的 a=ssrc-group:SIM
•播放端如何拉取指定layer的流
4. SRS 开发支持
Simulcast 场景(simulcast stream)与普通场景(single stream)的几点主要的不同之处, 这个几点不同,实际对应着几个开发需求. 解决它们就解决问题
为了简化描述, 应对Simulcast的细节, 我们需要约定两个概念
•推流端启用Simulcast层级个数, 也就是 SSRC 序列 {layer0 layer1 layer2...}
的长度, 如上述所见, 用变量 numberOfSimulcastLayers
标示, 使用举例 webrtc://127.0.0.1/live/livestream?numberOfSimulcastLayers=3
•播放端播放层级, 即指定播放的哪一层, 用变量 layer
标示, 举例 webrtc://127.0.0.1/live/livestream?layer=0
4.1 补充 offer 对 a=ssrc-group:SIM
的支持
补充 offer 对 a=ssrc-group:SIM
的支持 主要由 function UpdateNativeCreateOffer(numberOfSimulcastLayers)
完成 UpdateNativeCreateOffer 参考 simulcast-playground 项目, 详见 参考2, 其参数 numberOfSimulcastLayers 即为 Simulcast 开的层数
numberOfSimulcastLayers 取值为 1,2,3; 当 numberOfSimulcastLayers=1 时, Simulcast 场景 (simulcast stream) 向下退化为 普通场景(single stream)
UpdateNativeCreateOffer 解析offer的视频媒体行的 SSRC 和 RTX SSRC, 将其作为Simulcast的其中一层, 并根据 numberOfSimulcastLayers 取值, 补充剩下的层级, 并将各层的 SSRC 聚合为
SSRC 序列 {layer0 layer1 layer2...}
添加形如 2.1 特点
描述的 a=ssrc-group:SIM layer0 layer1 layer2...
最后组合为新的offer并返回
4.2 补充 SRS 对 a=ssrc-group:SIM
的解析
SRS 解析 a=ssrc-group:SIM
保存至 SrsSSRCGroup 在获得 a=ssrc-group:SIM
及相应的 SSRC 序列
后, SRS 为 publisher 建立与 SSRC 序列
与之相对应的 video_track_desc, 此后 publisher启动, 处理推流端的各layer的流
4.3 建立 SSRC 序列
转发映射, SRS 生成并回复正确的 answer
首先,我们需要知道播放端需要播放哪个层级的视频, 即layer是多少,这个可以在播放端发给offer的消息体中, 扩展一个layer字段, 如
代码语言:javascript复制var data = {
api: conf.apiUrl,
tid: conf.tid,
streamurl: 'webrtc://127.0.0.1/live/livestream?layer=0',
clientip: null,
sdp: offer.sdp
};
然后, SRS 解析 streamurl 取得layer后, 以其为索引, 筛选SSRC 序列
, 然后查询 pulisher 对应的 video_track_desc, 以此创建 player 所需的 video_track_desc 和 SSRC, 返回给播放端, 并处理后续的player逻辑
5. 补充
Simulcast 主要存在两种API接口, 这次介绍的 SDP munging 风格的 Simulcast 在 SRS 里已得到支持; 另一种 RID based 风格的 Simulcast, 开发已基本完成, 相关PR和介绍文档随后给出.
Simulcast (SDP munging) PR 详见: https://github.com/ossrs/srs/pull/2420
分支详见: https://github.com/ossrs/srs/tree/feature/simulcast
Simucast (RID based) PR 将于近期给出
体验分支详见: https://github.com/johzzy/srs/tree/johnny/experimental
欢迎大家下载尝试, 反馈问题, 改进功能.
6. 参考
1.https://www.meetecho.com/blog/simulcast-janus-ssrc/ https://webrtchacks.com/not-a-guide-to-sdp-munging/2.https://github.com/fippo/simulcast-playground https://fippo.github.io/simulcast-playground/ https://webrtchacks.com/a-playground-for-simulcast-without-an-sfu/
7. 附录
一份 SDP munging 风格 Simulcast 的 sdp
https://gist.github.com/johzzy/067dd46a1222bf211f79aef5bcf4cd3a
References
[1]
#2420: https://github.com/ossrs/srs/pull/2420#issuecomment-864359462