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.
Related
I am getting the error from this page. The error is client.js:166 Uncaught TypeError: Cannot read property 'addIceCandidate' of undefined. Below is the Code. How to remove that error? The video from the other browser is sending stream to the server, while adding the stream in both browser, there becomes the error. Where is the error occurs after it got stream.
var divSelectRoom = document.getElementById("selectRoom");
var divConsultingRoom = document.getElementById("consultingRoom");
var inputRoomNumber = document.getElementById("roomNumber");
var btnGoRoom = document.getElementById("goRoom");
var localVideo = document.getElementById("localVideo");
var remoteVideo = document.getElementById("remoteVideo");
// these are the global variables
var roomNumber;
var localStream;
var remotestream;
var rtcPeerConnection;
//these are the STUN servers
var iceServers = {
'iceServers': [
{
url:'stun:stun.l.google.com:19302'
},
{
url:'stun:stun.services.mozilla.com'
},
{
url: 'turn:numb.viagenie.ca',
credential: 'muazkh',
username: 'webrtc#live.com'
}
]
};
var streamConstraints = { audio: true, video: true };
var isCaller;
// Here we connect to the socket iO server. We Will create it later.
var socket = io();
// Here we Odd a click event to the button
btnGoRoom.onclick = function() {
if (inputRoomNumber.value == ""){
alert("Please type a room number");
}
else {
roomNumber = inputRoomNumber.value; //we take the value from the element
socket.emit('create or join', roomNumber); //we send a message to server
divSelectRoom.style = "display: none;"; //hide selectRoom div
divConsultingRoom.style = "display block;"; //show consultingRoom div
}
};
// when server emits created
socket.on("created", function(room){
console.log('created function');
//caller gets user media devices with defined constraints
navigator.mediaDevices.getUserMedia(streamConstraints).then(function(stream){
console.log('Created function');
const mediaStream = new MediaStream();
const video = document.getElementById('localVideo');
video.srcObject = stream;
localStream = stream; //sets local stream to variable
//localVideo.src = URL.createObjectURL(stream); //shows stream to user
isCaller = true;//sets current user as caller
}).catch(function(err){
console.log('An error occured when accessing media devices');
console.log(err.name + ": " + err.message);
});
});
// when server emits ends
socket.on("joined", function(room){
console.log('Joined function');
//caller gets user media devices with defined constraints
navigator.mediaDevices.getUserMedia(streamConstraints).then(function(stream){
localStream = stream; //sets local stream to variable
const mediaStream = new MediaStream();
const video = document.getElementById('localVideo');
video.srcObject = stream;
//localVideo.src = URL.createObjectURL(stream); //shows stream to user
socket.emit('ready',roomNumber); //sends message to the server
console.log('Joined function');
}).catch(function(err){
console.log('An error occured when accessing media devices');
console.log(err.name + ": " + err.message);
});
});
//when server emits ready
socket.on('ready', function(){
console.log('client ready function');
if(isCaller){
//creates an RTCPeerConnection object
rtcPeerConnection = new RTCPeerConnection(iceServers);
//adds event listeners to the newly created object
rtcPeerConnection.onicecandidate = onIceCandidate;
rtcPeerConnection.ontrack = onAddStream;
//add the current local stream to the object
rtcPeerConnection.addStream(localStream);
//prepares an offer
rtcPeerConnection.createOffer(setLocalAndOffer, function(e){
console.log(e);
});
}
});
//when server emits offer
socket.on('offer',function(event){
if(isCaller){
console.log('client offer function');
//creates an RTCPeerConnection object
rtcPeerConnection = new RTCPeerConnection(iceServers);
//adds event listeners to the newly created object
rtcPeerConnection.onicecandidate = onIceCandidate;
rtcPeerConnection.ontrack = onAddStream;
//adds the current local stream to the object
rtcPeerConnection.addStream(localStream);
//stores the offer as remote description
rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(event));
//Prepares an Answer
rtcPeerConnection.createAnswer(setLocalAndAnswer, function(e){
console.log(e);
});
}
});
//when server emits answer
socket.on('answer', function(event){
console.log('client answer function');
//stores it as remote description
rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(event));
});
//when server emits candidate
socket.on('candidate', function(event){
console.log('client candidate function');
var pc1 = {
addIceCandidate : function(val) {
console.log(val);
}
}
//creates a candidate object
var candidate1 = new RTCIceCandidate({
type: 'offer',
sdpMLineIndex: event.label,
candidate: event.candidate
});
addIceCandidate(candidate1);
// if(rtcPeerConnection)
// console.log('Okay Peer');
// //stores candidate
// rtcPeerConnection.addIceCandidate(candidate);
});
function addIceCandidate(message) {
if (message.candidate != null) {
rtcPeerConnection.addIceCandidate(message);
}
}
//when a user receives the other user's video and audio stream
function onAddStream(event){
console.log('On Add Stream function');
const mediaStream = new MediaStream();
const rvideo = document.getElementById('remoteVideo');
rvideo.srcObject = event.stream;
//remoteVideo.src = URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
//These are the functions referenced before as listeners for the peer connection
//sends a candidate message to server
function onIceCandidate(event){
console.log('On Ice candidate function');
if(event.candidate){
console.log('sending ice candidate');
socket.emit('candidate', {
type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate,
room: roomNumber
});
}
}
//stores offer and sends message to server
function setLocalAndOffer(sessionDescription){
console.log('LocalAndOffer function');
rtcPeerConnection.setLocalDescription(sessionDescription);
socket.emit('offer', {
type: 'offer',
sdp: sessionDescription,
room: roomNumber
});
}
//stores answer and sends message to server
function setLocalAndAnswer(sessionDescription){
console.log('LocalAndAnswer function');
rtcPeerConnection.setLocalDescription(sessionDescription);
socket.emit('answer', {
type: 'answer',
sdp: sessionDescription,
room: roomNumber
});
}
You are probably sending the candidate socket message before the rtcPeerConnection is initialized. Then you get the error in the addIceCandidate function.
You need to check if you have a webrtc peer connection object before you call addIceCandidate when candidate web socket message arrives.
Once you init it and add event andlers it can any moment find ice candidates and hence trigger the related event, onicecandidate. Same for the session descriptions and its related event onnegotiationneeded event.
So be ready on other end you send those messages over websocket to upon they trigger on one end.
I have node js server which receives BufferArrays over UDP, once a packet is received, socket.io do the next job which is emitting the BufferArray to the client (Angular).
Here's a sample code of the node js server implementation:
io.on("connection", () => {
server.on("listening", () => {
let address = server.address();
console.log("server host", address.address);
console.log("server port", address.port);
});
server.on("message", function (message, remote) {
arrayBuffer = message.slice(28);
io.emit("audio", buffer); // // console.log(`Received packet: ${remote.address}:${remote.port}`)
});
server.bind(PORT, HOST);
});
In the client-side, I've created a blob object from the received BufferArray, then created an URL object to pass it to the audio element src.
socket.on('audio', (audio) => {
this.startAudio(audio);
});
private startAudio(audio) {
const blob = new Blob([audio], { type: "audio/wav" });
var audioElement = document.createElement('audio');
// // console.log('blob', blob);
var url = window.URL.createObjectURL(blob);
// // console.log('url', url);
audioElement.src = url;
audioElement.play();
}
But it seems, I'm doing it wrong since the audio isn't being played!!
So, I'm very curious to know the correct process of playing live audio streams in the browser.
I was able to play live audio in the browser by using FFmpeg, here's the link to the answer
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.
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
WebRTC is a free, open project that enables web browsers with Real-Time Communications (RTC) capabilities via simple Javascript APIs
and i can use this code to Capture my camera,this is the demo
if (navigator.webkitGetUserMedia) {
navigator.webkitGetUserMedia('video', gotStream, noStream);
var video = document.getElementById('monitor');
var canvas = document.getElementById('photo');
function gotStream(stream) {
video.src = webkitURL.createObjectURL(stream);
video.onerror = function () {
stream.stop();
streamError();
};
document.getElementById('splash').hidden = true;
document.getElementById('app').hidden = false;
}
function noStream() {
document.getElementById('errorMessage').textContent = 'No camera available.';
}
function streamError() {
document.getElementById('errorMessage').textContent = 'Camera error.';
}
function snapshot() {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0);
}
} else {
document.getElementById('errorMessage').textContent = 'No native camera support available.';
}
and now , i want to send my camera stream to other people,
i have already create a chat room using nowjs,
so i want to know , Does the nowjs has method to hold the camera stream and show on
the web-browser,
thanks
The video stream need not be stored anywhere. It is transmitted from one client to the other through a Peer Connection. All you need to do is create such a connection, by transmitting the Signaling Messages (something like a 'Handshake') between the clients. The streaming is taken care by the API.