I have installed coturn in AWS ubuntu and it works well
I’ve checked both STUN and TURN servers on Trickle ICE, and I’m getting all candidates, including the relay candidates with the IP address of my public server
I am using react-native webrtc for my video calling, this code works for local peers when devices are connected to same network, but does not work when devices are located in two different networks (relay)
This is my webrtc code.
import React, { useEffect, useState, useRef } from 'react';
import SocketIOClient from 'socket.io-client';
import {
mediaDevices,
RTCPeerConnection,
RTCView,
RTCIceCandidate,
RTCSessionDescription,
MediaStream,
} from 'react-native-webrtc';
import config from "../../Services/config";
import { SafeAreaView } from 'react-native-safe-area-context';
import InCallManager from 'react-native-incall-manager';
import { useTheme } from '@react-navigation/native';
import { clientRole, startClass } from '../../Services/meetingService';
export default function VideoMeeting({ route, navigation }) {
const { student, instructorId, clientId, studentName, instructorName } = route.params;
const studentId = student.id;
const [localStream, setLocalStream] = useState(null);
const [remoteStream, setRemoteStream] = useState(null);
const [type, setType] = useState('JOIN');
const [role, setRole] = useState('');
const [classResponse, setClassResponse] = useState({});
const [meetingId, setMeetingId] = useState(`${instructorId}_${studentId}`);
const otherUserId = useRef(null);
const socket = useRef(null);
const sessionStream = useRef(null);
const peerConnection = useRef(
new RTCPeerConnection({
iceServers: [
{
urls: ['stun:3.145.86.100:3478'],
},
{ urls: ['turn:3.145.86.100:3478?transport=tcp'], credential: 'iqra', username: 'iqra' }
],
iceTransportPolicy: 'all',
}),
);
let remoteRTCMessage = useRef(null);
useEffect(() => {
const [callerId] = useState("caller_" + clientId);
socket.current = SocketIOClient(config.SIGNALLING_HOST, {
transports: ['websocket'],
query: { callerId: callerId },
});
const handleStudentJoined = data => {
sendCall({ calleeId: otherUserId.current, rtcMessage: sessionStream.current, meetingId: meetingId });
};
const handleNewCall = data => {
remoteRTCMessage.current = data.rtcMessage;
otherUserId.current = data.callerId;
processAccept();
};
const handleCallAnswered = data => {
let request = { instructorId, studentId, meetingId };
startClass(request).then(classResponse => setClassResponse(classResponse));
remoteRTCMessage.current = data.rtcMessage;
peerConnection.current.setRemoteDescription(new RTCSessionDescription(remoteRTCMessage.current));
setType('WEBRTC_ROOM');
};
const handleICECandidate = data => {
let message = data.rtcMessage;
if (peerConnection.current) {
peerConnection.current
.addIceCandidate(new RTCIceCandidate({ candidate: message.candidate, sdpMid: message.id, sdpMLineIndex: message.label }))
.then(() => console.log('SUCCESS'))
.catch(err => console.log('Error', err));
}
};
socket.current.on('student_joined', handleStudentJoined);
socket.current.on('newCall', handleNewCall);
socket.current.on('callAnswered', handleCallAnswered);
socket.current.on('ICEcandidate', handleICECandidate);
let isFront = true;
mediaDevices.enumerateDevices().then(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;
}
}
mediaDevices.getUserMedia({
audio: true,
video: {
mandatory: {
minWidth: 500,
minHeight: 300,
minFrameRate: 30,
},
facingMode: isFront ? 'user' : 'environment',
optional: videoSourceId ? [{ sourceId: videoSourceId }] : [],
},
})
.then(stream => {
setLocalStream(stream);
stream.getTracks().forEach(track => peerConnection.current.addTrack(track, stream));
})
.catch(error => console.error('Error accessing media devices.', error));
});
peerConnection.current.ontrack = (e) => {
const newStream = new MediaStream();
e.streams[0].getTracks().forEach(track => newStream.addTrack(track));
setRemoteStream(newStream);
};
peerConnection.current.onicecandidate = event => {
if (event.candidate) {
sendICEcandidate({
calleeId: otherUserId.current,
rtcMessage: {
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate,
},
meetingId: meetingId,
});
} else {
console.log('End of candidates.');
}
};
const backAction = () => {
leave();
return true;
};
const backHandler = BackHandler.addEventListener("hardwareBackPress", backAction);
return () => {
backHandler.remove();
socket.current.off('student_joined', handleStudentJoined);
socket.current.off('newCall', handleNewCall);
socket.current.off('callAnswered', handleCallAnswered);
socket.current.off('ICEcandidate', handleICECandidate);
socket.current.disconnect();
peerConnection.current.close();
};
}, []);
useEffect(() => {
const fetchData = async () => {
const role = await clientRole();
setRole(role);
if (role === 'INSTRUCTOR') {
otherUserId.current = 'caller_' + studentId;
} else {
joinMeeting(meetingId, (response) => {
if (response.status === 'joined') {
console.log('process accept');
}
});
}
};
fetchData();
InCallManager.start();
InCallManager.setKeepScreenOn(true);
InCallManager.setForceSpeakerphoneOn(true);
return () => {
InCallManager.stop();
};
}, []);
function sendICEcandidate(data) {
socket.current.emit('ICEcandidate', data);
}
async function processCall() {
const sessionDescription = await peerConnection.current.createOffer();
await peerConnection.current.setLocalDescription(sessionDescription);
sessionStream.current = sessionDescription;
setType('WEBRTC_ROOM');
}
async function processAccept() {
await peerConnection.current.setRemoteDescription(new RTCSessionDescription(remoteRTCMessage.current));
const sessionDescription = await peerConnection.current.createAnswer();
await peerConnection.current.setLocalDescription(sessionDescription);
answerCall({
callerId: otherUserId.current,
rtcMessage: sessionDescription,
meetingId: meetingId,
});
setType('WEBRTC_ROOM');
}
function answerCall(data) {
socket.current.emit('answerCall', data);
}
function sendCall(data) {
socket.current.emit('call', data);
}
function createMeeting(meetingId, callback) {
socket.current.emit('createMeeting', { meetingId }, (response) => {
if (response.status === 'created') {
setMeetingId(meetingId);
}
callback(response);
});
}
function joinMeeting(meetingId, callback) {
socket.current.emit('joinMeeting', { meetingId, instructor: 'caller_' + instructorId }, (response) => {
if (response.status === 'joined') {
setMeetingId(meetingId);
}
callback(response);
});
}
function leave() {
peerConnection.current.close();
setLocalStream(null);
setType('JOIN');
socket.current.emit('cancelCall');
if (role === 'STUDENT') {
navigation.navigate('Home');
} else {
navigation.replace('CourseUpdate', { student, instructorId, clientId, studentName, instructorName, classSessionId: classResponse.classSessionId });
}
}
return (
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
{type === 'WEBRTC_ROOM' && (
<View style={styles.webrtcRoomContainer}>
{localStream && (
<Draggable x={10} y={10}>
<View style={styles.rtcView}>
<RTCView objectFit={'cover'} style={styles.rtcViewContent} streamURL={localStream.toURL()} />
</View>
</Draggable>
)}
{remoteStream && (
<Draggable x={140} y={10}>
<View style={styles.rtcView}>
<RTCView objectFit={'cover'} style={styles.rtcViewContent} streamURL={remoteStream.toURL()} />
</View>
</Draggable>
)}
</View>
)}
</SafeAreaView>
);
}