The error I am getting in the browser console (only appears in chrome, no errors in firefox) is Error: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': The ICE candidate could not be added.
I followed a tutorial and was able to get p2p video chat to work using nodejs. Now I am using Flask and python on the server side and angularjs on client side.
Signaling process for two peers is being done with angular-socketio.
console.log("The user connected to the socket");
socket.emit('readyToJoinRoom', {"signal_room": SIGNAL_ROOM});
//Send a first signaling message to anyone listening
//This normally would be on a button click
socket.emit('signal',{"type":"user_joined", "message":"Are you ready for a call?", "room":SIGNAL_ROOM});
socket.forward('signaling_message', $scope);
$scope.$on('socket:signaling_message', function (ev, data) {
displaySignalMessage("Signal received: " + data.type);
// Setup the RTC Peer Connection object
if (!rtcPeerConn) {
startSignaling();
}
if(data.type != "user_joined") {
console.log(data.message);
var message = JSON.parse(data.message);
console.log(message);
if(message.sdp) {
console.log("inside 2nd if statement");
rtcPeerConn.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {
// if we received an offer, we need to answer
if(rtcPeerConn.remoteDescription.type === 'offer') {
console.log("inside third if for remoteDescription."); // This never executes, error happens right before this line
rtcPeerConn.createAnswer(sendLocalDesc, logError);
}
}, logError);
}
else {
console.log("addedddddddd ice candidate.");
rtcPeerConn.addIceCandidate(new RTCIceCandidate(message.candidate));
}
}
});
Once two people join the room the startSignaling() method is called. It sets the local description and completes 3 ice candidates then I receive an SDP but this is never true if(rtcPeerConn.remoteDescription.type === 'offer') even though it prints the SDP in the console with a type equal to offer. I am not sure why it never goes inside this if statement. I am not sure why I am getting an error. If you have any questions just ask. Thanks for the help.
I think
rtcPeerConn.setRemoteDescription(new RTCSessionDescription(message.sdp),...
will not work because the constructor of RTCSessionDescription needs the information about the type and the sdp. Try:
var desc = new RTCSessionDescription();
desc.sdp = message.sdp;
desc.type = "offer";
rtcPeerConn.setRemoteDescription(desc,.....
I had some issues constructing the RTCSessionDescription from JSON as well.
Hope this helps...
Related
In my project backend sends a lot of messages published to different channels.
I can see from browser console the message arrived has channel property.
But the problem is a callback passed to swampdragon.onChannelMessage doesn't get that channel information. It gets strange channels list instead.
So when a message arrives (in browser) I can't figure out the channel it was published to and therefore handle it properly.
I found the code where that channel info is stripped off https://github.com/jonashagstedt/swampdragon/blob/master/swampdragon/static/swampdragon/js/dist/swampdragon.js#L261
if ('channel' in e.data) {
var channel = swampDragon.channels[e.data.channel];
delete(e.data['channel']);
swampDragon.settings.onchannelmessage(channel, e.data);
return;
}
So my question is how frontend developer can figure out what channel the message arrived was published to in order to be able to handle the message properly?
A little late, but in case you weren't able to solve this:
swampdragon.open(function() {
swampdragon.subscribe('notification', 'notification', null, function (context, data) {
// Successfully subscribed to the notification channel
}, function () {
console.error('Error', arguments);
});
});
swampdragon.onChannelMessage(function(channels, message) {
if (channels.indexOf('notification') > -1) {
// Message sent on the notification channel
}
});
In onChannelMessage the channels argument is an array of channels the incoming messages was sent to. You can use indexOf to check the channel you are interested in exists in the list.
I am trying to set up a webrtc app. For now, it connects two people when they click connect. But if I have multiple connections open, and then when one of the people from the very first connection clicks disconnect, it literally disconnects EVERYONE who has a connection. I tried this with 3 connections, and disconnected the 2nd connection, and that only makes the 3rd connection disconnect, while the first is still connected.
So basically, it's a downward spiral. If a connection disconnects, then all connections created AFTER that one connection also get to disconnect. But all connections before that connection are still active.
I think it has something to do with the socket.io code, because in the console.log I am receiving the message from socket.io as someone in a totally different connection disconnects. Server code:
socket.on("sessiondesc", function(sessiondesc)
{
if (sessiondesc == true || sessiondesc.type === 'answer')
{
//the random room is generated before the socket.on sessiondesc thing.
socket.join(room)
}
socket.in(room).emit("sessiondesc", sessiondesc);
});
socket.on("quit", function(quit)
{
socket.leave(room)
});
This is client code:
socket.on("sessiondesc", function(sessiondesc)
{
//stuff I do here with webrtc... like create offer, answer, and such.
else if (sessiondesc == false)
{
pc.close();
pc = null;
console.log("GOT THE FALSE SESSION DESC! GOT IT");
quit = true;
socket.emit("quit", quit);
}
document.getElementById("disconnect").addEventListener("click", function(e)
{
console.log("The person clicked disconnect!");
quit = true;
sessiondesc = false;
socket.emit("sessiondesc", sessiondesc);
socket.emit("quit", quit);
pc.close();
pc = null;
});
So basically on all the other connections that disconnect, I can see the console.log("GOT THE SESSION DESC") thing... And I'm thinking like, why are these other rooms getting this sessiondesc? They shouldn't be, I'm only emitting it to a specific room!
I am just starting to try out node.js and its very likely that I am mistaken so please bear with me :)
From what I understand, the emit function sends out a custom event and on picks up events and then does something.
Here is of bit of the code from the book I'm learning from.
socket.on('rooms', function(rooms) {
console.log("room received");
$('#room-list').empty();
for(var room in rooms) {
room = room.substring(1, room.length);
if (room != '') {
$('#room-list').append(divEscapedContentElement(room));
}
}
$('#room-list div').click(function() {
chatApp.processCommand('/join ' + $(this).text());
$('#send-message').focus();
});
});
setInterval(function() {
socket.emit('rooms');
console.log("room emitted");
}, 1000);
It is logging "room emitted" in the console every second however, it isn't logging "room received"
This shows that it should be emitting the rooms event and the on function should be picking it up. However for some reason it isn't.
Is there something I'm doing wrong???
socket.emit() sends information from the server to the client(s).
socket.on() receives information sent from the clients to the server.
You can't emit a message from the server to the server unless the server is connected as a client to itself.
Hopefully this was helpful.
I've been developing browser-based multi player game for a while now and I've been testing different ports accessibility in various environment (client's office, public wifi etc.). All is going quite well, except one thing: I can't figure out is how to read error no. or description when onerror event is received.
Client websocket is done in javascript.
For example:
// Init of websocket
websocket = new WebSocket(wsUri);
websocket.onerror = OnSocketError;
...etc...
// Handler for onerror:
function OnSocketError(ev)
{
output("Socket error: " + ev.data);
}
'output' is just some utility function that writes into a div.
What I am getting is 'undefined' for ev.data. Always. And I've been googling around but it seems there's no specs on what params this event has and how to properly read it.
Any help is appreciated!
Alongside nmaier's answer, as he said you'll always receive code 1006. However, if you were to somehow theoretically receive other codes, here is code to display the results (via RFC6455).
you will almost never get these codes in practice so this code is pretty much pointless
var websocket;
if ("WebSocket" in window)
{
websocket = new WebSocket("ws://yourDomainNameHere.org/");
websocket.onopen = function (event) {
$("#thingsThatHappened").html($("#thingsThatHappened").html() + "<br />" + "The connection was opened");
};
websocket.onclose = function (event) {
var reason;
alert(event.code);
// See https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1
if (event.code == 1000)
reason = "Normal closure, meaning that the purpose for which the connection was established has been fulfilled.";
else if(event.code == 1001)
reason = "An endpoint is \"going away\", such as a server going down or a browser having navigated away from a page.";
else if(event.code == 1002)
reason = "An endpoint is terminating the connection due to a protocol error";
else if(event.code == 1003)
reason = "An endpoint is terminating the connection because it has received a type of data it cannot accept (e.g., an endpoint that understands only text data MAY send this if it receives a binary message).";
else if(event.code == 1004)
reason = "Reserved. The specific meaning might be defined in the future.";
else if(event.code == 1005)
reason = "No status code was actually present.";
else if(event.code == 1006)
reason = "The connection was closed abnormally, e.g., without sending or receiving a Close control frame";
else if(event.code == 1007)
reason = "An endpoint is terminating the connection because it has received data within a message that was not consistent with the type of the message (e.g., non-UTF-8 [https://www.rfc-editor.org/rfc/rfc3629] data within a text message).";
else if(event.code == 1008)
reason = "An endpoint is terminating the connection because it has received a message that \"violates its policy\". This reason is given either if there is no other sutible reason, or if there is a need to hide specific details about the policy.";
else if(event.code == 1009)
reason = "An endpoint is terminating the connection because it has received a message that is too big for it to process.";
else if(event.code == 1010) // Note that this status code is not used by the server, because it can fail the WebSocket handshake instead.
reason = "An endpoint (client) is terminating the connection because it has expected the server to negotiate one or more extension, but the server didn't return them in the response message of the WebSocket handshake. <br /> Specifically, the extensions that are needed are: " + event.reason;
else if(event.code == 1011)
reason = "A server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request.";
else if(event.code == 1015)
reason = "The connection was closed due to a failure to perform a TLS handshake (e.g., the server certificate can't be verified).";
else
reason = "Unknown reason";
$("#thingsThatHappened").html($("#thingsThatHappened").html() + "<br />" + "The connection was closed for reason: " + reason);
};
websocket.onmessage = function (event) {
$("#thingsThatHappened").html($("#thingsThatHappened").html() + "<br />" + "New message arrived: " + event.data);
};
websocket.onerror = function (event) {
$("#thingsThatHappened").html($("#thingsThatHappened").html() + "<br />" + "There was an error with your websocket.");
};
}
else
{
alert("Websocket is not supported by your browser");
return;
}
websocket.send("Yo wazzup");
websocket.close();
See http://jsfiddle.net/gr0bhrqr/
The error Event the onerror handler receives is a simple event not containing such information:
If the user agent was required to fail the WebSocket connection or the WebSocket connection is closed with prejudice, fire a simple event named error at the WebSocket object.
You may have better luck listening for the close event, which is a CloseEvent and indeed has a CloseEvent.code property containing a numerical code according to RFC 6455 11.7 and a CloseEvent.reason string property.
Please note however, that CloseEvent.code (and CloseEvent.reason) are limited in such a way that network probing and other security issues are avoided.
Potential stupid fix for those who throw caution to the wind: return a status code. The status code can be viewed from the onerror event handler by accessing the message property of the argument received by the handler. I recommend going with the 440s--seems to be free real estate.
"Unexpected server response: 440"
Little bit of regex does the trick:
const socket = new WebSocket(/* yuh */);
socket.onerror = e => {
const errorCode = e.message.match(/\d{3}/)[0];
// errorCode = '440'
// make your own rudimentary standard for error codes and handle them accordingly
};
Might be useful in a pinch, but don't come crying to me for any unforeseen repercussions.
I'm trying to learn how to create an RTCPeerConnection so that I can use the DataChannel API. Here's what I have tried from what I understood:
var client = new mozRTCPeerConnection;
var server = new mozRTCPeerConnection;
client.createOffer(function (description) {
client.setLocalDescription(description);
server.setRemoteDescription(description);
server.createAnswer(function (description) {
server.setLocalDescription(description);
client.setRemoteDescription(description);
var clientChannel = client.createDataChannel("chat");
var serverChannel = server.createDataChannel("chat");
clientChannel.onmessage = serverChannel.onmessage = onmessage;
clientChannel.send("Hello Server!");
serverChannel.send("Hello Client!");
function onmessage(event) {
alert(event.data);
}
});
});
I'm not sure what's going wrong, but I'm assuming that the connection is never established because no messages are being displayed.
Where do I learn more about this? I've already read the Getting Started with WebRTC - HTML5 Rocks tutorial.
I finally got it to work after sifting through a lot of articles: http://jsfiddle.net/LcQzV/
First we create the peer connections:
var media = {};
media.fake = media.audio = true;
var client = new mozRTCPeerConnection;
var server = new mozRTCPeerConnection;
When the client connects to the server it must open a data channel:
client.onconnection = function () {
var channel = client.createDataChannel("chat", {});
channel.onmessage = function (event) {
alert("Server: " + event.data);
};
channel.onopen = function () {
channel.send("Hello Server!");
};
};
When the client creates a data channel the server may respond:
server.ondatachannel = function (channel) {
channel.onmessage = function (event) {
alert("Client: " + event.data);
};
channel.onopen = function () {
channel.send("Hello Client!");
};
};
We need to add a fake audio stream to the client and the server to establish a connection:
navigator.mozGetUserMedia(media, callback, errback);
function callback(fakeAudio) {
server.addStream(fakeAudio);
client.addStream(fakeAudio);
client.createOffer(offer);
}
function errback(error) {
alert(error);
}
The client creates an offer:
function offer(description) {
client.setLocalDescription(description, function () {
server.setRemoteDescription(description, function () {
server.createAnswer(answer);
});
});
}
The server accepts the offer and establishes a connection:
function answer(description) {
server.setLocalDescription(description, function () {
client.setRemoteDescription(description, function () {
var port1 = Date.now();
var port2 = port1 + 1;
client.connectDataConnection(port1, port2);
server.connectDataConnection(port2, port1);
});
});
}
Phew. That took a while to understand.
I've posted a gist that shows setting up a data connection, compatible with both Chrome and Firefox.
The main difference is that where in FF you have to wait until the connection is set up, in Chrome it's just the opposite: it seems you need to create the data connection before any offers are sent back/forth:
var pc1 = new RTCPeerConnection(cfg, con);
if (!pc1.connectDataConnection) setupDC1(); // Chrome...Firefox defers per other answer
The other difference is that Chrome passes an event object to .ondatachannel whereas FF passes just a raw channel:
pc2.ondatachannel = function (e) {
var datachannel = e.channel || e;
Note that you currently need Chrome Nightly started with --enable-data-channels for it to work as well.
Here is a sequence of events I have working today (Feb 2014) in Chrome. This is for a simplified case where peer 1 will stream video to peer 2.
Set up some way for the peers to exchange messages. (The variance in how people accomplish this is what makes different WebRTC code samples so incommensurable, sadly. But mentally, and in your code organization, try to separate this logic out from the rest.)
On each side, set up message handlers for the important signalling messages. You can set them up and leave them up. There are 3 core messages to handle & send:
an ice candidate sent from the other side ==> call addIceCandidate with it
an offer message ==> SetRemoteDescription with it, then make an answer & send it
an answer message ===> SetRemoteDescription with it
On each side, create a new peerconnection object and attach event handlers to it for important events: onicecandidate, onremovestream, onaddstream, etc.
ice candidate ===> send it to other side
stream added ===> attach it to a video element so you can see it
When both peers are present and all the handlers are in place, peer 1 gets a trigger message of some kind to start video capture (using the getUserMedia call)
Once getUserMedia succeeds, we have a stream. Call addStream on the peer 1's peer connection object.
Then -- and only then -- peer 1 makes an offer
Due to the handlers we set up in step 2, peer 2 gets this and sends an answer
Concurrently with this (and somewhat obscurely), the peer connection object starts producing ice candidates. They get sent back and forth between the two peers and handled (steps 2 & 3 above)
Streaming starts by itself, opaquely, as a result of 2 conditions:
offer/answer exchange
ice candidates received, exchanged, and added
I haven't found a way to add video after step 9. When I want to change something, I go back to step 3.