Why do I have a client reconnect every 10s - socketcluster

Trying to use socketcluster to exchange events between browser windows.
On the sender side I have :
var options = {
hostname: "myserver.com",
secure: true,
port: 443,
connectTimeout: 86400000,
autoReconnectOptions: {
initialDelay: 100, //milliseconds
randomness: 10, //milliseconds
multiplier: 1.5, //decimal
maxDelay: 60000 //milliseconds
}
};
// Initiate the connection to the server
var socket = socketCluster.connect(options);
socket.on('connect', function () {
console.log('CONNECTED');
});
function sendTime() {
var currentDate = new Date();
var theId = document.getElementById("object_id").value;
count++;
console.log("id "+theId);
socket.emit('identity1', { timestamp: currentDate, id: theId, counter:count});
}
Then on the server I have the worker publish a new event :
socket.on('identity1', function (data) {
count++;
console.log('Handled identity1', data);
scServer.exchange.publish('identity-' + data.id, data);
});
And on the receiver side I have :
// Initiate the connection to the server
var socket = socketCluster.connect(options);
socket.on('connect', function () {
console.log('CONNECTED');
identityChannel = socket.subscribe('identity-' + document.getElementById("object_id").value);
identityChannel.watch(function (data) {
var theTime=data.timestamp;
console.log('ID:' + data.id + ' TIME: ' + theTime);
document.getElementById("data2").innerHTML = 'TIME: ' + theTime + 'COUNTER : ' + data.counter ;
});
});
In the js console of Chrome I see that after 10s on both sides, the client connection is refused like that :
socketcluster.js:678 Uncaught SocketProtocolError {name: "SocketProtocolError", message: "Socket hung up", code: 1006, stack: "SocketProtocolError: Socket hung up↵ at SCSocke…myserver.com/socketcluster.js:1392:10)"}
(anonymous) # socketcluster.js:678
setTimeout (async)
SCSocket._onSCError # socketcluster.js:676
SCSocket._onSCClose # socketcluster.js:781
(anonymous) # socketcluster.js:426
Emitter.emit # socketcluster.js:4152
SCTransport._onClose # socketcluster.js:1494
wsSocket.onclose # socketcluster.js:1392
sender.html:26 CONNECTED
I see that when reconnecting some events are lost.
Q : is this normal ?
Q : can the 10s limit be tuned ?

Actually you have to set the GCP load balancer connection timeout other than the default value of 10s.

Related

Django channels without redis

I have a django app based on this tutorial that works perfectly. It uses Redis in the Channel layers
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('127.0.0.1', 6379)],
},
},
}
The problem I have is that my web hosting provider will not allow Redis (unless I pay ££££).
Every example that I can find uses Redis in this role. Is there an alternative I could use?
there are a few options.
you can run your channel layer on a different service to were the main instance runs. AWS ElastiCache or many other redis hosts out there.
There is also a RabbitMQ channel layer but if your hosting provider charges a lot for reddis i expect they will also charge a lot for this ... https://github.com/CJWorkbench/channels_rabbitmq/
It turned out that channels is a non-starter on an affordable web-hosting platform. So I reverted to using Ajax and long polling. My application is based on this Django Tutorial.
models.py
class Message(models.Model):
room_name = models.CharField(null=False, blank=False, max_length=50)
sender = models.CharField(null=False, blank=False, max_length=50, default='Sender username')
datetime = models.DateTimeField(null=True, auto_now_add=True)
type = models.IntegerField(null=True, blank=True)
text = models.CharField(null=False, blank=False, max_length=250, default='message text')
context = models.TextField(null=True, blank=True)
urls.py
urlpatterns = [
path('<str:partner_pk>/check-message', views.CheckMessage.as_view(), name="check-message"),
path('<str:partner_pk>/send-message/<str:chat_text>', views.SendMessage.as_view(), name="send-message"),
]
views.py
class CheckMessage(View):
"""Duo check message."""
def get(self, request, partner_pk):
"""Render the GET request."""
pair, room_name = sort_pair(partner_pk, request.user.pk)
partner = User.objects.get(pk=partner_pk)
profile = get_object_or_404(Profile, user=request.user)
message = Message.objects.filter(room_name=room_name, sender=partner.username).earliest('datetime')
context = {'type': -1}
context = json.loads(message.context)
context['sender'] = message.sender
context['datetime'] = message.datetime
context['message_type'] = message.type
context['text'] = message.text
context['seat'] = profile.seat
message.delete()
return JsonResponse(context, safe=False)
class SendMessage(View):
def get(self, request, partner_pk, chat_text):
message_type = app.MESSAGE_TYPES['chat']
send_message(request, partner_pk, message_type, text=chat_text, context={})
return JsonResponse({}, safe=False)
chat.js
window.setInterval(checkMessage, 3000);
function checkMessage () {
$.ajax(
{
type:"GET",
url: "check-message",
cache: false,
success: function(message) {
processMessage(message);
}
}
)
}
// Take action when a message is received
function processMessage(context) {
switch (context.message_type) {
case 0:
sendMessage(context)
functionOne()
break;
case 1:
sendMessage(context)
functionTwo()
break;
case 2:
sendMessage(context)
functionThree()
break;
}
}
// Send a message to chat
function sendMessage (context) {
if (context.sender != username) {
var messageObject = {
'username': context.sender,
'text': context.text,
};
displayChat(context);
}
}
// Display a chat message in the chat box.
function displayChat(context) {
if (context.text !== '') {
var today = new Date();
var hours = pad(today.getHours(), 2)
var minutes = pad(today.getMinutes(), 2)
var seconds = pad(today.getSeconds(), 2)
var time = hours + ":" + minutes + ":" + seconds;
var chat_log = document.getElementById("chat-log");
chat_log.value += ('('+time+') '+context.sender + ': ' + context.text + '\n');
chat_log.scrollTop = chat_log.scrollHeight;
}
}
//pad string with leading zeros
function pad(num, size) {
var s = num+"";
while (s.length < size) s = "0" + s;
return s;
}
// Call submit chat message if the user presses <return>.
document.querySelector('#chat-message-input').focus();
document.querySelector('#chat-message-input').onkeyup = function (e) {
if (e.keyCode === 13) { // enter, return
document.querySelector('#chat-message-submit').click();
}
};
// Submit the chat message if the user clicks on 'Send'.
document.querySelector('#chat-message-submit').onclick = function (e) {
var messageField = document.querySelector('#chat-message-input'), text = messageField.value, chat_log = document.getElementById("chat-log");
context = {sender: username, text: messageField.value}
displayChat(context)
sendChat(messageField.value)
chat_log.scrollTop = chat_log.scrollHeight;
messageField.value = '';
};
// Call the send-chat view
function sendChat(chat_text) {
$.ajax(
{
type:"GET",
url: "send-message/"+chat_text,
cache: false,
}
)
}

How to fix InvalidStateError: Cannot add ICE candidate when there is no remote SDP

I am creating a webRTC video chat that shows a caller all active members when initiating a call from firefox and the receiver is using chrome this error is displayed "Uncaught (in promise) DOMException: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Error processing ICE candidate". And when a call is initiated from firefox and receiver uses firefox I get two errors Invalidstate: cannot add ICE candidate when there is no remote SDP and ICE failed, add a STUN and see about:webrtc for details
I dont know where I am making a mistake
/ define all data here
var usersOnline,id,currentCaller,room,caller,localUser,media,memberInfo;
// All subscribed members.
var users = [];
var token = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
// create random user id
var userId = Math.random().toString(36).substring(2, 15);
// create random username
var username = token;
// authonticating user
var currentUser = {
username: token,
userId: userId
}
// stringify user data
currentUser = JSON.stringify(currentUser);
var pusher = new Pusher('KEY', {
authEndpoint: '../auth.php',
auth: {
params: JSON.parse(currentUser)
},
cluster: 'ap2',
forceTLS: true
});
var state = pusher.connection.state;
var channel = pusher.subscribe('presence-conference');
channel.bind("pusher:subscription_succeeded", function (members) {
console.log(members);
id = channel.members.me.id;
document.getElementById('mydetails').innerHTML = 'Online Now: ' + ' ( ' + (members.count - 1) +')';
members.each(member => {
if (member.id != channel.members.me.id) {
users.push(member.id);
}
});
renderOnline();
});
// Add user online
channel.bind("pusher:member_added", member => {
users.push(member.id);
renderOnline();
});
channel.bind("pusher:member_removed", member => {
// for remove member from list:
var index = users.indexOf(member.id);
users.splice(index, 1);
if (member.id == room) {
endCall();
}
renderOnline();
});
function renderOnline(){
var list = "";
users.forEach(function(user) {
list +=
`<li>` +
user +//this will call user
` <input type="button" style="float:right;" value="Call" onclick="callUser('` +
user +
`')" id="makeCall" /></li>`;
});
document.getElementById("userDetails").innerHTML = list;
}
//To iron over browser implementation anomalies like prefixes
GetRTCPeerConnection();
GetRTCSessionDescription();
GetRTCIceCandidate();
prepareCaller();
function prepareCaller() {
//Initializing a peer connection
caller = new window.RTCPeerConnection();
//Listen for ICE Candidates and send them to remote peers
caller.onicecandidate = function(evt) {
if (!evt.candidate) return;
console.log("onicecandidate called");
onIceCandidate(caller, evt);
};
//onaddstream handler to receive remote feed and show in remoteview video element
caller.onaddstream = function(evt) {
console.log("onaddstream called");
if("srcObject" in document.getElementById("selfview")){
document.getElementById("selfview").srcObject = evt.stream;
}else{
if (window.URL) {
document.getElementById("remoteview").src = window.URL.createObjectURL(
evt.stream
);
} else {
document.getElementById("remoteview").src = evt.stream;
}
}
};
}
function getCam() {
//Get local audio/video feed and show it in selfview video element
return navigator.mediaDevices.getUserMedia({
audio: {
echoCancellation: true,
sampleSize:8
},
video: {
width: 1080,
height: 720,
aspectRatio: { ideal: 1.777778 }
}
});
}
function GetRTCIceCandidate() {
window.RTCIceCandidate =
window.RTCIceCandidate ||
window.webkitRTCIceCandidate ||
window.mozRTCIceCandidate ||
window.msRTCIceCandidate;
return window.RTCIceCandidate;
}
function GetRTCPeerConnection() {
window.RTCPeerConnection =
window.RTCPeerConnection ||
window.webkitRTCPeerConnection ||
window.mozRTCPeerConnection ||
window.msRTCPeerConnection;
return window.RTCPeerConnection;
}
function GetRTCSessionDescription() {
window.RTCSessionDescription =
window.RTCSessionDescription ||
window.webkitRTCSessionDescription ||
window.mozRTCSessionDescription ||
window.msRTCSessionDescription;
return window.RTCSessionDescription;
}
//Create and send offer to remote peer on button click
function callUser(user) {
getCam()
.then(stream => {
if("srcObject" in document.getElementById("selfview")){
document.getElementById("selfview").srcObject = stream;
}else{
if (window.URL) {
document.getElementById("selfview").src = window.URL.createObjectURL(
stream
);
} else {
document.getElementById("selfview").src = stream;
}
}
toggleEndCallButton();
caller.addStream(stream);
localUserMedia = stream;
caller.createOffer().then(function(desc) {
caller.setLocalDescription(new RTCSessionDescription(desc));
channel.trigger("client-sdp", {
sdp: desc,
room: user,
from: id
});
room = user;
});
})
.catch(error => {
console.log("an error occured", error);
});
}
function endCall() {
room = undefined;
caller.close();
for (let track of localUserMedia.getTracks()) {
track.stop();
}
prepareCaller();
toggleEndCallButton();
}
function endCurrentCall() {
channel.trigger("client-endcall", {
room: room
});
endCall();
}
//Send the ICE Candidate to the remote peer
function onIceCandidate(peer, evt) {
if (evt.candidate) {
channel.trigger("client-candidate", {
candidate: evt.candidate,
room: room
});
}
}
function toggleEndCallButton() {
if (document.getElementById("endCall").style.display == "block") {
document.getElementById("endCall").style.display = "none";
} else {
document.getElementById("endCall").style.display = "block";
}
}
//Listening for the candidate message from a peer sent from onicecandidate handler
channel.bind("client-candidate", function(msg) {
if (msg.room == room) {
console.log("candidate received");
caller.addIceCandidate(new RTCIceCandidate(msg.candidate));
}
});
//Listening for Session Description Protocol message with session details from remote peer
channel.bind("client-sdp", function(msg) {
if (msg.room == id) {
console.log("sdp received");
var answer = confirm(
"You have a call from: " + msg.from + "Would you like to answer?"
);
if (!answer) {
return channel.trigger("client-reject", { room: msg.room, rejected: id });
}
room = msg.room;
getCam()
.then(stream => {
localUserMedia = stream;
toggleEndCallButton();
if("srcObject" in document.getElementById("selfview")){
document.getElementById("selfview").srcObject = stream;
}else{
if (window.URL) {
document.getElementById("selfview").src = window.URL.createObjectURL(
stream
);
} else {
document.getElementById("selfview").src = stream;
}
}
caller.addStream(stream);
var sessionDesc = new RTCSessionDescription(msg.sdp);
caller.setRemoteDescription(sessionDesc);
caller.createAnswer().then(function(sdp) {
caller.setLocalDescription(new RTCSessionDescription(sdp));
channel.trigger("client-answer", {
sdp: sdp,
room: room
});
});
})
.catch(error => {
console.log("an error occured", error);
});
}
});
//Listening for answer to offer sent to remote peer
channel.bind("client-answer", function(answer) {
if (answer.room == room) {
console.log("answer received");
caller.setRemoteDescription(new RTCSessionDescription(answer.sdp));
}
});
channel.bind("client-reject", function(answer) {
if (answer.room == room) {
console.log("Call declined");
alert("call to " + answer.rejected + "was politely declined");
endCall();
}
});
channel.bind("client-endcall", function(answer) {
if (answer.room == room) {
console.log("Call Ended");
endCall();
}
});
I EXPECTED that the video call will work don't want to use any API, help me see where I went wrong.
Call setRemoteDescription(offer) before requesting the camera.
This puts the RTCPeerConnection in the right signaling state ("have-remote-offer") to receive and process remote ICE candidates correctly.
There's no time to request the camera first when an offer comes in. Incoming offers are typically followed closely by trickled ICE candidates on your signaling channel. addIceCandidate won't know what to do with those if it hasn't seen an offer.
Move the setRemoteDescription call ahead of the getMedia call in the promise chain to fix it. You have more time then before returning an answer.
Though that's still not great, since this approach often ends up blocking initial WebRTC negotiation on a user permission prompt for the camera. This is called tight coupling. Sadly, the current state of WebRTC encourages it, since getting the best IP mode is gated on getUserMedia in most browsers.
Lastly, there's a lot of old API usage here. See my other answer for newer APIs to use.

webrtc onRemote Stream null

I am fairly new in webRTC finding problems with its documentation. I cannot figure out why joiner does not receive stream from initiator since the messages on console look to me quite normal. Also i receive warnings about deprecated methods but not sure what to correct.
html:
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.mediaDevices.getUserMedia ||
navigator.msGetUserMedia;
var isInitiator = false
, isChannelReady = false
, isStarted = false;
//Send 'create' or 'join' message to singnalling server
console.log('Create or join room', room);
socket.emit('create or join', room);
//Handle 'created' message coming back from server:
//this peer is the initiator
socket.on('created', function (room){
console.log('Created room ' + room);
isInitiator = true;
var video = $('#sidebar').append("<video class='student' autoplay='true'></video>");
});
//Handle 'join' message coming back from server:
//another peer is joining the channel
socket.on('join', function (room){
console.log('Another peer made a request to join room ' + room);
console.log('This peer is the initiator of room ' + room + '!');
isChannelReady = true;
var video = $('#sidebar').append("<video class='student' autoplay='true'></video>");
});
//Handle 'joined' message coming back from server:
//this is the second peer joining the channel
socket.on('joined', function (room){
console.log('This peer has joined room ' + room);
isChannelReady = true;
var video = $('#sidebar').append("<video class='student' autoplay='true'></video>");
navigator.getUserMedia({ video: true, audio: true },
function (stream) {
$('#sidebar').children().last().attr('src', window.URL.createObjectURL(stream))
if(!isStarted && typeof stream!= 'undefined' && isChannelReady) {
createPeerConnectionInit(stream);
isStarted = true;
} else{
}
}, function (error) {
console.log('error'+error);
});
});
socket.on('message', function (message){
if (message === 'got user media') {
}
else if (message.type === 'offer') {
if(isChannelReady){
console.log('Im the initiator. Channel is ready!!!');
createPeerConnectionNotInit(message);
isStarted = true;
}
}
else if (message.type === 'answer' && isStarted) {
peer.addAnswerSDP(message);
console.log('addAnswerSDP:', message);
}
else if (message.type === 'candidate' && isStarted) {
console.log("im adding candidate!!!");
var candidate = new RTCIceCandidate({sdpMLineIndex:message.label,
candidate:message.candidate});
peer.addICE(candidate);// εδω ο initiator προσθέτει στο ice τον candidate
}
else if (message === 'bye' && isStarted) {
}
});
function createPeerConnectionInit(stream){
peer = RTCPeerConnection({
attachStream : stream,
onICE : function (candidate) {
if (candidate) {
sendMessage({
type: 'candidate',
label: candidate.sdpMLineIndex,
id: candidate.sdpMid,
candidate: candidate.candidate});
} else {
console.log('End of candidates.');
}
},
onRemoteStream : function (stream) {
console.log('onRemoteStream Init = yes');
document.getElementById('video').src = stream;
},
onOfferSDP : function(sdp) {
console.log('sdp ='+JSON.stringify(sdp));
sendMessage(sdp);
}
});
}
function createPeerConnectionNotInit(offer_sdp){
peer = RTCPeerConnection({
onICE : function (candidate) {
if (candidate) {
sendMessage({
type: 'candidate',
label: candidate.sdpMLineIndex,
id: candidate.sdpMid,
candidate: candidate.candidate});
} else {
console.log('End of candidates.');
}
},
onRemoteStream : function (stream) {
console.log('onRemoteStream Not Init = yes');
document.getElementById('video').src = URL.createObjectURL(stream);;
},
// see below two additions ↓
offerSDP : offer_sdp,
onAnswerSDP : function(sdp) {
sendMessage(sdp);
}
});
}
Console log from initiator view:
sdp ={"type":"offer","sdp":"v=0\r\no=mozilla...THIS_IS_SDPARTA-53.0.3 8347568228516099874 0 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\na=fingerprint:sha-256
Sending message: Object { type: "candidate", label: 0, id: "sdparta_0", candidate: "candidate:0 1 UDP 2122187007 2a02:5…" } boardWithD3.js.html:798:13
Many same messages following..
addAnswerSDP: Object { type: "answer", sdp: "v=0 o=mozilla...THIS_IS_SDPARTA-53.…" } boardWithD3.js.html:727:17
adding-ice candidate:0 1 UDP 2122187007 2a02:582:1096:a500:f03d:34da:c0a:75b0 50729 typ host RTCPeerConnection-v1.5.js:264:13
A couple similar messages following…..
Console log from joiner view:
offer_sdp:{"type":"offer","sdp":"v=0\r\no=mozilla...THIS_IS_SDPARTA-53.0.3 8347568228516099874 0 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\na=fingerprint:sha-256 ………
ice-servers [{"url": "stun:23.21.150.121" },
{"url": "stun:stun.services.mozilla.com" }]
sdp-constraints {
"OfferToReceiveAudio": true,
"OfferToReceiveVideo": true
} RTCPeerConnection-v1.5.js:123:5
offer-sdp v=0 o=mozilla...THIS_IS_SDPARTA-53.0.3 8347568228516099874 0 IN IP4 0.0.0.0
……
WebRTC interfaces with the “moz” prefix (mozRTCPeerConnection, mozRTCSessionDescription, mozRTCIceCandidate) have been deprecated. RTCPeerConnection-v1.5.js:79:15
RTCIceServer.url is deprecated! Use urls instead. RTCPeerConnection-v1.5.js:79
onaddstream is deprecated! Use peerConnection.ontrack instead. RTCPeerConnection-v1.5.js:101
onRemoteStream Not Init = yes boardWithD3.js.html:784:21
Sending message: Object { type: "answer", sdp: "v=0 o=mozilla...THIS_IS_SDPARTA-53.…" } boardWithD3.js.html:798:13……
adding-ice candidate:0 1 UDP 2122187007 2a02:582:1096:a500:f03d:34da:c0a:75b0 50006 typ host RTCPeerConnection-v1.5.js:264:13
**Uncaught TypeError: Cannot set property 'src' of null
at Object.onRemoteStream (boardWithD3.js.html?andora:785)
at RTCPeerConnection.peer.onaddstream (RTCPeerConnection-v1.5.js:110)
**
adding-ice candidate:3 1 UDP 2122252543 2a02:582:1096:a500:7de6:9361:ecf4:476a 50007 typ host RTCPeerConnection-v1.5.js:264:13
followed by many similar messages…..
Sending message: Object { type: "candidate", label: 0, id: "sdparta_0", candidate: "candidate:0 1 UDP 2122187007 2a02:5…" } boardWithD3.js.html:798:13
followed by many similar messages…..
Thanks in advance
As per below log
Uncaught TypeError: Cannot set property 'src' of null
at Object.onRemoteStream (boardWithD3.js.html?andora:785)
at RTCPeerConnection.peer.onaddstream (RTCPeerConnection-v1.5.js:110)
Issue is in below method with video element.
onRemoteStream : function (stream) {
console.log('onRemoteStream stream', stream);
//document.getElementById('video').src = stream; // Issue could be here, add a video element with id="video" in html body.
document.getElementById('video').srcObject = stream; //As per latest API use srcObject instead of src
}
As #jib mentioned, you using old API's.
See the samples and demo for latest API's

Remote video is black screen or blank in WebRTC

I have signaling server in java and websocket.
It works well with local video. but Remote video is black screen or blank
But it is not always a blank. If you turn off the server and turn it on again, the remote video will show up on your remote.
Why does not it always come out sometimes, and sometimes it does not come out?
this is my code...
navigator.getUserMedia = navigator.getUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
window.RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate || window.webkitRTCIceCandidate;
window.RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription || window.webkitRTCSessionDescription;
window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition
|| window.msSpeechRecognition || window.oSpeechRecognition;
var localVideoStream = null;
var peerConn = null,
wsc = new WebSocket("ws://localhost:8080/signaling"),
peerConnCfg = {
'iceServers': [{
'url': 'stun:stun.l.google.com:19302'
}]
};
var videoCallButton = document.getElementById("caller");
var endCallButton = document.getElementById("callee");
var localVideo = document.getElementById('localVideo');
var remoteVideo = document.getElementById('remoteVideo');
videoCallButton.addEventListener("click", initiateCall);
endCallButton.addEventListener("click", function (evt) {
wsc.send(JSON.stringify({"closeConnection": true }));
});
var sdpConstraints = {
'mandatory': {
'OfferToReceiveAudio': true,
'OfferToReceiveVideo': true
}
};
function prepareCall() {
peerConn = new RTCPeerConnection(peerConnCfg);
// send any ice candidates to the other peer
peerConn.onicecandidate = onIceCandidateHandler;
// once remote stream arrives, show it in the remote video element
peerConn.onaddstream = onAddStreamHandler;
};
// run start(true) to initiate a call
function initiateCall() {
prepareCall();
// get the local stream, show it in the local video element and send it
navigator.getUserMedia({ "audio": true, "video": true }, function (stream) {
localVideoStream = stream;
localVideo.src = URL.createObjectURL(localVideoStream);
peerConn.addStream(localVideoStream);
createAndSendOffer();
}, function(error) { console.log(error);});
};
function answerCall() {
prepareCall();
// get the local stream, show it in the local video element and send it
navigator.getUserMedia({ "audio": true, "video": true }, function (stream) {
localVideoStream = stream;
localVideo.src = URL.createObjectURL(localVideoStream);
peerConn.addStream(localVideoStream);
createAndSendAnswer();
}, function(error) { console.log(error);});
};
wsc.onmessage = function (evt) {
var signal = null;
if (!peerConn) answerCall();
signal = JSON.parse(evt.data);
if (signal.sdp) {
console.log("Received SDP from remote peer.");
console.log("signal"+ signal);
peerConn.setRemoteDescription(new RTCSessionDescription(signal.sdp));
}
else if (signal.candidate) {
console.log("signal"+ signal.candidate);
console.log("Received ICECandidate from remote peer.");
peerConn.addIceCandidate(new RTCIceCandidate(signal.candidate));
} else if ( signal.closeConnection){
console.log("Received 'close call' signal from remote peer.");
endCall();
}else{
console.log("signal"+ signal.candidate);
}
};
function createAndSendOffer() {
peerConn.createOffer(
function (offer) {
var off = new RTCSessionDescription(offer);
peerConn.setLocalDescription(new RTCSessionDescription(off),
function() {
wsc.send(JSON.stringify({"sdp": off }));
},
function(error) { console.log(error);}
);
},
function (error) { console.log(error);}
);
};
function createAndSendAnswer() {
peerConn.createAnswer(
function (answer) {
var ans = new RTCSessionDescription(answer);
peerConn.setLocalDescription(ans, function() {
wsc.send(JSON.stringify({"sdp": ans }));
},
function (error) { console.log(error);}
);
},
function (error) {console.log(error);}
);
};
function onIceCandidateHandler(evt) {
if (!evt || !evt.candidate) return;
wsc.send(JSON.stringify({"candidate": evt.candidate }));
};
function onAddStreamHandler(evt) {
videoCallButton.setAttribute("disabled", true);
endCallButton.removeAttribute("disabled");
// set remote video stream as source for remote video HTML5 element
remoteVideo.src = window.URL.createObjectURL(evt.stream);
remoteVideo.play();
console.log("remote src : "+ remoteVideo.src);
};
function endCall() {
peerConn.close();
peerConn = null;
videoCallButton.removeAttribute("disabled");
endCallButton.setAttribute("disabled", true);
if (localVideoStream) {
localVideoStream.getTracks().forEach(function (track) {
track.stop();
});
localVideo.src = "";
}
if (remoteVideo){
remoteVideo.src = "";
window.URL.revokeObjectURL(remoteVideo);
}
};
One of the reasons for WebRTC blank / empty video is having high packet loss. In that scenario, in server and client logs it will show as the connection is successful and video is playing normally, so you won't see any warning or error.
To check if there is a high packet loss, you can go to about:webrtc on firefox, or chrome://webrtc-internals on chrome. For firefox, you can navigate to "RTP Stats". You'll see it shows Received: ... packets and Lost: ... packets. You can calculate packet loss ratio using these counts. For chrome, there is a graph for packet loss ratio. You might have a very high packet loss such as 70% for example.
If you have this extreme high packet loss, one reason is having a smaller MTU https://en.wikipedia.org/wiki/Maximum_transmission_unit on the client network interface than the MTU used by the server. For example, your client network interface can have MTU=1500 bytes when not connected to VPN, and MTU=1250 bytes when connected to VPN. If the server is sending RTP packets (over UDP) with MTU=1400, it can be received by the client if the client is not using VPN, but the packets larger than 1250 bytes will get dropped by the client network interface.
If you want to check the client MTU locally, you can run ifconfig on mac or linux.
Mac example, without example vpn:
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
...
inet SOME_IP netmask 0xffffff00 broadcast 192.168.1.255
media: autoselect
status: active
Mac example, with example vpn:
utun2: flags=80d1<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1250
inet SOME_IP --> SOME_IP netmask 0xffffffff
nd6 options=201<PERFORMNUD,DAD>
How to configure MTU for server:
If you are using GStreamer for WebRTC server, the payload generator element, for example rtpvp8pay has a property to set desired MTU value.
By using gst-inspect-1.0 rtpvp8pay you can see that it uses 1400 as MTU by default which can be larger than your client network interface can handle (e.g. 1250 on above example).
You can make it work by setting lower MTU on your GStreamer pipeline so that your client network interface won't drop majority of the packages anymore (package loss ratio can reduce to 0.01% just by changing MTU on GStreamer pipeline on the server).
When this is the case, the incoming video can work for ~10 seconds when VPN is freshly reconnected, then the incoming video can freeze and subsequent page refreshes can lead to just blank video with 70%+ packet loss.
This is a very specific scenario but when it happens it is a complete silent/hidden error so hopefully this helps someone.
add oniceconnectionstatechange to your prepeareCall Function and see if there is any ICE failure because of NAT issues
function prepareCall() {
peerConn = new RTCPeerConnection(peerConnCfg);
// send any ice candidates to the other peer
peerConn.onicecandidate = onIceCandidateHandler;
// once remote stream arrives, show it in the remote video element
peerConn.onaddstream = onAddStreamHandler;
peerConn.oniceconnectionstatechange = function(){
console.log('ICE state: ',peerConn.iceConnectionState);
}
};
in func did recieve remote video track perform a timer then in timer selector add track to view

Creating Peer Connection for Web RTC Data Channel

I have been trying to establish peer connection between browsers, to use data channel, but i am unsuccessful.
Everytime I correct one statement another error appears.
First I established a socketting server using socket.io and Node.js. In the server when any client is connection I am sending 'beacon' packets. On listening 'beacon' packet 1st client requests to join a 'room'. Then I allow the second client to join the same 'room'.
As soon as the second client connects, Server sends a confirmation packet to Client 1.
Then Client 1 sends the RTC Peer Connection 'offer' to Client 2, after setting local Description.
if( isChrome ) {
localPC = new window.webkitRTCPeerConnection(server, contraints);
rslt.innerHTML = "Webkit Variables Set";
}else {
localPC = new mozRTCPeerConnection(server, contraints);
rslt.innerHTML = "Mozilla Variables Set";
}
localPC.onicecandidate = function(event) {
if( event.candidate )
localPC.addIceCandidate( event.candidate );
};
localPC.onnegotiationneeded = function() {
localPC.createOffer( setOffer, sendFail );
};
sendChannel = localPC.createDataChannel( "sendDataChannel", {reliable: false} );
localPC.ondatachannel = function(event) {
receiveChannel = event.channel;
receiveChannel.onmessage = function(event) {
rslt.innerHTML = event.data;
};
};
localPC.createOffer( setOffer, sendFail );
function setOffer( offer ) {
lDescp = new RTCSessionDescription(offer);
localPC.setLocalDescription( lDescp );
socket.emit( 'offer', JSON.stringify(offer) );
rslt.innerHTML += "Offer Sent...<br/>";//+offer.sdp;
}//End Of setOffer()
Client 2 on receiving the 'offer' sets its as remote Description and creates a 'reply'. Sets the 'reply' as local Description, and sends it.
if( message.type == 'offer' ) {
rDescp = new RTCSessionDescription(message.sdp);
localPC.setRemoteDescription( rDescp );
localPC.createAnswer(
function( answer ) {
lDescp = new RTCSessionDescription(answer);
localPC.setLocalDescription( lDescp );
socket.emit( 'reply', JSON.stringify(answer) );
}, sendFail
);
}else {
localPC.addIceCandidate = new RTCIceCandidate( message.candidate );
}//End Of IF ELse
Client 1 on receiving the 'reply' sets it as remote Description and the connection should get established???
localPC.setRemoteDescription( new RTCSessionDescription( message.sdp ) );
But its not working!!! Pleease Help.
Seems like you got the flow correct, although I don't see the entire code.
One thing that strikes me weird is this:
localPC.onicecandidate = function(event) {
if( event.candidate )
localPC.addIceCandidate( event.candidate );
};
You need to send the icecandidate recieved in the onicecandidate event to the other peer. and not add it yourself.