I have a problem with implementing Video call with WebRTC peer connection

54 views Asked by At
import {
    mediaDevices,
    RTCPeerConnection,
    RTCView,
    RTCIceCandidate,
    RTCSessionDescription,
    MediaStream,
} from 'react-native-webrtc';

import styles from './callPageStyle';
import { useEffect, useState, useRef } from "react";

const CallPage = ({ route, navigation }) => {

// const navigation = useNavigation();
const userData = useSelector((state) => state.userData);
const dispatch = useDispatch();

const { callerId, calleeId, calleeName, isVideo, isCreator } = route.params;

const [localStream, setLocalStream] = useState(null);
const [remoteStream, setRemoteStream] = useState(null);
const [callStarted, setCallStarted] = useState(false);
const peerConnectionRef = useRef(null);
const remoteRTCMessage = useRef(null);

useEffect(() => {
    const recipientID = isCreator ? calleeId : callerId;
    webSocketService.registerSendCallMessageCallback(recipientID, onNewCall);
    webSocketService.registerAnswerCallMessageCallback(recipientID, onCallAnswered);
    webSocketService.registerICECandidateMessageCallback(recipientID, onICEcandidate);

    const initLocalStream = async () => {
        const stream = await mediaDevices.getUserMedia({ audio: true, video: true });
        setLocalStream(stream);
    };

    const delayProcessCall = setTimeout(() => {
        if (!isCreator) {
            startCall();
        } else {
            initLocalStream();
        }
    }, 2000);

    return () => {
        clearTimeout(delayProcessCall);

        if (localStream) {
            localStream.getTracks().forEach((track) => track.stop());
        }
    };
}, []);


const createPeerConnection = async () => {
    const configuration = {
      iceServers: [
        { urls: 'stun:stun.l.google.com:19302' },
        { urls: 'stun:stun1.l.google.com:19302' },
        { urls: 'stun:stun2.l.google.com:19302' },
      ],
    };
    const peerConnection = new RTCPeerConnection(configuration);
  
    // Use a promise to wait for getUserMedia to complete
    const stream = await mediaDevices.getUserMedia({ audio: true, video: true });
    stream.getTracks().forEach((track) => peerConnection.addTrack(track, stream));
    setLocalStream(stream);
  
    return new Promise((resolve) => {
        peerConnection.onicecandidate = (event) => {
            if (event.candidate) {
            webSocketService.sendICECandidateToPeerServer(userData.id, isCreator ? calleeId : callerId, {
                calleeId: isCreator ? calleeId : callerId,
                rtcMessage: {
                label: event.candidate.sdpMLineIndex,
                id: event.candidate.sdpMid,
                candidate: event.candidate.candidate,
                },
            });
            } else {
                console.log("End of candidates.");
            }
        };
    
        peerConnection.ontrack = (event) => {
            setRemoteStream(event.streams[0]);
        };
    
        peerConnectionRef.current = peerConnection;
        resolve();
    });
};

const startCall = async () => {    
    try {
        console.log("=========sendNewCallToPeerServer===========");
        await createPeerConnection();

        const offer = await peerConnectionRef.current.createOffer();
        await peerConnectionRef.current.setLocalDescription(offer);

        webSocketService.sendNewCallToPeerServer(userData.id, isCreator ? calleeId : callerId, {
            calleeId: isCreator ? calleeId : callerId,
            rtcMessage: offer,
        });

        console.log("=========sendNewCallToPeerServer===========");
    } catch (error) {
      console.error('Error creating offer:', error);
    }
};

const handleAnswer = async () => {    
    try {
        const answer = await peerConnectionRef.current.createAnswer();
        await peerConnectionRef.current.setLocalDescription(answer);

        webSocketService.sendAnswerCallToPeerServer(userData.id, isCreator ? calleeId : callerId, {
            calleeId: isCreator ? calleeId : callerId,
            rtcMessage: answer,
        });

        setCallStarted(true);
    } catch (error) {
        console.error('Error creating answer:', error);
    }
};

const onNewCall = async (payload) => {
    console.log("On New Call !!!");
    const bigData = JSON.parse(payload.body);
    const data = JSON.parse(bigData.data);

    remoteRTCMessage.current = data.rtcMessage;

    await createPeerConnection();
    peerConnectionRef.current.setRemoteDescription(
        new RTCSessionDescription(remoteRTCMessage.current)
    );

    handleAnswer();
};

const onCallAnswered = (payload) => {
    console.log("onCallAnswered !!!");
    const bigData = JSON.parse(payload.body);
    const data = JSON.parse(bigData.data);
    
    remoteRTCMessage.current = data.rtcMessage;
    peerConnectionRef.current.setRemoteDescription(
        new RTCSessionDescription(remoteRTCMessage.current)
    );

    setCallStarted(true);
};

const onICEcandidate = (payload) => {
    console.log("onICEcandidate !!!");
    const bigData = JSON.parse(payload.body);
    const data = JSON.parse(bigData.data);

    let message = data.rtcMessage;

    if (peerConnectionRef.current) {
        peerConnectionRef.current
        .addIceCandidate(new RTCIceCandidate({candidate: message.candidate, sdpMLineIndex: message.label, sdpMid: message.id}))
        .then((data) => {
            console.log("SUCCESS");
        })
        .catch((err) => {
            console.log("Error", err);
        });
    }
};

This is my source code implementing video call feature in react-native expo app.

I am currently using libraries for react-native-webrtc below.

@config-plugins/react-native-webrtc": "^7.0.0

react-native-webrtc": "^111.0.3,

I am using Android emulator like this:

  • API level 34
  • image: system-images/android-34/google_apis/x86_x64

All the logic for getting connection between peers is working well. But have problems with video streaming. Help me with correcting the source code or giving any advice. Thanks.

1

There are 1 answers

0
Dark Moon On

I also faced that kind of problem before. Implementing Video call wit WebRTC streaming contains such a complex process. There is more easier way implementing video call without using basic libraries. I suggest to use 'agora-rn-uikit' library in React Native.

More details instructions here:

https://www.npmjs.com/package/agora-rn-uikit

Installation Command:

npm i react-native-agora agora-react-native-rtm agora-rn-uikit

Sample Code:

const connectionData = {
    appId: '<Agora App ID>',
    channel: 'test',
};
const rtcCallbacks = {
    EndCall: () => setVideoCall(false),
};

<AgoraUIKit connectionData={connectionData} rtcCallbacks={rtcCallbacks} />

Easy to use and faster development speed.