您当前的位置是:  首页 > 新闻 > 国内 >
 首页 > 新闻 > 国内 >

不需要SFU实现WebRTC联播

2018-08-08 10:38:56   作者:Philipp Hancke 译 / 元宝   来源:CTI论坛   评论:0  点击:


  不需要SFU而实现WebRTC联播,appear.in的WebRTC工程师Philipp Hancke实现了在Chrome和Firefox之间的联播。LiveVideoStack对原文进行了摘译。
  https://webrtchacks.com/a-playground-for-simulcast-without-an-sfu/
  同步联播是WebRTC用于多方会议的一个很有趣的方面。简而言之,它意味着可以同时发送三种不同的分辨率(空间可伸缩性)和不同的帧速率(时间可伸缩性)。
  通常,需要一个SFU才能利用联播。但是有一个方法可以使在两个浏览器之间或在单个页面内效果可见。这对于做单页测试或使用联播功能是非常有用的,特别是能够只启用某些空间层或控制特定流的目标比特率。
  Playground
  Playground有两种变体,一种用于Chrome,另一种用于Firefox。Chrome版本使用了我们在2014年夏天的谷歌环聊中首次看到的旧的SDP munging hack 。Firefox 使用‘RTCRtpSender.setParameters’来启用同步联播。两者都没有遵守最新的规范,但这并不会影响任何人对它的使用。
  两种变体都可以显示视频图像,首先是发送者图像和总体比特率/帧速率图,其后是三个不同的空间流,每个空间流具有用于比特率和帧率的对应图。
  由于我们不想涉及到服务器的内容,所以我们需要破解一些东西。幸运的是,有一个Chrome的测试验证了这个想法。Chrome和Firefox都使用一个数据包的RTP SSRC将其路由到某一个特定的媒体流。这些SSRC可以在SDP提供中找到:
  原始的Chrome SDP
  1type: offer, sdp: v=0
  2o=- 7356021969196541917 2 IN ImP4 127.0.0.1
  3s=-
  4t=0 0
  5a=group:BUNDLE video
  6a=msid-semantic: WMS 0AQNo1bnpzlGvB7aE2InJRz85M9lmId7Es9J
  7m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 124 127 123 125
  8c=IN IP4 0.0.0.0
  9a=rtcp:9 IN IP4 0.0.0.0
  10a=ice-ufrag:cTTs
  11a=ice-pwd:W9/g2uTwfb6UCRxfIoMkd5nV
  12a=ice-options:trickle
  13a=fingerprint:sha-256 01:09:17:BA:CD:91:FE:E0:24:24:86:5C:17:71:CC:37:61:CF:BA:D1:31:49:80:F1:BC:B8:B2:6F:8C:D5:39:F2
  14a=setup:actpass
  15a=mid:video
  16a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
  17a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
  18a=extmap:4 urn:3gpp:video-orientation
  19a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
  20a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
  21a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
  22a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
  23a=sendrecv
  24a=rtcp-mux
  25a=rtcp-rsize
  26a=rtpmap:96 VP8/90000
  27a=rtcp-fb:96 goog-remb
  28a=rtcp-fb:96 transport-cc
  29a=rtcp-fb:96 ccm fir
  30a=rtcp-fb:96 nack
  31a=rtcp-fb:96 nack pli
  32a=rtpmap:97 rtx/90000
  33a=fmtp:97 apt=96
  34a=rtpmap:98 VP9/90000
  35a=rtcp-fb:98 goog-remb
  36a=rtcp-fb:98 transport-cc
  37a=rtcp-fb:98 ccm fir
  38a=rtcp-fb:98 nack
  39a=rtcp-fb:98 nack pli
  40a=rtpmap:99 rtx/90000
  41a=fmtp:99 apt=98
  42a=rtpmap:100 H264/90000
  43a=rtcp-fb:100 goog-remb
  44a=rtcp-fb:100 transport-cc
  45a=rtcp-fb:100 ccm fir
  46a=rtcp-fb:100 nack
  47a=rtcp-fb:100 nack pli
  48a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
  49a=rtpmap:101 rtx/90000
  50a=fmtp:101 apt=100
  51a=rtpmap:102 H264/90000
  52a=rtcp-fb:102 goog-remb
  53a=rtcp-fb:102 transport-cc
  54a=rtcp-fb:102 ccm fir
  55a=rtcp-fb:102 nack
  56a=rtcp-fb:102 nack pli
  57a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
  58a=rtpmap:124 rtx/90000
  59a=fmtp:124 apt=102
  60a=rtpmap:127 red/90000
  61a=rtpmap:123 rtx/90000
  62a=fmtp:123 apt=127
  63a=rtpmap:125 ulpfec/90000
  64a=ssrc-group:FID 3818935445 664019814
  65a=ssrc:3818935445 cname:/9xLVG5y0PJqxacG
  66a=ssrc:3818935445 msid:0AQNo1bnpzlGvB7aE2InJRz85M9lmId7Es9J d1c9f46b-0328-4ef1-9c31-c1b279ac554a
  67a=ssrc:3818935445 mslabel:0AQNo1bnpzlGvB7aE2InJRz85M9lmId7Es9J
  68a=ssrc:3818935445 label:d1c9f46b-0328-4ef1-9c31-c1b279ac554a
  69a=ssrc:664019814 cname:/9xLVG5y0PJqxacG
  70a=ssrc:664019814 msid:0AQNo1bnpzlGvB7aE2InJRz85M9lmId7Es9J d1c9f46b-0328-4ef1-9c31-c1b279ac554a
  71a=ssrc:3818935446 cname:/9xLVG5y0PJqxacG
  72a=ssrc:3818935446 msid:0AQNo1bnpzlGvB7aE2InJRz85M9lmId7Es9J d1c9f46b-0328-4ef1-9c31-c1b279ac554a
  73a=ssrc:3818935447 cname:/9xLVG5y0PJqxacG
  74a=ssrc:3818935447 msid:0AQNo1bnpzlGvB7aE2InJRz85M9lmId7Es9J d1c9f46b-0328-4ef1-9c31-c1b279ac554a
  75a=ssrc:3818935448 cname:/9xLVG5y0PJqxacG
  76a=ssrc:3818935448 msid:0AQNo1bnpzlGvB7aE2InJRz85M9lmId7Es9J d1c9f46b-0328-4ef1-9c31-c1b279ac554a
  77a=ssrc:3818935449 cname:/9xLVG5y0PJqxacG
  78a=ssrc:3818935449 msid:0AQNo1bnpzlGvB7aE2InJRz85M9lmId7Es9J d1c9f46b-0328-4ef1-9c31-c1b279ac554a
  79a=ssrc-group:FID 3818935446 3818935447
  80a=ssrc-group:FID 3818935448 3818935449
  81a=ssrc-group:SIM 3818935445 3818935446 3818935448
  和2014年一样,重要的是多个‘a=ssrc:’的行以及‘a=ssrc-group:SIM’的行。
  原始的Firefox SDP
  1v=0
  2o=mozilla…THIS_IS_SDPARTA-61.0.1 2246157997147315987 0 IN IP4 0.0.0.0
  3s=-
  4t=0 0
  5a=sendrecv
  6a=fingerprint:sha-256 00:3E:DC:02:92:60:97:0D:D8:F7:7F:E9:AD:41:46:CD:B5:FC:33:35:3F:5C:C4:BC:CD:85:17:96:F8:D6:14:57
  7a=group:BUNDLE sdparta_0
  8a=ice-options:trickle
  9a=msid-semantic:WMS *
  10m=video 40601 UDP/TLS/RTP/SAVPF 120 121 126 97
  11c=IN IP4 192.168.1.230
  12a=sendrecv
  13a=end-of-candidates
  14a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid
  15a=extmap:4 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
  16a=extmap:5 urn:ietf:params:rtp-hdrext:toffset
  17a=extmap:6/sendonly urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
  18a=fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1
  19a=fmtp:97 profile-level-id=42e01f;level-asymmetry-allowed=1
  20a=fmtp:120 max-fs=12288;max-fr=60
  21a=fmtp:121 max-fs=12288;max-fr=60
  22a=ice-pwd:f317cfe0aaa529381815e208a6cdec19
  23a=ice-ufrag:07366c17
  24a=mid:sdparta_0
  25a=msid:{8de6968c-0ea9-4a19-aaa6-51355d4a5f4e} {9bdb3a96-19f3-4730-8459-e56db515b5da}
  26a=rid:hi send
  27a=rid:mid send
  28a=rid:lo send
  29a=rtcp-fb:120 nack
  30a=rtcp-fb:120 nack pli
  31a=rtcp-fb:120 ccm fir
  32a=rtcp-fb:120 goog-remb
  33a=rtcp-fb:121 nack
  34a=rtcp-fb:121 nack pli
  35a=rtcp-fb:121 ccm fir
  36a=rtcp-fb:121 goog-remb
  37a=rtcp-fb:126 nack
  38a=rtcp-fb:126 nack pli
  39a=rtcp-fb:126 ccm fir
  40a=rtcp-fb:126 goog-remb
  41a=rtcp-fb:97 nack
  42a=rtcp-fb:97 nack pli
  43a=rtcp-fb:97 ccm fir
  44a=rtcp-fb:97 goog-remb
  45a=rtcp-mux
  46a=rtpmap:120 VP8/90000
  47a=rtpmap:121 VP9/90000
  48a=rtpmap:126 H264/90000
  49a=rtpmap:97 H264/90000
  50a=setup:actpass
  51a=simulcast: send rid=hi;mid;lo
  52a=ssrc:252082699 cname:{004db4a6-943d-496b-9e8d-1689f7be7355}
  53a=ssrc:2611961929 cname:{004db4a6-943d-496b-9e8d-1689f7be7355}
  54a=ssrc:1452646660 cname:{004db4a6-943d-496b-9e8d-1689f7be7355}
  在Firefox中,重要的联播比特数是‘a = rid’的行和‘a = simulcast’的行(从底部开始的第4行)。注意:这是来自规范的旧版本,可能会有所变化。
  WebRTC hack
  我们需要让我们的同行相信,它实际上正在接收三种不同的视频流——低、中、高的比特率——而不仅仅是其中一种。为实现这一目标,我们需要创建自己的SDP,其中包含从SSRC到跟踪的不同映射。这是有点苛刻的,但这个网站被称为webrtchacks是有原因的!
  我们最终将Firefox产品转换为:
  1v=0
  2o=mozilla…THIS_IS_SDPARTA-61.0 8324701712193024513 0 IN IP4 0.0.0.0
  3s=-
  4t=0 0
  5a=sendrecv
  6a=fingerprint:sha-256 00:3E:DC:02:92:60:97:0D:D8:F7:7F:E9:AD:41:46:CD:B5:FC:33:35:3F:5C:C4:BC:CD:85:17:96:F8:D6:14:57
  7a=group:BUNDLE sdparta_0 sdparta_1 sdparta_2
  8a=msid-semantic:WMS *
  9m=video 9 UDP/TLS/RTP/SAVPF 120 121 126 97
  10c=IN IP4 0.0.0.0
  11a=sendrecv
  12a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid
  13a=extmap:4 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
  14a=extmap:5 urn:ietf:params:rtp-hdrext:toffset
  15a=extmap:6/sendonly urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
  16a=fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1
  17a=fmtp:97 profile-level-id=42e01f;level-asymmetry-allowed=1
  18a=fmtp:120 max-fs=12288;max-fr=60
  19a=fmtp:121 max-fs=12288;max-fr=60
  20a=ice-pwd:f317cfe0aaa529381815e208a6cdec19
  21a=ice-ufrag:07366c17
  22a=mid:sdparta_0
  23a=msid:low low
  24a=rtcp-fb:120 nack
  25a=rtcp-fb:120 nack pli
  26a=rtcp-fb:120 ccm fir
  27a=rtcp-fb:120 goog-remb
  28a=rtcp-fb:121 nack
  29a=rtcp-fb:121 nack pli
  30a=rtcp-fb:121 ccm fir
  31a=rtcp-fb:121 goog-remb
  32a=rtcp-fb:126 nack
  33a=rtcp-fb:126 nack pli
  34a=rtcp-fb:126 ccm fir
  35a=rtcp-fb:126 goog-remb
  36a=rtcp-fb:97 nack
  37a=rtcp-fb:97 nack pli
  38a=rtcp-fb:97 ccm fir
  39a=rtcp-fb:97 goog-remb
  40a=rtcp-mux
  41a=rtpmap:120 VP8/90000
  42a=rtpmap:121 VP9/90000
  43a=rtpmap:126 H264/90000
  44a=rtpmap:97 H264/90000
  45a=setup:actpass
  46a=ssrc:252082699 cname:{004db4a6-943d-496b-9e8d-1689f7be7355}
  47m=video 9 UDP/TLS/RTP/SAVPF 120 121 126 97
  48c=IN IP4 0.0.0.0
  49a=sendrecv
  50a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid
  51a=extmap:4 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
  52a=extmap:5 urn:ietf:params:rtp-hdrext:toffset
  53a=extmap:6/sendonly urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
  54a=fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1
  55a=fmtp:97 profile-level-id=42e01f;level-asymmetry-allowed=1
  56a=fmtp:120 max-fs=12288;max-fr=60
  57a=fmtp:121 max-fs=12288;max-fr=60
  58a=ice-pwd:f317cfe0aaa529381815e208a6cdec19
  59a=ice-ufrag:07366c17
  60a=mid:sdparta_1
  61a=msid:mid mid
  62a=rtcp-fb:120 nack
  63a=rtcp-fb:120 nack pli
  64a=rtcp-fb:120 ccm fir
  65a=rtcp-fb:120 goog-remb
  66a=rtcp-fb:121 nack
  67a=rtcp-fb:121 nack pli
  68a=rtcp-fb:121 ccm fir
  69a=rtcp-fb:121 goog-remb
  70a=rtcp-fb:126 nack
  71a=rtcp-fb:126 nack pli
  72a=rtcp-fb:126 ccm fir
  73a=rtcp-fb:126 goog-remb
  74a=rtcp-fb:97 nack
  75a=rtcp-fb:97 nack pli
  76a=rtcp-fb:97 ccm fir
  77a=rtcp-fb:97 goog-remb
  78a=rtcp-mux
  79a=rtpmap:120 VP8/90000
  80a=rtpmap:121 VP9/90000
  81a=rtpmap:126 H264/90000
  82a=rtpmap:97 H264/90000
  83a=setup:actpass
  84a=ssrc:2611961929 cname:{004db4a6-943d-496b-9e8d-1689f7be7355}
  85m=video 9 UDP/TLS/RTP/SAVPF 120 121 126 97
  86c=IN IP4 0.0.0.0
  87a=sendrecv
  88a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid
  89a=extmap:4 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
  90a=extmap:5 urn:ietf:params:rtp-hdrext:toffset
  91a=extmap:6/sendonly urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
  92a=fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1
  93a=fmtp:97 profile-level-id=42e01f;level-asymmetry-allowed=1
  94a=fmtp:120 max-fs=12288;max-fr=60
  95a=fmtp:121 max-fs=12288;max-fr=60
  96a=ice-pwd:f317cfe0aaa529381815e208a6cdec19
  97a=ice-ufrag:07366c17
  98a=mid:sdparta_2
  99a=msid:hi hi
  100a=rtcp-fb:120 nack
  101a=rtcp-fb:120 nack pli
  102a=rtcp-fb:120 ccm fir
  103a=rtcp-fb:120 goog-remb
  104a=rtcp-fb:121 nack
  105a=rtcp-fb:121 nack pli
  106a=rtcp-fb:121 ccm fir
  107a=rtcp-fb:121 goog-remb
  108a=rtcp-fb:126 nack
  109a=rtcp-fb:126 nack pli
  110a=rtcp-fb:126 ccm fir
  111a=rtcp-fb:126 goog-remb
  112a=rtcp-fb:97 nack
  113a=rtcp-fb:97 nack pli
  114a=rtcp-fb:97 ccm fir
  115a=rtcp-fb:97 goog-remb
  116a=rtcp-mux
  117a=rtpmap:120 VP8/90000
  118a=rtpmap:121 VP9/90000
  119a=rtpmap:126 H264/90000
  120a=rtpmap:97 H264/90000
  121a=setup:actpass
  122a=ssrc:1452646660 cname:{004db4a6-943d-496b-9e8d-1689f7be7355}
  这显示了三个不同的媒体部分(按照统一计划的规定),它将在接收器处触发“跟踪”事件三次,为我们提供三个不同的 MediaStream对象以附加到视频元素,并且还允许使用getStats API来为各个比特率制作图表。
  请参阅SDP的源代码,了解如何创建它。
  调整各个层的比特率
  Chrome长期以来一直使用硬编码表来表示各个空间层的同步比特率。
  1. These tables describe from which resolution we can use how many
  2. simulcast layers at what bitrates (maximum, target, and minimum).
  3. Important!! Keep this table from high resolution to low resolution.
  4. clang-format off
  5. const SimulcastFormat kSimulcastFormats[] = {
  6.  {1920, 1080, 3, 5000, 4000, 800},
  7. {1280, 720, 3,  2500, 2500, 600},
  8.  {960, 540, 3, 900, 900, 450},
  9. {640, 360, 2, 700, 500, 150},
  10. {480, 270, 2, 450, 350, 150},
  11. {320, 180, 1, 200, 150, 30},
  12.  {0, 0, 1, 200, 150, 30}
  13. ;
  该表显示了分辨率(例如1920×1080),空间层数(3)以及最大,最佳和最小比特率。
  感谢‘setParameters’让我们现在可以偏离这个表中定义的比特率并获得一些创意。如果你运行该示例而不进行任何修改,你可以看到联播以大约3.2mbps的比特率在发送。它分为三个不同的空间流,大约是150kbps,500kbps和2500kbps,与表中的设置相匹配。
  让我们通过将这个JavaScript粘贴到控制台来修改它:
  1var p = pc1.getSenders()[0].getParameters();
  2p.encodings[1].maxBitrate = 300*1000;
  3p.encodings[2].maxBitrate = 400*1000;
  4pc1.getSenders()[0].setParameters(p)
  5  .catch(e => console.error(e))
  这将中分辨率(640×360)空间层的目标比特率设置为300kbps,将720p流的目标比特率设置为400kbps。编码器可以很好地匹配这些比特率,它们的质量非常好,即使将720p流的比特率设置为400kbps也是如此。
  如果我们将720p的目标比特率降低到200 kbps,那么我们可以看到由于帧率下降而导致的视觉退化,因为只有基本的时间层被发送。对于720p的流,你可以用像200kbps一样低的比特率来使用…
  此图表(以及下面的图表)中均是左侧为比特率,右侧为帧率。
  这对于在SFU中成功实现同步联播的任何人来说都不是新闻,但是在没有任何服务器的情况下,在单个页面中展示这个效果还是很惊人的。
  到处都是Bug
  最初构建页面的主要动机之一是探索Firefox中对联播的支持。它没有预期的那么好,最大的问题是因为Firefox以及我在SFU实现中的错误。在playground的页面上,我可以证明它不在SFU中,但在Firefox中出现了问题。
  中分辨率图层的比特率仅以300kbps,而不是Chrome发送的500kbps。尽管请求超过2000kbps,我们只能为高分辨率层获得500kbps的速率。部分原因是SDP munging中的一个非常巧妙的错误,它影响了低分辨率层的设置。这很容易在JavaScript中修复。
  接下来,有一个问题是高分辨率层的配置被修改了,这将很快在Firefox中登陆,并将被提升到Beta和ESR。有了修复,比特率就会高得多:
  中等分辨率层的目标比特率也根据我的要求更改为500kbps。把我当成一个非常满意的客户!
  Jitsi的Brian Baldino发现了另外一个有趣的问题。当禁用高中空间层时,Chrome将以每秒超过一兆比特的比特率继续发送。这是为了保持比特率估计值高而填充数据。
  这实际上是对旧版Chrome问题非常好的再现,希望在一个更具体的用例中出现的不良行为,以及在单个页面测试中进行复制,使这更容易修复。
  最后但并非最不重要的是,您可能已经注意到Chrome中本地视频的高帧率,接近90帧。Chrome似乎将所有发送流的比特率和帧率都加了起来。这(可能)不太正确,所以这里有另一个bug报告。
  同步联播对于构建高级WebRTC应用程序非常重要。希望这个playground让Web开发人员更容易访问它。特别感谢Florent Castelli在Chrome中实现‘setParameters’,并允许更多的修补以及Byron Campen在Firefox问题上的快速支持。
【免责声明】本文仅代表作者本人观点,与CTI论坛无关。CTI论坛对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。请读者仅作参考,并请自行承担全部责任。

专题