Crazy Programmer
Crazy Programmer

Reputation: 9

Issues with Handling Remote Tracks and Updating UI in WebRTC Peer Connections

I'm working on a WebRTC application where I need to handle remote tracks and update the UI for each participant. I’ve encountered several issues with handling remote tracks, updating the participant UI, and ensuring proper display of video streams. Here are the details of the problems I'm facing:

Code Overview Creating Peer Connections:

function addParticipantToUI(id, name) {
    //console.log(id);
    if (id === currentUserId) {
        console.warn(`Skipping UI update for current user ID: ${id}`);
        return;
    }
    //console.log(`Adding participant UI for ID: ${id}, Name: ${name}`);

    let participantDiv = document.getElementById(`participant-${id}`);
    if (!participantDiv) {
        participantDiv = document.createElement('div');
        participantDiv.className = 'video-container';
        participantDiv.id = `participant-${id}`;

        let nameLabel = document.createElement('div');
        nameLabel.className = 'name-label';
        nameLabel.innerText = name;
        participantDiv.appendChild(nameLabel);

        let videoElement = document.createElement('video');
        videoElement.id = `video-${id}`;
        videoElement.autoplay = true;
        participantDiv.appendChild(videoElement);

        document.getElementById('videos').appendChild(participantDiv);
        //console.log(`Participant UI added for ID: ${id}`);
    } else {
        console.warn(`Participant UI already exists for ID: ${id}`);
    }
}


async function initiateOffer(to) {
    //console.log(to);
    let peer = createPeerConnection(to);

    try {
        const offer = await peer.createOffer();
        await peer.setLocalDescription(offer);
        socket.send(JSON.stringify({ type: 'offer', offer: peer.localDescription, to: to }));
    } catch (error) {
        console.error('Error initiating offer:', error);
    }
}

async function handleOffer(offer, from) {
    //console.log(`Received offer from: ${from}`);
    let peer = createPeerConnection(from);

    try {
        await peer.setRemoteDescription(new RTCSessionDescription(offer));
        const answer = await peer.createAnswer();
        await peer.setLocalDescription(answer);
        socket.send(JSON.stringify({ type: 'answer', answer: peer.localDescription, to: from }));
    } catch (error) {
        console.error('Error handling offer:', error);
    }
}

async function handleAnswer(answer, from) {
    //console.log(`Received answer from: ${from}`);
    if (peers[from]) {
        try {
            //console.log(`Current signaling state: ${peers[from].signalingState}`);
            if (peers[from].signalingState === "have-local-offer") {
                await peers[from].setRemoteDescription(new RTCSessionDescription(answer));
                //console.log(`Set remote description successfully for peer ${from}`);
            } else if (peers[from].signalingState === "stable") {
                console.info(`Peer connection ${from} is already stable. Skipping setting remote description.`);
            } else {
                console.warn(`Skipping setting remote description for peer ${from} in state ${peers[from].signalingState}`);
            }
        } catch (error) {
            console.error('Error setting remote description:', error);
        }
    } else {
        console.error(`Peer connection not found for ID: ${from}`);
    }
}

async function handleCandidate(candidate, from) {
    //console.log(`Received ICE candidate from: ${from}`);
    if (peers[from]) {
        try {
            await peers[from].addIceCandidate(new RTCIceCandidate(candidate));
            //console.log(`Added ICE candidate for peer ${from}`);
        } catch (error) {
            console.error('Error adding ICE candidate:', error);
        }
    } else {
        console.error(`Peer connection not found for ID: ${from}`);
    }
}




function createPeerConnection(peerId) {
    console.log(`Creating peer connection for ID: ${peerId}`);
    //console.log(peers);

    // Create a new RTCPeerConnection
    let peer = new RTCPeerConnection();
    let hasStreamSet = false; // Flag to ensure stream is set only once

    // Handle ICE candidates
    peer.onicecandidate = (event) => {
        if (event.candidate) {
            if (peers[peerId]) {
                //console.log(`Sending ICE candidate to ${peerId} from tab ${tabId}`);
                socket.send(JSON.stringify({ type: 'candidate', candidate: event.candidate, to: peerId }));
            } else {
                console.warn(`No peer connection found for ${peerId}, cannot send ICE candidate`);
            }
        }
    };

    // Handle remote tracks
    peer.ontrack = (event) => {
        if (peers[peerId]) {
            console.log(`Received remote track from ${peerId}`);

            event.streams.forEach(stream => {
                //console.log('Remote Stream ID:', stream.id);
                stream.getTracks().forEach(track => {
                    //console.log(`Remote Track ID: ${track.id}, kind: ${track.kind}`);
                });

                let participantDiv = document.getElementById(`participant-${peerId}`);
                let videoElement = document.getElementById(`video-${peerId}`);

                if (!participantDiv) {
                    console.log(`Participant div not found for ID: ${peerId}. Adding to UI.`);
                    addParticipantToUI(peerId, userName);
                    participantDiv = document.getElementById(`participant-${peerId}`);
                    videoElement = document.getElementById(`video-${peerId}`);
                }

                if (videoElement && !hasStreamSet) {
                    console.log(`Setting remote stream for ${peerId}`);
                    videoElement.srcObject = stream;
                    console.log('Video element:', videoElement);
                    console.log('Stream assigned to video element:', videoElement.srcObject);
                    hasStreamSet = true; // Prevent redundant setting of the stream
                } else {
                    console.warn(`Video element not found for ${peerId} or stream already set`);
                }
            });
        } else {
            console.warn(`Ignoring remote track from unknown peer ID: ${peerId}`);
        }
    };

    // Add local tracks to peer connection
    localStream.getTracks().forEach(track => peer.addTrack(track, localStream));

    // Store the peer connection in the peers object
    peers[peerId] = peer;
    console.log(peers);
    return peer;
}

Logging Outputs:

Creating peer connection for ID: 1d317817-94d3-438b-95d4-ed7c0df3214f  // Remote user
Creating peer connection for ID: 05ee421f-3cfd-41cc-a997-29734c698624  // Current user
Received remote track from 05ee421f-3cfd-41cc-a997-29734c698624
Skipping UI update for current user ID: 05ee421f-3cfd-41cc-a997-29734c698624
Video element not found for 05ee421f-3cfd-41cc-a997-29734c698624 or stream already set // Intentionally not set because it's the current user
Received remote track from 05ee421f-3cfd-41cc-a997-29734c698624
Participant div not found for ID: 05ee421f-3cfd-41cc-a997-29734c698624. Adding to UI.
Skipping UI update for current user ID: 05ee421f-3cfd-41cc-a997-29734c698624
addParticipantToUI // Intentionally not set because it's the current user
Video element not found for 05ee421f-3cfd-41cc-a997-29734c698624 or stream already set        
Peer connection 05ee421f-3cfd-41cc-a997-29734c698624 is already stable. Skipping setting remote description. // Intentionally not set because it's already stable

Issues: Participant UI Not Updating:

The UI for the participant is being created but the media is not populated

Remote user's media track not found:

As said earlier The UI for the participant is being created but the media is not populated but when I log the peers after

// Add local tracks to peer connection
    localStream.getTracks().forEach(track => peer.addTrack(track, localStream));

    // Store the peer connection in the peers object
    peers[peerId] = peer;
    console.log(peers);

It shows both the peers

Questions

How can I ensure that the participant video is correctly updated to the ui?

What might be causing the video not being populated to its intended video element, and how can I resolve it?

Is there a more reliable way to handle the peer connection states and ensure that remote tracks are processed correctly?

I would appreciate any guidance or suggestions to help resolve these issues. Thank you!

Upvotes: 0

Views: 63

Answers (1)

vrobbi
vrobbi

Reputation: 76

First of all I have to say that in my webrtc demo I use the library rtcmulticonnection (author muaz khan).

After loading the library for starting with this library the code is: var connection = new RTCMultiConnection();

Then in my webrtc demo you can add and remove the cam whenever you want to with the code below for example (valid for all users):

 if (stream.isVideo) {

          stream.getTracks().forEach(track => track.stop());
          stream.getTracks().forEach(track => stream.removeTrack(track));
          console.info('cam removed');
         
        }

      });

But have a look at my demo here https://vrobbi-webrtc-demo.glitch.me/ with the related code. I hope it will be a cue for you

Upvotes: 0

Related Questions