Adding a transceiver for sending audio, then stopping it, then adding a new transceiver for sending video. During the second SDP O/A, pc.setLocalDescription() fails with "OperationError: Failed to set local offer sdp: The m= section:1 should be rejected." It's a clear bug when offer produced by pc.createOffer() is later rejected in pc.setLocalDescription(offer). Below the code to reproduce it. IMPORTANT: * Safari Tech Preview 12.1. * "WebRTC Unified-Plan" enabled in menu "Develop / Experimental Features". Just open the browser console and paste this code: ------------------------------------------------------------- let micTrack; let webcamTrack; let pc; let micTransceiver; let webcamTransceiver; navigator.mediaDevices.getUserMedia({ audio: true, video: true }) .then((stream) => { micTrack = stream.getAudioTracks()[0]; webcamTrack = stream.getVideoTracks()[0]; }) .then(() => { pc = new RTCPeerConnection( { iceServers : [], bundlePolicy : 'max-bundle', rtcpMuxPolicy : 'require', sdpSemantics : 'unified-plan' }); console.warn('pc: adding mic track'); micTransceiver = pc.addTransceiver(micTrack, { direction: 'sendonly' }); }) .then(() => doSdpStuff()) .then(() => { console.warn('pc: removing mic track'); micTrack.stop(); pc.removeTrack(micTransceiver.sender); // NOTE: Chrome does not implement transceive.stop(). try { micTransceiver.stop(); } catch(error) {} // NOTE: Safari 12.1 does not allow setting transceiver.direction. try { micTransceiver.direction = 'inactive'; } catch(error) {} }) .then(() => { // Add the webcam track to pc. console.warn('pc: adding webcam'); webcamTransceiver = pc.addTransceiver(webcamTrack, { direction: 'sendonly' }); }) .then(() => doSdpStuff()) .catch((error) => { console.error(error); }); function doSdpStuff() { let errored = false; return Promise.resolve() .then(() => pc.createOffer()) .catch((error) => { console.error('pc.createOffer() failed'); errored = true; throw error; }) .then((offer) => { console.warn('pc.createOffer() succeeded:\n%s', offer.sdp); return pc.setLocalDescription(offer); }) .catch((error) => { if (errored) throw error; console.error('pc.setLocalDescription() failed'); throw error; }) } ------------------------------------------------------------- NOTE: This bug does not happen in Chrome.
If you need a way to reproduce it, I can deploy it and provide exact instructions.
Created attachment 353683 [details] reduced test
Created attachment 353684 [details] reduced test without max bundle
Thanks Inaki, I wrote a reduced test (in attachment). The problem seems to arise only with the max bundle option so you might have a workaround for now. Looking at the generated SDP, I would have thought the m section to be recycled. It seems to me the SDP should only have one m-section.
Created attachment 353686 [details] reduced test with UI
(In reply to youenn fablet from comment #5) > Created attachment 353686 [details] > reduced test with UI Tried the test with Firefox and it generates one m section, but with two a=mid lines.
(In reply to youenn fablet from comment #6) > (In reply to youenn fablet from comment #5) > > Created attachment 353686 [details] > > reduced test with UI > > Tried the test with Firefox and it generates one m section, but with two > a=mid lines. Looking more closely: Firefox generates two m sections, one zero-port/inactive, the other sendrecv. WebKit generates two m sections, one zero-port/recvonly, the other sendrecv
WebKit is generating "a=group:BUNDLE 0 1" but 0 is zero ported and 1 is not.
Hi, I've changed my mind and I no longer call transceiver.stop() nor produce "remote" SDP answers with port 0 in any m= section. It's painful as it involves transport closure if there is no other active stream (DTLS close alert is sent by the browser). It's much easier by just removing the sending track with pc.receiveTrack(sender).
Unfortunately calling sender.replaceTrack(null) does not set "a=inactive". Instead it remains "a=sendonly" even if I set transceiver.direction = "inactive" once the replaceTrack(null) promise resolves. But this is wrose, as by using replaceTrack(null) and later replaceTrack(newTrack), a *NEW* transceiver / m= section is created! Attaching a new script.
Created attachment 353844 [details] Test script 2 with sender.replaceTrack(null) Same script as before but instead of pc.removeTrack(sender) it uses sender.replaceTrack(null).then(() => transceiver.direction = "inactive"): * Once replaceTrack(null) resolves the script changes the transceiver direction to "inactive" and generates an offer. The offer has a=sendonly, so this is not a valid solution yet. * And much worse: If after that I call sameSender.replaceTrack(newTrack), it generates a new audio transceiver with its corresponding new m=audio section, both with "a=sendonly". This is an important bug somewhere IMHO.
The second script "Test script 2 with sender.replaceTrack(null)" in Chrome and Firefox works fine. NOTE: In Safari, changing transceiver.direction produces an exception (something like "cannot change readonly property") if I do it before the replaceTrack(null) promise resolves. This does not happen in Chrome or Firefox and the spec says nothing about when transceiver.direction can be changed. It's supposed to be changed at any time. It just happens that its effective value (pc.currentDirection) takes an effective value later, may be after a proper SDP O/A.
I'm sorry, I've written here comments related to issue #191202... My fault. Feel free to delete them. I'll write them in the corresponding issue.
Hi, any news about this issue?
(In reply to Iñaki Baz from comment #14) > Hi, any news about this issue? We are working with the libwebrtc team on a fix. See https://bugs.chromium.org/p/webrtc/issues/detail?id=9954
Youenn: It appears that libwebrtc has now fixed the issue. Can you confirm we have the correct fix (your test case appears to PASS).
Fixed upstream