idraulicoubriaco
idraulicoubriaco

Reputation: 41

WebRTC react stream multiple tracks issue. Camera + Screen Share

I am working on a new website. You can join room calls and make a voicecall so use your webcam but you can also share your screen.

The problem is when i add the track to the stream the peer doesn't work more... I don't know how to solve this. I also tried to change the code from adding the track to adding a new stream or anything else but cannot find a solution. Can anyone help me?

Client:

export default function Meeting(props) {
const userVideo = useRef();
const partnerVideo = useRef();
const screenShare = useRef();
const peerRef = useRef();
const socketRef = useRef();
const otherUser = useRef();
const userStream = useRef();
const userScreen = useRef();

useEffect(() => {
    navigator.mediaDevices.getUserMedia({ audio: true, video: true }).then(stream => {
        userVideo.current.srcObject = stream;
        userStream.current = stream;

        socketRef.current = socketIOClient.connect(ENDPOINT);
        socketRef.current.emit("join room", props.room);

        socketRef.current.on('other user', userID => {
            callUser(userID);
            otherUser.current = userID;
        });

        socketRef.current.on("user joined", userID => {
            otherUser.current = userID;
        });

        socketRef.current.on("offer", handleRecieveCall);

        socketRef.current.on("answer", handleAnswer);

        socketRef.current.on("ice-candidate", handleNewICECandidateMsg);
    });

}, []);

function callUser(userID) {
    console.log("call user - - -d-s-d-s-d--sd-sdd-sd-ssd-sd-sd--");
    peerRef.current = createPeer(userID);
    userStream.current.getTracks().forEach(track => peerRef.current.addTrack(track, userStream.current));
}

function createPeer(userID) {
    const peer = new RTCPeerConnection({
        //sdpSemantics: 'unified-plan',
        iceServers: [
            {
                urls: "stun:stun.stunprotocol.org"
            },
            {
                urls: 'turn:numb.viagenie.ca',
                credential: 'muazkh',
                username: '[email protected]'
            },
        ]
    });

    peer.onicecandidate = handleICECandidateEvent;
    peer.ontrack = handleTrackEvent;
    peer.onnegotiationneeded = () => {
        if (peer.signalingState != "stable") return;
        handleNegotiationNeededEvent(userID);
    }

    return peer;
}

function handleNegotiationNeededEvent(userID) {
    console.log("negotiationsad-das-d-as-d-asd--asd-a-sd-a-sd-");

    peerRef.current.createOffer().then(offer => {
        return peerRef.current.setLocalDescription(offer);
    }).then(() => {
        const payload = {
            target: userID,
            type: "video-offer",
            caller: socketRef.current.id,
            sdp: peerRef.current.localDescription
        };
        socketRef.current.emit("offer", payload);
    }).catch(e => console.log(e));
}

function handleRecieveCall(incoming) {
    peerRef.current = createPeer();
    const desc = new RTCSessionDescription(incoming.sdp);
    peerRef.current.setRemoteDescription(desc).then(() => {
        userStream.current.getTracks().forEach(track => peerRef.current.addTrack(track, userStream.current));
    }).then(() => {
        return peerRef.current.createAnswer();
    }).then(answer => {
        return peerRef.current.setLocalDescription(answer);
    }).then(() => {
        const payload = {
            target: incoming.caller,
            type: "video-offer",
            caller: socketRef.current.id,
            sdp: peerRef.current.localDescription
        }
        socketRef.current.emit("answer", payload);
    })
}

function handleAnswer(message) {
    const desc = new RTCSessionDescription(message.sdp);
    peerRef.current.setRemoteDescription(desc).catch(e => console.log(e));
}

function handleICECandidateEvent(e) {
    if (e.candidate) {
        const payload = {
            target: otherUser.current,
            candidate: e.candidate,
        }
        socketRef.current.emit("ice-candidate", payload);
    }
}

function handleNewICECandidateMsg(incoming) {
    const candidate = new RTCIceCandidate(incoming);

    if (peerRef.current && candidate) {
        peerRef.current.addIceCandidate(candidate).catch(e => console.log(e));
    }
}

function handleTrackEvent(e) {
    var stream = e.streams[0];
    var tracks = stream.getTracks();
    var lun = tracks.length;

    console.log(tracks);

    if (lun === 2) {
        partnerVideo.current.srcObject = stream;
    } else if (lun === 1) {
        screenShare.current.srcObject = stream;
    }
};

function shareScreen() {
    navigator.mediaDevices.getDisplayMedia({ cursor: true }).then(stream => {
        screenShare.current.srcObject = stream;
        userScreen.current = stream;

        const screenTrack = stream.getTracks()[0];

        callUser(otherUser.current);

        peerRef.current.addTrack(screenTrack, stream);

        screenTrack.onended = function () {
            peerRef.current.removeTrack(screenTrack);
        }
    })
}

return (
    <div>
        <video controls style={{ height: 500, width: 500 }} autoPlay ref={userVideo} />
        <video controls style={{ height: 500, width: 500 }} autoPlay ref={partnerVideo} />
        <video controls style={{ height: 500, width: 500 }} autoPlay ref={screenShare} />
        <button onClick={shareScreen}>Share screen</button>
    </div>
);

};

Upvotes: 3

Views: 1174

Answers (1)

jib
jib

Reputation: 42430

In shareScreen you're adding a second track to the existing peer connection with addTrack, which triggers renegotiation (causes your onnegotiationneeded handler to run again). This is correct so far, and triggers a second offer/answer exchange, which is needed for media track additions.

You're just not set up to handle renegotiation correctly, because you've mixed initialization code in with your (re)negotiation code, causing a new RTCPeerConnection object to be created when you already have one.

Renegotiation is going to happen whenever you add tracks or stop their underlying transceivers, so you don't want initialization code in your negotiation code. Instead do this:

  1. Move the creation of your RTCPeerConnection out of handleReceiveCall. Create it on page load instead.

  2. Move the addition of tracks out of handleReceiveCall as well if you can, or at least skip re-adding them if they've already been added.

  3. Your ontrack is going to fire again with the screen-sharing track, so you need to add code to handle that. You can differentiate it from the camera track by event.streams[0].id and event.transceiver.mid being different, or simply from this being the third track.

Upvotes: 1

Related Questions