webRTC losses stream when a phone call is answered - javascript

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.

Related

Send proactive message based on setInterval data for Bot v4

So I'm trying to migrate my bot to v4 whilst adding a livechat functionality to it.
My use case is this:
I have a secondary non-azure bot which contains all my knowledge base, triggers and responses. This secondary bot has an API for direct communication, one of it's methods handles direct messages to the bot, let's call it "Talk". Then there is another method that fetches data from a livechat, this one we'll call "Poll", you can "Poll" a livechat each second and if there is a response there, you'll get it, if not, the API responds blank, which is acceptable and I'm ready to handle it in my code.
Where I'm stuck: I can send and receive messages to the Bot using "Talk", I can even start Polling a livechat conversation and see what's there. But I need to get what's there and send it back to the user.
I've tried declaring the context as "global" but it seems like i'm doing something wrong, I receive: TypeError: Cannot perform 'set' on a proxy that has been revoked
This is my Talk function
It works as expected, meaning that it always sends information to the secondary bot when the user types something to the AzureBot v4.
It also triggers the islivechat to true when it receives a SpecialAction to start a livechat (handled by the secondary bot).
async function Talk(context, next) {
var Talk_parameters = {
"type": "talk",
"parameters": {
"userUrl": "aHR0cHM6Ly9zdGF0aWw=",
"alreadyCame": true,
"os": "V2luZG93cyAxMC4w",
"browser": "RmlyZWZveCA2OA==",
"disableLanguageDetection": true,
"contextType": "V2Vi",
"mode": "U3luY2hyb24=",
"botId": "a3f3b9fb-********-5f6ba69bb9da",
"qualificationMode": true,
"language": "cHQ=",
"space": "Default",
"solutionUsed": "QVNTSVNUQU5U",
"clientId": "UEZIdDhrMXVVbGU5b0lw",
"useServerCookieForContext": false,
"saml2_info": "",
"pureLivechat": false,
"userInput": Buffer.from(VArequestData.parameters.userInput).toString('base64'),
"templateFormats": "",
"contextId": VArequestData.parameters.contextId,
"timestamp": new Date().valueOf()
}
}
console.log(Talk_parameters);
return new Promise(resolve => {
request({
url: "https://sma********.com/servlet/chatHttp",
method: "GET",
body: Talk_parameters,
headers: {},
json: true
}, async function (error, response, body) {
if(!error)
resolve(body);
})
}).then(response_values => {
console.log(response_values);
VAresponseData.text = Buffer.from(response_values.values.text,'base64')
VAresponseData.context = response_values.values.contextId
VArequestData.parameters.contextId = response_values.values.contextId
if (response_values.values.specialAction == "U3RhcnRQb2xsaW5n"){
islivechat = true;
}
})
}
the SetInterval part
setInterval(async function(){
if (islivechat == true){
Poll()
console.log("Polling data!")
msg();
}
}, 1000);
the Poll function
When islivechat == true this guy runs each second, fetching data from the conversation with the secondary bot. I can get the exact value I need in the line "VAresponseData.livechatText = response_values.values.text", but I can't send it back to the user.
async function Poll(context) {
// Setting URL and headers for request
var Polling_parameters = {
type:"poll",
parameters:{
lastPoll: VArequestData.parameters.lastPoll,
mode : "Polling",
contextId: VArequestData.parameters.contextId,
botId : VArequestData.parameters.botId,
qualificationMode : VArequestData.parameters.qualificationMode,
language : VArequestData.parameters.language,
space : VArequestData.parameters.space,
solutionUsed :"LIVECHAT",
timestamp : new Date().valueOf()
}
}
console.log(Polling_parameters);
return new Promise(resolve => {
request({
url: "https://sma**************.com/servlet/chatHttp",
method: "POST",
body: Polling_parameters,
headers: {},
json: true
}, async function (error, response, body) {
if(!error)
resolve(body);
})
}).then(async (response_values) => {
if(response_values.values.specialAction == "RW5kUG9sbGluZw=="){
islivechat = false;
}
console.log(response_values);
console.log("CONTEXT")
console.log(response_values.values.contextId);
console.log(Polling_parameters.parameters.contextId);
VArequestData.parameters.lastPoll = response_values.values.serverTime
VAresponseData.livechatText = response_values.values.text
console.log('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx >>> ' + response_values.values.text)
sendLivechatMsg();
})
}
My failed attempt:
This function is declared outside of the main code, meaning it is accessible to any other function, but I receive TypeError: Cannot perform 'set' on a proxy that has been revoked when I run it. I tried running a console.log for it and the code doesn't even reach that part.
async function sendLivechatMsg(globalContext){
context = globalContext;
await msg(VAresponseData.livechatText, context)
.then(async function(){
context.sendActivity(VAresponseData.livechatText)
.then(
console.log('activity')
)
})
await next();
}
The main stage
I used the sample code for Bot v4 and changed just a few things
class EchoBot extends ActivityHandler {
constructor() {
super();
// See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types.
this.onMessage(async (context, next) => {
VArequestData.parameters.userInput = context.activity.text;
await Talk(context, next)
.then(async function(data){
context.sendActivity(`'${ VAresponseData.text }'`)
// context.sendActivity(`'${ VAresponseData.context }'`)
}, error => {
console.log('ERROR: ' + error)
})
await next();
}
);
// this.onMembersAdded(async (context, next) => {
// setTimeout(() => startPolling(context), 3000);
// const membersAdded = context.activity.membersAdded;
// for (let cnt = 0; cnt < membersAdded.length; ++cnt) {
// if (membersAdded[cnt].id !== context.activity.recipient.id) {
// // await context.sendActivity('Hello and welcome!');
// }
// }
// // By calling next() you ensure that the next BotHandler is run.
// await next();
// });
this.onTurn
}
}
The expected scenario is:
If the user is in a livechat and the variable "VAresponseData.livechatText" has a value, it needs to be sent to the user immediately, without the need for him to "Ask".
But I can't figure out a way to sent this back to the user.

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);
});
});

nodejs does not emit data to client

I have simple nodejs app with sockets and I've faced an error where I can't find any solution. So I'm emiting from app to client and nothing happens there. Or client can't receive it - I don't know, because I can't check if it was successfully emited to client. This is the error I got when I tried to debug callback of emit:
Error: Callbacks are not supported when broadcasting
This my app code:
http.listen(6060, function () {
console.log("Listening on *: 6060");
});
io.set('authorization', function (handshakeData, accept) {
var domain = handshakeData.headers.referer.replace('http://', '').replace('https://', '').split(/[/?#]/)[0];
if ('***' == domain) {
accept(null, true);
} else {
return accept('You must be logged in to take an action in this site!', false);
}
});
io.use(function (sock, next) {
var handshakeData = sock.request;
var userToken = handshakeData._query.key;
if (typeof userToken !== null && userToken !== 0 && userToken !== '0' && userToken.length > 0) {
connection.query('***',
[xssfilter.filter(validator.escape(userToken))],
function (error, data) {
if (error) {
debug('Cant receive user data from database by token');
next(new Error('Failed to parse user data! Please login!'));
} else {
// load data to this user.
_updateUsers(xssfilter.filter(validator.escape(userToken)), 'add', data[0], sock.id);
_loadPreData();
next(null, true);
}
});
} else {
debug('Cant receive user token');
next(new Error('Failed to parse user data! Please login!'));
}
sock.on("disconnect", function () {
_updateUsers(false, 'remove', false, sock.id);
});
});
// we need to show people online count
io.emit('online-count', {
count: Object.keys(connectedUsers).length
});
And the function used above:
function _updateUsers(userToken, action, userData, sockedID) {
switch (action) {
case 'add':
connectedUsers[sockedID] = {...};
io.emit('online-count', io.emit('online-count', {
count: Object.keys(connectedUsers).length
}););
break;
case 'remove':
delete connectedUsers[sockedID];
io.emit('online-count', io.emit('online-count', {
count: Object.keys(connectedUsers).length
}););
break;
}
}
so after emiting online-count I should accept it on the client side as I'm doing it:
var socket;
socket = io(globalData.socketConn, {query: "key=" + globalData.userData.token});
socket.on('connect', function (data) {
console.log('Client side successfully connected with APP.');
});
socket.on('error', function (err) {
error('danger', 'top', err);
});
socket.on('online-count', function (data) {
console.log('Got online count: ' + data.count);
$('#online_count').html(data.count);
});
but the problem is with this online-count.. Nothing happens and it seems that it's not was even sent from node app. Any suggestions?
The problem was with my logic - I was sending online count only if new user were connecting/disconnecting. Problem were solved by adding function to repeat itself every few seconds and send online count to client side.

Azure + Node Js Service bus persistent subscription of topic

Using node js i want to create persistent subscription for Azure service bus service topic. right now it is execute only once. Please guide me I am new to this. Thanks in advance. I am using following code to subscribe topic.
var azure = require('azure');
var azureConnection = "Endpoint=sb:My connection string"
var retryOperations = new azure.ExponentialRetryPolicyFilter();
var serviceBusService = azure.createServiceBusService(azureConnection).withFilter(retryOperations);
serviceBusService.receiveSubscriptionMessage('mytopic01', 'mytopicsub', function (error, receivedMessage) {
if (!error) {
// // // Message received and deleted
console.log(receivedMessage);
}
});
Also I don't want to use setInterval function. I want to solution if message publish to the topic it should automatically trigger subscription.
Actually, if your client application is an independent node.js application, we usually set up a cycle program to receive message from service bus in loop.
E.G.
var azure = require('azure');
var sbService = azure.createServiceBusService(<connection_string>);
function checkForMessages(sbService, queueName, callback) {
sbService.receiveSubscriptionMessage(queueName, { isPeekLock: true }, function (err, lockedMessage) {
if (err) {
if (err === 'No messages to receive') {
console.log('No messages');
} else {
callback(err);
}
} else {
callback(null, lockedMessage);
}
});
}
function processMessage(sbService, err, lockedMsg) {
if (err) {
console.log('Error on Rx: ', err);
} else {
console.log('Rx: ', lockedMsg);
sbService.deleteMessage(lockedMsg, function(err2) {
if (err2) {
console.log('Failed to delete message: ', err2);
} else {
console.log('Deleted message.');
}
})
}
}
setInterval(checkForMessages.bind(null, sbService, queueName, processMessage.bind(null, sbService)), 5000);
You can refer to the code sample in the similar scenario at GitHub provided by Azure Team.
Any further concern, please feel free to let me know.

Node , SSh2 Server waiting for input

I am currently building a Web based SSH client. I am using node and SSH2 module to connect to Linux machine . The issue is trying to identify when the server is waiting for a response from the client say "sudo" .
this.onWsMessage = function(packet) {
log('Message Received from ');
if (cmd != '') {
log('----------------------------------------->' + cmd);
source[this.getId()].sshStream.write(cmd + '\n');
}
};
var client = clients[cid];
sshClient
.on('ready', function() {
console.log('CONNECTED');
sshClient.shell({pty: true}, function(err, stream) {
_shellHandler(err, stream, client);
});
})
.on('error', function(err) {
console.log('ERROR', err);
})
.connect(serverInfo);
}
function _shellHandler(err, stream, client) {
source[client.getId()].sshStream = stream;
stream
.on('close', function() {
log('ssh connection close');
//sshClient.end();
this._client.end();
})
.on('data', function(data) {
console.log(stream);
var msg = data.toString().replace(msgPattern, '');
this.sendUTF(msg, 'ssh');
}.bind(client));
}
I have been going through the documentation and i was unable to identify any event that trigger as a result.
There is no easy way to do this. Basically you have to either buffer stream output and continually search that buffer for expected (sudo) prompts or use a module like streamsearch to search the stream for a particular string without explicitly buffering and continual searching.

Categories