[Problem/Question]
On Video Call When I accpet call Local and Remote Camera screen gets Stuck on just Reciever Side. On Caller side Reciever and local Video is working perdecty. There is no any exception or error I found duriung that process.
[Reproduction Steps]
import React, { useState, useEffect, useRef } from ‘react’;
import { View, Text, TouchableOpacity, StyleSheet, Dimensions, Image, Platform } from ‘react-native’;
import Permissions, { PERMISSIONS, RESULTS } from ‘react-native-permissions’;
import MIcons from ‘react-native-vector-icons/MaterialIcons’;
import Utils from ‘…/…/utils/Utils’
import { boldTextFont, textFont } from ‘…/…/utils/Style’
import { DirectCallVideoView, SendbirdCalls } from ‘@sendbird/calls-react-native’;
import { useSelector } from ‘react-redux’;
import InCallManager from ‘react-native-incall-manager’;
const dimensions = {
width: Dimensions.get(‘window’).width,
height: Dimensions.get(‘window’).height,
};
const USER_OFFLINE = 1400106;
const USER_NOT_AVAILABLE = 1400103;
const CALL_PERMISSIONS = Platform.select({
android: [PERMISSIONS.ANDROID.CAMERA, PERMISSIONS.ANDROID.RECORD_AUDIO, PERMISSIONS.ANDROID.BLUETOOTH_CONNECT],
ios: [PERMISSIONS.IOS.CAMERA, PERMISSIONS.IOS.MICROPHONE],
default: ,
});
const CALLER_MODE = 1, RECEIVER_MODE = 2
const AUDIO_CALL = ‘audio’, VIDEO_CALL = ‘video’
const CALL_STATUS = {
RINGING: ‘Ringing’,
CONNECTING: ‘Connecting’,
CONNECTED: ‘Connected’,
RECONNECTING: ‘Reconnecting’,
BUSY: ‘Busy’,
ENDED: ‘Ended’
};
const myUtils = new Utils()
const CallManager = (props) => {
const { route, navigation } = props
const theme = useSelector(state => state.themeReducer.theme);
const [isCamOn, setIsCamOn] = useState(true);
const [isMicOn, setIsMicOn] = useState(true);
const [isSpeakerOn, setIsSpeakerOn] = useState(false);
const [callDuration, setCallDuration] = useState(0);
const [isSettingUpConnection, setIsSettingUpConnection] = useState(false);
const callerInfo = route.params._userInfo ?? { name: 'Unknown', profilePic: 'default-profile-pic-url' };
const callType = route.params._callType ?? VIDEO_CALL; // Default to VIDEO_CALL
const callMode = route.params._mode ?? CALLER_MODE; // Default to CALLER_MODE
const [callStatus, setCallStatus] = useState(CALL_STATUS.RINGING);
const [currentCall, setCurrentCall] = useState(null);
const initCall = async () => {
try {
let directCall;
if (callMode === CALLER_MODE) {
setIsSettingUpConnection(true)
const IS_VIDEO_CALL = callType === VIDEO_CALL ? true : false;
const userId = String(callerInfo.id);
const callOptions = {
audioEnabled: true,
videoEnabled: IS_VIDEO_CALL,
frontCamera: IS_VIDEO_CALL,
};
const callProps = await SendbirdCalls.dial(userId, IS_VIDEO_CALL, callOptions);
directCall = await SendbirdCalls.getDirectCall(callProps.callId);
} else if (callMode === RECEIVER_MODE) {
directCall = props.route.params._directCall;
}
if (directCall) {
setIsSettingUpConnection(false)
setupCallListeners(directCall);
setCurrentCall(directCall);
}
} catch (error) {
setTimeout(() => {
navigation.goBack();
}, 1000);
if (error.code == USER_OFFLINE) {
myUtils.showSnackbar(callerInfo.name + " is currently offline")
} else if (error.code == USER_NOT_AVAILABLE) {
myUtils.showSnackbar(callerInfo.name + " haven't setup yet for calls")
} else {
myUtils.showSnackbar(error.message)
}
}
};
const callDurationTimerRef = useRef(null);
const setupCallListeners = (directCall) => {
directCall.addListener({
onEstablished: (call) => {
setCallStatus(CALL_STATUS.CONNECTING)
},
onConnected: (call) => {
setCallStatus(CALL_STATUS.CONNECTED)
startCallDurationTimer();
},
onReconnecting: (call) => {
setCallStatus(CALL_STATUS.RECONNECTING)
},
onReconnected: (call) => { setCallStatus(CALL_STATUS.CONNECTED) },
onEnded: (call) => {
setCallStatus(CALL_STATUS.ENDED)
setCurrentCall(null);
clearCallDurationTimer();
setTimeout(() => {
navigation.goBack();
}, 1000);
}
});
};
const startCallDurationTimer = () => {
setCallDuration(0); // Reset duration
callDurationTimerRef.current = setInterval(() => {
setCallDuration(prevDuration => prevDuration + 1);
}, 1000); // Increment duration every second
};
const clearCallDurationTimer = () => {
if (callDurationTimerRef.current) {
clearInterval(callDurationTimerRef.current);
callDurationTimerRef.current = null;
}
};
useEffect(() => {
let permissions = checkAndRequestPermissions();
if (permissions && SendbirdCalls.currentUser) {
initCall();
} else {
console.error("Permissions or Authorization issue");
}
return () => {
setCallStatus(CALL_STATUS.RINGING);
if (currentCall) {
currentCall.end();
setCurrentCall(null);
}
clearCallDurationTimer();
};
}, []);
const checkAndRequestPermissions = async () => {
try {
const statuses = await Permissions.checkMultiple(CALL_PERMISSIONS);
const allPermissionsGranted = Object.values(statuses).every(status => status === RESULTS.GRANTED);
if (!allPermissionsGranted) {
// Request permissions if not all are granted
const result = await Permissions.requestMultiple(CALL_PERMISSIONS);
const newAllPermissionsGranted = Object.values(result).every(status => status === RESULTS.GRANTED);
if (newAllPermissionsGranted) {
return true;
} else {
console.log("Required permissions not granted.");
return false;
}
} else {
return true;
}
} catch (error) {
console.error("Error in permissions check:", error);
return false;
}
};
const toggleVideo = () => {
setIsCamOn(prevState => {
const newCamState = !prevState;
if (currentCall) {
if (newCamState) {
// Start showing video if new state is on (true)
currentCall.startVideo();
} else {
// Stop showing video if new state is off (false)
currentCall.stopVideo();
}
}
return newCamState;
});
};
const toggleSpeaker = () => {
InCallManager.setSpeakerphoneOn(!isSpeakerOn)
setIsSpeakerOn(!isSpeakerOn)
};
const toggleAudio = () => {
setIsMicOn(prevState => {
const newMicState = !prevState;
if (currentCall) {
if (newMicState) {
// Unmute the microphone if new state is on (true)
currentCall.unmuteMicrophone();
} else {
// Mute the microphone if new state is off (false)
currentCall.muteMicrophone();
}
}
return newMicState;
});
};
const endCall = () => {
try {
if (currentCall) {
currentCall.end();
setTimeout(() => {
navigation.goBack();
}, 1000);
}
} catch (error) {
console.log("🚀 ~ file: CallManager.js:121 ~ endCall ~ error:", error)
}
};
const acceptCall = () => {
try {
if (currentCall) {
const IS_VIDEO_CALL = callType === VIDEO_CALL ? true : false;
const acceptParams = {
callOptions: {
audioEnabled: true,
videoEnabled: IS_VIDEO_CALL,
frontCamera: IS_VIDEO_CALL,
}
}
currentCall.accept(acceptParams);
}
} catch (error) {
console.log("🚀 ~ acceptCall ~ error:", error)
}
}
return (
<>
<View style={[styles.max, { backgroundColor: theme.background }]}>
{isSettingUpConnection &&
<View style={{
flex: 1, justifyContent: "center", alignItems: "center",
position: "absolute", zIndex: 100, top: 0, left: 0, right: 0, bottom: 0
}}>
{myUtils.renderLoadingstate()}
</View>
}
{((callMode == CALLER_MODE || callMode == RECEIVER_MODE)) &&
<View style={callType === AUDIO_CALL ? { flex: 1, position: 'relative' } : { zIndex: 99999 }}>
{callType != VIDEO_CALL && <Image style={{ flex: 1 }}
source={{ uri: callerInfo.profilePic }} />}
<View style={[styles.callInfoContainer, { backgroundColor: callType === AUDIO_CALL ? myUtils.hexToRgbA("#000", 0.5) : '' }]}>
{callMode == CALLER_MODE &&
<Text style={{ ...textFont, color: theme.text, fontSize: 18 }}>
{`Calling ${callerInfo.name}...`}
</Text>
}
{callMode == RECEIVER_MODE &&
<Text style={{ ...textFont, color: theme.text, fontSize: 18 }}>
{`${callerInfo.name}`}
</Text>
}
<Text style={{ ...textFont, fontSize: 16, color: theme.text, }}>
{callType === VIDEO_CALL ? "Video Call" : "Audio Call"}
</Text>
<Text style={{ ...textFont, fontSize: 14, color: theme.text, }}>
{callStatus}
</Text>
{callStatus == CALL_STATUS.CONNECTING || callStatus == CALL_STATUS.CONNECTED &&
<View style={[styles.durationContainer, { backgroundColor: callType === AUDIO_CALL ? myUtils.hexToRgbA("#000", 0.5) : '' }]}>
<Text style={{ ...boldTextFont, fontSize: 18, color: "#fff" }}>
{myUtils.getSecAsTimeString(callDuration)}
</Text>
</View>
}
</View>
</View>
}
{(callType === VIDEO_CALL && currentCall) &&
<View style={styles.videoCallView}>
<DirectCallVideoView
viewType={'remote'}
callId={currentCall.callId}
style={callStatus === CALL_STATUS.CONNECTED ? StyleSheet.absoluteFill : styles.localView}
/>
<DirectCallVideoView
viewType={'local'}
callId={currentCall.callId}
style={callStatus === CALL_STATUS.CONNECTED ? styles.localView : StyleSheet.absoluteFill}
/>
</View>
}
<View style={styles.buttonHolder}>
{(callMode == CALLER_MODE || callStatus == CALL_STATUS.CONNECTED) && callType == VIDEO_CALL &&
<TouchableOpacity
onPress={() => { toggleVideo() }}
style={[styles.button, { backgroundColor: myUtils.hexToRgbA("#000", 0.3), }]}>
<MIcons name={isCamOn ? "videocam" : "videocam-off"}
size={24} color={"#fff"} />
</TouchableOpacity>
}
{(callMode == CALLER_MODE || callStatus == CALL_STATUS.CONNECTED) &&
<TouchableOpacity
onPress={() => { endCall() }}
style={[styles.button, { backgroundColor: "red" }]}>
<MIcons name={"call-end"} size={40} color={"#fff"} />
</TouchableOpacity>
}
{(callMode == RECEIVER_MODE && callStatus == CALL_STATUS.RINGING) &&
<View style={{ width: "80%", flexDirection: "row", justifyContent: "space-between", marginHorizontal: 10 }}>
<TouchableOpacity
onPress={() => { endCall() }}
style={[styles.button, { backgroundColor: "red" }]}>
<MIcons name={"call-end"} size={40} color={"#fff"} />
</TouchableOpacity>
<TouchableOpacity
onPress={() => { acceptCall() }}
style={[styles.button, { backgroundColor: "green" }]}>
<MIcons name={"call"} size={40} color={"#fff"} />
</TouchableOpacity>
</View>
}
{(callMode == CALLER_MODE || callStatus == CALL_STATUS.CONNECTED) &&
<View style={{ flexDirection: 'row' }}>
<TouchableOpacity
onPress={() => { toggleAudio() }}
style={[styles.button, { backgroundColor: myUtils.hexToRgbA("#000", 0.3) }]}>
<MIcons name={isMicOn ? "mic" : "mic-off"}
size={24} color={"#fff"} />
</TouchableOpacity>
<TouchableOpacity
onPress={() => { toggleSpeaker() }}
style={[styles.button, { backgroundColor: myUtils.hexToRgbA("#000", 0.3) }]}>
<MIcons name={isSpeakerOn ? "volume-up" : "volume-down"}
size={24} color={"#fff"} />
</TouchableOpacity>
</View>
}
</View>
</View>
</>
);
};
const styles = StyleSheet.create({
max: {
flex: 1,
},
callStatusContainer: {
alignItems: ‘center’,
paddingTop: 20,
},
callStatusText: {
fontSize: 18,
color: ‘#000’,
},
callTypeText: {
fontSize: 16,
color: ‘#000’,
},
profilePic: {
flex: 1,
resizeMode: 'cover',
},
callInfoContainer: {
alignItems: 'center',
paddingTop: 20,
position: 'absolute',
top: 0,
right: 0,
left: 0,
bottom: 0,
},
durationContainer: {
borderRadius: 7,
padding: 7,
},
videoCallView: {
flex: 1
},
localView: {
width: 120,
height: 150,
position: 'absolute',
bottom: 100,
right: 20,
borderRadius: 10, // Optional: for rounded corners
overflow: 'hidden', // Optional: if you want rounded corners
},
remoteView: {
width: dimensions.width,
height: dimensions.height,
},
buttonHolder: {
height: 100,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
},
button: {
padding: 10,
borderRadius: 100,
marginHorizontal: 20,
backgroundColor: '#00000060', // Semi-transparent black
},
});
export default CallManager;
[Frequency]
Every time I accept Video call I face this issue.