0

I am using react-native-webrtc to create a video call between 2 people. First, 2 users must open the app and have theirs peerConnection set up with local media stream. Then the other one will send the offer and the other will automatically accept the offer. The ICE candidates are collected but the iceConnectionState always go from connecting to failed.

This is the code for client

let peerConstraints = {
    iceServers: [
      {
        urls: 'stun:stun.l.google.com:19302',
      },
      {
        urls: 'stun:stun1.l.google.com:19302',
      },
      {
        urls: 'stun:stun2.l.google.com:19302',
      },
    ],
  };
  const chatRoomId = 1;
  const {isCaller} = route.params;
  const [remoteMediaStream, setRemoteMediaStream] = useState<any>(null);
  const [localMediaStream, setLocalMediaStream] = useState<MediaStream | null>(
    null,
  );
  const [remoteCandidates, setRemoteCandidates] = useState<RTCIceCandidate[]>(
    [],
  );
  const [negotiationNeeded, setNegotiationNeeded] = useState(false);
  const peerConnection = useRef(new RTCPeerConnection(peerConstraints));
  const [isVoiceOnly, setIsVoiceOnly] = useState(false);

  const sendOfferVideoCall = async () => {
    if (!negotiationNeeded) return;

    let sessionConstraints = {
      mandatory: {
        OfferToReceiveAudio: true,
        OfferToReceiveVideo: true,
        VoiceActivityDetection: true,
      },
    };
    try {
      const offerDescription = await peerConnection.current.createOffer(
        sessionConstraints,
      );
      await peerConnection.current.setLocalDescription(offerDescription);
      socket.emit('offerVideoCall', {offerDescription, chatRoomId});
    } catch (err) {
      // Handle Errors
    }
  };

  function handleRemoteCandidate(receivedIceCandidate: any) {
    const iceCandidate = new RTCIceCandidate(receivedIceCandidate);
    if (peerConnection.current.remoteDescription == null) {
      return setRemoteCandidates(prevCandidates => [
        ...prevCandidates,
        iceCandidate,
      ]);
    }

    return peerConnection.current.addIceCandidate(iceCandidate);
  }

  function processCandidates() {
    if (remoteCandidates.length < 1) {
      return;
    }

    remoteCandidates.map(candidate =>
      peerConnection.current.addIceCandidate(candidate),
    );
    setRemoteCandidates([]);
  }

  const answerOfferVideoCall = async (receivedOfferDescription: any) => {
    try {
      const offerDescription = new RTCSessionDescription(
        receivedOfferDescription,
      );
      await peerConnection.current.setRemoteDescription(offerDescription);

      const answerDescription = await peerConnection.current.createAnswer();
      await peerConnection.current.setLocalDescription(answerDescription);

      processCandidates();
      socket.emit('answerOfferVideoCall', {answerDescription, chatRoomId});
    } catch (err) {}
  };

  const onAnswerOfferVideoCall = async (receivedAnswerDescription: any) => {
    try {
      const answerDescription = new RTCSessionDescription(
        receivedAnswerDescription,
      );
      await peerConnection.current.setRemoteDescription(answerDescription);
    } catch (err) {}
  };

  useEffect(() => {
    const startCamera = async () => {
      let mediaConstraints = {
        audio: true,
        video: {
          frameRate: 30,
          facingMode: 'user',
        },
      };

      const mediaStream = await mediaDevices.getUserMedia(mediaConstraints);

      if (isVoiceOnly) {
        let videoTrack = await mediaStream.getVideoTracks()[0];
        videoTrack.enabled = false;
      }

      // Add our stream to the peer connection.
      mediaStream.getTracks().forEach(track => {
        peerConnection.current.addTrack(track, mediaStream);
      });

      setLocalMediaStream(mediaStream);
    };

    socket.on('offerVideoCall', (data: any) => {
      const {offerDescription} = data;
      answerOfferVideoCall(offerDescription);
    });

    socket.on('answerOfferVideoCall', (data: any) => {
      onAnswerOfferVideoCall(data);
    });

    socket.on('iceCandidate', (data: any) => {
      handleRemoteCandidate(data);
    });

    peerConnection.current.addEventListener('connectionstatechange', event => {
      console.error(
        `connectionstatechange ${peerConnection.current.connectionState}`,
      );
      switch (peerConnection.current.connectionState) {
        case 'closed':
          // You can handle the call being disconnected here.

          break;
      }
    });

    peerConnection.current.addEventListener('icecandidate', (event: any) => {
      if (!event.candidate) {
        return;
      }
      socket.emit('iceCandidate', {iceCandidate: event.candidate, chatRoomId});
    });

    peerConnection.current.addEventListener('negotiationneeded', event => {
      if (!negotiationNeeded) setNegotiationNeeded(true);
    });

    peerConnection.current.addEventListener(
      'iceconnectionstatechange',
      event => {
        switch (peerConnection.current.iceConnectionState) {
          case 'failed': {
            peerConnection.current.restartIce();
            break;
          }
          case 'connected':
          case 'completed':
            break;
        }
      },
    );

    peerConnection.current.addEventListener('track', event => {
      try {
        const remoteStream = new MediaStream(undefined);
        event.streams[0].getTracks().forEach((track, index) => {
          remoteStream.addTrack(track);
        });
        setRemoteMediaStream(remoteStream);
      } catch (e) {}
    });

    startCamera();
  }, []);

This is the code for signaling server

io.on('connection', (socket) => {
    socket.join(1)
    socket.on('offerVideoCall', (data) => {
        const { offerDescription, chatRoomId } = data
        socket.to(chatRoomId).emit('offerVideoCall', {
            offerDescription,
        })
    })

    socket.on('answerOfferVideoCall', (data) => {
        const { answerDescription, chatRoomId } = data
        socket.to(chatRoomId).emit('answerOfferVideoCall', answerDescription)
    })

    socket.on('iceCandidate', (data) => {
        const { iceCandidate, chatRoomId } = data
        socket.to(chatRoomId).emit('iceCandidate', iceCandidate)
    })
})

I was following the guide from github and try to create on my own but have lost it completely and I dont know where to fix.

0 Answers0