Search code examples
javascriptnode.jswebrtcp2p

ontrack event not firing for caller


I'm trying to develop a vedio conferencing app but facing some problem with ontrack() track event of RTCpeerconnection.

In below code first user joins is set as userType=caller and other user is set as userType=receiver. Sesson Description Obejcts and ICe Candidates are passed correctly between them and peerConnection.iceConnectionState shows connected for both caller and receiver.

The problem is that ontrack get correctly fired on receiver end but at caller end it doesn't get fired. receiver is able to see both caller and himself. This is my entire code that i wrote for client side:

const socket = io();

const $localStream = document.querySelector("#local-stream");
const $remoteStream = document.querySelector("#remote-stream");

const config = {
  iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
};

let mediaStream,
  remoteStream = new MediaStream(),
  userType = null;
const peerConnection = new RTCPeerConnection(config);

$remoteStream.srcObject = remoteStream;

peerConnection.onicecandidate = (e) => {
  if (e.candidate) {
    socket.emit("send-icecandidate", { candidate: e.candidate });
  }
};

peerConnection.onconnectionstatechange = (e) => {
  if (peerConnection.connectionState === "connected") {
    console.log("connection established");
  }
};

peerConnection.ontrack = (e) => {
  console.log("Event " + e);
  remoteStream.addTrack(e.track, remoteStream);
};

const init = async function () {
  mediaStream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: {
      facingMode: { exact: "user" },
    },
  });
  mediaStream
    .getTracks()
    .forEach((track) => peerConnection.addTrack(track, mediaStream));
  $localStream.srcObject = mediaStream;
};

init();

socket.emit("start");

socket.on("start", (user) => {
  userType = user.userType;
  console.log(userType);
  if (userType === "receiver") {
    socket.emit("ready");
  }
});

socket.on("ready", () => {
  console.log("receiver is ready for connection establishment");
  makeCall();
});

const makeCall = async () => {
  const offer = await peerConnection.createOffer({
    offerToReceiveAudio: true,
    offerToReceiveVideo: true,
  });
  await peerConnection.setLocalDescription(offer);
  socket.emit("offer", { offer });
};

socket.on("offer", async ({ offer }) => {
  console.log("offer received", offer);
  await peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
  const answer = await peerConnection.createAnswer({
    offerToReceiveAudio: true,
    offerToReceiveVideo: true,
  });
  await peerConnection.setLocalDescription(answer);
  socket.emit("answer", { answer });
});

socket.on("answer", async ({ answer }) => {
  console.log("answer received", answer);
  await peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
});

socket.on("send-icecandidate", async ({ candidate }) => {
  console.log("ice candidate received", candidate);
  await peerConnection.addIceCandidate(candidate);
});

Solution

  • I found solution for my own question, init() was used to request for media permission which was marked as async function. As a result I was not properly handling promise and initiating connection before media permission request was accpected and stream was added to peerconnection object. caller who is the one initiating the connection establishment and first to join call get time to access media but as receiver when it comes to page directly goes for connection eastablishment and stream is not added to peerConnection object. Thus blocking ontrack event to fire at caller end.

    code changed from init(). Code pasted below if anyone gets same issue:

    init().then(()=>{ 
    socket.emit("start");
    
    socket.on("start", (user) => {
      userType = user.userType;
      console.log(userType);
      if (userType === "receiver") {
        socket.emit("ready");
      }
    });
    
    socket.on("ready", () => {
      console.log("receiver is ready for connection establishment");
      makeCall();
    });
    
    const makeCall = async () => {
      const offer = await peerConnection.createOffer({
        offerToReceiveAudio: true,
        offerToReceiveVideo: true,
      });
      await peerConnection.setLocalDescription(offer);
      socket.emit("offer", { offer });
    };
    
    socket.on("offer", async ({ offer }) => {
      console.log("offer received", offer);
      await peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
      const answer = await peerConnection.createAnswer({
        offerToReceiveAudio: true,
        offerToReceiveVideo: true,
      });
      await peerConnection.setLocalDescription(answer);
      socket.emit("answer", { answer });
    });
    
    socket.on("answer", async ({ answer }) => {
      console.log("answer received", answer);
      await peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
    });
    
    socket.on("send-icecandidate", async ({ candidate }) => {
      console.log("ice candidate received", candidate);
      await peerConnection.addIceCandidate(candidate);
    });
    
    })