In this article, we will learn how to make a video call using sendbird in react js.
first, we will login and create an application in sendbird and as per the below dashboard, u can see the application details.
You can start making a 1-to-1 call by installing Sendbird Calls for JavaScript.
step 1
Install sendbird-calls
dependency to the package.json
file in your project.
# npm npm install sendbird-calls #yarn yarn add sendbird-calls
step 2
Import and initialize with appid as shown below:
import SendBirdCall from "sendbird-calls"; SendBirdCall.init(APP_ID)
step 3
To make and receive a 1-to-1 call or start a group call, create a user and authenticate a user to the Sendbird server by using their user ID through the authenticate()
method. To receive calls, the SendBirdCall
instance should be connected with the Sendbird server. Connect socket by using the SendBirdCall.connectWebSocket()
method after a user’s authentication has been completed.
// The USER_ID below should be unique to your Sendbird application. const authOption = { userId: USER_ID, accessToken: ACCESS_TOKEN }; SendBirdCall.authenticate(authOption, (result, error) => { if (error) { // Handle authentication failure. } else { // The user has been successfully authenticated and is connected to Sendbird server. ... } }); // Establishing websocket connection. SendBirdCall.connectWebSocket() .then(/* Succeeded to connect */) .catch(/* Failed to connect */);
step 4
Sendbird Calls provides SendBirdCallListener
event handler for events related to Direct calls. It is used to manage device-specific events such as incoming calls.
// The UNIQUE_HANDLER_ID below is a unique user-defined ID for a specific event handler. SendBirdCall.addListener(UNIQUE_HANDLER_ID, { onRinging: (call) => { ... } });
step 5
You are now ready to make your first 1-to-1 call. To make a call, provide the callee’s user ID into the SendBirdCall.dial()
method. To choose initial call configuration such as audio or video capabilities, video settings, and mute settings, use the CallOption
object.
After making a call, add the call-specific DirectCallListener
event handler to the call object. It allows the callee’s app to respond to events happening during the call through its callback methods.
const dialParams = { userId: CALLEE_ID, isVideoCall: true, callOption: { localMediaView: document.getElementById('local_video_element_id'), remoteMediaView: document.getElementById('remote_video_element_id'), audioEnabled: true, videoEnabled: true } }; const call = SendBirdCall.dial(dialParams, (call, error) => { if (error) { // Dialing failed. } // Dialing succeeded. }); call.onEstablished = (call) => { ... }; call.onConnected = (call) => { ... }; call.onEnded = (call) => { ... }; call.onRemoteAudioSettingsChanged = (call) => { ... }; call.onRemoteVideoSettingsChanged = (call) => { ... };
step 6
You can accept or decline an incoming call. To accept an incoming call, use the directCall.accept()
. To decline a call, use the directCall.end()
method.
const acceptParams = { callOption: { localMediaView: document.getElementById('local_video_element_id'), remoteMediaView: document.getElementById('remote_video_element_id'), audioEnabled: true, videoEnabled: true } }; call.accept(acceptParams);
you can see the video call code below
import React, { useState, useEffect } from 'react' import SendBirdCall from "sendbird-calls"; function Home() { const [userId, setuserId] = useState('him') const [appId, setappId] = useState('412F3DBD-3AFC-40FE-8C38-8CB43F3AA7D8') const [display, setdisplay] = useState(false) const [callView, setcallView] = useState(false) const [currentCall, setcurrentCall] = useState() const [acceptCalls, setacceptCall] = useState(false) const [callEnds, setcallEnd] = useState(false) const [disabled, setdisabled] = useState(true) const [callerName, setcallerName] = useState(null) const [hold, sethold] = useState(false) const [fullWidth, setFullWidth] = useState(false) const [RemortVideo, setRemortVideo] = useState(false) const [muteMicro, setMuteMicro] = useState(false) const [remortOnHold, setRemortOnHold] = useState(false) const [unholdflag, setunholdflag] = useState(false) const [holdflag, setholdflag] = useState(false) const [ab, setab] = useState() const login = () => { const option = { userId } SendBirdCall.init(appId) return SendBirdCall.authenticate(option) .then(user => { setab(userId) localStorage.setItem('userId', userId) setdisplay(true) }) .catch(error => { alert("please check details") }) } function getCallOption(callOption) { console.log(callOption, "callOption") setcallView(true) return { localMediaView: document.getElementById('local_video_element_id'), remoteMediaView: document.getElementById('remote_video_element_id'), audioEnabled: true, videoEnabled: true, nickname: "dbsfkjsdbf", ...callOption } } const dial = (isVideoCall) => { SendBirdCall.connectWebSocket() .then((re) => { console.log("connect") const call = SendBirdCall.dial({ userId, isVideoCall, callOption: getCallOption({ dial }) }) console.log(call, "me") call.onEnded = (call) => { console.log('onEnded') setcallView(false) }; setcurrentCall(call) }).catch((err) => { console.log(err) }) } const callEnd = (call) => { console.log("call end") setcallEnd(true) currentCall.end() } const acceptCall = () => { setacceptCall(false) const acceptParams = { callOption: { localMediaView: document.getElementById('local_video_element_id'), remoteMediaView: document.getElementById('remote_video_element_id'), audioEnabled: true, videoEnabled: true } }; currentCall.accept(acceptParams) } const stopVideo = () => { setdisabled(!disabled) if (!disabled) { currentCall.startVideo() } else { currentCall.stopVideo() } } const holeCall = () => { try { setholdflag(true) console.log('call hold') currentCall.hold() sethold(true) } catch (error) { console.log("another call is already on hold") } } const unholdCall = () => { try { currentCall.unhold() console.log('call unhold') sethold(false) setunholdflag(true) } catch (error) { console.log("another call is already on hold") } } const handleVideoClick = () => { setFullWidth(!fullWidth) } const handleMute = () => { if (muteMicro) { currentCall.unmuteMicrophone() setMuteMicro(false) } else { currentCall.muteMicrophone() setMuteMicro(true) } } useEffect(() => { if (currentCall) { currentCall.unmuteMicrophone() currentCall.onRemoteVideoSettingsChanged = (call) => { if (call.isRemoteVideoEnabled) { setRemortVideo(false) } else { setRemortVideo(true) } } currentCall.onUserHoldStatusChanged = (call, isLocalUser, isUserOnHold) => { if (!(holdflag && unholdflag)) { if (isUserOnHold && !holdflag) { setRemortOnHold(true) console.log("remot call hold") } else { setRemortOnHold(false) console.log("remot call unhold") setunholdflag(false) setholdflag(false) } } } } }, [currentCall, hold]) useEffect(() => { if (display) { SendBirdCall.addDirectCallSound(SendBirdCall.SoundType.DIALING, "horse.mp3") SendBirdCall.init(appId) const option = { userId } return SendBirdCall.authenticate(option) .then(user => { SendBirdCall.connectWebSocket() .then((result) => { SendBirdCall.addListener('ANY UNIQUE_HANDLER_ID HERE', { onRinging: (call) => { setcallView(true) setcurrentCall(call) setacceptCall(true) call.onEstablished = (call) => { console.log("trueeee") }; call.onConnected = (call) => { setcurrentCall(call) }; call.onEnded = (call) => { setcallView(false) setcurrentCall(call) }; call.unmuteMicrophone() for (const key in call._caller) { if (Object.hasOwnProperty.call(call._caller, key)) { setcallerName(call.caller.userId) } } } }) }).catch((err) => { console.log(err) }) }) .catch(error => { console.log(error) }) } }, [display]) return ( <div className='main-root'> { display ? <> <div className='main-root'> { callView ? <> <div className="call_view"> <div className={fullWidth ? `local_video_full` : `local_video`} onClick={() => handleVideoClick()}> <video id="local_video_element_id" className="localdevice" autoPlay={true} muted> </video> {disabled ? <><div className={fullWidth ? `active_vcall_lfull` : `active_vcall`}><div className={fullWidth ? `video-none1-lfull` : `video-none`}>{localStorage.getItem('userId').charAt(0).toUpperCase()} </div></div> {acceptCalls ? <div className="callername"> <p>{callerName} calling...</p> </div> : ''} </> : <div className={fullWidth ? 'video-none-lfull' : 'video-none1'}>{localStorage.getItem('userId').charAt(0).toUpperCase()} </div>} </div> <div className={fullWidth ? `remort_video_full` : `remort_video`} onClick={() => handleVideoClick()}> <video id='remote_video_element_id' className="remotedevice" autoPlay={true} onClick={() => handleVideoClick()}></video> {RemortVideo ? <div className={fullWidth ? 'video-none1' : 'video-none-lfull'}>{callerName ? callerName.charAt(0).toUpperCase() : userId.charAt(0).toUpperCase()} </div> : <div className={fullWidth ? `active_vcall` : `active_vcall_lfull`}><div className={fullWidth ? `video-none` : `video-none1-lfull`}>{callerName ? callerName.charAt(0).toUpperCase() : userId.charAt(0).toUpperCase()} </div></div> } </div> <div className="calls"> { acceptCalls && <> <div className="acceptcall" onClick={() => acceptCall()}> <i class="fa fa-phone"></i> </div> </> } <div className={disabled ? "mutevideo" : 'unmute'} onClick={() => stopVideo()} > <i class="fa fa-video-camera" ></i> </div> {hold ? <>{remortOnHold ? <div className="unhold another" > <i class="fa fa-pause"></i> </div> : <div className="unhold" onClick={() => unholdCall()} > <i class="fa fa-pause"></i> </div>}</> : <>{remortOnHold ? <div className="hold another" > <i class="fa fa-pause"></i> </div> : <div className="hold" onClick={() => holeCall()} > <i class="fa fa-pause"></i> </div>} </>} <div className="endcalls" onClick={() => callEnd()}> <i class="fa fa-phone" ></i> </div> <div className='microphone-active' onClick={() => handleMute()} > <i class={muteMicro ? "fa fa-microphone-slash" : "fa fa-microphone"}></i> </div> </div> </div> </> : <> <div className="container"> <div>{`${localStorage.getItem('userId')} Login`}</div> <h5>Make a Call</h5> <input className="mb-2" type="text" placeholder="enter caller name" onChange={(e) => setuserId(e.target.value)} /><br /> <i class="fa fa-phone makecall" onClick={() => dial(true)}></i> </div> </> } </div> </> : <> <div className="container"> <h5>Login</h5> <input type="text" className="mb-2" placeholder="enter your name" onChange={(e) => setuserId(e.target.value)} /> <button onClick={() => login()} type="button" class="btn btn-warning">Login</button> </div> </> } </div> ) } export default Home
you can see the output below…