Video Calling กลายเป็นกิจกรรมประจำวันที่จำเป็นในช่วงวิกฤต COVID-19 ด้วยฟีเจอร์เช่น Chat Apps, Audio Calling, และ Video Calling เราจึงสามารถเชื่อมต่อกับเพื่อนและครอบครัวได้
ตอนนี้ มาสร้าง React Native App ของเราเองที่จะให้เราทำ Video Calls ได้
ในบทช่วยสอนนี้ เราจะเรียนรู้วิธี Implement Video Calling Feature ใน React Native App โดยใช้ Twilio Programmable Video Call API
กระบวนการค่อนข้างตรงไปตรงมา เราจะสร้าง Video Conference Room และเชิญผู้อื่นเข้าร่วม Room นั้น เพื่อทำเช่นนี้ เราต้องการการเข้าถึง Camera และ Microphone ดังนั้นเราจะต้องใช้ Smartphone จริงสำหรับการทดสอบ
Package หลักที่เราจะใช้เพื่อเข้าถึง Twilio API คือ react-native-twilio-video-webrtc
ข้อกำหนด
- Twilio Account
- อุปกรณ์ iOS หรือ Android อย่างน้อย 2 เครื่องสำหรับทดสอบ
- React Native Environment Setup
Let’s get started!
วิธีได้ Twilio API Key
เพื่อได้ Twilio API Key คุณต้องมี Twilio Account สำหรับนั้น เยี่ยมชม URL นี้ หลังตั้งค่าบัญชีแล้ว ไปที่ตำแหน่งที่ Screenshot ชี้ไปด้านล่าง:
วิธีตั้งค่า Server เพื่อจัดการ Fetch Access Token
เพื่อ Fetch Access Token เราต้องสร้าง Node Server Project ใหม่ สำหรับนั้นเราต้องติดตั้ง Packages ที่จำเป็นโดยรันคำสั่ง:
yarn add dotenv express ngrok nodemon twilio
ต่อไป เราต้องเพิ่ม Twilio Credentials ใน Environment Variables File — .env ตามโค้ดด้านล่าง:
PORT=3000
ACCOUNT_SID=AC5ceb0847c50c91b143ce07
API_KEY_SID=SKa173c10de99a26fd86969b
API_KEY_SECRET=Czv7IjNIZJis8s7jb5FePi
ตอนนี้เราต้องสร้าง API Endpoint ก่อนอื่นต้อง Import Packages ที่จำเป็น และสร้าง Object Instances เพื่อได้ Access Token ตามโค้ดด้านล่าง:
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();
ที่นี่เราจะสร้าง API Endpoint เพื่อได้ Access Token โดยใช้ Get Method จาก Express Instance สร้าง Endpoint Function ที่ตอบสนองด้วย Access Token
ภายในฟังก์ชัน เราต้องสร้าง Instance ใหม่ด้วย Twilio Credentials จากนั้นเพิ่ม Username ที่ได้จาก Register Screen บน Mobile Device เป็น Identity Attribute
สุดท้าย เรา Grant Access ให้ User ใช้ Video และคืน JWT Token กลับไปยัง Device นี่คือโค้ดทำทั้งหมด:
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);
});
เรายัง Expose Endpoint API ที่สร้างไปสู่ Internet เพื่อการเข้าถึงง่าย สำหรับนั้นใช้โค้ดจาก Snippet ต่อไปนี้:
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}`);
});
สุดท้าย เราต้อง Run Server ตาม Screenshot ด้านล่าง:
ที่นี่ เราสร้าง API Endpoint สำหรับคืน Access Token สำเร็จแล้ว
วิธี Configure React Native Project ของเรา
ใน React Native Project เราต้อง Setup Packages แบบ Manual และ Configure Permissions เข้าถึง Camera และ Microphone สำหรับ Android และ iOS
แต่ก่อนอื่น ต้อง Install Packages ที่จำเป็น คือ react-navigation และ react-native-twilio-video-webrtc โดยรันคำสั่งใน Terminal โปรเจกต์:
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
Setup สำหรับ iOS
สำหรับ iOS เราต้อง Setup Package แบบ Manual ก่อนอื่น ต้องเพิ่ม IOS Target เป็น 11 ใน Podfile จำเป็นเพราะ Twilio Native Video SDK รองรับ iOS 11.0+ เท่านั้น:
platform :ios, '11.0'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
ใน Podfile เราต้อง Setup Permission Request ตามโค้ดด้านล่าง:
permissions_path = '../node_modules/react-native-permissions/ios'
pod 'Permission-Camera', :path => "#{permissions_path}/Camera.podspec"
pod 'Permission-Microphone', :path => "#{permissions_path}/Microphone.podspec"
จากนั้น เปิด info.plist ใส่โค้ดขอ Permission เข้าถึง Camera และ Microphone ตามโค้ดด้านล่าง:
<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>
ตอนนี้เราทำ iOS Setup เสร็จแล้ว
Setup สำหรับ Android
ก่อนอื่น ต้องเพิ่มบรรทัดโค้ดต่อไปนี้ในไฟล์ ./android/settings.gradle:
project(':react-native-twilio-video-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-twilio-video-webrtc/android')
นอกจากนี้ ต้องเพิ่ม Package Implementation Code ใน ./android/app/build.gradle:
implementation project(':react-native-twilio-video-webrtc')
สุดท้าย ต้อง Import เข้า MainApplication.java ด้วย:
import com.twiliorn.library.TwilioPackage;
จากนั้น Activate Package ด้วยโค้ด:
@Override
protected List getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List packages = new PackageList(this).getPackages();
// add the following code
packages.add(new TwilioPackage());
return packages;
}
วิธีสร้าง Register Room Screen
ที่นี่เราจะสร้าง Screen ชื่อ "Register Room" ซึ่งจะให้เราลงทะเบียนเข้าห้องใน Video Calling React Native App
ก่อนอื่น ต้อง Import Packages ที่จำเป็นตามโค้ดด้านล่าง:
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: จัดการ Navigation ของ Register Screen และ Video Call Screen
- react-native: Package นี้ช่วยจัดการ Permissions เข้าถึง Camera และ Microphone
- react-native-twilio-video-webrtc: เข้าถึง Twilio Video Call Programmable API
วิธี Initialize Instances และ Variables
ก่อนอื่น สร้าง Instance สำหรับ react-navigation จากนั้น Initialize States และ Context Variables เพื่อแจกจ่าย States ตามโค้ดด้านล่าง:
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
ในไฟล์ App.js เราจะสร้าง Navigation Container Stack ใช้ Component Stack แจกจ่าย State ไปทุก Screen ผ่าน Context ตามโค้ดด้านล่าง:
export default () => {
const [props, setProps] = useState(initialState);
return (
<>
>
);
};
วิธีสร้าง Register Screen
Register Screen จะมี Modal Dialog รับ Credentials ผู้ใช้ และให้ User เข้าร่วม Video Call Room
ก่อนอื่น ต้อง Fetch Props จาก Context ไปที่ไฟล์ register.js ตามโค้ดด้านล่าง:
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);
ต่อไป สร้างฟังก์ชันจัดการ Camera และ Microphone Permissions โค้ดฟังก์ชันอยู่ใน Snippet ด้านล่าง:
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();
}
}
});
};
จากนั้น เราต้องเรียก Permission-Checking Function ทุกครั้งที่แอปเริ่มต้น สำหรับนั้น เรียกฟังก์ชันภายใน Hook useEffect ตามโค้ดด้านล่าง:
useEffect(() => {
_checkPermissions();
}, []);
สุดท้าย สร้าง Form เรียบง่าย สอง Input รับ Room Name และ User Name จากนั้นส่ง Input ไป Server เพื่อ Register บน Twilio API โค้ดอยู่ใน Snippet ด้านล่าง:
return (
User Name
setProps({...props, userName: text})} />
Room Name
setProps({...props, roomName: text})} />
{
_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');
});
});
}}>
Connect to Video Call
);
เราจะได้ผลลัพธ์ตาม Screenshot Emulator ด้านล่าง:
ที่นี่เราสังเกต Register Room Screen พร้อม Modal Form ที่เราสามารถ Input Room Name และ User Name เพื่อ Register เข้า Twilio API ที่รันบน Server
วิธีสร้าง Video Call Screen
ใน Video Call Screen เราจะมีสองหน้าต่าง — หนึ่งแสดง View ของ Camera ตัวเอง อีกอันแสดง View ของ Camera ผู้รับ
ก่อนอื่น Initialize Context รับ States จากนั้นสร้าง Reference Variable ด้วย Hook useRef เพื่อเข้าถึง States ตามโค้ดด้านล่าง:
const VideoCallScreen = ({navigation}) => {
const twilioVideo = useRef(null);
const {props, setProps} = useContext(AppContext);
ต่อไป Initialize Connection ด้วยเมธอด connect จาก Object twilioVideo ให้ Room Name และ Access Token ตามโค้ดด้านล่าง:
useEffect(() => {
twilioVideo.current.connect({
roomName: props.roomName,
accessToken: props.token,
});
setProps({...props, status: 'connecting'});
return () => {
_onEndButtonPress();
};
}, []);
ตอนนี้ สร้าง Main Body Template สำหรับ Video Call Screen ที่นี่เราแสดง Participant Camera View เฉพาะเมื่อ Connection Established และ Streaming โดยใช้ Conditional Rendering โค้ดรวมอยู่ใน Snippet ด้านล่าง:
{(props.status === 'connected' || props.status === 'connecting') && (
{props.status === 'connected' && (
{Array.from(props.videoTracks, ([trackSid, trackIdentifier]) => (
))}
)}
)}
ต่อไป สร้างฟังก์ชันควบคุม In-Video Features เช่น End Call, Mute, และ Switch Front/Back Camera การ Implement ฟังก์ชันที่จำเป็นอยู่ใน Snippet ด้านล่าง:
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();
};
ที่นี่เราใช้เมธอด disconnect, setLocalAudioEnabled และ flipCamera จาก Instance twilioVideo เพื่อ Trigger In-Video Features
ตอนนี้เราต้อง Render ปุ่มบางปุ่มเพื่อ Trigger ฟังก์ชัน สำหนั้นใช้โค้ดจาก Snippet ต่อไปนี้:
End
{props.isAudioEnabled ? 'Mute' : 'Unmute'}
Flip
ขั้นตอนสุดท้าย เพิ่ม Component TwilioVideo ที่ Config จัดการและสังเกต Video Calling Events ทั้งหมด Component TwilioVideo ที่ Config แล้วอยู่ใน Snippet ด้านล่าง:
{
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});
}}
/>
เราจะได้ผลลัพธ์ดังนี้ ถ้าสามารถสร้าง Connection ระหว่าง Users ใน Room ได้อย่างถูกต้อง:
Screenshots ด้านบนแสดง Video Calling ระหว่างสอง Participants ใน Room
ด้วยนี้ เรา Implement Video Calling Feature ใน React Native App สำเร็จแล้ว
สรุป
บทช่วยสอนนี้มุ่งส่งมอบ Learning Resource ระดับ Beginner เกี่ยวกับการ Setup Video Calling ใน React Native App เราทำโดยใช้ Twilio Programmable Video Call API
เราไม่ได้ Cover แค่ React Native Part แต่ยังรวม Overall API Implementation ใน Node Server Project แยกต่างหาก
ขั้นตอนต่อไป อาจเพิ่ม Advanced Features เช่น เริ่ม Anonymous Call หรือ Multiple Participant Video Calling Rooms
สำหรับ Feature Inspiration และ Video Calling App ที่เหมาะสม คุณสามารถดู instamobile.io ที่เสนอ State of a Video Chat App พร้อม Features อันทรงพลัง
See ya next time folks, Happy Coding!