I am working on peer to peer video conference application, using below code, the local Stream video is showing but the remote is not working, it is just showing black video in RTCView tag. it is like
it is like the ice server is getting reached, peerconnection. oniceconnectionstatechange is only showing checking state , am working on wireless mobile hotspot, not sure if the issue is with iceservers
export default function CallerJoinScreen({}) {
const state = useSelector(state => state);
const [localStream, setlocalStream] = useState(null);
const [remoteStream, setRemoteStream] = useState(null);
const [type, setType] = useState('JOIN');
const [callerId] = useState(state.chat.selectedRoom);
const [socket, setSocket] = useState();
var mediaConstraints = {
mandatory: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true,
},
};
let otherUserId = {current: state.chat.selectedRoom};
const [remoteRTCMessage, setRemoteRTCMessage] = useState({current: null});
const localVideoRef = useRef(null);
const remoteVideoRef = useRef(null);
const [localMicOn, setlocalMicOn] = useState(true);
const [localWebcamOn, setlocalWebcamOn] = useState(true);
let isFront = false;
const onSuccess = data => console.log('fx success data=', data);
const onError = data => console.log('fx error data=', data);
const configuration = {
iceServers: [{urls: 'stun:stun.l.google.com:19302'}],
};
const peerConnection = useRef(new RTCPeerConnection(configuration));
useEffect(() => {
async function setSocketIoFx() {
let socketVar = await socketClient();
setSocket(socketVar);
}
setSocketIoFx();
return () => console.log('CallerJoinScreen cleanup useEffect');
}, []);
useEffect(() => {
if (socket) {
socket.emit('newRoomId', state.chat.selectedRoom);
socket.on('newCall', data => {
//console.log('socket newCall data=', data);
setRemoteRTCMessage({current: data.rtcMessage});
//remoteRTCMessage.current = data.rtcMessage;
otherUserId.current = data.callerId;
//console.log('socket newCall data=', data);
setType('INCOMING_CALL');
});
socket.on('callAnswered', data => {
//remoteRTCMessage.current = data.rtcMessage;
setRemoteRTCMessage({current: data.rtcMessage});
peerConnection?.current.setRemoteDescription(
new RTCSessionDescription(data.rtcMessage),
);
setType('WEBRTC_ROOM');
});
socket.on('ICEcandidate', data => {
let message = data.rtcMessage;
if (peerConnection.current) {
peerConnection?.current
.addIceCandidate(
new RTCIceCandidate({
candidate: message.candidate,
sdpMid: message.id,
sdpMLineIndex: message.label,
}),
)
.then(data => {
console.log('SUCCESS');
})
.catch(err => {
console.log('Error', err);
});
}
});
mediaDevices.enumerateDevices().then(async sourceInfos => {
let videoSourceId;
for (let i = 0; i < sourceInfos.length; i++) {
const sourceInfo = sourceInfos[i];
if (
sourceInfo.kind == 'videoinput' &&
sourceInfo.facing == (isFront ? 'user' : 'environment')
) {
videoSourceId = sourceInfo.deviceId;
}
}
let stream = await mediaDevices.getUserMedia({
audio: true,
video: {
mandatory: {
minWidth: 500, // Provide your own width, height and frame rate here
minHeight: 300,
minFrameRate: 30,
},
facingMode: isFront ? 'user' : 'environment',
optional: videoSourceId ? [{sourceId: videoSourceId}] : [],
},
});
//let loc = await peerConnection.current.getLocalStreams();
console.log(
'before stream.getTracks peerConnection.current.addStream=',
);
//await peerConnection.current.addStream(stream);
// Got stream!
console.log(
'before setting local stream stream.getTracks()=',
stream.getTracks(),
);
stream.getTracks().forEach(track => {
peerConnection.current.addTrack(track, stream);
});
let remote = new MediaStream();
setRemoteStream(remote);
setlocalStream(stream);
/*
for (const track of stream.getTracks()) {
stream.addTrack(track);
peerConnection.current.getLocalStreams()[0].addTrack(track);
}
*/
});
if (peerConnection) {
peerConnection.current.onnegotiationneeded = async () => {
let offer = await peerConnection.current.createOffer({
offerToReceiveAudio: 1,
offerToReceiveVideo: 1,
});
// signaling and invite
console.log('before setting setLocalDescription offer=', offer);
await peerConnection.current.setLocalDescription(offer);
};
}
if (peerConnection.current.ontrack !== undefined) {
peerConnection.current.ontrack = ev => {
console.log('peerConnection.current.ontrack ev.track=', ev.track);
const newStream = new MediaStream();
newStream.addTrack(ev.track);
setRemoteStream(newStream);
};
} else {
peerConnection.current.onaddstream = ev => {
setRemoteStream(ev.stream);
};
}
// Setup ice handling
peerConnection.current.onicecandidate = event => {
console.log('onicecandidate event=', event);
if (event.candidate) {
sendICEcandidate({
calleeId: otherUserId.current,
rtcMessage: {
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate,
},
});
} else {
console.log('End of candidates.');
}
};
peerConnection.current.oniceconnectionstatechange = function (newState) {
console.log('oniceconnectionstatechange newState=', newState);
console.log('ICE state: ', peerConnection.current.iceConnectionState);
if (peerConnection.current.iceConnectionState === 'failed') {
/* possibly reconfigure the connection in some way here */
/* then request ICE restart */
peerConnection.current.restartIce();
}
};
return () => {
socket.off('newCall');
socket.off('callAnswered');
socket.off('ICEcandidate');
};
}
}, [socket]);
useEffect(() => {
InCallManager.start();
InCallManager.setKeepScreenOn(true);
InCallManager.setForceSpeakerphoneOn(true);
return () => {
InCallManager.stop();
};
}, []);
function sendICEcandidate(data) {
socket.emit('ICEcandidate', data);
}
async function processCall() {
const sessionDescription = await peerConnection.current.createOffer({
iceRestart: true,
offerToReceiveAudio: 1,
offerToReceiveVideo: 1,
});
await peerConnection.current.setLocalDescription(sessionDescription);
sendCall({
calleeId: otherUserId.current,
rtcMessage: sessionDescription,
});
}
async function processAccept() {
//console.log('processAccept remoteRTCMessage', remoteRTCMessage);
peerConnection.current
.setRemoteDescription(new RTCSessionDescription(remoteRTCMessage.current))
.then(async function () {
//console.log('processAccept stream =', stream);
return await peerConnection.current.createAnswer();
})
.then(function (answer) {
//signaling to caller and send answer
return answerCall({
callerId: otherUserId.current,
rtcMessage: answer,
});
})
.catch(function (err) {
//console.log(err.message);
});
//const sessionDescription = await peerConnection.current.createAnswer();
//await peerConnection.current.setLocalDescription(sessionDescription);
}
function answerCall(data) {
//console.log('function answerCall data=', data);
socket.emit('answerCall', data);
peerConnection.current.setLocalDescription(rtcMessage.answer);
}
function sendCall(data) {
socket.emit('call', data);
}
const JoinScreen = () => {
return (
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={{
flex: 1,
backgroundColor: '#050A0E',
justifyContent: 'center',
paddingHorizontal: 42,
}}>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<>
<View
style={{
padding: 35,
backgroundColor: '#1A1C22',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 14,
}}>
<Text
style={{
fontSize: 18,
color: '#D0D4DD',
}}>
Your Caller ID
</Text>
<View
style={{
flexDirection: 'row',
marginTop: 12,
alignItems: 'center',
}}>
<Text
style={{
fontSize: 32,
color: '#ffff',
letterSpacing: 6,
}}>
{callerId}
</Text>
</View>
</View>
<View
style={{
backgroundColor: '#1A1C22',
padding: 40,
marginTop: 25,
justifyContent: 'center',
borderRadius: 14,
}}>
<Text
style={{
fontSize: 18,
color: '#D0D4DD',
}}>
Enter call id of another user
</Text>
<TextInputContainer
placeholder={'Enter Caller ID'}
value={otherUserId.current}
setValue={text => {
//otherUserId.current = text;
otherUserId.current = state.chat.selectedRoom;
//console.log('TEST', otherUserId.current);
}}
keyboardType={'number-pad'}
/>
<TouchableOpacity
onPress={() => {
setType('OUTGOING_CALL');
processCall();
}}
style={{
height: 50,
backgroundColor: '#5568FE',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 12,
marginTop: 16,
}}>
<Text
style={{
fontSize: 16,
color: '#FFFFFF',
}}>
Call Now
</Text>
</TouchableOpacity>
</View>
</>
</TouchableWithoutFeedback>
</KeyboardAvoidingView>
);
};
const OutgoingCallScreen = () => {
return (
<View
style={{
flex: 1,
justifyContent: 'space-around',
backgroundColor: '#050A0E',
}}>
<View
style={{
padding: 35,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 14,
}}>
<Text
style={{
fontSize: 16,
color: '#D0D4DD',
}}>
Calling to...
</Text>
<Text
style={{
fontSize: 36,
marginTop: 12,
color: '#ffff',
letterSpacing: 6,
}}>
{otherUserId.current}
</Text>
</View>
<View
style={{
justifyContent: 'center',
alignItems: 'center',
}}>
<TouchableOpacity
onPress={() => {
setType('JOIN');
otherUserId.current = null;
}}
style={{
backgroundColor: '#FF5D5D',
borderRadius: 30,
height: 60,
aspectRatio: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<Text>CallEnd</Text>
</TouchableOpacity>
</View>
</View>
);
};
const IncomingCallScreen = () => {
return (
<View
style={{
flex: 1,
justifyContent: 'space-around',
backgroundColor: '#050A0E',
}}>
<View
style={{
padding: 35,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 14,
}}>
<Text
style={{
fontSize: 36,
marginTop: 12,
color: '#ffff',
}}>
{otherUserId.current} is calling..
</Text>
</View>
<View
style={{
justifyContent: 'center',
alignItems: 'center',
}}>
<TouchableOpacity
onPress={() => {
processAccept();
setType('WEBRTC_ROOM');
}}
style={{
backgroundColor: 'green',
borderRadius: 30,
height: 60,
aspectRatio: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<Text color="#fff'">CallAnswer </Text>
</TouchableOpacity>
</View>
</View>
);
};
function switchCamera() {
localStream.getVideoTracks().forEach(track => {
track._switchCamera();
});
}
function toggleCamera() {
localWebcamOn ? setlocalWebcamOn(false) : setlocalWebcamOn(true);
localStream.getVideoTracks().forEach(track => {
localWebcamOn ? (track.enabled = false) : (track.enabled = true);
});
}
function toggleMic(localStream) {
localMicOn ? setlocalMicOn(false) : setlocalMicOn(true);
localStream.getAudioTracks().forEach(track => {
localMicOn ? (track.enabled = false) : (track.enabled = true);
});
}
function leave() {
peerConnection.current.close();
setlocalStream(null);
setType('JOIN');
}
const WebrtcRoomScreen = () => {
return (
<View
style={{
flex: 1,
backgroundColor: '#050A0E',
paddingHorizontal: 12,
paddingVertical: 12,
}}>
{localStream ? (
<RTCView
objectFit={'cover'}
style={{flex: 1, backgroundColor: '#050A0E'}}
streamURL={localStream.toURL()}
/>
) : null}
{remoteStream ? (
typeof remoteStream.toURL === 'function' ? (
<>
{
<RTCView
objectFit={'cover'}
style={{
flex: 1,
}}
streamURL={remoteStream?.toURL()}
/>
}
</>
) : null
) : null}
<View
style={{
marginVertical: 12,
flexDirection: 'row',
justifyContent: 'space-evenly',
}}>
<View
style={{backgroundColor: 'red'}}
onPress={() => {
leave();
}}
Icon={() => {
return <Text color="#FFF">CallEnd </Text>;
}}
/>
<View
style={{
borderWidth: 1.5,
borderColor: '#2B3034',
}}
backgroundColor={!localMicOn ? '#fff' : 'transparent'}
onPress={() => {
toggleMic();
}}
Icon={() => {
return localMicOn ? (
<Text color="#FFF">MicOn </Text>
) : (
<Text color="1D2939">MicOff </Text>
);
}}
/>
<View
style={{
borderWidth: 1.5,
borderColor: '#2B3034',
}}
backgroundColor={!localWebcamOn ? '#fff' : 'transparent'}
onPress={() => {
toggleCamera();
}}
Icon={() => {
return localWebcamOn ? (
<Text color="#FFF">VideoOn</Text>
) : (
<Text color="1D2939">VideoOff</Text>
);
}}
/>
<View
style={{
borderWidth: 1.5,
borderColor: '#2B3034',
}}
backgroundColor={'transparent'}
onPress={() => {
switchCamera();
}}
Icon={() => {
return <Text color="#FFF">CameraSwitch</Text>;
}}
/>
</View>
</View>
);
};
console.log(
'Before render remoteStream=',
remoteStream ? JSON.stringify(remoteStream) : '',
);
console.log('Before render remoteStream.toURL=', remoteStream?.toURL());
console.log(
'Before render localStream=',
localStream ? JSON.stringify(localStream) : '',
);
console.log('Before render localStream.toURL=', localStream?.toURL());
switch (type) {
case 'JOIN':
return JoinScreen();
case 'INCOMING_CALL':
return IncomingCallScreen();
case 'OUTGOING_CALL':
return OutgoingCallScreen();
case 'WEBRTC_ROOM':
return WebrtcRoomScreen();
default:
return null;
}
}
I run the app on my physical android device and call the emulator but I can see remote video