
  https://webrtchacks.com/a-playground-for-simulcast-without-an-sfu/
  同步聯(lián)播是WebRTC用于多方會議的一個很有趣的方面。簡而言之,它意味著可以同時發(fā)送三種不同的分辨率(空間可伸縮性)和不同的幀速率(時間可伸縮性)。
  通常,需要一個SFU才能利用聯(lián)播。但是有一個方法可以使在兩個瀏覽器之間或在單個頁面內(nèi)效果可見。這對于做單頁測試或使用聯(lián)播功能是非常有用的,特別是能夠只啟用某些空間層或控制特定流的目標比特率。
  Playground
  Playground有兩種變體,一種用于Chrome,另一種用于Firefox。Chrome版本使用了我們在2014年夏天的谷歌環(huán)聊中首次看到的舊的SDP munging hack 。Firefox 使用‘RTCRtpSender.setParameters’來啟用同步聯(lián)播。兩者都沒有遵守最新的規(guī)范,但這并不會影響任何人對它的使用。
  兩種變體都可以顯示視頻圖像,首先是發(fā)送者圖像和總體比特率/幀速率圖,其后是三個不同的空間流,每個空間流具有用于比特率和幀率的對應(yīng)圖。
  由于我們不想涉及到服務(wù)器的內(nèi)容,所以我們需要破解一些東西。幸運的是,有一個Chrome的測試驗證了這個想法。Chrome和Firefox都使用一個數(shù)據(jù)包的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中,重要的聯(lián)播比特數(shù)是‘a = rid’的行和‘a = simulcast’的行(從底部開始的第4行)。注意:這是來自規(guī)范的舊版本,可能會有所變化。
  WebRTC hack
  我們需要讓我們的同行相信,它實際上正在接收三種不同的視頻流——低、中、高的比特率——而不僅僅是其中一種。為實現(xiàn)這一目標,我們需要創(chuàng)建自己的SDP,其中包含從SSRC到跟蹤的不同映射。這是有點苛刻的,但這個網(wǎng)站被稱為webrtchacks是有原因的!
  我們最終將Firefox產(chǎn)品轉(zhuǎn)換為:
  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}
  這顯示了三個不同的媒體部分(按照統(tǒng)一計劃的規(guī)定),它將在接收器處觸發(fā)“跟蹤”事件三次,為我們提供三個不同的 MediaStream對象以附加到視頻元素,并且還允許使用getStats API來為各個比特率制作圖表。
  請參閱SDP的源代碼,了解如何創(chuàng)建它。
  調(diào)整各個層的比特率
  Chrome長期以來一直使用硬編碼表來表示各個空間層的同步比特率。
- These tables describe from which resolution we can use how many
- simulcast layers at what bitrates (maximum, target, and minimum).
- Important!! Keep this table from high resolution to low resolution.
- clang-format off
- const SimulcastFormat kSimulcastFormats[] = {
- {1920, 1080, 3, 5000, 4000, 800},
- {1280, 720, 3, 2500, 2500, 600},
- {960, 540, 3, 900, 900, 450},
- {640, 360, 2, 700, 500, 150},
- {480, 270, 2, 450, 350, 150},
- {320, 180, 1, 200, 150, 30},
- {0, 0, 1, 200, 150, 30}
- ;
  該表顯示了分辨率(例如1920×1080),空間層數(shù)(3)以及最大,最佳和最小比特率。
  感謝‘setParameters’讓我們現(xiàn)在可以偏離這個表中定義的比特率并獲得一些創(chuàng)意。如果你運行該示例而不進行任何修改,你可以看到聯(lián)播以大約3.2mbps的比特率在發(fā)送。它分為三個不同的空間流,大約是150kbps,500kbps和2500kbps,與表中的設(shè)置相匹配。
  讓我們通過將這個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)空間層的目標比特率設(shè)置為300kbps,將720p流的目標比特率設(shè)置為400kbps。編碼器可以很好地匹配這些比特率,它們的質(zhì)量非常好,即使將720p流的比特率設(shè)置為400kbps也是如此。
  如果我們將720p的目標比特率降低到200 kbps,那么我們可以看到由于幀率下降而導(dǎo)致的視覺退化,因為只有基本的時間層被發(fā)送。對于720p的流,你可以用像200kbps一樣低的比特率來使用…
  此圖表(以及下面的圖表)中均是左側(cè)為比特率,右側(cè)為幀率。
  這對于在SFU中成功實現(xiàn)同步聯(lián)播的任何人來說都不是新聞,但是在沒有任何服務(wù)器的情況下,在單個頁面中展示這個效果還是很驚人的。
  到處都是Bug
  最初構(gòu)建頁面的主要動機之一是探索Firefox中對聯(lián)播的支持。它沒有預(yù)期的那么好,最大的問題是因為Firefox以及我在SFU實現(xiàn)中的錯誤。在playground的頁面上,我可以證明它不在SFU中,但在Firefox中出現(xiàn)了問題。
  中分辨率圖層的比特率僅以300kbps,而不是Chrome發(fā)送的500kbps。盡管請求超過2000kbps,我們只能為高分辨率層獲得500kbps的速率。部分原因是SDP munging中的一個非常巧妙的錯誤,它影響了低分辨率層的設(shè)置。這很容易在JavaScript中修復(fù)。
  接下來,有一個問題是高分辨率層的配置被修改了,這將很快在Firefox中登陸,并將被提升到Beta和ESR。有了修復(fù),比特率就會高得多:
  中等分辨率層的目標比特率也根據(jù)我的要求更改為500kbps。把我當(dāng)成一個非常滿意的客戶!
  Jitsi的Brian Baldino發(fā)現(xiàn)了另外一個有趣的問題。當(dāng)禁用高中空間層時,Chrome將以每秒超過一兆比特的比特率繼續(xù)發(fā)送。這是為了保持比特率估計值高而填充數(shù)據(jù)。
  這實際上是對舊版Chrome問題非常好的再現(xiàn),希望在一個更具體的用例中出現(xiàn)的不良行為,以及在單個頁面測試中進行復(fù)制,使這更容易修復(fù)。
  最后但并非最不重要的是,您可能已經(jīng)注意到Chrome中本地視頻的高幀率,接近90幀。Chrome似乎將所有發(fā)送流的比特率和幀率都加了起來。這(可能)不太正確,所以這里有另一個bug報告。
  同步聯(lián)播對于構(gòu)建高級WebRTC應(yīng)用程序非常重要。希望這個playground讓W(xué)eb開發(fā)人員更容易訪問它。特別感謝Florent Castelli在Chrome中實現(xiàn)‘setParameters’,并允許更多的修補以及Byron Campen在Firefox問題上的快速支持。
				
				                
						








