node.jsreactjswebrtcsimplewebrtc

WebRTC peerConnection always failed between different devices


I was building Video Call App using webRTC for my thesis. I already build the app and it works perfectly if I use the same device (different tab browser/different browser). Then, when I try to call another user that logged in on another device, the peer connection always failed. My app was already online at a public address. It happens in my local too. I already used Turn server that work when I tested it on here . In that case, where is my mistake?

Here, the flow how I create and handle the peer connection :

// Call Function
export const call = async (from, to) => {
    let configuration = {
        iceServers: [
            {
                urls: "stun:stun1.l.google.com:19302"
            },
            {
                urls: "turn:a.relay.metered.ca:80",
                username: "xxxxxxxxxx",
                credential: "xxxxxxxxxx",
            },
            {
                urls: "turn:a.relay.metered.ca:80?transport=tcp",
                username: "xxxxxxxxxx",
                credential: "xxxxxxxxxx",
            },
            {
                urls: "turn:a.relay.metered.ca:443",
                username: "xxxxxxxxxx",
                credential: "xxxxxxxxxx",
            },
            {
                urls: "turn:a.relay.metered.ca:443?transport=tcp",
                username: "xxxxxxxxxx",
                credential: "xxxxxxxxxx",
            },
        ],
    };

    const peer = new RTCPeerConnection(configuration);

    const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true })
    peer.addTrack(stream.getTracks()[0], stream)

    const offer = await peer.createOffer();

    let message = {
        id : 'call',
        from : from,
        to : to,
        sdpOffer : offer,
        state: 'req_calling'
    };
    sendMessage(message);
    
    await peer.setLocalDescription(offer);
    WebRtcPeer.addPeer(peer)
    
    peer.onicecandidate = function (event) {
        if (event.candidate) {
            const message = {
                id : 'onIceCandidate',
                candidate : event.candidate,
                to : to,
                from: from
            }
            sendMessage(message);
        }
    }

    // get to know when connected to peer
    peer.onconnectionstatechange = function (event) {
        console.log('masuk sono')
        if (peer.connectionState === 'connected') {
            const message = {
                id: 'peerConnected',
                from: localStorage.getItem('me'),
                to: localStorage.getItem('they')
            }
            sendMessage(message)
        }
    }
}
// Answering Call Function

export const incomingCall = async (message) => {
    const configuration = {
        iceServers: [
            {
                urls: "stun:stun1.l.google.com:19302"
            },
            {
                urls: "turn:a.relay.metered.ca:80",
                username: "xxxxxxxxxx",
                credential: "xxxxxxxxxx",
            },
            {
                urls: "turn:a.relay.metered.ca:80?transport=tcp",
                username: "xxxxxxxxxx",
                credential: "xxxxxxxxxx",
            },
            {
                urls: "turn:a.relay.metered.ca:443",
                username: "xxxxxxxxxx",
                credential: "xxxxxxxxxx",
            },
            {
                urls: "turn:a.relay.metered.ca:443?transport=tcp",
                username: "xxxxxxxxxx",
                credential: "xxxxxxxxxx",
            },
        ],
    }

    // create peer using RTC
    const peer = new RTCPeerConnection(configuration);

    peer.setRemoteDescription(new RTCSessionDescription(message.sdpOffer))

    const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true })
    peer.addTrack(stream.getTracks()[0], stream)

    const answer = await peer.createAnswer();

    await peer.setLocalDescription(answer);

    WebRtcPeer.addPeer(peer)

    peer.onicecandidate = function (event) {
        if (event.candidate) {
            const msg = {
                id : 'onIceCandidate',
                candidate : event.candidate,
                to : message.from,
                from: message.to
            }
            sendMessage(msg);
        }
    }

    // get to know when connected to peer
    peer.onconnectionstatechange = function (event) {
        if (peer.connectionState === 'connected') {
            const message = {
                id: 'peerConnected',
                from: localStorage.getItem('me'),
                to: localStorage.getItem('they')
            }
            sendMessage(message)
        }
    }

    let response = {
        id: 'incomingCallResponse',
        from: message.from,
        callResponse: 'accept',
        sdpOffer: answer,
        state: 'acc_calling'
    }
    sendMessage(response);
    localStorage.setItem('they', message.from)
}

Every candidate that sends to peer user, I save it to CandidatesQueue in my signalling server because of waiting calling state, if the call is accepted and peer connection on the peer user was created, then I start to sending all the candidates.

If that information is still not enough, here is the repo that I work on : client repo, server repo

I was confused where's the mistake, is it the Turn server ? or is it my code?

if you want to try my app, here's the link : myApp

you can register an account and log in with your registered account.

Thanks!

UPDATE

After doing testing over and over again, it could be connected, but sometimes can't. I already try with 3 different devices, but the behavior was same, sometimes admin1 can be connected to client1, but when client1 tries to connect to admin1 it can't. Also with another user, the behavior was really random, I think the problem was on my laptop1, but when I try the other laptop, the problem occur with random behavior. Any idea?


Solution

  • Have you used the "perfect negotiation" logic when establish a connection?

    You may refer to the following page for detail:

    https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Perfect_negotiation