Closed Bug 1355486 Opened 4 years ago Closed 3 years ago
Firefox attempts to establish a new ICE+DTLS transport when moving from 0 local tracks to N
Scenario: 1) Firefox joins an empty SFU room (no other participants) and receives an initial offer from the SFU. Such a SDP offer has no active audio/video streams, but invites the remote (Firefox) to send audio/video by including two m= sections with a=recvonly: --------------------------- v=0 o=mediasoup 5961177484430050 1 IN IP4 0.0.0.0 s=- t=0 0 a=ice-lite a=fingerprint:sha-256 9E:74:0A:8B:DF:4F:AB:23:70:1A:C3:6E:D0:2E:3A:D2:83:97:2E:3D:28:0E:64:98:29:9C:4E:79:F8:FB:F3:9A a=msid-semantic: WMS * a=group:BUNDLE recv-audio-track-1 recv-video-track-1 m=audio 7 RTP/SAVPF 100 c=IN IP4 127.0.0.1 a=rtpmap:100 opus/48000/2 a=fmtp:100 maxplaybackrate=48000;stereo=1;useinbandfec=1 a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level a=setup:actpass a=mid:recv-audio-track-1 a=recvonly a=ice-ufrag:p7lkty7yq69zmtjw a=ice-pwd:i86xavcbr3qoneolb9v2ngh8s4r1o7hm a=candidate:udpcandidate 1 udp 1078862079 188.8.131.52 28569 typ host a=candidate:tcpcandidate 1 tcp 1078862079 184.108.40.206 27939 typ host tcptype passive a=end-of-candidates a=rtcp-mux a=rtcp-rsize m=video 7 RTP/SAVPF 123 c=IN IP4 127.0.0.1 a=rtpmap:123 VP8/90000 a=fmtp:123 max-fr=60;max-fs=12288 a=rtcp-fb:123 nack a=rtcp-fb:123 nack pli a=rtcp-fb:123 ccm fir a=rtcp-fb:123 goog-remb a=setup:actpass a=mid:recv-video-track-1 a=recvonly a=ice-ufrag:p7lkty7yq69zmtjw a=ice-pwd:i86xavcbr3qoneolb9v2ngh8s4r1o7hm a=candidate:udpcandidate 1 udp 1078862079 220.127.116.11 28569 typ host a=candidate:tcpcandidate 1 tcp 1078862079 18.104.22.168 27939 typ host tcptype passive a=end-of-candidates a=rtcp-mux a=rtcp-rsize --------------------------- 2) Firefox does NOT attach a local MediaStream into its RTCPeerConnection, calls pc.setRemoteDescription(), then pc.createAnswer() and then pc.setLocalDescription(). This is the SDP answer it produces: --------------------------- v=0 o=mozilla...THIS_IS_SDPARTA-53.0 5803778436392814460 1 IN IP4 0.0.0.0 s=- t=0 0 a=fingerprint:sha-256 94:35:42:EB:CD:3E:CF:CB:44:3D:A4:CB:89:99:C3:3E:0A:83:4B:B2:EB:23:BD:6A:F4:C9:0B:BE:42:A3:E3:53 a=ice-options:trickle a=msid-semantic:WMS * m=audio 0 RTP/SAVPF 0 c=IN IP4 0.0.0.0 a=inactive a=rtpmap:0 PCMU/8000 m=video 0 RTP/SAVPF 120 c=IN IP4 0.0.0.0 a=inactive a=rtpmap:120 VP8/90000 --------------------------- As you can see, Firefox does not include ICE/DTLS information in its answer (missing a=ice-ufrag, a=setup, etc) so no transport can be created. But, at the same time, Firefox complains in the console: "ICE failed, add a STUN server and see about:webrtc for more details" Well, Firefox did not help much to avoid that... My expectation is that the (BUNDLEd) transport and audio/video streams should be much more "decoupled", so a ICE+DTLS transport should be negotiated and established even if there are no active inbound/outbound audio/video streams.
This is much worse than what I expected above. In fact, the above reported issue may be invalid (why to negotiate and establish a ICE+DTLS transport if there are not active inbound or outbound streams?). However, things are worse due to the following real flow: 1) Firefox receives an initial offer with with two m=video sections: - One with a=inactive and ICE+DTLS stuff. - Another one with a=sendonly and ICE+DTLS stuff (an active inbound remote video stream here). 2) Firefox did not attach any local MediaStream to the peerconnection, so creates the SDP answer (which obviously has a m=video with a=inactive and a m=video with a=recvonly). Firefox properly establishes the ICE+DTLS transport and receives the remote video stream over the second m=video section. OK. 3) Later, Firefox adds a local video track to its peerconnection. Then Firefox receives a SDP re-offer from the remote (which is the same as the previous one). 4) Firefox calls pc.setRemoteDescription(), pc.createAsnwer() and pc.setLocalDescription(). The resulting SDP answer has (of course) two m=video sections: - One with a=sendonly and ICE+DTLS stuff (for the outbound video). - Another one with a=sendonly and ICE+DTLS stuff (the active inbound remote video stream). The problem here is that, after 4), Firefox starts a NEW ICE+DTLS connection (without even sending a DTLS Close message within the previous one). It shouldn't. BUNDLE was announced everywhere in the SDPs. And since Firefox was DTLS active, it starts a new DTLS handshake which is ignored by the remote (note that the remote peer did NOT change its ICE/DTLS/candidates stuff at all in its latest SDP re-offer). So the remote has NO way to realize that Firefox wishes to establish a new DTLS connection. Again, IMHO it shouldn't attempt to establish a new DTLS transport at all. So, what I see is that Firefox attempts to establish a new ICE+DTLS transport at any time its local peerconnection was no local tracks and some track was added. It does it even if there was an already established ICE+DTLS transport for which Firefox was receiving inbound streams from the remote peer. Again, worst thing here is that there is no way for the remote to know that Firefox is attempting to establish a new transport. The remote changed nothing regarding its local ICE/DTLS/candidates.
Summary: Negotiate ICE+DTLS transport even if there are no local/remote streams in the SPD O/A → Firefox attempts to establish a new ICE+DTLS transport when moving from 0 local tracks to N
NOTE: Yes, in the re-answer after adding any local track to the PeerConnection, Firefox does announce different ICE candidates (different ports, etc). Still I don't think it should do that at all, even less when it was *already* receiving an inbound stream over a established transport.
I've tested by adding a fake datachannel a=application section into the SDP offer received by Firefox. No remote/local audio tracks at all. In this case, Firefox DOES establish a ICE+DTLS transport (due the presence of the bundled datachannel m= section). However, if later I add a local audio/video track to the peerconnection and renegotiate, Firefox still restarts ICE. Of course the datachannel itself was never connected (the remote peer does not implement it). Well, to summarize, the important thing is this: 1) Firefox receives a SDP with a m=video line with a=recvonly, and also a m=video line with a=sendonly (carrying an active remote video stream). Both m= sections are BUNDLEd. 2) Firefox does not attach any local MediaStreamTrack to its peerconnection. 3) Firefox generates an answer with a m=video line with a=inactive and port 0 (makes sense), and a m=video line with a=recvonly (makes sense). 4) Firefox establishes a ICE+DTLS transport with the remote and receives the remote video. Then... 5) Firefox calls pc.addTrack(videoTrack). 6) Firefox creates a re-offer having a m=video line with a=sendonly (to send its local video), and a m=video with a=recvonly (to receive the remote video). Fine. But... 7) Firefox restarts ICE. Well, no reason for that. It seems that Firefox restarts ICE as long as it did not have any local audio/video track into its peerconnection and then adds one and renegotiates. That's the problem.
> It seems that Firefox restarts ICE as long as it did not have any local audio/video track into its peerconnection and then adds one and renegotiates. That's the problem. And also when it had local tracks, removes all of them, and renegotiates. And it restarts ICE even if the remote SDP still contains the same previous remote active audio/video streams.
So this is mostly a problem caused by Firefox being too eager to disable m-sections, which will be fixed in the transceiver work.
Just to clarify: port 0 in an m-line means the m-line got rejected even in bundle. So with two rejected m-lines (as in comment #1) there is never going to be any transport established. Plus some of the scenarios sound like the bundle master (tag) got moved, which is a nasty, not well documented corner case of bundle. But yeah the case where adding a track causes an ICE restart although we have an established transport sounds like a bug. But as Byron said: right now I think the best option is to wait for bug 1290948 and check (and fix as needed) after that again.
See Also: → 1290948
Nice to know that there is ongoing work that would also fix this. > Just to clarify: port 0 in an m-line means the m-line got rejected even in bundle. So with two rejected m-lines (as in comment #1) there is never going to be any transport established. Agreed, but take into account that when the session moves from N active m= lines to 0 active m= lines (with port 0): a) Firefox does not even send a DTLS "Close" message, so it's just impossible for the remote to realize that Firefox has "closed" the transport, specially if it's an ICE-Lite server. b) Firefox closes the transport even if it was a negotiated DataChannel (NOTE: the datachannel was not open, not sure if Firefox would have keep the transport open if so). Regarding a), we do need something. Just inspecting the SDP in order to figure out whether Firefox may have closed an established transport is so weak and error prune. A proper DTLS "Close" message is a good fit here.
(In reply to Iñaki Baz Castillo from comment #7) > Regarding a), we do need something. Just inspecting the SDP in order to > figure out whether Firefox may have closed an established transport is so > weak and error prune. A proper DTLS "Close" message is a good fit here. I'm not aware of any such message (AFAIK the TLS close_notify does not exist in DTLS). If you can point us what message you are referring to, we can certainly have a look (although I think you always will have to support the scenario of a dead transport without a close/bye notification, as with DTLS such a message could get lost).
> I'm not aware of any such message (AFAIK the TLS close_notify does not exist in DTLS) Oh, DTLS close alert does exist. In Chrome you can produce it by just calling peerconnection.close() (Firefox does not produce it however). In OpenSSL it's accomplished via SSL_shutdown(ssl). And you can "listen" for DTLS close alert using "SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN". > you always will have to support the scenario of a dead transport without a close/bye notification Well, I may accept that, if all the m= lines have port 0 after a renegotiation, the previous transport may have been "closed" by the remote.
(In reply to Iñaki Baz Castillo from comment #9) > > I'm not aware of any such message (AFAIK the TLS close_notify does not exist in DTLS) > > Oh, DTLS close alert does exist. In Chrome you can produce it by just > calling peerconnection.close() (Firefox does not produce it however). In > OpenSSL it's accomplished via SSL_shutdown(ssl). And you can "listen" for > DTLS close alert using "SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN". I just landed a patch for that problem over in bug 1303867. So that should be fixed soon in Firefox Nightly.
To re-confirm: So, if I have a PeerConnection with just an active sending track (no receiving tracks at all), and I call pc.removeTrack(rtpSender) and then pc.createOffer() + pc.setLocalDescription() + pc.setRemoteDescription(), then I assume that Firefox will close the DTLS and send a DTLS CLOSE, right? Since I want to leave the ICE+DTLS open for future usage, what about if I just call pc.remoteTrack(rtpSender) but I don't do SDP O/A stuff? AFAIS Firefox does not close the DTLS in this case.
Mass change P2->P3 to align with new Mozilla triage process.
Priority: P2 → P3
This should be fixed now, but probably needs a test-case.
Assignee: nobody → docfaraday
Byron, has this landed in Nightly? If so I can test it and confirm whether it works.
Yes Tranceivers are in Nightly. So you should be able to test it. Note: depending on which Nightly build you get you might see high CPU utilization. But that should get fixed in one of the next builds.
Sorry, I cannot even test. My FirefoxNightly just crashes as far as I open my WebRTC app. Firefox 57 does not.
Can we get a pointer to the crash report?
Yes. And it's easy to reproduce. Just enter https://demo.mediasoup.org/?roomId=foo with two tabs in Nightly, or one in Chrome and a second one in Nightly. Nightly crashes once it has to receive a remote MediaStream (even if such a remote stream has just an audio track). NOTE: Different crashes here: - https://crash-stats.mozilla.com/report/index/32a9d4ad-38e3-4656-9ace-4e4d50171224 - https://crash-stats.mozilla.com/report/index/91e793a2-66b5-4de5-9349-ad3500171224
Looking into it.
Are you still seeing this bug in 59/60?
I confirm that Firefox 60 (current Nightly) no longer destroys the single BUNDLEd transport when there are no active m-sections on it. It works fine now regardless the PeerConnection is just for sending media, or receiving media, or both. So good work :)
NOTE: The way I use it is by never setting port 0 into any m-line but, instead, just set a=inactive and keeping the a=mid value into the global "a=group:BUNDLE". So I don't know if Firefox would close the ICE+DTLS in case all the m-sections using it are closed by setting port 0 (here I just mean remote streams and how the remote SDP re-offers look).
(In reply to Iñaki Baz Castillo from comment #25) > NOTE: The way I use it is by never setting port 0 into any m-line but, > instead, just set a=inactive and keeping the a=mid value into the global > "a=group:BUNDLE". > > So I don't know if Firefox would close the ICE+DTLS in case all the > m-sections using it are closed by setting port 0 (here I just mean remote > streams and how the remote SDP re-offers look). If the bundle tag is rejected (port 0), then the transport will be closed. That transport cannot be disassociated from the bundle tag right now, although eventually we'll get around to using ICE ufrag as a transport identifier that can move around.
Status: NEW → RESOLVED
Closed: 3 years ago
Resolution: --- → DUPLICATE
Duplicate of bug: 1290948
You need to log in before you can comment on or make changes to this bug.