2

I am working on a video call app using webRTC and socket.io. I had a look at other projects like this and am trying to implement those my own.

The first peer creates offer and the other peer gets the offer and saves it to its remote decription and creates answer which is recieved by the first peer. But the ice candidates are not exchanged and also the stream. Here is the client-side code:

var socket = io();
var peerConn,
  onlineUsers = [],
  username,
  caller;

function createOffer(callee) {
  peerConn = new RTCPeerConnection();
  peerConn.onicecandidate = onIce;
  peerConn.onaddstream = onAddStream;
  navigator.mediaDevices
    .getUserMedia({ audio: true, video: { height: 240, width: 320 } })
    .then(stream => (window.myVideo.srcObject = stream))
    .then(stream => peerConn.addStream(stream));
  peerConn.createOffer().then(offer => {
    peerConn.setLocalDescription(offer);
    socket.emit("call", callee, peerConn.localDescription);
  });
  caller = callee;
}

socket.on("call", (callee, caller, sdp) => {
  if (callee == username) createAnswer(sdp, caller);
});
function createAnswer(sdp, caller) {
  peerConn = new RTCPeerConnection();
  peerConn.onicecandidate = onIce;
  peerConn.onaddstream = onAddStream;
  window.caller = caller;
  navigator.mediaDevices
    .getUserMedia({ audio: true, video: { height: 240, width: 320 } })
    .then(stream => (window.myVideo.srcObject = stream))
    .then(stream => peerConn.addStream(stream));
  peerConn.setRemoteDescription(new RTCSessionDescription(sdp));
  peerConn.createAnswer().then(answer => {
    peerConn.setLocalDescription(answer);
    socket.emit("answer", caller, peerConn.localDescription);
  });
  console.log(peerConn.localDescription);
  console.log(peerConn.remoteDescription);
}

function onIce(event) {
  if (event.candidate) {
    socket.emit("ice", caller, event.candidate);
    console.log("sent ice");
  } else {
    console.log("Sent all ice");
  }
}

function onAddStream(event) {
  console.log("remote stream added");
  frndsVideo.srcObject = event.stream;
}

socket.on("answer", (callee, caller, sdp) => {
  if (caller == username) setRemoteDes(sdp);
});
function setRemoteDes(sdp) {
  peerConn.setRemoteDescription(sdp);
  console.log(peerConn.localDescription);
  console.log(peerConn.remoteDescription);
}

function addIce(caller, callee, ice) {
  peerConn.addIceCandidate(new RTCIceCandidate(ice));
  console.log("ice added");
}

socket.on("ice", addIce);

And the server-side js code:

socket.on("call", (callee, sdp) => {
    console.log(`${socket.username} calling ${callee}  ${sdp}`);
    onlineUsers.forEach((user, i) => {
      if (user.username == callee || user.username == socket.username) {
        onlineUsers[i].inCall = true;
      }
    });
    socket.broadcast.emit(`call`, callee, socket.username, sdp);
  });
  socket.on("answer", (caller, sdp) => {
    console.log(`${socket.username} answered ${caller}  ${sdp}`);
    socket.broadcast.emit("answer", socket.username, caller, sdp);
  });
  socket.on("ice", (caller, ice) => {
    socket.broadcast.emit("ice", socket.username, caller, ice);
    console.log("ice recived and sent");
  });

Please help me with this.

Shivam Kumar
  • 120
  • 11

1 Answers1

3

Run it in Firefox, and you'll see this error:

InvalidStateError: Cannot create offer when there are no valid transceivers.

You're calling createOffer before addStream, which produces an empty offer and no candidates.

You're also using peerConn.localDescription before setLocalDescription has finished.

These are asynchronous methods that need to run in sequence, not in parallel. Try chaining them:

function createOffer(callee) {
  peerConn = new RTCPeerConnection();
  peerConn.onicecandidate = onIce;
  peerConn.onaddstream = onAddStream;
  navigator.mediaDevices
    .getUserMedia({audio: true, video: {height: 240, width: 320}})
    .then(stream => {
      peerConn.addStream(myVideo.srcObject = stream);
      return peerConn.createOffer();
    })
    .then(offer => peerConn.setLocalDescription(offer))
    .then(() => socket.emit("call", callee, peerConn.localDescription))
    .catch(e => console.log(e));
  caller = callee;
}

Furthermore, on the answering side, never delay calling setRemoteDescription or you risk missing candidates. This is time sensitive, as I explain in another answer.

function createAnswer(sdp, caller) {
  peerConn = new RTCPeerConnection();
  peerConn.onicecandidate = onIce;
  peerConn.onaddstream = onAddStream;
  window.caller = caller;
  peerConn.setRemoteDescription(sdp)
  .then(() => navigator.mediaDevices
    .getUserMedia({audio: true, video: {height: 240, width: 320}}))
  .then(stream => {
    peerConn.addStream(myVideo.srcObject = stream);
    return peerConn.createAnswer();
  })
  .then(answer => peerConn.setLocalDescription(answer))
  .then(() => socket.emit("answer", caller, peerConn.localDescription))
  .catch(e => console.log(e));
}
jib
  • 40,579
  • 17
  • 100
  • 158
  • Also see my comments on `addStream` and `onaddstream` in this [other answer](https://stackoverflow.com/a/53269679/918910). – jib Nov 13 '18 at 05:16
  • would you please help me to sort out my problem on https://stackoverflow.com/questions/63862778/webrtc-video-streaming-is-working-in-firefox-but-not-in-chrome I will be very much delighted if you help me – Rakibul Islam Prince Sep 13 '20 at 18:21