Failed to set local answer sdp: Called in wrong state: kStable

后端 未结 2 1698
旧巷少年郎
旧巷少年郎 2020-12-16 11:57

for a couple of days I\'m now stuck with trying to get my webRTC client to work and I can\'t figure out what I\'m doing wrong. I\'m trying to create multi peer webrtc client

相关标签:
2条回答
  • 2020-12-16 12:49

    I have been using a similar construct to build up the WebRTC connections between sender and receiver peers, by calling the method RTCPeerConnection.addTrack twice (one for the audio track, and one for the video track).

    I used the same structure as shown in the Stage 2 example shown in The evolution of WebRTC 1.0:

    let pc1 = new RTCPeerConnection(), pc2 = new RTCPeerConnection(), stream, videoTrack, videoSender;
    
    (async () => {
      try {
        stream = await navigator.mediaDevices.getUserMedia({video: true, audio: true});
        videoTrack = stream.getVideoTracks()[0];
        pc1.addTrack(stream.getAudioTracks()[0], stream);
      } catch (e) {
        console.log(e);  
      }
    })();
    
    checkbox.onclick = () => {
      if (checkbox.checked) {
        videoSender = pc1.addTrack(videoTrack, stream);
      } else {
        pc1.removeTrack(videoSender);
      }
    }
    
    pc2.ontrack = e => {
      video.srcObject = e.streams[0];
      e.track.onended = e => video.srcObject = video.srcObject; // Chrome/Firefox bug
    }
    
    pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
    pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);
    pc1.onnegotiationneeded = async e => {
      try {
        await pc1.setLocalDescription(await pc1.createOffer());
        await pc2.setRemoteDescription(pc1.localDescription);
        await pc2.setLocalDescription(await pc2.createAnswer());
        await pc1.setRemoteDescription(pc2.localDescription);
      } catch (e) {
        console.log(e);  
      }
    }
    

    Test it here: https://jsfiddle.net/q8Lw39fd/

    As you'll notice, in this example the method createOffer is never called directly; instead, it is indirectly called via addTrack triggering an RTCPeerConnection.onnegotiationneeded event.

    However, just as in your case, Chrome triggers this event twice, once for each track, and this causes the error message you mentioned:

    DOMException: Failed to set local answer sdp: Called in wrong state: kStable

    This doesn't happen in Firefox, by the way: it triggers the event only once.

    The solution to this issue is to write a workaround for the Chrome behavior: a guard that prevents nested calls to the (re)negotiation mechanism.

    The relevant part of the fixed example would be like this:

    pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
    pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);
    
    var isNegotiating = false;  // Workaround for Chrome: skip nested negotiations
    pc1.onnegotiationneeded = async e => {
      if (isNegotiating) {
        console.log("SKIP nested negotiations");
        return;
      }
      isNegotiating = true;
      try {
        await pc1.setLocalDescription(await pc1.createOffer());
        await pc2.setRemoteDescription(pc1.localDescription);
        await pc2.setLocalDescription(await pc2.createAnswer());
        await pc1.setRemoteDescription(pc2.localDescription);
      } catch (e) {
        console.log(e);  
      }
    }
    
    pc1.onsignalingstatechange = (e) => {  // Workaround for Chrome: skip nested negotiations
      isNegotiating = (pc1.signalingState != "stable");
    }
    

    Test it here: https://jsfiddle.net/q8Lw39fd/8/

    You should be able to easily implement this guard mechanism into your own code.

    0 讨论(0)
  • 2020-12-16 12:54

    You're sending the answer here:

    .then( function (answer) { return self.peerConns[peerId].setLocalDescription(answer); } )
    

    Look at mine:

         var callback = function (answer) {
             createdDescription(answer, fromId);
         };
         peerConnection[fromId].createAnswer().then(callback).catch(errorHandler);
    
    
        function createdDescription(description, fromId) {
            console.log('Got description');
    
            peerConnection[fromId].setLocalDescription(description).then(function() {
                console.log("Sending SDP:", fromId, description);
                serverConnection.emit('signal', fromId, {'sdp': description});
            }).catch(errorHandler);
        }
    
    0 讨论(0)
提交回复
热议问题