3

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: 'webrtc@live.com'
            },
        ]
    });

    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>
);

};

  • Adding another track to an existing connection triggers renegotiation, which you're not set up to handle: it triggers `handleRecieveCall` where you create a new (third) `RTCPeerConnection` which is not going to work. – jib Dec 31 '20 at 23:56
  • @jib Hi jib. The only way this app works is bi triggering callUser and restart all peer. How can I only renegotiate? – idraulicoubriaco Jan 01 '21 at 20:01

1 Answers1

1

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.

jib
  • 40,579
  • 17
  • 100
  • 158