react-native-webrtc / react-native-webrtc

The WebRTC module for React Native
https://react-native-webrtc.discourse.group
MIT License
4.54k stars 1.23k forks source link

Sometimes setRemoteDescription & onTracks event is not fired #1573

Open bagusaff opened 4 weeks ago

bagusaff commented 4 weeks ago

App Flow :

  1. There's Callee who can request a Video Call session. Using Web Socket we create unique 5digits temporary room for any Admin to joins the rooms.
  2. Admin will get a Incoming Pop Up Modal on their screen and when they answer the call they will be randomly assigned into the room.

VideoCallScreen.tsx

  useEffect(() => {
      InCallManager.start({media: 'audio'});
      socket.current = new WebSocket(WEBSOCKET_URL);
      console.log('Connecting to WebSocket:', WEBSOCKET_URL);

      socket.current.onopen = () => {
        setIsConnected(true);
      };

      socket.current.onmessage = e => {
        const parsedData = JSON.parse(e.data);
        handleWebSocketMessage(parsedData);
      };

      socket.current.onerror = e => {
        console.log('On Error', e.message);
        setIsConnected(false);
      };

      socket.current.onclose = e => {
        console.log('On Close', e.code, e.reason);
        setIsConnected(false);
      };
      return () => {
        if (socket.current) {
          socket.current.close();
        }
      };
    }, []);

    const handleWebSocketMessage = useCallback(
      (data: any) => {
        switch (data.type) {
          case 'created':
            console.log('Room Created:', data);
            break;
          case 'roomList':
            console.log('Room List:', data.rooms);
            break;
          case 'joined':
            console.log('Room Joined:', data);
            break;
          case 'currentUsers':
            console.log('Current Users in Room:', data.users);
            if (!localPC && data.users.length > 1) {
              setupPeerConnection();
            }
            break;
          case 'offer':
            console.log('Offers Received');
            if (!localPC) {
              setupPeerConnection();
            }
            handleRemoteOffer(data.offer);
            break;
          case 'answer':
            console.log('Answer Received:');
            handleRemoteAnswer(data.answer);
            break;
          case 'participantLeft':
            console.log('Participant Left :', data);
            handleEndCall();
            break;
          case 'candidate':
            remoteCandidates.push(data?.candidate);
            break;
          default:
            console.log('Unknown message type :', data);
        }
      },
      [localPC],
    );

    const setupPeerConnection = async () => {
      if (!localStream) return;
      if (localPC) {
        localPC.getSenders().forEach(sender => {
          localPC.removeTrack(sender);
        });
      }

      localStream.getTracks().forEach(track => {
        console.log('Adding track:', userType, track);
        localPC.addTrack(track, localStream);
      });

      const offer = await localPC.createOffer(sessionConstraints);
      console.log('trying to make an offer');
      localPC.addEventListener('track', event => {
        const newStream = new MediaStream();
        event.streams[0].getTracks().forEach(track => {
          console.log('Received remote track:', userType, track);
          newStream.addTrack(track);
        });
        setRemoteStream(newStream);
        console.log('NEW STREAM', userType, newStream.toURL());
      });
      await localPC.setLocalDescription(offer);
      const offerBody = {
        type: 'offer',
        offer: offer,
        room: roomIdToJoin,
        userId: userData?.id,
      };
      socket.current?.send(JSON.stringify(offerBody));

      localPC.addEventListener('icecandidate', async e => {
        if (!e.candidate) {
          return;
        }
        const addIceCandidateBody = {
          type: 'candidate',
          candidate: e.candidate?.toJSON(),
          room: roomIdToJoin,
          userId: userData?.id,
        };
        socket.current?.send(JSON.stringify(addIceCandidateBody));
      });

      localPC.addEventListener('connectionstatechange', event => {
        console.log('connection state ===>', event);
      });
    };

    const handleRemoteOffer = async (offer: any) => {
      if (!localPC) {
        await setupPeerConnection();
      }
      await localPC.setRemoteDescription(new RTCSessionDescription(offer));
      const answer = await localPC.createAnswer();
      await localPC.setLocalDescription(answer);

      socket.current?.send(
        JSON.stringify({type: 'answer', answer, roomId: roomIdToJoin}),
      );

      if (remoteCandidates.length > 1) {
        for (let candidate of remoteCandidates) {
          handleRemoteICECandidate(candidate);
        }
        remoteCandidates = [];
      }
    };

    const handleRemoteAnswer = async (answer: any) => {
      await localPC.setRemoteDescription(new RTCSessionDescription(answer));

      if (remoteCandidates.length > 1) {
        for (let candidate of remoteCandidates) {
          handleRemoteICECandidate(candidate);
        }
        remoteCandidates = [];
      }
    };

    const handleRemoteICECandidate = async (candidate: any) => {
      try {
        console.log('handling candidate', userType);
        await localPC.addIceCandidate(new RTCIceCandidate(candidate));
      } catch (e) {
        console.error('Error adding received ice candidate', e);
      }
    };

    const createRoomCall = async () => {
      if (!socket.current) return;
      const createRoomBody = {
        type: 'create',
        room: roomIdToJoin,
        userId: userData?.id,
        contentType: sharedContentType,
      };
      socket.current?.send(JSON.stringify(createRoomBody));
      console.log('Room Call Created ==>');
      return setupPeerConnection();
    };

    const joinRoomCall = async () => {
      if (!socket.current) return;
      const joinRoomBody = {
        type: 'join',
        room: roomIdToJoin,
        userId: userData?.id,
        contentType: sharedContentType,
      };
      console.log('Joining Room of ==> ', joinRoomBody);
      socket.current?.send(JSON.stringify(joinRoomBody));
      await setupPeerConnection();
    };

Expected Behavior:

Call should work all the times

Observed Behavior:

Sometimes when Callee Joined the room I noticed from the Logs that the onTrack & setRemoteDescription event is not fired, resulting on video is not showing on either side. It happens like 30% of the times..

Steps to reproduce the issue:

I have provided my main logic, and my main concern is that Im not sure if the flow is correct.

Platform Information

LOGS when it's succeeded

LOG  rn-webrtc:pc:DEBUG 252 ctor +9s
 LOG  Connecting to WebSocket: wss://my-api
 LOG  rn-webrtc:pc:DEBUG 253 ctor +163ms
 LOG  Local stream started: {"_id": "755fa7bf-78de-482a-bd43-05fdddd211ad", "_reactTag": "755fa7bf-78de-482a-bd43-05fdddd211ad", "_tracks": [{"_constraints": [Object], "_enabled": true, "_muted": false, "_peerConnectionId": undefined, "_readyState": "live", "_settings": [Object], "id": "cc5c995e-f878-451f-85ff-725d5fd19934", "kind": "audio", "label": "", "remote": false}, {"_constraints": [Object], "_enabled": true, "_muted": false, "_peerConnectionId": undefined, "_readyState": "live", "_settings": [Object], "id": "f5d35299-cac2-4202-ba96-28c28932c7cd", "kind": "video", "label": "", "remote": false}]}
 LOG  rn-webrtc:pc:DEBUG 254 ctor +131ms
 LOG  rn-webrtc:pc:DEBUG 255 ctor +27ms
 LOG  rn-webrtc:pc:DEBUG 256 ctor +184ms
 LOG  Connected To WebSocket : 27fc9fe3-5557-4566-8088-e7ccfc9fe862
 LOG  Room Call Created ==>
 LOG  Adding track: caller {"_constraints": {}, "_enabled": true, "_muted": false, "_peerConnectionId": undefined, "_readyState": "live", "_settings": {}, "id": "cc5c995e-f878-451f-85ff-725d5fd19934", "kind": "audio", "label": "", "remote": false}
 LOG  rn-webrtc:pc:DEBUG 252 addTrack +25ms
 LOG  Adding track: caller {"_constraints": {"facingMode": "user", "frameRate": 30, "height": 720, "width": 1280}, "_enabled": true, "_muted": false, "_peerConnectionId": undefined, "_readyState": "live", "_settings": {"frameRate": 30, "height": 720, "width": 1280}, "id": "f5d35299-cac2-4202-ba96-28c28932c7cd", "kind": "video", "label": "", "remote": false}
 LOG  rn-webrtc:pc:DEBUG 252 addTrack +11ms
 LOG  rn-webrtc:pc:DEBUG 252 createOffer +7ms
 LOG  Room List: []
 LOG  rn-webrtc:pc:DEBUG 252 createOffer OK +20ms
 LOG  trying to make an offer
 LOG  rn-webrtc:pc:DEBUG 252 setLocalDescription +0ms
 LOG  rn-webrtc:pc:DEBUG 252 setLocalDescription OK +81ms
 LOG  Room Created: {"room": "65xxi", "type": "created"}
 LOG  Room List: ["65xxi"]
 LOG  Current Users in Room: [{"contentType": "video", "id": "4a461a0c-8022-4371-83d9-1abfaecd22de", "isActive": true, "socketId": "27fc9fe3-5557-4566-8088-e7ccfc9fe862"}]
 LOG  Room List: ["65xxi"]
 LOG  Room List: ["65xxi"]
 LOG  Room List: []
 LOG  Current Users in Room: [{"contentType": "video", "id": "4a461a0c-8022-4371-83d9-1abfaecd22de", "isActive": true, "socketId": "27fc9fe3-5557-4566-8088-e7ccfc9fe862"}, {"id": "6f7ca64f-37ae-4653-be49-26f59ad66e6d", "isActive": true, "socketId": "946ffda5-91f6-4df3-856d-237add11807a"}]
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  Offers Received
 LOG  rn-webrtc:pc:DEBUG 252 setRemoteDescription +9s
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  rn-webrtc:pc:DEBUG 252 ontrack +56ms
 LOG  rn-webrtc:pc:DEBUG 252 ontrack +3ms
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  Received remote track: caller {"_constraints": {}, "_enabled": true, "_muted": false, "_peerConnectionId": 252, "_readyState": "live", "_settings": {}, "id": "f44bd21c-36d3-429b-83fd-ac65203f56f8", "kind": "audio", "label": "", "remote": true}
 LOG  rn-webrtc:pc:DEBUG 257 ctor +15ms
 LOG  NEW STREAM caller aeec32da-3e88-c2ef-da14-ccf8ef0c1414
 LOG  Received remote track: caller {"_constraints": {}, "_enabled": true, "_muted": false, "_peerConnectionId": 252, "_readyState": "live", "_settings": {}, "id": "f44bd21c-36d3-429b-83fd-ac65203f56f8", "kind": "audio", "label": "", "remote": true}
 LOG  Received remote track: caller {"_constraints": {}, "_enabled": true, "_muted": false, "_peerConnectionId": 252, "_readyState": "live", "_settings": {}, "id": "4eb59dd8-80a9-4056-9401-aead38fe3262", "kind": "video", "label": "", "remote": true}
 LOG  rn-webrtc:pc:DEBUG 258 ctor +38ms
 LOG  NEW STREAM caller 899dc408-f208-7a78-9d78-a54888947398
 LOG  rn-webrtc:pc:DEBUG 252 setRemoteDescription OK +21ms
 LOG  rn-webrtc:pc:DEBUG 252 createAnswer +2ms
 LOG  handling candidate caller
 LOG  rn-webrtc:pc:DEBUG 252 addIceCandidate +5ms
 LOG  rn-webrtc:pc:DEBUG 252 setLocalDescription +2ms
 LOG  rn-webrtc:pc:DEBUG 252 setLocalDescription OK +59ms
 LOG  handling candidate caller
 LOG  rn-webrtc:pc:DEBUG 252 addIceCandidate +1ms
 LOG  handling candidate caller
 LOG  rn-webrtc:pc:DEBUG 252 addIceCandidate +0ms
 LOG  handling candidate caller
 LOG  rn-webrtc:pc:DEBUG 252 addIceCandidate +1ms
 LOG  handling candidate caller
 LOG  rn-webrtc:pc:DEBUG 252 addIceCandidate +0ms
 LOG  handling candidate caller
 LOG  rn-webrtc:pc:DEBUG 252 addIceCandidate +0ms
 LOG  connection state ===> {"isTrusted": false}
 LOG  connection state ===> {"isTrusted": false}

LOGS when it failed to display video

LOG  rn-webrtc:pc:DEBUG 231 ctor +327ms
 LOG  connection state ===> {"isTrusted": false}
 LOG  rn-webrtc:pc:DEBUG 232 ctor +5s
 LOG  Connecting to WebSocket: wss://my-api
 LOG  rn-webrtc:pc:DEBUG 233 ctor +183ms
 LOG  Local stream started: {"_id": "2970296d-1cc1-47eb-a2f7-4e663622c94a", "_reactTag": "2970296d-1cc1-47eb-a2f7-4e663622c94a", "_tracks": [{"_constraints": [Object], "_enabled": true, "_muted": false, "_peerConnectionId": undefined, "_readyState": "live", "_settings": [Object], "id": "05811ae3-2a85-48a1-a0d4-14d87dd4c59d", "kind": "audio", "label": "", "remote": false}, {"_constraints": [Object], "_enabled": true, "_muted": false, "_peerConnectionId": undefined, "_readyState": "live", "_settings": [Object], "id": "89496fbc-4398-46ad-a28a-c3713099f9ad", "kind": "video", "label": "", "remote": false}]}
 LOG  rn-webrtc:pc:DEBUG 234 ctor +93ms
 LOG  rn-webrtc:pc:DEBUG 235 ctor +33ms
 LOG  rn-webrtc:pc:DEBUG 236 ctor +204ms
 LOG  Connected To WebSocket : ddfd944e-728d-4b20-99cd-b3484cfce9c3
 LOG  Room List: []
 LOG  Room Call Created ==>
 LOG  Adding track: caller {"_constraints": {}, "_enabled": true, "_muted": false, "_peerConnectionId": undefined, "_readyState": "live", "_settings": {}, "id": "05811ae3-2a85-48a1-a0d4-14d87dd4c59d", "kind": "audio", "label": "", "remote": false}
 LOG  rn-webrtc:pc:DEBUG 232 addTrack +35ms
 LOG  Adding track: caller {"_constraints": {"facingMode": "user", "frameRate": 30, "height": 720, "width": 1280}, "_enabled": true, "_muted": false, "_peerConnectionId": undefined, "_readyState": "live", "_settings": {"frameRate": 30, "height": 720, "width": 1280}, "id": "89496fbc-4398-46ad-a28a-c3713099f9ad", "kind": "video", "label": "", "remote": false}
 LOG  rn-webrtc:pc:DEBUG 232 addTrack +14ms
 LOG  rn-webrtc:pc:DEBUG 232 createOffer +6ms
 LOG  rn-webrtc:pc:DEBUG 232 createOffer OK +12ms
 LOG  trying to make an offer
 LOG  rn-webrtc:pc:DEBUG 232 setLocalDescription +1ms
 LOG  rn-webrtc:pc:DEBUG 232 setLocalDescription OK +33ms
 LOG  Room Created: {"room": "d7u4o", "type": "created"}
 LOG  Room List: ["d7u4o"]
 LOG  Current Users in Room: [{"contentType": "video", "id": "4a461a0c-8022-4371-83d9-1abfaecd22de", "isActive": true, "socketId": "ddfd944e-728d-4b20-99cd-b3484cfce9c3"}]
 LOG  Room List: ["d7u4o"]
 LOG  Room List: ["d7u4o"]
 LOG  Room List: []
 LOG  Current Users in Room: [{"contentType": "video", "id": "4a461a0c-8022-4371-83d9-1abfaecd22de", "isActive": true, "socketId": "ddfd944e-728d-4b20-99cd-b3484cfce9c3"}, {"contentType": "video", "id": "6f7ca64f-37ae-4653-be49-26f59ad66e6d", "isActive": true, "socketId": "83a9e8f7-0b5e-4814-90b8-e2633df0b25b"}]
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  Cant find peerconnection, adding candidates to queue
 LOG  Participant Left : {"contentType": "video", "id": "6f7ca64f-37ae-4653-be49-26f59ad66e6d", "type": "participantLeft"}
 LOG  rn-webrtc:pc:DEBUG 232 removeTrack +11s
 LOG  rn-webrtc:pc:DEBUG 232 removeTrack +4ms
 LOG  rn-webrtc:pc:DEBUG 232 close +3ms
 LOG  Current Users in Room: [{"contentType": "video", "id": "4a461a0c-8022-4371-83d9-1abfaecd22de", "isActive": true,"socketId": "ddfd944e-728d-4b20-99cd-b3484cfce9c3"}]
 LOG  connection state ===> {"isTrusted": false}
 LOG  On Close 1000 
saghul commented 4 weeks ago

Can you please make a more reduced example?

bagusaff commented 4 weeks ago

Can you please make a more reduced example?

Hi, really appreciate your fast response! I have updated & reduced my code example.

saghul commented 4 weeks ago
    const handleRemoteOffer = async (offer: any) => {
      if (!localPC) {
        await setupPeerConnection();
      }
      await localPC.setRemoteDescription(new RTCSessionDescription(offer));
      const answer = await localPC.createAnswer();
      await localPC.setLocalDescription(answer);

      socket.current?.send(
        JSON.stringify({type: 'answer', answer, roomId: roomIdToJoin}),
      );

      if (remoteCandidates.length > 1) {
        for (let candidate of remoteCandidates) {
          handleRemoteICECandidate(candidate);
        }
        remoteCandidates = [];
      }
    };

Not sure if this is it, but if you end up calling setupPeerConnection here things will break because you are attempting to set a remote offer while having a local offer.

bagusaff commented 4 weeks ago
```js
    const handleRemoteOffer = async (offer: any) => {
      if (!localPC) {
        await setupPeerConnection();
      }
      await localPC.setRemoteDescription(new RTCSessionDescription(offer));
      const answer = await localPC.createAnswer();
      await localPC.setLocalDescription(answer);

      socket.current?.send(
        JSON.stringify({type: 'answer', answer, roomId: roomIdToJoin}),
      );

      if (remoteCandidates.length > 1) {
        for (let candidate of remoteCandidates) {
          handleRemoteICECandidate(candidate);
        }
        remoteCandidates = [];
      }
    };

Not sure if this is it, but if you end up calling setupPeerConnection here things will break because you are attempting to set a remote offer while having a local offer.

Thanks for the insight! It was actually my most recently added code while I tried to fix the issue, which means that the issue happens even before I added that line :"))

bagusaff commented 3 weeks ago
case 'candidate':
            if (!localPC || !localPC.remoteDescription?.type) {
              console.log(
                'Cant find peerconnection, adding candidates to queue',
              );
remoteCandidates.push(data?.candidate);
            } else {
              handleRemoteICECandidate(data?.candidate);
            }
            break;

I dont know if this information is important, but If I tried to handleRemoteICE with this method it will resulted to error :

Error adding received ice candidate [Error: The remote description was null]