Cuộc gọi video đã trở thành hoạt động thiết yếu hàng ngày trong đại dịch COVID-19. Nhờ các tính năng như chat app, gọi âm thanh, và gọi video, chúng ta đã giữ kết nối với bạn bè và gia đình.
Bây giờ, hãy tạo app React Native riêng để gọi video.
Trong tutorial này, chúng ta sẽ học cách implement tính năng gọi video trong app React Native dùng Twilio programmable video call API.
Quy trình khá đơn giản. Chúng ta chỉ tạo một phòng hội nghị video và mời người khác tham gia phòng đó. Để làm vậy, cần quyền truy cập camera và microphone. Vậy cần dùng smartphone thật để test.
Package chính dùng để truy cập Twilio API là react-native-twilio-video-webrtc.
Yêu Cầu
- Tài khoản Twilio
- Tối thiểu hai thiết bị iOS hoặc Android để test.
- Môi trường React Native đã setup.
Hãy bắt đầu!
Cách Lấy Twilio API Key
Để lấy Twilio API key, bạn cần tài khoản Twilio. Truy cập URL này. Sau khi setup tài khoản, vào vị trí theo screenshot dưới:

Cách Setup Server Xử Lý Lấy Access Token
Để lấy access token, cần tạo project Node server mới. Cần cài packages cần thiết bằng lệnh:
yarn add dotenv express ngrok nodemon twilioTiếp theo, thêm Twilio credentials vào file biến môi trường .env, như code snippet dưới:
PORT=3000 ACCOUNT_SID=AC5ceb0847c50c91b143ce07 API_KEY_SID=SKa173c10de99a26fd86969b API_KEY_SECRET=Czv7IjNIZJis8s7jb5FePiBây giờ cần tạo API endpoint. Trước tiên, import packages cần thiết và tạo object instances để lấy access token như code snippet dưới:
import 'dotenv/config'; import express from 'express'; import twilio from 'twilio'; import ngrok from 'ngrok'; const AccessToken = twilio.jwt.AccessToken; const VideoGrant = AccessToken.VideoGrant; const app = express();Ở đây, tạo API endpoint lấy access token. Dùng method get của Express instance, tạo endpoint function respond với access token.
Bên trong function, tạo instance mới với Twilio credentials. Thêm username nhận từ màn hình register trên mobile device làm identity attribute.
Cuối cùng, grant quyền truy cập cho user để dùng video và trả JWT token về device. Code làm tất cả điều này trong snippet dưới:
app.get('/getToken', (req, res) => { if (!req.query || !req.query.userName) { return res.status(400).send('Username 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 accessToken.identity = req.query.userName; // Grant access to Video var grant = new VideoGrant(); accessToken.addGrant(grant); // Serialize the token as a JWT var jwt = accessToken.toJwt(); return res.send(jwt); }); Chúng ta cũng expose endpoint API tạo ra ra internet để dễ truy cập. Dùng code từ snippet sau:
app.listen(process.env.PORT, () => console.log(`Server listening on port ${process.env.PORT}!`), ); ngrok.connect(process.env.PORT).then((url) => { console.log(`Server forwarded to public url ${url}`); }); Cuối cùng, chạy server như screenshot dưới:

Ở đây, đã tạo thành công API endpoint trả access token.
Cách Cấu Hình Project React Native Của Chúng Ta
Trong project React Native, cần setup packages thủ công và config permissions truy cập camera và microphone cho cả Android và iOS.
Nhưng trước tiên, cần cài packages cần thiết: react-navigation và react-native-twilio-video-webrtc, bằng lệnh sau trong terminal project:
yarn add @react-navigation/native @react-navigation/stack react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view react-native-dotenv react-native-permissions <https://github.com/blackuy/react-native-twilio-video-webrtc> Setup Cho iOS
Với iOS, cần setup package thủ công. Trước hết, cần tăng IOS target lên 11 trên Podfile. Cần thiết vì Twilio Native Video SDK chỉ hỗ trợ iOS 11.0+:
platform :ios, '11.0' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' Trong Podfile, setup permission request như code snippet dưới:
permissions_path = '../node_modules/react-native-permissions/ios' pod 'Permission-Camera', :path => "#{permissions_path}/Camera.podspec" pod 'Permission-Microphone', :path => "#{permissions_path}/Microphone.podspec" Sau đó, mở info.plist thêm code yêu cầu permission camera và microphone như snippet dưới:
<key>UIViewControllerBasedStatusBarAppearance</key> <false/> <key>NSCameraUsageDescription</key> <string>We require your permission to access the camera while in a video call</string> <key>NSMicrophoneUsageDescription</key> <string>We require your permission to access the microphone while in a video call</string> Xong setup iOS.
Setup Cho Android
Trước hết, thêm dòng code sau vào file ./android/settings.gradle:
project(':react-native-twilio-video-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-twilio-video-webrtc/android') Cũng cần thêm package implementation code vào ./android/app/build.gradle:
implementation project(':react-native-twilio-video-webrtc') Cuối cùng, import vào file MainApplication.java:
import com.twiliorn.library.TwilioPackage; Sau đó, activate package bằng code sau:
@Override protected List getPackages() { @SuppressWarnings("UnnecessaryLocalVariable") List packages = new PackageList(this).getPackages(); // add the following code packages.add(new TwilioPackage()); return packages; } Cách Xây Dựng Màn Hình Register Room
Ở đây, tạo màn hình "Register Room" cho phép register vào phòng trong app video call React Native.
Trước hết, import packages cần thiết như snippet dưới:
import React, {useState, useRef, useEffect, useContext} from 'react'; import { StyleSheet, View, Text, StatusBar, TouchableOpacity, TextInput, Alert, KeyboardAvoidingView, Platform, ScrollView, Dimensions, } from 'react-native'; import { TwilioVideoLocalView, TwilioVideoParticipantView, TwilioVideo, } from 'react-native-twilio-video-webrtc'; import {NavigationContainer} from '@react-navigation/native'; import {createStackNavigator} from '@react-navigation/stack'; - react-navigation: Xử lý navigation màn hình register và video call.
- react-native: Package cho phép handle permissions truy cập camera và microphone.
- react-native-twilio-video-webrtc: Cho phép truy cập Twilio Video call programmable API.
Cách Khởi Tạo Instances Và Variables
Trước hết, tạo instance cho react-navigation. Sau đó, khởi tạo states và context variables để phân phối states như snippet dưới:
const Stack = createStackNavigator(); const initialState = { isAudioEnabled: true, status: 'disconnected', participants: new Map(), videoTracks: new Map(), userName: '', roomName: '', token: '', }; const AppContext = React.createContext(initialState); const dimensions = Dimensions.get('window'); Bootstrap Navigation
Trong file App.js, tạo navigation container stack. Dùng component Stack phân phối state đến mọi màn hình dùng context như snippet dưới:
export default () => { const [props, setProps] = useState(initialState); return ( <> <StatusBar barStyle="dark-content" /> <AppContext.Provider value={{props, setProps}}> <NavigationContainer> <Stack.Navigator> <Stack.Screen name="Home" component={HomeScreen} /> <Stack.Screen name="Video Call" component={VideoCallScreen} /> </Stack.Navigator> </NavigationContainer> </AppContext.Provider> </> ); }; Cách Tạo Màn Hình Register
Màn hình Register sẽ có modal dialog lấy credentials user và cho user join phòng video call.
Trước hết, fetch props từ context vào file register.js như snippet dưới:
import React, {useState, useRef, useEffect, useContext} from 'react'; import { checkMultiple, request, requestMultiple, PERMISSIONS, RESULTS, } from 'react-native-permissions'; const RegisterScreen = ({navigation}) => { const {props, setProps} = useContext(AppContext); Tiếp theo, tạo function handle camera và microphone permissions. Code function trong snippet dưới:
const _checkPermissions = (callback) => { const iosPermissions = [PERMISSIONS.IOS.CAMERA, PERMISSIONS.IOS.MICROPHONE]; const androidPermissions = [ PERMISSIONS.ANDROID.CAMERA, PERMISSIONS.ANDROID.RECORD_AUDIO, ]; checkMultiple( Platform.OS === 'ios' ? iosPermissions : androidPermissions, ).then((statuses) => { const [CAMERA, AUDIO] = Platform.OS === 'ios' ? iosPermissions : androidPermissions; if ( statuses[CAMERA] === RESULTS.UNAVAILABLE || statuses[AUDIO] === RESULTS.UNAVAILABLE ) { Alert.alert( 'Error', 'Hardware to support video calls is not available', ); } else if ( statuses[CAMERA] === RESULTS.BLOCKED || statuses[AUDIO] === RESULTS.BLOCKED ) { Alert.alert( 'Error', 'Permission to access hardware was blocked, please grant manually', ); } else { if ( statuses[CAMERA] === RESULTS.DENIED && statuses[AUDIO] === RESULTS.DENIED ) { requestMultiple( Platform.OS === 'ios' ? iosPermissions : androidPermissions, ).then((newStatuses) => { if ( newStatuses[CAMERA] === RESULTS.GRANTED && newStatuses[AUDIO] === RESULTS.GRANTED ) { callback && callback(); } else { Alert.alert('Error', 'One of the permissions was not granted'); } }); } else if ( statuses[CAMERA] === RESULTS.DENIED || statuses[AUDIO] === RESULTS.DENIED ) { request(statuses[CAMERA] === RESULTS.DENIED ? CAMERA : AUDIO).then( (result) => { if (result === RESULTS.GRANTED) { callback && callback(); } else { Alert.alert('Error', 'Permission not granted'); } }, ); } else if ( statuses[CAMERA] === RESULTS.GRANTED || statuses[AUDIO] === RESULTS.GRANTED ) { callback && callback(); } } }); }; Sau đó, gọi function check permission này mỗi lần app start. Gọi function trong hook useEffect như snippet dưới:
useEffect(() => { _checkPermissions(); }, []); Cuối cùng, tạo form đơn giản với hai input nhận room name và user name. Sau đó, gửi input đến server để register trên Twilio API. Code trong snippet dưới:
return ( <KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : 'height'} style={styles.container}> <ScrollView contentContainerStyle={styles.container}> <View style={styles.form}> <View style={styles.formGroup}> <Text style={styles.text}>User Name</Text> <TextInput style={styles.textInput} autoCapitalize="none" value={props.userName} onChangeText={(text) => setProps({...props, userName: text})} /> </View> <View style={styles.formGroup}> <Text style={styles.text}>Room Name</Text> <TextInput style={styles.textInput} autoCapitalize="none" value={props.roomName} onChangeText={(text) => setProps({...props, roomName: text})} /> </View> <View style={styles.formGroup}> <TouchableOpacity disabled={false} style={styles.button} onPress={() => { _checkPermissions(() => { fetch(`https://ae7a722dc260.ngrok.io/getToken?userName=${props.userName}`) .then((response) => { if (response.ok) { response.text().then((jwt) => { setProps({...props, token: jwt}); navigation.navigate('Video Call'); return true; }); } else { response.text().then((error) => { Alert.alert(error); }); } }) .catch((error) => { console.log('error', error); Alert.alert('API not available'); }); }); }}> <Text style={styles.buttonText}>Connect to Video Call</Text> </TouchableOpacity> </View> </View> </ScrollView> </KeyboardAvoidingView> ); Kết quả như screenshot emulator dưới:

Ở đây, thấy màn hình Register Room với modal form nhập room name và user name để register vào Twilio API chạy trên server.
Cách Xây Dựng Màn Hình Video Call
Trong màn hình Video Call, sẽ có hai cửa sổ – một hiển thị view camera của mình, cái kia hiển thị view camera của người nhận.
Trước hết, khởi tạo context nhận states. Sau đó tạo reference variable dùng hook useRef truy cập states như snippet dưới:
const VideoCallScreen = ({navigation}) => { const twilioVideo = useRef(null); const {props, setProps} = useContext(AppContext); Tiếp theo, khởi tạo connection dùng method connect từ object twilioVideo, cung cấp room name và access token như snippet dưới:
useEffect(() => { twilioVideo.current.connect({ roomName: props.roomName, accessToken: props.token, }); setProps({...props, status: 'connecting'}); return () => { _onEndButtonPress(); }; }, []); Bây giờ, tạo main body template cho Video Call Screen. Ở đây, hiển thị participant camera view chỉ khi connection established và streaming dùng conditional rendering. Code tổng thể trong snippet dưới:
{(props.status === 'connected' || props.status === 'connecting') && ( <View style={styles.callWrapper}> {props.status === 'connected' && ( <View style={styles.grid}> {Array.from(props.videoTracks, ([trackSid, trackIdentifier]) => ( <TwilioVideoParticipantView style={styles.remoteVideo} key={trackSid} trackIdentifier={trackIdentifier} /> ))} </View> )} </View> )} Tiếp theo, tạo functions điều khiển tính năng trong video như end call, mute, chuyển camera trước/sau. Implementation trong snippet dưới:
const _onEndButtonPress = () => { twilioVideo.current.disconnect(); setProps(initialState); }; const _onMuteButtonPress = () => { twilioVideo.current .setLocalAudioEnabled(!props.isAudioEnabled) .then((isEnabled) => setProps({...props, isAudioEnabled: isEnabled})); }; const _onFlipButtonPress = () => { twilioVideo.current.flipCamera(); }; Ở đây, dùng method disconnect, setLocalAudioEnabled và flipCamera từ instance twilioVideo trigger các tính năng trong video.
Bây giờ render buttons trigger functions. Dùng code từ snippet sau:
<View style={styles.optionsContainer}> <TouchableOpacity style={styles.button} onPress={_onEndButtonPress}> <Text style={styles.buttonText}>End</Text> </TouchableOpacity> <TouchableOpacity style={styles.button} onPress={_onMuteButtonPress}> <Text style={styles.buttonText}> {props.isAudioEnabled ? 'Mute' : 'Unmute'} </Text> </TouchableOpacity> <TouchableOpacity style={styles.button} onPress={_onFlipButtonPress}> <Text style={styles.buttonText}>Flip</Text> </TouchableOpacity> </View> Bước cuối, thêm component TwilioVideo config handle và observe tất cả event video call. Component TwilioVideo config tổng thể trong snippet dưới:
<TwilioVideo ref={twilioVideo} onRoomDidConnect={() => { setProps({...props, status: 'connected'}); }} onRoomDidDisconnect={() => { setProps({...props, status: 'disconnected'}); navigation.goBack(); }} onRoomDidFailToConnect={(error) => { Alert.alert('Error', error.error); setProps({...props, status: 'disconnected'}); navigation.goBack(); }} onParticipantAddedVideoTrack={({participant, track}) => { if (track.enabled) { setProps({ ...props, videoTracks: new Map([ ...props.videoTracks, [ track.trackSid, { participantSid: participant.sid, videoTrackSid: track.trackSid, }, ], ]), }); } }} onParticipantRemovedVideoTrack={({track}) => { const videoTracks = props.videoTracks; videoTracks.delete(track.trackSid); setProps({...props, videoTracks}); }} /> Kết quả sau nếu thiết lập kết nối đúng giữa user trong phòng:

Screenshot trên demo video call giữa hai participant trong phòng.
Với này, đã implement thành công tính năng Video Calling trong app React Native.
Kết Luận
Tutorial này nhằm cung cấp tài nguyên học tập mức beginner về setup Video Calling trong app React Native. Làm bằng Twilio programmable video call API.
Chúng ta không chỉ cover phần React Native mà còn overall API implementation trong project Node server riêng.
Bước tiếp theo có thể thêm advanced features như anonymous call hoặc multiple participant video calling rooms.
Để cảm hứng tính năng và app video call proper, check instamobile.io cung cấp state of Video Chat app với powerful features.
See ya next time folks, Happy Coding!