Create a React Native Video Calling App

Requirements:
Server-side implementation (node.js)
Client-side implementation with the module — react-native-twilio-video-webrtc
Twiilio Account
Before moving forward, you need to signup here and get ur necessary keys - ACCOUNT SID, API KEY SID, API KEY SECRET.

Then navigate to Settings => API Keys
Add a new api key, when ask for type, select the dropdown and choose main. Your api secret will only be shown once. please copy and paste somewhere safe as you will need all keys here for server-side implementation.

Server-side implementation
The step here is quite simple. Implement ur regular node express and add this route:
import 'dotenv/config';
import express from 'express';
import twilio from 'twilio';
const AccessToken = twilio.jwt.AccessToken;
const VideoGrant = AccessToken.VideoGrant;
const app = express();
app.get('/getToken', (req, res) => {
if (!req.query || !req.query.room || !req.query.username) {
return res.status(400).send('username and room parameter is required');
}
const accessToken = new AccessToken(
process.env.ACCOUNT_SID,
process.env.API_KEY_SID,
process.env.API_KEY_SECRET
); // Set the Identity of this token
const grant = new VideoGrant();
accessToken.identity = req.query.username;// Grant access to Video
grant.room = req.query.room;
accessToken.addGrant(grant); // Serialize the token as a JWT
const jwt = accessToken.toJwt();
return res.send(jwt);
});
What we have done here is simple. This endpoint get a username as string and a room as string in the body of the query. We create a room and add the user to that room awaiting other participants.
Client-side implementation
We add our twilio video webrtc module
yarn add https://github.com/blackuy/react-native-twilio-video-webrtc
After doing that, you follow the simple installation instructions here for both android and iOS.
import React, { useState, useRef } from "react";
import {
Alert,
AppRegistry,
StyleSheet,
Text,
TextInput,
View,
Button,
PermissionsAndroid,
Platform,
TouchableOpacity,
} from "react-native";
import {
TwilioVideoLocalView,
TwilioVideoParticipantView,
TwilioVideo,
} from "react-native-twilio-video-webrtc";
import styleSheet from "./styles";
const styles = StyleSheet.create(styleSheet);
const App = (props) => {
const [isAudioEnabled, setIsAudioEnabled] = useState(true);
const [isVideoEnabled, setIsVideoEnabled] = useState(true);
const [status, setStatus] = useState("disconnected");
const [participants, setParticipants] = useState(new Map());
const [videoTracks, setVideoTracks] = useState(new Map());
const [room, setRoom] = useState("");
const [username, setUsername] = useState("");
const twilioVideo = useRef(null);
const fetchToken = async () => {
try {
const res = await fetch(
`https://<your_base_url>/getToken?username=${username}&room=${room}`
);
if (!res.ok) {
console.log("error", error);
Alert.alert("API not available");
return null;
}
const jwt = await res.text();
return jwt;
} catch (error) {
console.log("error", error);
Alert.alert("An Error occurred");
return null;
}
};
const _onConnectButtonPress = async () => {
if (Platform.OS === "android") {
await _requestAudioPermission();
await _requestCameraPermission();
}
const token = await fetchToken();
if (!token) {
return;
}
twilioVideo.current.connect({
accessToken: token,
enableNetworkQualityReporting: true,
dominantSpeakerEnabled: true,
});
setStatus("connecting");
};
const _onEndButtonPress = () => {
twilioVideo.current.disconnect();
};
const _onMuteButtonPress = () => {
twilioVideo.current
.setLocalAudioEnabled(!isAudioEnabled)
.then((isEnabled) => setIsAudioEnabled(isEnabled));
};
const _onFlipButtonPress = () => {
twilioVideo.current.flipCamera();
};
const _onRoomDidConnect = () => {
setStatus("connected");
};
const _onRoomDidDisconnect = ({ error }) => {
console.log("ERROR: ", error);
setStatus("disconnected");
};
const _onRoomDidFailToConnect = (error) => {
console.log("ERROR: ", error);
setStatus("disconnected");
};
const _onParticipantAddedVideoTrack = ({ participant, track }) => {
console.log("onParticipantAddedVideoTrack: ", participant, track);
setVideoTracks(
new Map([
...videoTracks,
[
track.trackSid,
{ participantSid: participant.sid, videoTrackSid: track.trackSid },
],
])
);
};
const _onParticipantRemovedVideoTrack = ({ participant, track }) => {
console.log("onParticipantRemovedVideoTrack: ", participant, track);
const videoTracks = new Map(videoTracks);
videoTracks.delete(track.trackSid);
setVideoTracks(videoTracks);
};
const _onNetworkLevelChanged = ({ participant, isLocalUser, quality }) => {
console.log(
"Participant",
participant,
"isLocalUser",
isLocalUser,
"quality",
quality
);
};
const _onDominantSpeakerDidChange = ({ roomName, roomSid, participant }) => {
console.log(
"onDominantSpeakerDidChange",
`roomName: ${roomName}`,
`roomSid: ${roomSid}`,
"participant:",
participant
);
};
const _requestAudioPermission = () => {
return PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
{
title: "Need permission to access microphone",
message:
"To run this demo we need permission to access your microphone",
buttonNegative: "Cancel",
buttonPositive: "OK",
}
);
};
const _requestCameraPermission = () => {
return PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA, {
title: "Need permission to access camera",
message: "To run this demo we need permission to access your camera",
buttonNegative: "Cancel",
buttonPositive: "OK",
});
};
return (
<View style={styles.container}>
{status === "disconnected" && (
<View>
<Text style={styles.welcome}>React Native Twilio Video</Text>
<TextInput
style={styles.input}
autoCapitalize="none"
value={username}
onChangeText={(text) => setUsername(text)}
/>
<TextInput
style={styles.input}
autoCapitalize="none"
value={room}
onChangeText={(text) => setRoom(text)}
/>
<Button
title="Connect"
style={styles.button}
onPress={_onConnectButtonPress}
></Button>
</View>
)}
{(status === "connected" || status === "connecting") && (
<View style={styles.callContainer}>
{status === "connected" && (
<View style={styles.remoteGrid}>
{Array.from(videoTracks, ([trackSid, trackIdentifier]) => {
return (
<TwilioVideoParticipantView
style={styles.remoteVideo}
key={trackSid}
trackIdentifier={trackIdentifier}
/>
);
})}
</View>
)}
<View style={styles.optionsContainer}>
<TouchableOpacity
style={styles.optionButton}
onPress={_onEndButtonPress}
>
<Text style={{ fontSize: 12 }}>End</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.optionButton}
onPress={_onMuteButtonPress}
>
<Text style={{ fontSize: 12 }}>
{isAudioEnabled ? "Mute" : "Unmute"}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.optionButton}
onPress={_onFlipButtonPress}
>
<Text style={{ fontSize: 12 }}>Flip</Text>
</TouchableOpacity>
<TwilioVideoLocalView enabled={true} style={styles.localVideo} />
</View>
</View>
)}
<TwilioVideo
ref={twilioVideo}
onRoomDidConnect={_onRoomDidConnect}
onRoomDidDisconnect={_onRoomDidDisconnect}
onRoomDidFailToConnect={_onRoomDidFailToConnect}
onParticipantAddedVideoTrack={_onParticipantAddedVideoTrack}
onParticipantRemovedVideoTrack={_onParticipantRemovedVideoTrack}
onNetworkQualityLevelsChanged={_onNetworkLevelChanged}
onDominantSpeakerDidChange={_onDominantSpeakerDidChange}
/>
</View>
);
};
export default App;
Paste the above code in ur react native project and hit the connect button to start your video call. Oh, and make sure you have at least two devices to test and see that it actually works. Now that we have implemented a basic implementation of video calling in react native using Twilio , implementing both server-side and client side, showing how easy it is to add video call in ur project.
Now, the next step can be to add advanced features such as:
Ringing another participant or multiple participant video calling rooms, group calls, managing and displaying participant status such as busy, declined call or not answered.
Checkout this proper Video calling app, that offers phone contact chats like WhatsApp with powerful features.