Generating ICE Candidates - webrtc

I am working WebRTC API's to make a video call between two PC's running on chrome browser. My observation is ICE Candidates are generated only if i connected to internet else no ice candidates are generated. Why is it like that?
connection block
var pc_config = {"iceServers":[]};
pc = new webkitRTCPeerConnection(pc_config);
pc.onicecandidate=function (evt) {
if(evt.candidate){
console.log("Sending candidate to other peer"+evt);
jWebSocketClient.broadcastText("",evt);
}
};
Thanks,
Sureshkumar Menon

As far as I understand, there is four types of ICE candidate :
Host candidate : from your local interface.
Server reflexive candidate : provided by the STUN server, a translation of your local address into public network.
Relayed candidate : provided by a TURN server, data will be relayed by the server
Peer reflexive candidate : a rare case (?) where candidate is discovered during the connectivity checks. I'll skip this part as it is quite rare and I'm not sure to understand the big picture of it.
If you don't provide any STUN / TURN addresses to your program or if they are unreachable, the only candidate which can be retrieved is the host one. Note that your local address (127.0.0.1) is not taken as a potential candidate.
Hope it helps.
However, I'm not totally sure to understand your use case.. Are both computers on the same local network ? If your interface is up, you should get at least the host candidate. I only worked with the C++ API, but I don't see why it would have a different behavior with the Javascript's.

If I'm not mistaken, ICE Candidates are created by contacting a STUN server, thus you need internet connection. This is done to translate a private address into a public one to enable your clients to connect (and be connected) to other clients.

Yes you have to connect to internet before your pcs share SDP. This is because ICE Server is not on your local computers but on the internet. The ICE server is connected in the WEB RTC in this line:
if (browser === 'firefox') {
PeerConnConfig = {
iceServers: [{
url: "stun:23.21.150.121" // FF doesn't support resolving DNS in iceServers yet
}
]
};
mediaConstraints = {
mandatory: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true,
MozDontOfferDataChannel: true // Tell FF not to put datachannel info in SDP or chrome will crash
}
};
// FF doesn't expose this yet
MediaStream.prototype.getVideoTracks = function () {
return [];
};
MediaStream.prototype.getAudioTracks = function () {
return [];
};
} else {
PeerConnConfig = {
iceServers: [{
url: "stun:stun.l.google.com:19302"
}
]
};
mediaConstraints = {
mandatory: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
},
optional: [{
DtlsSrtpKeyAgreement: true
}
]
};
// API compat for older versions of chrome
if (!MediaStream.prototype.getVideoTracks) {
MediaStream.prototype.getVideoTracks = function () {
return this.videoTracks;
};
MediaStream.prototype.getAudioTracks = function () {
return this.audioTracks;
};
}
if (!PeerConnection.prototype.getLocalStreams) {
PeerConnection.prototype.getLocalStreams = function () {
return this.localStreams;
};
PeerConnection.prototype.getRemoteStreams = function () {
return this.remoteStreams;
};
}
}
I cut above code from WEBRTC_SHIM. consider especially line that defines the ICE server as: url: "stun:stun.l.google.com:19302".

Related

Is there a way to multi browser communicate each other without peers?still able to communicate after lose peers connecting?

Is there a way to multi browser communicate each other without peers?or still able to communicate after lose peers connecting?
I created sample with gun.js like below:
server.js:
const express = require('express')
const Gun = require('gun')
const app = express()
const port = 8000
app.use(Gun.serve)
const server = app.listen(port, () => {
console.log("Listening at: http://localhost://" + port)
})
Gun({web: server})
test.ts on angular demo:
gun = GUN({
peers: ['http:localhost:8000/gun']
});
data: any;
initDate(): void {
this.gun.get('mark').put({
name: "Mark",
email: "mark#gun.eco",
});
}
listenDate(): void {
this.gun.get('mark').on((data, key) => {
console.log("realtime updates:", data);
this.data = data;
});
}
submit(): void {
this.gun.get('mark').get('live').put(Math.random());
}
I start server.js as a peer and start angular app,open two broswer with same url,the two broswer communicate well.
but after i stop server.js , the two broswer are unable to communicate each other.
Is there a way to the two browser communicate each other without server.js?or how still able to communicate after I stop server.js?

Where should be the location for coturn or ice setting for sipjs 0.11.0?

I am moving from sipjs 0.7x to sipjs 0.11
After reading the Git issue https://github.com/onsip/SIP.js/pull/426#issuecomment-312065734
and
https://sipjs.com/api/0.8.0/sessionDescriptionHandler/
I have found that the ice options (coturn, turn, stun) is not in User Agent anymore,
but the problem is that I am not quite understand where should I use the
setDescription(sessionDescription, options, modifiers)
I have seen that the ice is set in options, using
options.peerConnectionOptions.rtcConfiguration.iceServers
below is what I haved tried
session.on('trackAdded', function () {
// We need to check the peer connection to determine which track was added
var modifierArray = [
SIP.WebRTC.Modifiers.stripTcpCandidates,
SIP.WebRTC.Modifiers.stripG722,
SIP.WebRTC.Modifiers.stripTelephoneEvent
];
var options = {
peerConnectionOptions:{
rtcConfiguration:{
iceServers : {
[{urls: 'turn:35.227.67.199:3478',
username: 'leon',
credential: 'leon_pass'}]
}
}
}
}
session.setDescription('trackAdded', options,modifierArray);
var pc = session.sessionDescriptionHandler.peerConnection;
// Gets remote tracks
var remoteStream = new MediaStream();
pc.getReceivers().forEach(function (receiver) {
remoteStream.addTrack(receiver.track);
});
remoteAudio.srcObject = remoteStream;
remoteAudio.play();
// Gets local tracks
// var localStream = new MediaStream();
// pc.getSenders().forEach(function(sender) {
// localStream.addTrack(sender.track);
// });
// localVideo.srcObject = localStream;
// localVideo.play();
});
}
I have tried this and it seems that the traffic is not going to the coturn server.
I have used Trickle Ice "https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/" to test and it is fine, but I have found there is not traffic going through the coturn server. You could also use this one and I do not mind.
There is even no demo on the official website to show how could we use the setDescription(sessionDescription, options, modifiers). In this case can I please ask some recommendations?
Configure STUN/TURN servers in the parameters passed to new UserAgent.
Here is sample, it seems to be working on v0.17.1:
const userAgentOptions = {
...
sessionDescriptionHandlerFactoryOptions: {
peerConnectionConfiguration: {
iceServers: [{
urls: "stun:stun.l.google.com:19302"
}, {
urls: "turn:TURN_SERVER_HOST:PORT",
username: "USERNAME",
credential: "PASSWORD"
}]
},
},
...
};
const userAgent = new SIP.UserAgent(userAgentOptions);
When using SimpleUser - pass it inside SimpleUserOptions:
const simpleUser = new Web.SimpleUser(url, { userAgentOptions })

Chrome on Android not connecting to peer over WebRTC

I'm building a WebRTC app with a central electron app that browsers connect to. The scenario I'm testing is running the electron app on my computer (Ubuntu 16.04) and connecting from chrome (69) on Android (7.0). Following the debugging the offer, answer and candidate are passed, but fails on the last stop of generating the connection. The ice connection state switchs to "checking" then "failed". I'm able to load the browser app on my laptop and connect to an electron app on the same computer.
Should I be using a collection of ice servers? What do I need to make the WebRTC connection more robust? Should I not write my own signaling process and use something pre-made? Is their anyway to debug the reason why the connection failed? For debugging I've tried the webrtc debugging tab on chrome but all it says is connection failled at the bottom of the stack.
The configuration for my RTCPeerConnection is:
const configuration = {
"iceServers": [{ "urls": "stun:stun.1.google.com:19302" }]
};
This is the code I use to form iniate the connection to the electron app from the browser app (The function are attached to a ts class which are called in order: setupPeerConnection, setupDataChannel, makeOffer):
setupPeerConnection(gameID:string) {
this.gameID = gameID;
let configuration:RTCConfiguration = {
"iceServers": [{ "urls": "stun:stun.1.google.com:19302" }]
};
this.connection = new RTCPeerConnection(configuration);
const that:PeerConnection = this;
//Definition of the data channel
this.connection.ondatachannel = function(ev:RTCDataChannelEvent) {
};
//When we get our own ICE Candidate, we provide it to the other Peer.
this.connection.onicecandidate = function(event:RTCPeerConnectionIceEvent) {
if (event.candidate && that.connectedToGame) {
that.serverConnection.send({
type: "candidate",
candidate: event.candidate
});
}
};
this.connection.oniceconnectionstatechange = function(e:Event) {
let iceState:RTCIceConnectionState = this.iceConnectionState;
console.log("Changing connection state:", iceState)
if (iceState == "connected") {
console.log("Connection established with server");
} else if (iceState =="disconnected" || iceState == "closed") {
// We lost the user
that.connectedToGame = false;
that.onLeave();
}
};
}
setupDataChannel(onopen:(error:ErrorEvent)=>void, onmessage:(message:MessageEvent)=>void) {
let dataChannelOptions:RTCDataChannelInit = <RTCDataChannelInit>{
reliable: true
};
this.dataChannel = this.connection.createDataChannel(this.gameID + "-dataChannel", dataChannelOptions);
this.dataChannel.onerror = function (error:ErrorEvent) {
console.log("Error on data channel:", error);
};
this.dataChannel.onmessage = onmessage.bind(this);
this.dataChannel.onopen = onopen;
this.dataChannel.onclose = function() {
console.log("Channel closed.");
};
}
makeOffer() {
let that:PeerConnection = this;
this.connection.createOffer().then(function (offer:RTCSessionDescriptionInit) {
that.serverConnection.send({
gameID: that.gameID,
type: "offer",
offer: offer
});
that.connection.setLocalDescription(offer);
}, function (error) {
console.log("Error contacting remote peer: ", error);
});
}

ICE candidates gathered only from one network interface

Got very simple code:
<script type="text/javascript">
pc = new window.RTCPeerConnection();
pc.onicecandidate = function(event) {
console.log("onicecandidate\n", event);
}
pc.onicegatheringstatechange = function(event) {
console.log("onicegatheringstatechange\n", event);
}
dc = pc.createDataChannel("dataChannel");
errFunc = function(err) {
console.log("errFunc\n", err);
}
successCback = function() {
console.log("setLocalDescription is a success\n");
}
pc.createOffer()
.then(function(offer) { pc.setLocalDescription(offer)})
.then(successCback)
.catch(errFunc);
</script>
Got ubuntu running chromium and TWO local ethernet interfaces.
Running aforementioned code yields only 1 call to onicecandidate and
1 call to onicegatheringstatechange. (any STUN/TURN servers are deliberately not specified, so I do expect only local host candidates, but from all interfaces). Only one ethernet interface is examined by ICE.
Why ?
Unless you have permissions for getUserMedia, Chrome will restrict ICE candidates to the interface of the default route. The rationale is explained in this draft

Node.js + socket.io + node-amqp and queue binginds when "re" connecting thru socket.io

I have one scenario which is very close to this sample:
One main screen:
this screen (client side) will connect to the socket.io server thru server:9090/scope (io.connect("http://server:9090/scope)) and will send one event "userBindOk" (socket.emit("userBindOk", message)) to the socket.io server;
the server receives the connection and the "userBindOk". At this moment, the server should get the active connection to rabbitmq server and bind the queue to the respective user that just connected to the application thru socket.io. sample:
socket.on("connection", function(client){
//client id is 1234
// bind rabbitmq exchange, queue, and:
queue.subscribe(//receive callback);
})
So far, no problem - I can send/receive messages thru socket.io without problems.
BUT, If I refresh the page, all those steps will be done again. As consequence, the binding to the queue will occur, but this time related to another session of the socket.io client. This means that if I send a message to the queue which is related to the first socket.io session (before the page refresh), that bind should (I think) receive the message and send it to a invalid socket.io client (page refresh = new client.id on the socket.io context). I can prove this behaviour because every time I refresh the page I need to send x times more messages. For instance: I`ve connected for the first time: - so, 1 message - one screen update; refresh the page: I need to send 2 messages to the queue and only the second message will be received from the "actual" socket.io client session - this behaviour will occur as many as I refresh the page (20 page refreshs, 20 messages to be sent to a queue and the server socket.io "last" client will send the message to the client socket.io to render into the screen).
The solutions I believe are:
Find a way to "unbind" the queue when disconnecting from the socket.io server - I didn`t see this option at the node-amqp api yet (waiting for it :D)
find a way to reconnect the socket.io client using the same client.id. This way I can identify the client that is coming and apply some logic to cache the socket.
Any ideas? I tried to be very clear... But, as you know, it`s not so eaey to expose your problem when trying to clarify something that is very specific to some context...
tks
I solved it like this:
I used to declare the rabbitMq queue as durable=true,autoDelete=false,exclusive=false and in my app there was 1 queue/user and 1 exchange(type=direct) with the routing_key name=queueName, my app also used the queue for other client diffent to browsers like android app or iphone app as push fallback, so i use to crear 1 queue for earch user.
The solution to this problem was to change my rabbitMQ queue and exchange declaration. Now i declare the exchange/user as fanout and autoDelete=True, and the user is going to have N queues with durable=true, autoDelete=true, exclusive=true (No. queue = No. clients) and all the queues are bind to the user-exchange(multicast).
NOTE: my app is wirten in django, and i use node+socket+amqp to be able to comunicate with the browser using web.scokets, so i use node-restler to query my app api to get the user-queue info.
thats the rabbitMQ side, for the node+amqp+socket i did this:
server-side:
onConnect: the declaration of the user exchange as fanout, autoDelete, durable. then declaration of the queue as durable, autodelete and exclusive, then the queue.bind to the user-exchange and finaly the queue.subscribe and the socket.disconnect will destroy the queue so there are going to exist queue as client connected the app and this solve the problem of the refresh and allow the user to have more than 1 window-tab with the app:
Server-side:
/*
* unCaught exception handler
*/
process.on('uncaughtException', function (err) {
sys.p('Caught exception: ' + err);
global.connection.end();
});
/*
* Requiere libraries
*/
global.sys = require('sys');
global.amqp = require('amqp');
var rest = require('restler');
var io = require('socket.io').listen(8080);
/*
* Module global variables
*/
global.amqpReady = 0;
/*
* RabbitMQ connection
*/
global.connection = global.amqp.createConnection({
host: host,
login: adminuser,
password: adminpassword,
vhost: vhost
});
global.connection.addListener('ready',
function () {
sys.p("RabbitMQ connection stablished");
global.amqpReady = 1;
}
);
/*
* Web-Socket declaration
*/
io.sockets.on('connection', function (socket) {
socket.on('message', function (data) {
sys.p(data);
try{
var message = JSON.parse(data);
}catch(error){
socket.emit("message", JSON.stringify({"error": "invalid_params", "code": 400}));
var message = {};
}
var message = JSON.parse(data);
if(message.token != undefined) {
rest.get("http://dev.kinkajougames.com/api/push",
{headers:
{
"x-geochat-auth-token": message.token
}
}).on('complete',
function(data) {
a = data;
}).on('success',
function (data){
sys.p(data);
try{
sys.p("---- creating exchange");
socket.exchange = global.connection.exchange(data.data.bind, {type: 'fanout', durable: true, autoDelete: true});
sys.p("---- declarando queue");
socket.q = global.connection.queue(data.data.queue, {durable: true, autoDelete: true, exclusive: false},
function (){
sys.p("---- bind queue to exchange");
//socket.q.bind(socket.exchange, "*");
socket.q.bind(socket.exchange, "*");
sys.p("---- subscribing queue exchange");
socket.q.subscribe(function (message) {
socket.emit("message", message.data.toString());
});
}
);
}catch(err){
sys.p("Imposible to connection to rabbitMQ-server");
}
}).on('error', function (data){
a = {
data: data,
};
}).on('400', function() {
socket.emit("message", JSON.stringify({"error": "connection_error", "code": 400}));
}).on('401', function() {
socket.emit("message", JSON.stringify({"error": "invalid_token", "code": 401}));
});
}
else {
socket.emit("message", JSON.stringify({"error": "invalid_token", "code": 401}));
}
});
socket.on('disconnect', function () {
socket.q.destroy();
sys.p("closing socket");
});
});
client-side:
The socket intance with options 'force new connection'=true and 'sync disconnect on unload'= false.
The client side use the onbeforeunload and onunload windows object events to send socket.disconnect
The client on socket.connect event send the user token to node.
proces message from socket
var socket;
function webSocket(){
//var socket = new io.Socket();
socket = io.connect("ws.dev.kinkajougames.com", {'force new connection':true, 'sync disconnect on unload': false});
//socket.connect();
onSocketConnect = function(){
alert('Connected');
socket.send(JSON.stringify({
token: Get_Cookie('liveScoopToken')
}));
};
socket.on('connect', onSocketConnect);
socket.on('message', function(data){
message = JSON.parse(data);
if (message.action == "chat") {
if (idList[message.data.sender] != undefined) {
chatboxManager.dispatch(message.data.sender, {
first_name: message.data.sender
}, message.data.message);
}
else {
var username = message.data.sender;
Data.Collections.Chats.add({
id: username,
title: username,
user: username,
desc: "Chat",
first_name: username,
last_name: ""
});
idList[message.data.sender] = message.data.sender;
chatboxManager.addBox(message.data.sender, {
title: username,
user: username,
desc: "Chat",
first_name: username,
last_name: "",
boxClosed: function(id){
alert("closing");
}
});
chatboxManager.dispatch(message.data.sender, {
first_name: message.data.sender
}, message.data.message);
}
}
});
}
webSocket();
window.onbeforeunload = function() {
return "You have made unsaved changes. Would you still like to leave this page?";
}
window.onunload = function (){
socket.disconnect();
}
And that's it, so no more round-robing of the message.