javascriptwebrtcrtcpeerconnection

WebRTC peerConnection ontrack listener doesn't trigger


First of all, issue here is different from all I found in "google" as I'm using Spring Boot as signaling server to connect two different browser's tabs or they are using two peerConnection to send stream within one tab.

Peers seem to be connected and messages are sent correctly. However, when I run sendStream with button, second tab track event listener doesn't trigger.

//connecting to our signaling server 
var conn = new WebSocket('ws://localhost:8080/socket');

conn.onopen = function() {
    console.log("Connected to the signaling server");
    initialize();
};

conn.onmessage = function(msg) {
    console.log("Got message", msg.data);
    var content = JSON.parse(msg.data);
    var data = content.data;
    switch (content.event) {
    // when somebody wants to call us
    case "offer":
        handleOffer(data);
        break;
    case "answer":
        handleAnswer(data);
        break;
    // when a remote peer sends an ice candidate to us
    case "candidate":
        handleCandidate(data);
        break;
    default:
        break;
    }
};

function send(message) {
    conn.send(JSON.stringify(message));
}

var peerConnection;
var dataChannel;
var input = document.getElementById("messageInput");

function initialize() {
    var configuration = null;

    peerConnection = new RTCPeerConnection(configuration);

    // Setup ice handling
    peerConnection.onicecandidate = function(event) {
        if (event.candidate) {
            send({
                event : "candidate",
                data : event.candidate
            });
        }
    };

    const remoteVideo = document.getElementById('video');

    peerConnection.addEventListener('track', async (event) => {
        const [remoteStream] = event.streams;
        remoteVideo.srcObject = remoteStream;
        console.log("STREAM RECEIVED");
    });
    
    // creating data channel
    dataChannel = peerConnection.createDataChannel("dataChannel", {
        reliable : true
    });

    dataChannel.onerror = function(error) {
        console.log("Error occured on datachannel:", error);
    };

    // when we receive a message from the other peer, printing it on the console
    dataChannel.onmessage = function(event) {
        console.log("message:", event.data);
    };

    dataChannel.onclose = function() {
        console.log("data channel is closed");
    };
  
    peerConnection.ondatachannel = function (event) {
        dataChannel = event.channel;
    };
    
}

function createOffer() {
    peerConnection.createOffer(function(offer) {
        send({
            event : "offer",
            data : offer
        });
        peerConnection.setLocalDescription(offer);
    }, function(error) {
        alert("Error creating an offer");
    });
}

function handleOffer(offer) {
    peerConnection.setRemoteDescription(new RTCSessionDescription(offer));

    // create and send an answer to an offer
    peerConnection.createAnswer(function(answer) {
        peerConnection.setLocalDescription(answer);
        send({
            event : "answer",
            data : answer
        });
    }, function(error) {
        alert("Error creating an answer");
    });

};

function handleCandidate(candidate) {
    peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
};

function handleAnswer(answer) {
    peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
    console.log("connection established successfully!!");
};

function sendMessage() {
    dataChannel.send(input.value);
    input.value = "";
}

async function sendStream() {
    const localStream = await navigator.mediaDevices.getUserMedia({audio: false, video: true});
    localStream.getTracks().forEach(track => {
        peerConnection.addTrack(track, localStream);
        console.log("STREAM SENT")
        const localVideo = document.getElementById('video2');
        addVideoStream(localVideo, localStream);
    });
}

function addVideoStream(video, stream) {
    video.srcObject = stream;
    video.addEventListener('loadedmetadata', () => {
        video.play();
    })
}

Solution

  • If you already have an established WebRTC connection, renegotiation is needed for the new track to be sent.

    The reason is that peers need to choose codecs and other parameters included in SDP. Otherwise your browser won't tell how to compress the video you want to send so the receiver can decode it.

    See the note on top of the page: https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addTrack

    So you probably need to set up onnegotiationneeded callback and then send a new offer / receive a new answer.