0

Currently using Chrome 70, Firefox 64 and Safari 12.

The remote video from the other user is not getting displayed on both sides and I am not quite sure what could be the issue.

There is no errors coming from any of the browsers which does not help in debugging the code.

I am using chrome's internal WebRTC debugging tool (chrome://webrtc-internals) and there is zero packets that have been sent or received.

There's a parameter in the tool which is googCandidatePair but this does not show up at all during a call. enter image description here

ICEgatheringstatechange event triggers and state that it has completed but only when the host is the chrome user.

I have also tried using

pc.oniceconnectionstatechange = () => console.log(pc.iceConnectionState);

to check for the ICE state changes but this does not trigger at all.

One reason I think it might not be working correctly could be due to how RTCPeerconnection was configured as from this picture, the Ice candidate pool size is 0 but it was never stated in the code itself. enter image description here

Below are 2 pictures where the first one is when the host is chrome and the other being the receiver enter image description here enter image description here

The code is as follows :

'use strict';

var configuration = {
  iceServers: [
    {
      urls: 'stun:stun.l.google.com:19302'
    }
  ]
};
var pc = new RTCPeerConnection(configuration);

// Define action buttons.
const callButton = document.getElementById('callButton');
const hangupButton = document.getElementById('hangupButton');

/////////////////////////////////////////////

window.room = prompt('Enter room name:');

var socket = io.connect();

if (room !== '') {
  console.log('Message from client: Asking to join room ' + room);
  socket.emit('create or join', room);
}

socket.on('created', function(room) {
  console.log('Created room ' + room);
  startVideo();
});

socket.on('full', function(room) {
  console.log('Message from client: Room ' + room + ' is full :^(');
});

socket.on('joined', function(room) {
  console.log('joined: ' + room);
  startVideo();
  callButton.disabled = true;
});

socket.on('log', function(array) {
  console.log.apply(console, array);
});

////////////////////////////////////////////////

async function sendMessage(message) {
  console.log('Client sending message: ', message);
  await socket.emit('message', message);
}

// This client receives a message
socket.on('message', message => {
  if (message.sdp) {
    pc.setRemoteDescription(new RTCSessionDescription(message.sdp))
      .then(function() {
        if (pc.setRemoteDescription.type === 'offer') {
          pc.setLocalDescription(pc.createAnswer())
            .then(function() {
              sendMessage({ sdp: pc.localDescription });
            })
            .catch(function(err) {
              console.log(err.name + ': ' + err.message);
            });
        }
      })
      .catch(error => console.error(error));
  } else if (message.candidate) {
    pc.addIceCandidate(new RTCIceCandidate(message.candidate))
      .then(() => {
        console.log('Candidates received');
      })
      .catch(error => console.error(error));
  }
});

pc.onicecandidate = event => {
  if (event.candidate) {
    sendMessage({ candidate: event.candidate });
  }
};

pc.ontrack = event => {
  if (remoteVideo.srcObject !== event.streams[0]) {
    remoteVideo.srcObject = event.streams[0];
    console.log('Got remote stream');
  }
};

////////////////////////////////////////////////////

const localVideo = document.querySelector('#localVideo');
const remoteVideo = document.querySelector('#remoteVideo');

// Set up initial action buttons status: disable call and hangup.
callButton.disabled = true;
hangupButton.disabled = true;

// Add click event handlers for buttons.
callButton.addEventListener('click', callStart);
hangupButton.addEventListener('click', hangupCall);

function startVideo() {
  navigator.mediaDevices
    .getUserMedia({
      audio: true,
      video: true
    })
    .then(function(stream) {
      localVideo.srcObject = stream;
      stream.getTracks().forEach(track => pc.addTrack(track, stream));
    })
    .catch(function(err) {
      console.log('getUserMedia() error: ' + err.name);
    });
  callButton.disabled = false;
}

async function callStart() {
  callButton.disabled = true;
  hangupButton.disabled = false;

  console.log('Sending offer to peer');
  await pc
    .setLocalDescription(await pc.createOffer())
    .then(() => {
      sendMessage({ sdp: pc.localDescription });
    })
    .catch(err => {
      console.log(err.name + ': ' + err.message);
    });
}

/////////////////////////////////////////////////////////

function hangupCall() {
  pc.close();
  pc = null;
  callButton.disabled = false;
  hangupButton.disabled = true;
  console.log('Call Ended');
}
tngrj
  • 141
  • 3
  • 14

2 Answers2

2

You're mixing your promise styles, and you have a bug here:

          pc.setLocalDescription(pc.createAnswer()) // bug!
            .then(function() {

The above sets the local description to a promise object. Either pick async/await throughout:

          await pc.setLocalDescription(await pc.createAnswer());

...or .then() chains:

          pc.createAnswer()
            .then(answer => pc.setLocalDescription(answer))
            .then(function() {

If you pick the latter, don't forget to return all the promises.

Here's the message handler done solely with async/await:

// This client receives a message
socket.on('message', async message => {
  try {
    if (message.sdp) {
      await pc.setRemoteDescription(message.sdp);
      if (pc.setRemoteDescription.type === 'offer') {
        await pc.setLocalDescription(await pc.createAnswer());
        sendMessage({sdp: pc.localDescription});
      }
    } else if (message.candidate) {
      await pc.addIceCandidate(message.candidate);
      console.log('Candidates received');
    }
  } catch (err) {
    console.log(err.name + ': ' + err.message);
  }
}
jib
  • 40,579
  • 17
  • 100
  • 158
  • I did tried to use async but it was throwing errors like pc was unidentifiable or something which was why I remove async await totally from the receiving message function – tngrj Jan 13 '19 at 02:28
  • @RjTng Then use the other way. `await` is only allowed inside `async` functions. You'll want to refactor to use it. Read up [here](https://developers.google.com/web/fundamentals/primers/async-functions). – jib Jan 13 '19 at 02:36
  • I found out that the function inside the 2nd IF statement was not using async and await which was why I could not use it before but even as I have changed the syntax, it still acts the same which now leads me to think that the .then() is not passing the info. https://pastebin.com/uVfQYwaG. Going to try and pull the whole .then chain out and vscode debug tool when I wake up – tngrj Jan 13 '19 at 16:56
0

If anyone might have problems with the remote video not showing up, I found out that it was because the message was not through the second IF statement that was checking if message.type === offer and since it could not create the answer thus it could not send its local description over to the other user. But by splitting the message up at the start to sdp and candidate, it somehow works.

socket.on('message', async ({ sdp, candidate }) => {
  if (sdp) {
    await pc.setRemoteDescription(new RTCSessionDescription(sdp));
    if (sdp.type === 'offer') {
      await pc
        .setLocalDescription(await pc.createAnswer())
        .then(function() {
          sendMessage({ sdp: pc.localDescription });
        })
        .catch(function(err) {
          console.log(err.name + ': ' + err.message);
        });
    }
  } else if (candidate) {
    await pc
      .addIceCandidate(new RTCIceCandidate(candidate))
      .then(() => {
        console.log('Candidates received');
      })
      .catch(error => console.error(error));
  }
});
tngrj
  • 141
  • 3
  • 14