top of page
Search
  • Writer's pictureKyle

Create a React Native Video Calling App




Requirements:


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<