作者: Iñaki Baz Castillo(原文链接)
翻译:刘通
原标题:SDP: Your Fears Are Unleashed
前文连接:SDP:被释放的恐惧(二)
SDP是用来通知的?
让我们重新思考一下:
在上述所有的场景中,我们都没有兴趣建立一个新的DTLS关联。而是发送一个完整的SDP给另一方来通知与RTP流相关的改变。没有别的。但是,WebRTC“API”规定的“完全重新检查”让SDP接收方重新检查关于ICE,DTLS,RTP等内容,从而导致了上述问题。
记住,在ORTC中根本没有可能出现这种可笑的错误。这是因为,在ORTC中,传送关于新发送RTP流的信息也不包括再次发送ICE+DTLS参数。
完全重新检查
在上文中我们已经讨论过这点,但是为了明确,这是WebRTC终端在使用SDP重请求和重应答调用setRemoteDescription()的所有任务:
1. 检查a=ice-ufrag和/或a=ice-pwd被改变。如果改了,重启ICE。
2. 检查a=fingerprint是否被改,或者应答中a=setup属性是否具有可以破坏之前协商的DTLS角色的值。如果有,就建立一个新的DTLS关联。
3. 检查先前存在的媒体副本中的方向属性是否变成a=inactive或者a=recvonly,或者它的媒体端口是不是0。如果是这样,就假设这个媒体流已经被停了。
4. 检查是否添加了新的m=audio/video/data部分。
5. 检查SSRC,编解码器,负载值,RTP报头扩展映射,每个编解码器的RTCP可用反馈等等是否改变。如果改了的话,准备好接收修改的数据流。
这对于WebRTC实现以及在ORTC上搭建RTCPeerConnection shim或者其他媒体引擎的开发人员来说,是非常有问题的。
切勿禁用音频!
让我们假设我们有一个双向且BUNDLE的音频+视频会话,第一个m=部分对应于音频交换。
我们继续假设我们想要关闭/删除我们的麦克风。通常我们会得到相应的RTCRtpSender并且调用stop()。一个新的createOffer()调用会产生一个包括a=recvonly的m=audio部分。
幸运的是,收发器(或者媒体部分)因为我们正在接收远端的音频,所以还是处于活跃状态。现在让我们假设远端对等端也关闭/删除音频输入。在SDP请求/响应重协商之后,m=audio部分会包括a=inactive,意味着目前没有音频正在发送或者接收。
Firefox关于a=inactive的表现
那么Firefox的反应如何呢?当音频被完全移除之后,Firefox会设置端口0,以及在m=audio段中设置a=inactive。它还将其中所有的ICE和DTLS参数,并且将音频标签从BUNDLE组中移出。结果呢?ICE+DTLS被关闭,不管它是不是还被用来承载视频。
这里有一些从Firefox浏览器中提取出的真实的SDP来说明这个问题:
# Firefox产生一个启用音频(麦克风)和视频(网络摄像头)的SDP请求。RTCPeerConnection中有bundlePolicy: ‘max-bundle’,所以它只是将ICE内容设置到第一个媒体部分中(m=audio段):
v=0
o=mozilla…THIS_IS_SDPARTA-57.0a1 5362413390007385508 1 IN IP4 0.0.0.0
s=-
t=0 0
a=sendrecv
a=fingerprint:sha-256 CC:11:0E:0D:B2:7D:FC:F9:11:2F:68:74:11:B3:7E:13:C0:C3:05:78:F3:29:10:C6:E9:14:8A:0A:AB:A4:08:6A
a=group:BUNDLE sdparta_0 sdparta_1
a=ice-options:trickle
a=msid-semantic:WMS *
m=audio 58960 UDP/TLS/RTP/SAVPF 109 9 0 8 101
c=IN IP4 1.2.3.4
a=candidate:0 1 UDP 2122252543 192.168.1.34 58960 typ host
a=candidate:1 1 TCP 2105524479 192.168.1.34 9 typ host tcptype active
a=sendrecv
a=end-of-candidates
a=extmap:1/sendonly urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=fmtp:109 maxplaybackrate=48000;stereo=1;useinbandfec=1
a=fmtp:101 0-15
a=ice-pwd:4650fd5710897bee3bf874465c372e87
a=ice-ufrag:62c997d1
a=mid:sdparta_0
a=msid:{ee311a2b-98e0-dc49-b7ab-5d5058ef8a08} {0aac500e-e6f0-1f4a-88e4-706d215e3bfe}
a=rtcp-mux
a=rtpmap:109 opus/48000/2
a=rtpmap:9 G722/8000/1
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=setup:actpass
a=ssrc:858486657 cname:{18d5ea4e-9fdc-4844-90f6-10eabff59478}
m=video 0 UDP/TLS/RTP/SAVPF 120 121 126 97
c=IN IP4 1.2.3.4
a=bundle-only
a=sendrecv
a=extmap:1 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
a=fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1
a=fmtp:97 profile-level-id=42e01f;level-asymmetry-allowed=1
a=fmtp:120 max-fs=12288;max-fr=60
a=fmtp:121 max-fs=12288;max-fr=60
a=ice-pwd:4650fd5710897bee3bf874465c372e87
a=ice-ufrag:62c997d1
a=mid:sdparta_1
a=msid:{ee311a2b-98e0-dc49-b7ab-5d5058ef8a08} {8ba95436-369f-5b48-abe8-7c316b2b706d}
a=rtcp-fb:120 nack
a=rtcp-fb:120 nack pli
a=rtcp-fb:120 ccm fir
a=rtcp-fb:120 goog-remb
a=rtcp-fb:121 nack
a=rtcp-fb:121 nack pli
a=rtcp-fb:121 ccm fir
a=rtcp-fb:121 goog-remb
a=rtcp-fb:126 nack
a=rtcp-fb:126 nack pli
a=rtcp-fb:126 ccm fir
a=rtcp-fb:126 goog-remb
a=rtcp-fb:97 nack
a=rtcp-fb:97 nack pli
a=rtcp-fb:97 ccm fir
a=rtcp-fb:97 goog-remb
a=rtcp-mux
a=rtpmap:120 VP8/90000
a=rtpmap:121 VP9/90000
a=rtpmap:126 H264/90000
a=rtpmap:97 H264/90000
a=setup:actpass
a=ssrc:2390117206 cname:{18d5ea4e-9fdc-4844-90f6-10eabff59478}
# 远端提供不包括任何音频或者视频流的SDP应答(所以Firefox会发送音频和视频,但是不会从远端接收任何数据):
v=0
o=REMOTE 30596888 2 IN IP4 0.0.0.0
s=-
t=0 0
a=sendrecv
a=fingerprint:sha-512 E0:71:C6:F8:CD:DA:01:05:9A:9F:B7:E9:BD:BF:DA:5F:80:56:13:F4:48:F1:56:AC:2C:72:E1:54:8C:7C:9A:0C:2B:A1:B3:B3:A2:38:E7:83:87:D8:38:2B:4E:F0:CB:FD:FD:55:77:64:CF:26:45:FC:2C:59:A4:93:B8:3B:64:3B
a=group:BUNDLE sdparta_0 sdparta_1
a=ice-lite
a=msid-semantic:WMS *
m=audio 7 RTP/SAVPF 109
c=IN IP4 127.0.0.1
a=candidate:udpcandidate 1 udp 1082702079 99.99.99.99 42936 typ host
a=recvonly
a=end-of-candidates
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=fmtp:109 maxplaybackrate=0;stereo=0;useinbandfec=1
a=ice-options:renomination
a=ice-pwd:5rc2025mfkzff6cze0xj5ra34pogs7to
a=ice-ufrag:wardj9g9mvmeixf6
a=mid:sdparta_0
a=rtcp-mux
a=rtpmap:109 opus/48000/2
a=setup:active
m=video 7 RTP/SAVPF 120
c=IN IP4 127.0.0.1
a=candidate:udpcandidate 1 udp 1082702079 99.99.99.99 42936 typ host
a=recvonly
a=end-of-candidates
a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
a=extmap:1 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=ice-options:renomination
a=ice-pwd:5rc2025mfkzff6cze0xj5ra34pogs7to
a=ice-ufrag:wardj9g9mvmeixf6
a=mid:sdparta_1
a=rtcp-fb:120 nack
a=rtcp-fb:120 nack pli
a=rtcp-fb:120 ccm fir
a=rtcp-fb:120 goog-remb
a=rtcp-mux
a=rtpmap:120 VP8/90000
a=setup:active
# 之后,在Firefox中运行的应用决定关闭麦克分并且停止发送数据。基本上它是通过调用pc.removeTrack(micRtpSender)并且这是新的本地SDP的样子:
v=0
o=mozilla…THIS_IS_SDPARTA-57.0a1 5362413390007385508 2 IN IP4 0.0.0.0
s=-
t=0 0
a=sendrecv
a=fingerprint:sha-256 CC:11:0E:0D:B2:7D:FC:F9:11:2F:68:74:11:B3:7E:13:C0:C3:05:78:F3:29:10:C6:E9:14:8A:0A:AB:A4:08:6A
a=group:BUNDLE sdparta_1
a=ice-options:trickle
a=msid-semantic:WMS *
m=audio 58960 UDP/TLS/RTP/SAVPF 0
c=IN IP4 1.2.3.4
a=inactive
a=end-of-candidates
a=mid:sdparta_0
a=rtpmap:0 PCMU/8000
m=video 58960 UDP/TLS/RTP/SAVPF 120 121 126 97
c=IN IP4 1.2.3.4
a=sendrecv
a=extmap:1 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
a=fmtp:126 profile-level-id=42e01f;level-asymmetry-allowed=1;packetization-mode=1
a=fmtp:97 profile-level-id=42e01f;level-asymmetry-allowed=1
a=fmtp:120 max-fs=12288;max-fr=60
a=fmtp:121 max-fs=12288;max-fr=60
a=ice-pwd:4650fd5710897bee3bf874465c372e87
a=ice-ufrag:62c997d1
a=mid:sdparta_1
a=msid:{ee311a2b-98e0-dc49-b7ab-5d5058ef8a08} {8ba95436-369f-5b48-abe8-7c316b2b706d}
a=rtcp-fb:120 nack
a=rtcp-fb:120 nack pli
a=rtcp-fb:120 ccm fir
a=rtcp-fb:120 goog-remb
a=rtcp-fb:121 nack
a=rtcp-fb:121 nack pli
a=rtcp-fb:121 ccm fir
a=rtcp-fb:121 goog-remb
a=rtcp-fb:126 nack
a=rtcp-fb:126 nack pli
a=rtcp-fb:126 ccm fir
a=rtcp-fb:126 goog-remb
a=rtcp-fb:97 nack
a=rtcp-fb:97 nack pli
a=rtcp-fb:97 ccm fir
a=rtcp-fb:97 goog-remb
a=rtcp-mux
a=rtpmap:120 VP8/90000
a=rtpmap:121 VP9/90000
a=rtpmap:126 H264/90000
a=rtpmap:97 H264/90000
a=setup:actpass
a=ssrc:2390117206 cname:{18d5ea4e-9fdc-4844-90f6-10eabff59478}
注意到了吗?在第一媒体段中再也没有包括a=candidate或者a=ice-ufrag/pwd的行了,意味着,通过遵守SDP规则,浏览器关闭了ICE+DTLS连接,所以不管视频是否正在发送,媒体传输已经停止了。
这不是Firefox中的一个bug,而是规范中的bug,因为它取决于SDP语意所以这个bug会出现。
只是因为我们想要移除音频轨道就关闭或者重启ICE+DTLS传输是毫无理由的。显然在ORTC中不会发生。但是,在WebRTC中,我们不能只是发送“音频轨道关闭”,但是,我们发送的是一个完整的SDP。这个SDO包括有关所有层和流的信息。更坏的是:每个媒体段都包括传输的信息。然后,如果我们移除了第一媒体段,BUNDLE会变的乱七八糟,而且根据浏览器,传输也会被关掉。
那么这个问题能修复吗?当然可以。越来越多针对SDP的修改可以避免这个问题。WebRTC生态系统中最聪明的人可以花费宝贵的时间来讨论出标准化这些修改的最佳方法。
当SDP处理简单的双向音频时是没问题的。但是现今,SDP是无法维持的。
真的吗?!
这简直无法理喻。我们不需要它。我们不需要这些由传统媒体描述格式(SDP)而产生的问题。我们只需要一个API来进行发送,以及一个API进行接收,就不需要额外的东西了。