Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Webrtc Remote video stream not working

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.

like image 547
Varun Avatar asked Oct 19 '22 15:10

Varun


1 Answers

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.

like image 166
jib Avatar answered Oct 21 '22 04:10

jib