Creating and using a data channel between two peers with webRTC - javascript

I am trying to setup a peer to peer file sharing system using WebRTC. I'm able to open a data channel on each side, but I can't send messages from one user to another. Moreover, if one peer closes the channel, the other, the onclose event is only triggered for this user.
What's the proper way to setup and use a data channel with webRTC?
Could you tell me what's wrong or missing in my code?
//create RTC peer objet.
var RTCPeerConnection = webkitRTCPeerConnection;
var RTCIceCandidate = window.RTCIceCandidate;
var RTCSessionDescription = window.RTCSessionDescription;
var iceServers = {
iceServers: [{
url: 'stun:stun.l.google.com:19302'
}]
};
var p2p_connection = new RTCPeerConnection({
iceServers: [
{ 'url': (IS_CHROME ? 'stun:stun.l.google.com:19302' : 'stun:23.21.150.121') }
]
});
// send offer (only executes in one browser)
function initiateConnection() {
p2p_connection.createOffer(function (description) {
p2p_connection.setLocalDescription(description);
server_socket.emit('p2p request', description,my_username);
});
};
// receive offer and send answer
server_socket.on('p2p request', function(description,sender){
console.log('received p2p request');
p2p_connection.setRemoteDescription(new RTCSessionDescription(description));
p2p_connection.createAnswer(function (description) {
p2p_connection.setLocalDescription(description);
server_socket.emit('p2p reply', description,sender);
});
});
// receive answer
server_socket.on('p2p reply', function(description,sender){
console.log('received p2p reply');
p2p_connection.setRemoteDescription(new RTCSessionDescription(description));
});
// ICE candidates
p2p_connection.onicecandidate = onicecandidate; // sent event listener
// locally generated
function onicecandidate(event) {
if (!p2p_connection || !event || !event.candidate) return;
var candidate = event.candidate;
server_socket.emit('add candidate',candidate,my_username);
}
// sent by other peer
server_socket.on('add candidate', function(candidate,my_username){
p2p_connection.addIceCandidate(new RTCIceCandidate({
sdpMLineIndex: candidate.sdpMLineIndex,
candidate: candidate.candidate
}));
});
// data channel
var dataChannel = p2p_connection.createDataChannel('label');
dataChannel.onmessage = function (event) {
var data = event.data;
console.log("I got data channel message: ", data);
};
dataChannel.onopen = function (event) {
console.log("Data channel ready");
dataChannel.send("Hello World!");
};
dataChannel.onclose = function (event) {
console.log("Data channel closed.");
};
dataChannel.onerror = function (event) {
console.log("Data channel error!");
}
Update:
Found the solution there: http://www.html5rocks.com/en/tutorials/webrtc/basics/
p2p_connection.ondatachannel = function (event) {
receiveChannel = event.channel;
receiveChannel.onmessage = function(event){
console.log(event.data);
};
};

You might consider using the simple-peer library to avoid dealing with these complexities in the future. The WebRTC API calls are confusing and the ordering is sometimes hard to get right.
simple-peer supports video/voice streams, data channel (text and binary data), and you can even use the data channel as a node.js-style duplex stream. It also supports advanced options like disabling trickle ICE candidates (so each client only needs to send one offer/answer message instead of many repeated ice candidate messages). It's un-opinionated and works with any backend.
https://github.com/feross/simple-peer

Abstractions!
https://github.com/feross/simple-peer (noted above by #Feross)
https://github.com/rtc-io/rtc-mesh
https://github.com/dominictarr/scuttlebutt
https://github.com/mafintosh/peervision
https://github.com/muaz-khan/DataChannel
Gigantic list of related projects...
https://github.com/kgryte/awesome-peer-to-peer

Related

WebRTC more than one peer connection

I searched almost everywhere and can't seem to find any resource on this scenario for some reason. Any help would greatly be appreciated.
The problem is this:
I have 4 users. Lets say: A,B,C and D. I want to match them according to url. For example, if A and B connects to &room=1 and C and D connects to &room=2 I want to establish the connection between A and B pairs and C and D pairs.
Currently my code only establishes connection between A and B, and if one of the C or D users initiates the call, the connection between A and B gets disconnected and C cannot find D.
In order to solve this problem I tried to create an object like this:
{
room: 1,
peer: RTCPeerConnection
}
and store it in an array and get the peer connection according to room id and establish the connection based on RTCPeerConnection that is stored according to roomId. But that didn't work.
How would I go around fixing this without establishing a socket server that handles this communication?
I currently establish connections this way if it helps:
navigator.mediaDevices
.getUserMedia({
audio: true,
video: true
})
.then(function (stream) {
localStream = stream;
localVideo.srcObject = localStream;
try {
conn = new RTCPeerConnection(servers);
} catch (err) {
console.log("Can't establish connection");
return;
}
localStream.getTracks().forEach(function (track) {
conn.addTrack(track, localStream);
});
conn.onaddstream = function (event) {
setTimeout(() => {
callButton.click();
}, 2000);
remoteVideo.srcObject = event.stream;
};
conn.onicecandidate = function (event) {
if (event.candidate) {
chat.server.iceCandidate(
JSON.stringify({ candidate: event.candidate })
);
}
};
})
.catch(function (err) {
console.log("Error", err);
});
And this is my failed solution:
var connections = JSON.parse(localStorage.getItem("connections")) || [];
var connection;
if (connections) {
connection = connections.find(function (conn) {
return conn.id = roomId;
})
}
if (!connection) {
conn = new RTCPeerConnection(servers);
var data = {
id: roomId,
peer: conn
}
localStorage.removeItem("connections");
localStorage.setItem("connections", JSON.stringify(connections));
} else {
conn = JSON.parse(connection.peer);
conn.__proto__ = new RTCPeerConnection();
}
This fails because of course you can't store proto of an object in localStorage (RTCPeerConnection). When I stringify/parse it peer attribute comes as an empty object. If I try a global variable, instead of localStorage, it always comes empty. I'd very appreciate any tip about this issue.
It's like your messing up with the localStorage variable, for testing purpose and avoid the signaling server implementation, I advice to use the serverless-webrtc or an updated by jameshfisher
With this you can create the rooms (separate the clients) and show you a sdp message to copy and paste to the other client (without localStorage) to make the peer connection.
This will be better to test between browsers firefox <-> chrome, safari, etc.

Cannot get remote video displayed

I have implemented two webrtc clients (peers) and a simple signaling server. Three elements are in local. Despite the get media, the offer/answer methods between peers and the signaling server seems working, I am only able to display local videos in both peers (both in different browser tabs)
Here is the SDP that the offerer sends to the other peer
v=0
o=- 1118386230690454721 2 IN IP4 127.0.0.1
s=-
t=0 0
a=msid-semantic: WMS
The other peer answers with similar SDP but other session id.
localVideo = document.querySelector('#localVideo');
remoteVideo = document.querySelector('#remoteVideo');
socket = io.connect("http://localhost:3000");
pc = new RTCPeerConnection(null);
var constraints = {video: true, audio: true};
getUserMedia(constraints, handleUserMedia, handleUserMediaError);
pc.onaddstream = handleRemoteStreamAdded;
pc.onremovestream = handleRemoteStreamRemoved;
pc.onicecandidate = handleIceCandidate;
var lspd;
pc.createOffer().then(function(offer) {
lspd = offer;
return pc.setLocalDescription(offer);
}).then(function() {
var offerData = {
sdp: lspd,
customerName: "name",
room: room
}
socket.emit('offer', offerData);
}).catch(function(reason) {
console.log("Error on createOffer: " + reason);
});
socket.on('answering', function (msg){
pc.setRemoteDescription(msg);
});
function handleRemoteStreamAdded(event) {
remoteVideo.src = window.URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
function handleUserMedia(stream) {
localVideo.src = window.URL.createObjectURL(stream);
localStream = stream;
pc.addStream(stream);
}
The onaddstream is never added in both peers and I never can see remote video. Moreover it seems that the SDP is not complete.
Any ideas on what could I try or debug in order to reach communication between peers?
Thanks
you are calling createOffer before adding the stream to the peerconnection. You need to move your code that calls createOffer (and everything after that) to the handleUserMedia function.

Failed to set local answer sdp: Called in wrong state: STATE_INPROGRESS

I have two clients :
1) Chrome (version 50.0.2661.102 m) on Windows 7 PC
2) Chrome (version 50.0.2661.89) on Android tablet
Both are in the same network (so no need for STUN/TURN server).
I use my own signal server built with node.js (webSocket) on a VirtualBox VM with Centos 6.
The communication with video/sound between the clients works fine. Now I want to transfer a file from one client to another. As base of my code i use the code of this example
here
As this code suggess, I create the dataChannnel exactly after the creation of PeerConnection.
function createPeerConnection() {
....
myPeerConnection = new RTCPeerConnection(iceServers, optional);
myDataChannel = myPeerConnection.createDataChannel('myDataChannel');
// Set up event handlers for the ICE negotiation process.
myPeerConnection.onicecandidate = handleICECandidateEvent;
myPeerConnection.onaddstream = handleAddStreamEvent;
myPeerConnection.onnremovestream = handleRemoveStreamEvent;
myPeerConnection.oniceconnectionstatechange = handleICEConnectionStateChangeEvent;
myPeerConnection.onicegatheringstatechange = handleICEGatheringStateChangeEvent;
myPeerConnection.onsignalingstatechange = handleSignalingStateChangeEvent;
myPeerConnection.onnegotiationneeded = handleNegotiationNeededEvent;
myPeerConnection.ondatachannel = handleDataChannel;
myDataChannel.onmessage = handleDataChannelMessage;
myDataChannel.onopen = handleDataChannelOpen;
}
...
...
function invite(peerId) {
...
createPeerConnection();
...
}
...
...
function handleVideoOfferMsg(msg) {
thereIsNegotiation = true;
targetUsername = msg.name;
// Call createPeerConnection() to create the RTCPeerConnection.
log("Starting to accept invitation from " + targetUsername);
createPeerConnection();
// We need to set the remote description to the received SDP offer
// so that our local WebRTC layer knows how to talk to the caller.
var desc = new RTCSessionDescription(msg.sdp);
myPeerConnection.setRemoteDescription(desc)
.then(function(stream) {
log("-- Calling myPeerConnection.addStream()");
return myPeerConnection.addStream(localStream);
})
.then(function() {
log("------> Creating answer");
// Now that we've successfully set the remote description, we need to
// start our stream up locally then create an SDP answer. This SDP
// data describes the local end of our call, including the codec
// information, options agreed upon, and so forth.
return myPeerConnection.createAnswer();
})
.then(function(answer) {
log("------> Setting local description after creating answer");
// We now have our answer, so establish that as the local description.
// This actually configures our end of the call to match the settings
// specified in the SDP.
return myPeerConnection.setLocalDescription(answer);
})
.then(function() {
var msg = {
name: clientId,
room: roomId,
target: targetUsername,
type: "video-answer",
sdp: myPeerConnection.localDescription
};
// We've configured our end of the call now. Time to send our
// answer back to the caller so they know that we want to talk
// and how to talk to us.
log("Sending answer packet back to other peer");
sendToServer(msg);
})
.catch(handleGetUserMediaError);
}
When the second client makes the offer, the first client when tries to make the answer, I get the error
Error opening your camera and / or microphone : failed to set local answer
spd: Failed to push down transport description: Local fingerprint provided
but no identity available.
or
Error opening your camera and / or microphone : failed to set local answer
spd: Called in wrong state : STATE_INPROGRESS
Only one time the creation was successful.
Do I have to create DataChannel in other place? Like here :
function handleICEConnectionStateChangeEvent {
switch(myPeerConnection.iceConnectionState) {
...
case "connected":
createDataChannel();
break;
}
}
function createDataChannel(){
myDataChannel = myPeerConnection.createDataChannel('myDataChannel');
myPeerConnection.ondatachannel = handleDataChannel;
myDataChannel.onmessage = handleDataChannelMessage;
myDataChannel.onopen = handleDataChannelOpen;
}
Any suggestions?
The error in this code is that both sender and receiver create new datachannel. The right thing is, one to create the datachannel
myDataChannel = myPeerConnection.createDataChannel('myDataChannel')
and the other to wait for the creation of dataChannel:
myPeerConnection.ondatachannel = handleDataChannel;

webrtc data channel not workinig

I am trying to setup a text chat using webrtc data channel.
my network his a private network so i can't use any dependencies or frameworks like peerjs or similar.
I published my project on java play server so
i have one webrtsPeerConnection object that user can choose to initiate connection or to accept connection from someone else.
the problem : data channel is setup and active for the user who initiate the call.
but for the user who joined the call data channel don't activate and onDataChannel event never fires.
any suggestions??
Thanks in advance!
my code java script:
// init peer connection and data channel objects
var pc = new RTCpeerConnection(null,null);
var DC,DCnam;
function InitConnection(){
//created RTCpeerConnection
createDataChannel();
pc.createOffer(function(desc){
pc.setLocalDescripyion(desc,function(){},function(){})
enter code here
})
}
//create data channel
function createDataChannel(){
DC = pc.createDataChannel(DCname,{
reliable:true
});
}
//when user A call user B set remote description and create answer
function CheckCalls(){
&http.get("/checkCslls").success(function(data){
if(data[0])
{
//get offer and offerer
offerer = data[0].offerer;
pc.odataChannel function(e){
console.log(e);
}
pc.setRemoteDescription(new sessionDescription()data[0].offer));
pc.createAnswer(function(answerDesc){
pc.setLocalDescripyion(answerDesc);
})
}
})
}
//when user B send answer
(onDataChannel event fires on user A object).
function checkAnswers(){
$http.get("/checkAnswers").success(function(data){
if(data.answer){
pc.setRemoteDescription(new sessionDescription(data.answer));
}
})
It can be that you misspelled the callback:
pc.odataChannel function(e){
console.log(e);
}
it is ondatachannel with an "n" and lower case "c" and a "=" to define the function and callbacks to do something when messages are delivered;
something like:
var receiveChannel;
pc.ondatachannel = function (event) {
console.log('Receive Channel Callback');
receiveChannel = event.channel;
receiveChannel.onmessage = gotCMessage;
receiveChannel.onopen = dcOpen;
receiveChannel.onclose = dcClose;
console.log(event);
}

Connect two clients in websocket with PyWebSocket

i am new to websocket using pywebsocket, basically trying to create a simple chat application with websocket and pywebsocket.
Until now i am done with following script
if ("WebSocket" in window)
{
//alert("WebSocket is supported by your Browser!");
// Let us open a web socket
var ws = new WebSocket("ws://192.168.1.3:9998/echo");
ws.onopen = function()
{
// Web Socket is connected, send data using send()
console.log("Channel opened");
//ws.send("Message to send");
//alert("Message is sent...");
//console.log("Message is sent...");
};
ws.onmessage = function (evt)
{
var received_msg = evt.data;
//alert("Message is received..."+received_msg);
console.log("message : "+received_msg);
};
ws.onclose = function()
{
// websocket is closed.
//alert("Connection is closed...");
console.log("connection closed");
};
}
else
{
// The browser doesn't support WebSocket
alert("WebSocket NOT supported by your Browser!");
}
function sendmessage(){
msg=document.getElementById("chat").value;
console.log(msg);
ws.send(msg);
document.getElementById("chat").value="";
}
Now the problem is the messages that are sent are echoed to the system itself, if another client connect to same channel its messages are echoed to itself, they are sent to another client which is connected with same channel.
pywebsocket is initialized as follows
python standalone.py -p 9998 -w ../example/
So how can i connect two system and allow chat.
Thanks in advance
Not sure, but if you want to broadcast any received message (sent by any connected client) to all other currently connected clients, that is not "echo" .. but "broadcast".
Here is an example using AutobahnPython.
Disclosure: I am original author of Autobahn and work for Tavendo.

Categories