5
votes

While setting the remoteDescription , I am getting below error in firefox :

DOMException [InvalidStateError: "Cannot set remote offer in state have-local-offer" code: 11 nsresult: 0x8053000b location: http://localhost:8080/resources/assets/js/test-online.js:111]

Please find below my test-online.js code

var localVideo;
var remoteVideo;
var peerConnection;
var serverConnection;
var peerConnectionConfig = {'iceServers': [{'url': 'stun:stun.services.mozilla.com'}, {'url': 'stun:stun.l.google.com:19302'}]};
pageReady();
var offerOptions = {
          offerToReceiveAudio: 1,
          offerToReceiveVideo: 1
        };

var sdpConstraints = {'mandatory': {
      'OfferToReceiveAudio':true,
      'OfferToReceiveVideo':true }};

function pageReady() {
    localVideo = document.getElementById('localVideo');
    remoteVideo = document.getElementById('remoteVideo');

    localVideo.addEventListener('loadedmetadata', function() {
          trace('Local video videoWidth: ' + this.videoWidth +
            'px,  videoHeight: ' + this.videoHeight + 'px');
    });

    remoteVideo.addEventListener('loadedmetadata', function() {
          trace('Remote video videoWidth: ' + this.videoWidth +
            'px,  videoHeight: ' + this.videoHeight + 'px');
    });

    remoteVideo.onresize = function() {
          trace('Remote video size changed to ' +
            remoteVideo.videoWidth + 'x' + remoteVideo.videoHeight);
          // We'll use the first onsize callback as an indication that video has started
          // playing out.
          if (startTime) {
            var elapsedTime = window.performance.now() - startTime;
            trace('Setup time: ' + elapsedTime.toFixed(3) + 'ms');
            startTime = null;
          }
    };

    serverConnection = new SockJS("/onlineHandler");

    serverConnection.onopen = function() {
        console.log("Opening server connection");
     };
     serverConnection.onmessage = gotMessageFromServer;
     serverConnection.onclose = function() {
         console.log("Closing server connection");
     };
   //serverConnection.onmessage = gotMessageFromServer;

    var constraints = {
        video: true,
        audio: true,
    };

    navigator.mediaDevices.getUserMedia(constraints)
      .then(getUserMediaSuccess)
      .catch(function(e) {
        alert('getUserMedia() error: ' + e.name);
      });
}

function getUserMediaSuccess(stream) {
      trace('Received local stream');
      localVideo.srcObject = stream;
      localStream = stream;
}

function start(isCaller) {
    trace('Starting call');
      startTime = window.performance.now();
      var videoTracks = localStream.getVideoTracks();
      var audioTracks = localStream.getAudioTracks();
      if (videoTracks.length > 0) {
        trace('Using video device: ' + videoTracks[0].label);
      }
      if (audioTracks.length > 0) {
        trace('Using audio device: ' + audioTracks[0].label);
      }

    peerConnection = new RTCPeerConnection(peerConnectionConfig);
    peerConnection.onicecandidate = gotIceCandidate;
    peerConnection.oniceconnectionstatechange = onIceStateChange;
    peerConnection.onaddStream = gotRemoteStream;
    peerConnection.addStream(localStream);

    if(isCaller) {
        peerConnection.createOffer(gotDescription, errorHandler , offerOptions);
    }
}

function gotMessageFromServer(message) {
   /* if(!peerConnection) start(false);

    var signal = JSON.parse(message.data);
//    console.log("Got Message from server :" + message.data);
    if(signal.sdp) {;
        console.log("hi in sdp" + message.data);
        peerConnection.setRemoteDescription(new RTCSessionDescription(signal.sdp), function() {
            console.log("Creating answer :");
        if (peerConnection.remoteDescription.type == 'offer')
            peerConnection.createAnswer(gotDescription, errorHandler);
        }, errorHandler);
    } else if(signal.ice) {
        peerConnection.addIceCandidate(new RTCIceCandidate(signal.ice));
    }*/
    var signal = JSON.parse(message.data);
    if (signal.type === 'offer') {
        peerConnection.setRemoteDescription(new RTCSessionDescription(signal),doAnswer,errorHandler);
      } else if (signal.type === 'answer') {
          peerConnection.setRemoteDescription(new RTCSessionDescription(signal),doNothing, errorHandler);
      } else if (signal.type === 'candidate') {
        var candidate = new RTCIceCandidate({
          sdpMLineIndex:signal.label,
          candidate: signal.candidate
        });
        peerConnection.addIceCandidate(candidate);
      } else if (signal === 'bye' && isStarted) {
        handleRemoteHangup();
      }
}
function doNothing(){

}

function doAnswer() {
      console.log('Sending answer to peer.');
      peerConnection.createAnswer(gotDescription, errorHandler, sdpConstraints);
    }

function handleRemoteHangup() {
//  console.log('Session terminated.');
  // stop();
  // isInitiator = false;
}

function gotIceCandidate(event) {
    if(event.candidate != null) {

        var message ={
                  type: 'candidate',
                  label: event.candidate.sdpMLineIndex,
                  id: event.candidate.sdpMid,
                  candidate: event.candidate.candidate};

       // serverConnection.send(JSON.stringify({'ice': event.candidate}));
         serverConnection.send(JSON.stringify(message));
    }
}

function onIceStateChange(event) {
      if (peerConnection) {
        trace(' ICE state: ' + peerConnection.iceConnectionState);
        console.log('ICE state change event: ', event);
      }
    }

function gotDescription(description) {
   // trace('Offer from peerConnection\n' + description.sdp);
      description.sdp = preferOpus(description.sdp);
     // pc.setLocalDescription(description);
      console.log('setLocalAndSendMessage sending message' , description);
   // trace('peerConnection setLocalDescription start');
    peerConnection.setLocalDescription(
            description, 
            function () {
                serverConnection.send(JSON.stringify(description));
            }, 
            onSetSessionDescriptionError
    );
}

function preferOpus(sdp) {
      var sdpLines = sdp.split('\r\n');
      var mLineIndex;
      // Search for m line.
      for (var i = 0; i < sdpLines.length; i++) {
          if (sdpLines[i].search('m=audio') !== -1) {
            mLineIndex = i;
            break;
          }
      }
      if (mLineIndex === null) {
        return sdp;
      }

      // If Opus is available, set it as the default in m line.
      for (i = 0; i < sdpLines.length; i++) {
        if (sdpLines[i].search('opus/48000') !== -1) {
          var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
          if (opusPayload) {
            sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex], opusPayload);
          }
          break;
        }
      }

      // Remove CN in m line and sdp.
      sdpLines = removeCN(sdpLines, mLineIndex);

      sdp = sdpLines.join('\r\n');
      return sdp;
    }

    function extractSdp(sdpLine, pattern) {
      var result = sdpLine.match(pattern);
      return result && result.length === 2 ? result[1] : null;
    }

    // Set the selected codec to the first in m line.
    function setDefaultCodec(mLine, payload) {
      var elements = mLine.split(' ');
      var newLine = [];
      var index = 0;
      for (var i = 0; i < elements.length; i++) {
        if (index === 3) { // Format of media starts from the fourth.
          newLine[index++] = payload; // Put target payload to the first.
        }
        if (elements[i] !== payload) {
          newLine[index++] = elements[i];
        }
      }
      return newLine.join(' ');
    }

    // Strip CN from sdp before CN constraints is ready.
    function removeCN(sdpLines, mLineIndex) {
      var mLineElements = sdpLines[mLineIndex].split(' ');
      // Scan from end for the convenience of removing an item.
      for (var i = sdpLines.length-1; i >= 0; i--) {
        var payload = extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i);
        if (payload) {
          var cnPos = mLineElements.indexOf(payload);
          if (cnPos !== -1) {
            // Remove CN payload from m line.
            mLineElements.splice(cnPos, 1);
          }
          // Remove CN line in sdp
          sdpLines.splice(i, 1);
        }
      }

      sdpLines[mLineIndex] = mLineElements.join(' ');
      return sdpLines;
    }


function onSetSessionDescriptionError(error) {
  trace('Failed to set session description: ' + error.toString());
}

function gotRemoteStream(event) {
     remoteVideo.srcObject = event.stream;
     trace('Received remote stream');
}

function errorHandler(error) {
    console.log(error);
}

And my html code is below :

    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
    <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<html lang="en">
<head>
<link
    href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css"
    rel="stylesheet">
    <!-- Meta tag to not followed by search engine. -->
    <meta name="robots" content="noindex,nofollow,nosnippet,noodp,noarchive">
    <meta name="keywords" content="JavaScript, WebRTC" />
    <meta name="description" content="WebRTC codelab" />
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1">

    <style>
        video {
            filter: hue-rotate(180deg) saturate(200%);
            -moz-filter:  hue-rotate(180deg) saturate(200%);
            -webkit-filter: hue-rotate(180deg) saturate(200%); 
            /*-webkit-filter: grayscale(0.3) hue-rotate(360deg) saturate(10) opacity(0.7) sepia(0.5); */
        }
    </style>
</head>
<body>


    <div id='videos'>
                <video id='localVideo' autoplay muted></video>
                <video id='remoteVideo' autoplay></video>
            </div>

       <input type="button" id="start" onclick="start(true)" value="Start Video"></input>
    <script src="//cdn.jsdelivr.net/sockjs/1.0.0/sockjs.min.js"></script>
     <script
        src="${pageContext.request.contextPath}/resources/assets/js/jquery-2.1.1.min.js"></script>
     <script
        src="${pageContext.request.contextPath}/resources/assets/js/bootstrap.min.js"></script>
     <script src ="${pageContext.request.contextPath}/resources/assets/js/adapter-0.2.10.js"></script>
     <script src="${pageContext.request.contextPath}/resources/assets/js/test-online.js"></script>
</body>
</html>

I am not able to understand what I am doing wrong here.I am still a novice in webrtc field juts want to run this basic thing in my code.

1
Have you checked your browser version and type?Nalla Srinivas
@NallaSrinivas My firefox version is 46.0a2 (2016-02-12) and I am pretty sure that webrtc support starts way back in firefox and did you get chance to look into the above code and did you by change got something unusal with the code ?Varun
You've commented out the call to start(false), so I don't see how the receiver side creates a peer connection at all, so unless your signaling server is bouncing messages, I don't see how it gets far enough to produce that error. Also, which line is 111 exactly? The code sample seems a bit off when I count lines.jib
@jib Sorry for inconvenience, the line number 111 is Inside gotMessageFromServer function -> if (signal.type="offer") -> peerConnection.setRemoteDescription ...... line , where i try to set remoteDescription, then i got this error .Varun
@jib And for making peerConnection ,I have a start button which calls start(isCaller) function in javascript and creates the connection and then offer is sent to server and then server sent offer back to all the clients and we got offer message in gotMessageFromServer and there i am facing the problem.Varun

1 Answers

3
votes

The offer-answer exchange is a state machine, and some methods are disallowed in certain states.

Calling setLocalDescription (or setRemoteDescription) changes the signaling state to "have-local-offer" (or "have-remote-offer").

At that point, it is the application's job to bring the state back to stable or closed, as described in the spec.

For instance, it is the application's responsibility to handle glare (which is where both sides send an offer at the same time).

There is also a bug in Firefox that it doesn't allow you to call createOffer or setLocalDescription again once in have-local-offer and vice versa for the answer (the little hoops in the state diagram in the spec linked above). But it doesn't sound from your error message like you're hitting that.