In local server, video streaming is running proper on both ends but while network changed video of one peer is not going to display to other peer.
I am using google stun server for connection of peer with another network, but then video streaming of one user is not visible here is my code
I am not able to understand what I am doing wrong here.I am novice in webrtc
<!DOCTYPE html>
<html lang="en">
<body>
{####### variable Initialization #######}
<script>
let webSocket;
let mapPeers = {};
let username = 'user_{{ request.user.id }}';
</script>
<script>
const iceConfiguration = {
iceServers: [
{
urls: 'stun:stun.l.google.com:19302',
}
]
}
function webSocketOnMessage(event){
let parsedData = JSON.parse(event.data);
console.log(parsedData);
let peerUserName = parsedData['peer'];
let action = parsedData['action'];
console.log(parsedData);
if(username === peerUserName){
return;
}
let receiver_channel_name = parsedData['message']['receiver_channel_name'];
if(action === 'new-peer'){
if (!(peerUserName in mapPeers)){
createOfferer(peerUserName, receiver_channel_name);
return;
}
else {
setTimeout(() => {
if (!(peerUserName in mapPeers)){
createOfferer(peerUserName, receiver_channel_name);
return
}
}, 1000);
}
}
if(action === 'new-offer'){
let offer = parsedData['message']['sdp'];
createAnswerer(offer, peerUserName, receiver_channel_name);
return;
}
if(action === 'new-answer'){
let answer = parsedData['message']['sdp'];
let peer = mapPeers[peerUserName][0];
peer.setRemoteDescription(answer);
return;
}
if(action === 'video-on'){
manageVideoEl('on', peerUserName)
return;
}
if(action === 'video-off'){
manageVideoEl('off', peerUserName)
return;
}
}
</script>
<script>
$(document).ready(function(){
let loc = window.location;
let wsStart = 'ws://';
if(loc.protocol === 'https:'){
wsStart = 'wss://';
}
let endPoint = wsStart + loc.host + '/ws/video/meet/'+ '{{ group_name }}/';
webSocket = new WebSocket(endPoint);
webSocket.addEventListener('open', (e)=>{
sendSignal('new-peer', {'receiver_channel_name': '{{ group_name }}'});
});
webSocket.addEventListener('message', webSocketOnMessage);
webSocket.addEventListener('close', (e)=>{
});
webSocket.addEventListener('error', (e)=>{
});
{% if is_group_creator %}
sendNotificationOnMessage();
{% endif %}
});
let localStream = new MediaStream();
const constraints = {
'video': true,
'audio': true
}
const localVideo = document.querySelector('#local-video');
const btnToggleAudio = document.querySelector('#btn-toggle-audio');
const btnToggleVideo = document.querySelector('#btn-toggle-video');
let userMedia = navigator.mediaDevices.getUserMedia(constraints)
.then(stream => {
localStream = stream;
localVideo.srcObject = localStream;
localVideo.muted = true;
let audioTracks = stream.getAudioTracks();
let videoTracks = stream.getVideoTracks();
audioTracks[0].enabled = true;
videoTracks[0].enabled = true;
btnToggleAudio.addEventListener('click', ()=>{
audioTracks[0].enabled = !audioTracks[0].enabled;
if(audioTracks[0].enabled){
btnToggleAudio.classList.replace('mic-off', 'mic-on')
return;
}
btnToggleAudio.classList.replace('mic-on','mic-off');
});
btnToggleVideo.addEventListener('click', ()=>{
videoTracks[0].enabled = !videoTracks[0].enabled;
if(videoTracks[0].enabled){
btnToggleVideo.classList.replace('camera-off','camera-on');
sendSignal('video-on', {})
localVideo.srcObject = localStream;
return;
}
sendSignal('video-off', {})
localVideo.srcObject = null;
btnToggleVideo.classList.replace('camera-on','camera-off');
});
})
.catch(error =>{
{#console.log('Error accessing media devices', error);#}
});
</script>
<script>
function sendSignal(action, message){
console.log("Sending message to other end");
let jsonStr = JSON.stringify({
'peer': username,
'action': action,
'message': message
});
console.log(jsonStr);
webSocket.send(jsonStr);
}
function createOfferer(peerUserName, receiver_channel_name){
console.log("creating offer");
let peer = new RTCPeerConnection(iceConfiguration);
addLocalTracks(peer);
peer.addEventListener("icegatheringstatechange", ev => {
switch(peer.iceGatheringState) {
case "new":
console.log("gathering is either just starting or has been reset");
break;
case "gathering":
console.log("gathering has begun or is ongoing");
break;
case "complete":
console.log("gathering has ended");
break;
}
});
peer.addEventListener('icecandidate', (event)=>{
if(event.candidate){
console.log('new ice candidate', JSON.stringify(peer.localDescription));
return;
}
sendSignal('new-offer', {
'sdp':peer.localDescription,
'receiver_channel_name': receiver_channel_name
});
// to notify video status of other users when new users join
if(!localStream.getVideoTracks()[0].enabled){
sendSignal('video-off', {})
}
});
let dc = peer.createDataChannel('channel');
dc.addEventListener('open', ()=>{
console.log("dc connection opened");
});
dc.addEventListener('message', dcOnMessage);
console.log("Creating video");
let remoteVideo = createVideo(peerUserName);
console.log("video created, setting track");
setOnTrack(peer, remoteVideo);
console.log("track setted");
mapPeers[peerUserName] = [peer, dc];
peer.addEventListener('iceconnectionstatechange', ()=>{
let iceconnectionState = peer.iceConnectionState;
if(iceconnectionState === 'failed' || iceconnectionState === 'disconnected' || iceconnectionState === 'closed'){
delete mapPeers[peerUserName];
if(iceconnectionState !== 'closed'){
peer.close();
}
removeVideo(remoteVideo);
}
});
peer.createOffer()
.then(o => peer.setLocalDescription(o))
.then(() => {
{#console.log("local description set successfully");#}
});
}
function createAnswerer(offer, peerUserName, receiver_channel_name){
let peer = new RTCPeerConnection(iceConfiguration);
addLocalTracks(peer);
let remoteVideo = createVideo(peerUserName);
setOnTrack(peer, remoteVideo);
peer.addEventListener('datachannel', e=>{
peer.dc = e.channel;
peer.dc.addEventListener('open', ()=>{
{#console.log("dc connection opened");#}
});
peer.dc.addEventListener('message', dcOnMessage);
mapPeers[peerUserName] = [peer, peer.dc];
});
peer.addEventListener('iceconnectionstatechange', ()=>{
let iceconnectionState = peer.iceConnectionState;
if(iceconnectionState === 'failed' || iceconnectionState === 'disconnected' || iceconnectionState === 'closed'){
delete mapPeers[peerUserName];
if(iceconnectionState !== 'closed'){
peer.close();
}
removeVideo(remoteVideo);
}
});
peer.addEventListener('icecandidate', (event)=>{
if(event.candidate){
{#console.log('new ice candidate', JSON.stringify(peer.localDescription));#}
return;
}
sendSignal('new-answer', {
'sdp':peer.localDescription,
'receiver_channel_name': receiver_channel_name
});
});
peer.setRemoteDescription(offer)
.then(() => {
{#console.log('Remote description set successfully for %s', peerUserName);#}
return peer.createAnswer();
})
.then(a => {
{#console.log('Answer created');#}
peer.setLocalDescription(a);
})
}
function addLocalTracks(peer){
localStream.getTracks().forEach(track => {
peer.addTrack(track, localStream);
});
return;
}
function createVideo(peerUserName){
userId = peerUserName.split('_')[1]
// Video element Creation
let remoteVideo = document.createElement('video');
remoteVideo.id = peerUserName + '-video';
remoteVideo.autoplay = true;
remoteVideo.playsInline = true;
remoteVideo.classList.add('custom-video');
remoteVideo.setAttribute('data-id', userId);
addVideoToDOM(remoteVideo, userId)
return remoteVideo;
}
function addVideoToDOM(video, userId){
let videoContainer = document.querySelector('#video-container');
$.getJSON(`/chat/call/participant/${userId}`, function(data){
// Styling Elements
video.style.backgroundImage = `url('${data.profile_image_url}')` // if video off then show this bg
let nameTag = document.createElement('span');
nameTag.classList.add('name-tag');
nameTag.innerText = data.username;
let participantActionsEl = document.createElement('div');
participantActionsEl.classList.add('participant-actions');
let videoParticipantEl = document.createElement('div');
videoParticipantEl.classList.add('video-participant');
videoParticipantEl.appendChild(participantActionsEl);
videoParticipantEl.appendChild(nameTag);
videoParticipantEl.appendChild(video);
videoParticipantEl.setAttribute('data-delete', 'true') // For removing element
videoParticipantEl.setAttribute('data-id', userId) // For showing feature
videoContainer.appendChild(videoParticipantEl);
addMemberToList(data)
})
}
function manageVideoEl(status, peerUserName) {
const userId = peerUserName.split('_')[1]
// After element in DOM update video element
setTimeout(() => {
let videoEl = document.querySelector(`video[data-id="${userId}"]`)
if (videoEl !== null){
// Saving source
if (mapPeers[peerUserName]){
if (videoEl.srcObject != null){
mapPeers[peerUserName][2] = videoEl.srcObject;
}
if (status==='on'){
videoEl.srcObject = mapPeers[peerUserName][2] || null;
} else if (status==='off'){
videoEl.srcObject = null;
}
}
}
}, 1000);
}
function setOnTrack(peer, remoteVideo){
let remoteStream = new MediaStream();
remoteVideo.srcObject = remoteStream;
peer.addEventListener('track', async (event)=>{
console.log(remoteStream);
remoteStream.addTrack(event.track, remoteStream);
});
}
function removeVideo(video){
removeMemberFromList(video.dataset.id)
video.closest(`[data-delete='true']`).remove()
}
</script>
{######### script to talk with group consumer #######}
<script>
let group_chatSocket_chat;
let receiver_group = "{{group.id }}";
{% if is_call_starter and send_notifications %}
function sendNotificationOnMessage()
{
group_chatSocket_chat = new ReconnectingWebSocket(
'ws://' + window.location.host +
'/ws/chat/group/' + "{{ group.name }}" + '/'+ "{{ request.user.id }}" +'/');
group_chatSocket_chat.onopen = function(e){
{#console.log("Connection open");#}
let url = `${location.protocol + '//' + location.host}/chat/video/{{ join_url }}`;
group_chatSocket_chat.send(JSON.stringify({
'message': `New Video call is started! Join Now Link: ${url}`,
'receiver_id': receiver_group,
'command': 'new_message',
'bucket_id': 0,
}));
}
}
{% endif %}
</script>
</body>
as you move out side of the network the webrtc ICE candidate gathering process may fail because of Routers NAT and firewalls therefore you must have a turn server in your configuration that will relay the traffic if the direct p2p connection establishment fails