onaddstream method is not executed after RTCPeerconnection object is instantiated - javascript

Dear friends I am trying to build test application which allows to connect a browser window to itself (streming video data from user's camera).The final result is to get two video streams on the page, one coming from the camera directly and the other coming from a WebRTC connection that the browser has made locally.
I guess the problem is that onaddstream method is not invoked when RTCPeerconnection object is instantiated therefore the second screen does not recieve url from window.URL.createObjectURL(e.stream);
function hasUserMedia() {
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
return !!navigator.getUserMedia;
}
function hasRTCPeerConnection() {
window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
return !!window.RTCPeerConnection;
}
var yourVideo = document.querySelector('#yours'),
theirVideo = document.querySelector('#theirs'),
yourConnection, theirConnection;
if (hasUserMedia()) {
navigator.getUserMedia({ video: true, audio: false }, function (stream) {
yourVideo.src = window.URL.createObjectURL(stream);
if (hasRTCPeerConnection()) {
startPeerConnection(stream);
} else {
alert("Sorry, your browser does not support WebRTC.");
}
}, function (error) {
console.log(error);
});
} else {
alert("Sorry, your browser does not support WebRTC.");
}
function startPeerConnection(stream) {
var configuration = {
"iceServers": [{ "url": "stun:192.168.1.100:9876" }] // this is address of a local server
};
yourConnection = new mozRTCPeerConnection(configuration);
theirConnection = new mozRTCPeerConnection(configuration);
console.log(theirConnection);
// Setup stream listening
yourConnection.addStream(stream);
theirConnection.onaddstream = function (e) {
theirVideo.src = window.URL.createObjectURL(e.stream);
};
// Setup ice handling
yourConnection.onicecandidate = function (event) {
if (event.candidate) {
theirConnection.addIceCandidate(new RTCIceCandidate(event.candidate));
}
};
theirConnection.onicecandidate = function (event) {
if (event.candidate) {
yourConnection.addIceCandidate(new RTCIceCandidate(event.candidate));
}
};
// Begin the offer
yourConnection.createOffer(function (offer) {
yourConnection.setLocalDescription(offer);
theirConnection.setRemoteDescription(offer);
theirConnection.createAnswer(function (offer) {
theirConnection.setLocalDescription(offer);
yourConnection.setRemoteDescription(offer);
});
});
};
Here is the full code : https://gist.github.com/johannesMatevosyan/8e4529fdcc53dd711479
This is how it looks in browser: http://screencast.com/t/6dthclGcm

Your onaddstream event is not triggering because your connection is not started yet, you will have to get the offer/answer process done before that event can be triggered. I tried your code in Firefox 41.0.2 and the offer wasn't getting created because you are missing the error callback methods, try with the following:
function error () { console.log('There was an error'); };
yourConnection.createOffer(function (offer) { console.log('Offer:'); console.log(offer);
yourConnection.setLocalDescription(offer);
theirConnection.setRemoteDescription(offer);
theirConnection.createAnswer(function (answer) { console.log('Answer:'); console.log(answer);
theirConnection.setLocalDescription(answer);
yourConnection.setRemoteDescription(answer);
}, error);
}, error);

Related

I want to run this javascript code with NodeJS but I get this error if (!window.RTCPeerConnection) { ^ ReferenceError: window is not defined

This is my script js
I want to run this javascript code with NodeJS
but i get error
const JsSIP = require('jssip');
const NodeWebSocket = require('jssip-node-websocket');
const {execSync} = require('child_process');
JsSIP.debug.enable('*');
async function sleep(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
let socket = new NodeWebSocket('ws://192.168.90.27:8088/ws');
let ua = new JsSIP.UA(
{
sockets : [socket],
uri : 'sip:webrtc_client#192.168.90.27',
password : '123456',
display_name : 'webrtc_client'
});
ua.start();
ua.register();
ua.on('connected', function(e){ });
ua.on('disconnected', function(e){ });
ua.on('newRTCSession', function(e){ console.log(e) });
ua.on('newMessage', function(e){ console.log(e) });
// option for call
var eventHandlers = {
'progress': function(e) {
console.log('call is in progress');
},
'failed': function(e) {
console.log('call failed with cause: '+ e.data.cause);
},
'ended': function(e) {
console.log('call ended with cause: '+ e.data.cause);
},
'confirmed': function(e) {
console.log('call confirmed');
}
};
var options = {
'eventHandlers' : eventHandlers,
'mediaConstraints' : { 'audio': false, 'video': false }
};
// make a call
var session = ua.call('sip:100#192.168.90.27', options);
i want to make a call with asterisk server with JSsip lib
im trying to make a call with asterisk server with JSsip lib but i heard node js can not support WEBrtc and i want to know it this is correct ?
i installed packages like jssip node-jssip-websocket
how i can fix the problem ?
anybody can help me ?

PeerJs close video call not firing close event

I am trying to create a one-directional video app with PeerJs. I've succesfully been able to run my own peer server and connect on a button click, but I'm unable to close the connection so that the peers can receive/establish a new connection with a new peer.
Every user will either be a host or a client, it will never be backwards. So the host can choose which client to connect to and the client will start to stream its camera feed back to the host. The closeCon() function is called with a button click.
$(document).ready(function(){
peer = new Peer('100001', {host: 'my.url', port: '9900', path: '/peerjs', key: 'peerjs', secure: true, debug: 3});
peer.on("open", function(id) {
console.log("My peer ID is: " + id);
});
video = document.getElementById('vidSrc');
})
function callTheGuy(id){
var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
getUserMedia({video: true, audio: false}, function(stream) {
window.call = peer.call(id, stream);
localStream = stream;
window.call.on('stream', function(remoteStream) {
let video = document.getElementById('vidArea');
video.srcObject = remoteStream;
video.play();
$("#videoModal").modal('show')
});
}, function(err) {
console.log('Failed to get local stream' ,err);
});
}
function closeCon(){
window.call.close();
}
This all works great, i get my video feed, no problem. Here is my client code:
peer = new Peer(serverId, {
host: "my.url",
port: "9900",
path: "/peerjs",
key: "peerjs",
debug: 3,
secure: true
});
peer.on("open", function(id) {
console.log("My peer ID is: " + id);
});
var getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
peer.on("call", function(call) {
getUserMedia(
{ video: true, audio: false },
function(stream) {
localStream = stream;
call.answer(stream); // Answer the call with an A/V stream.
},
function(err) {
console.log("Failed to get local stream", err);
}
);
call.on("close", function() {
console.log("closing");
});
});
The issue is that when I call closeCon(), the client file is not receiving the close event. The part of
call.on("close", function() {
console.log("closing");
});
never gets fired. I'm not really sure why this is happening but unless that close event gets processed, the client stays connected to the original host and can't accept connections from subsequent host requests. Does anyone have any advice?
I ran into the issue after discovering my peerConnections continued to send stream data using chrome://webrtc-internals. I am currently using the public peerjs server. The MediaConnection does not fire the close event, but the DataConnection still class does. My particular flow waits for the remote to initiate a (data)connection and then starts a call.
I was able too close the MediaConnection by:
opening both a DataConnection and a MediaConnection to a remote peer
monitoring the MediaConnection close event
Closing all WebRTCpeerConnections after as part of the DataConnection close handler
This looks like:
function handlePeerDisconnect() {
// manually close the peer connections
for (let conns in peer.connections) {
peer.connections[conns].forEach((conn, index, array) => {
console.log(`closing ${conn.connectionId} peerConnection (${index + 1}/${array.length})`, conn.peerConnection);
conn.peerConnection.close();
// close it using peerjs methods
if (conn.close)
conn.close();
});
}
}
peer.on('connection', conn => {
let call = peer.call(peerToCall, localStream);
// peerjs bug prevents this from firing: https://github.com/peers/peerjs/issues/636
call.on('close', () => {
console.log("call close event");
handlePeerDisconnect();
});
}
// this one works
conn.on('close', () => {
console.log("conn close event");
handlePeerDisconnect();
});
});
Update on 7 Mar 2021
I found that is an issue, PeerJs hasn't fixed yet. (Issue link: https://github.com/peers/peerjs/issues/636)
You can trigger the close event in Socketio "disconnect" for a workaround solution
Server
socket.on("disconnect", (reason)=>{
socket.broadcast.emit("user-disconnected", userId);
});
Client
socket.on("user-disconnected", (userId)=>{
// remove video or add your code here
});
No, it fires the close event. but you first do as follows:
server
socket.on('disconnect', () =>{
socket.broadcast.to(roomId).emit('user-disconnected', userId);
})
client
const peers = {};
function connectToNewUser (userId, stream){
...
call.on('close', () =>{
video.remove();
})
peers[userId] = call;
}
and then in client emit the event:
socket.on('user-disconnected', userId =>{
console.log(userId);
if (peers[userId]){
peers[userId].close();
}
})

webRTC losses stream when a phone call is answered

In the scenario where two users are connected in a video call, one of them gets a phone call and he answers the call. this blocks the ongoing webRTC stream and the call session ends.
So is there a way we can maintain both the steam as well as the call session and resume the video call once the person return on our application.
I am using QuickBlox js-Sdk to make calls.
below is the code snippet attached to initiate the call.
var userCredentials = {
userId: 'XXXXXX',
password: "XXXXXXX"
};
QB.chat.connect(userCredentials, function (error, contactList) {
var calleesIds = [XXXXX];
var sessionType = QB.webrtc.CallType.VIDEO;
var session = QB.webrtc.createNewSession(calleesIds, sessionType);
this.userAuthService.markBusy(XXXXX).subscribe(data => data);
var mediaParams = {
audio: true,
video: true,
options: {
muted: true,
mirror: true
},
elemId: "selfStream"
};
session.getUserMedia(mediaParams, function (err, stream) {
if (err) {
console.log('getUserMedia err', err);
} else {
console.log('getUserMedia succ', stream);
// make call
var extension = {};
session.call(extension, function (error) {
console.log('call error: ', error);
});
}
});
});
and on the other side to receive the call.
QB.webrtc.onCallListener = function (session, extension) {
session.getUserMedia(self.getMediaParams('selfStream'), function (err, stream) {
if (err) {
console.log('getUserMedia err', err);
} else {
self.callSession.accept(self.callExt);
}
});
};
I have seen the same issue in some other webApps as well, Is there a fix/workaround for this problem, thanks in advance.
Get around this by adding an event on the remote user's stream. If the stream is null it'll start checking for the stream every second for 2 mins if stream is not restored back in 2 mins the call will be disconnected. else I'll use the restored stream and remove the timeInterval to check every second.

Can a MediaStream be used immediately after getUserMedia() returns?

I'm trying to capture the audio from a website user's phone, and transmit it to a remote RTCPeerConnection.
Assume that I have a function to get the local MediaStream:
function getLocalAudioStream(): Promise<*> {
const devices = navigator.mediaDevices;
if (!devices) {
return Promise.reject(new Error('[webrtc] Audio is not supported'));
} else {
return devices
.getUserMedia({
audio: true,
video: false,
})
.then(function(stream) {
return stream;
});
}
}
The following code works fine:
// variable is in 'global' scope
var LOCAL_STREAM: any = null;
// At application startup:
getLocalAudioStream().then(function(stream) {
LOCAL_STREAM = stream;
});
...
// Later, when the peer connection has been established:
// `pc` is an RTCPeerConnection
LOCAL_STREAM.getTracks().forEach(function(track) {
pc.addTrack(track, LOCAL_STREAM);
});
However, I don't want to have to keep a MediaStream open, and I would like to
delay fetching the stream later, so I tried this:
getLocalAudioStream().then(function(localStream) {
localStream.getTracks().forEach(function(track) {
pc.addTrack(track, localStream);
});
});
This does not work (the other end does not receive the sound.)
I tried keeping the global variable around, in case of a weird scoping / garbage collection issue:
// variable is in 'global' scope
var LOCAL_STREAM: any = null;
getLocalAudioStream().then(function(localStream) {
LOCAL_STREAM = localStream;
localStream.getTracks().forEach(function(track) {
pc.addTrack(track, localStream);
});
});
What am I missing here ?
Is there a delay to wait between the moment the getUserMedia promise is returned, and the moment it can be added to an RTCPeerConnection ? Or can I wait for a specific event ?
-- EDIT --
As #kontrollanten suggested, I made it work under Chrome by resetting my local description of the RTCPeerConnection:
getLocalAudioStream().then(function(localStream) {
localStream.getTracks().forEach(function(track) {
pc.addTrack(track, localStream);
});
pc
.createOffer({
voiceActivityDetection: false,
})
.then(offer => {
return pc.setLocalDescription(offer);
})
});
However:
it does not work on Firefox
I must still be doing something wrong, because I can not stop when I want to hang up:
I tried stopping with:
getLocalAudioStream().then(stream => {
stream.getTracks().forEach(track => {
track.stop();
});
});
No, there's no such delay. As soon as you have the media returned, you can send it to the RTCPeerConnection.
In your example
getLocalAudioStream().then(function(localStream) {
pc.addTrack(track, localStream);
});
It's unclear how stream is defined. Can it be that it's undefined?
Why can't you go with the following?
getLocalAudioStream()
.then(function (stream) {
stream
.getTracks()
.forEach(function(track) {
pc.addTrack(track, stream);
});
});

Webrtc Data Channel always in connecting state and not open

I am creating a small WebRTC app that for now used to exchange text message. I have got the WebRTC connection working but Datachannel always remains in "connecting" state and never goes to "Open".
Please tell me what I am missing here.
Following is the JS.
socket.onmessage = function(e){
console.log("Message from signaling server");
writeToScreen('<span class="server">Server: </span>'+e.data);
var data = JSON.parse(e.data);
switch(data.type) {
case "login":
onLogin(data.success);
break;
case "offer":
onOffer(data.offer, data.name);
break;
case "answer":
onAnswer(data.answer);
break;
case "candidate":
onCandidate(data.candidate);
break;
default:
break;
}
}
// Enable send and close button
$('#send').prop('disabled', false);
$('#close').prop('disabled', false);
$('#connect').prop('disabled', true);
}
function close(){
socket.close();
}
function writeToScreen(msg){
var screen = $('#screen');
screen.append('<p>'+msg+'</p>');
screen.animate({scrollTop: screen.height()}, 10);
}
function clearScreen(){
$('#screen').html('');
}
function sendMessage(){
if(!socket || socket == undefined) return false;
var mess = $.trim($('#message').val());
if(mess == '') return;
writeToScreen('<span class="client">Client: </span>'+mess);
socket.send(mess);
// Clear input
$('#message').val('');
}
$(document).ready(function(){
$('#message').focus();
$('#frmInput').submit(function(){
sendMessage();
});
$('#connect').click(function(){
connect();
});
$('#close').click(function(){
close();
});
$('#clear').click(function(){
clearScreen();
});
});
if (!window.RTCPeerConnection) {
window.RTCPeerConnection = window.webkitRTCPeerConnection;
}
var configuration = {
"iceServers": [
{
"urls": "stun:mmt-stun.verkstad.net"
},
{
"urls": "turn:mmt-turn.verkstad.net",
"username": "webrtc",
"credential": "secret"
}
]
};
myConnection = new RTCPeerConnection(configuration,{optional:[{RtpDataChannels: true},{DtlsSrtpKeyAgreement: true}]});
console.log("RTCPeerConnection object was created");
console.log(myConnection);
openDataChannel();
//when the browser finds an ice candidate we send it to another peer
myConnection.onicecandidate = function (event) {
console.log(event.candidate);
if (event.candidate) {
send({
type: "candidate",
candidate: event.candidate
});
}
};
// Datachannel
var mediaConstraints = {
'offerToReceiveAudio': 1,
'offerToReceiveVideo': 1
};
var connectToOtherUsernameBtn = document.getElementById("connectToOtherUsernameBtn");
console.log(connectToOtherUsernameBtn);
connectToOtherUsernameBtn.addEventListener("click", function () {
console.log("ice state : "+myConnection.iceGatheringState);
var otherUsername = connectToOtherUsernameBtn.value;
connectedUser = otherUsername;
if (otherUsername.length > 0) {
//make an offer
myConnection.createOffer(function (offer) {
send({
type: "offer",
offer: offer
});
console.log(offer);
console.log(typeof(offer));
myConnection.setLocalDescription(offer);
console.log("localDescription");
console.log(myConnection.localDescription);
}, function (error) {
alert("An error has occurred.");
console.log(error);
});
}
});
function send(message) {
if (connectedUser) {
message.name = connectedUser;
}
socket.send(JSON.stringify(message));
};
//when somebody wants to call us
function onOffer(offer, name) {
console.log("offer recieved");
connectedUser = name;
myConnection.setRemoteDescription(new RTCSessionDescription(offer));
myConnection.createAnswer(function (answer) {
myConnection.setLocalDescription(answer);
send({
type: "answer",
answer: answer
});
}, function (error) {
alert("oops...error");
console.log(error);
});
console.log("Answer sent");
}
//when another user answers to our offer
function onAnswer(answer) {
console.log("answer recieved");
myConnection.setRemoteDescription(new RTCSessionDescription(answer));
console.log(myConnection.iceConnectionState );
}
//when we got ice candidate from another user
function onCandidate(candidate) {
myConnection.addIceCandidate(new RTCIceCandidate(candidate));
}
});
//data channel
//creating data channel
function openDataChannel() {
console.log("opening Data Channel");
var dataChannelOptions = {
reliable:true,
};
dataChannel = myConnection.createDataChannel("myDataChannel",dataChannelOptions);
dataChannel.onerror = function (error) {
console.log("Error:", error);
};
dataChannel.onmessage = function (event) {
console.log("Got message:", event.data);
};
}
function sendmsg() {
console.log("send message");
var msgInput=document.getElementById("msgInput");
var val = msgInput.value;
console.log(val);
dataChannel.send(val);
}
function checkstatus(){
console.log("Checking Status");
console.log("signalingState: "+myConnection.signalingState);
console.log("iceConnectionState: "+myConnection.iceConnectionState);
console.log("iceGatheringState: "+myConnection.iceGatheringState);
console.log("localDescription: ");
console.log(myConnection.localDescription);
console.log("remoteDescription:");
console.log(myConnection.remoteDescription);
console.log("Connestion id");
console.log(dataChannel.id);
console.log("Connestion readyState");
console.log(dataChannel.readyState);
}
Following is the console log from chrome.
remove {RtpDataChannels: true}
try again and if it works burn the tutorial or book which recommended those "rtp data channels". They are broken.
I had the same problem. my code was working fine on mozilla using localhost signalling server without internet but on chrome i had this problem. Its Trickle ICE problem.
one solution is you may set trickle ice off.
In chrome, may be you need the internet connection to gather the all possible ICE candidates. because in Chrome Datachannel will not get opened untill peer get all possible ICE candidates.
you can try the following link with internet or without internet. you will have brief idea.
https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/
for further information you can check this
https://webrtcstandards.info/webrtc-trickle-ice/

Categories