chrome ask multiple time to allow use of speaker and mic for incoming call in twilio - twilio

I am creating a call center application ,and when a call come to twilio client on chrome browser it ask for permission to allow use of speaker and mic .When second call comes I have to allow it for two times and if third call come I have to allow three times and so on.
and in firfox browser ,when I acccept call second time it automatically disconnect and give error of mediastream.
Please suggest me where I am wrong and how to solve this problem.
Below is the javascript of twilio client,If need some more information please let me know
<script
type="text/javascript">
//var
token
=callaction();
var
connection=null;
var
content;
$(document).ready(function(){
$('#hangupcallbutton').hide();
$.post('cleanify?action=createcall',
function(data){
content=
data;
Twilio.Device.setup(content,
{
debug:
true}
);
}
);
Twilio.Device.ready(function
(device)
{
$("#log").text("Ready");
}
);
Twilio.Device.error(function
(error)
{
$("#log").text("Error:
"
+
error.message);
}
);
Twilio.Device.connect(function
(conn)
{
//alert("connect
handler");
$("#log").text("Successfully
established
call");
connection
=
conn;
}
);
Twilio.Device.disconnect(function
(conn)
{
//alert("in
disconnect");
$("#callbutton").html('Call');
$("#callbutton").removeClass("btn-danger
").addClass("btn-success");
//
$("#callbutton").toggleClass('btn-danger
btn-success');
$("#log").text("Call
ended");
$('#RejectCallButton').show();
$('#acceptcallbutton').show();
$('#hangupcallbutton').hide();
$('#myModal').modal('hide');
}
);
Twilio.Device.incoming(function
(conn)
{
//
alert("in
incomming");
connection
=
conn;
$("#log").text("aa
rahi
h
call");
$('#myModal').modal('show');
$('#incomingnumber').html(conn.parameters.From);
$('#RejectCallButton').click(function()
{
connection.reject();
}
);
$('#acceptcallbutton').click(function()
{
connection.accept();
$('#RejectCallButton').hide();
$('#acceptcallbutton').hide();
$('#hangupcallbutton').show();
}
);
$('#hangupcallbutton').click(function()
{
$('#RejectCallButton').show();
$('#acceptcallbutton').show();
$('#hangupcallbutton').hide();
connection.disconnect();
}
);
//
accept
the
incoming
connection
and
start
two-way
audio
}
);
Twilio.Device.cancel(
function
(conn)
{
//alert("in
cancel");
connection
=
conn;
$('#myModal').modal('hide');
}
);
$.each(['0','1','2','3','4','5','6','7','8','9','star','pound'],
function(index,
value)
{
$('#button'
+
value).click(function(){
//alert("hello");
if(connection)
{
if
(value=='star')
connection.sendDigits('*')
else
if
(value=='pound')
connection.sendDigits('#')
else
connection.sendDigits(value)
return
false;
}
}
);
}
);
//
Do
something
with
c
alert(content);
}
);
function
callhandle(){
if($("#callbutton").html().trim()
==
'Call'){
$("#callbutton").html('HangUp');
$("#callbutton").removeClass("btn-success").addClass("btn-danger");
//
$("#callbutton").toggleClass('btn-success
btn-danger');
call();
}
else
if($("#callbutton").html().trim()
==
'HangUp'){
hangup();
$("#callbutton").html('Call');
$("#callbutton").removeClass("btn-danger").addClass("btn-success");
//
$("#callbutton").toggleClass('btn-danger
btn-success');
}
}
function
call()
{
//
get
the
phone
number
to
connect
the
call
to
//
get
the
phone
number
to
connect
the
call
to
//
alert(
$("#selectOutgoing").val());
params
=
{"PhoneNumber":
$('.dialnumber').val(),"From":
$("#selectOutgoing").val(),"isclient":
"true"}
;
Twilio.Device.connect(params);
}
function
hangup()
{
Twilio.Device.disconnectAll();
}
</script>

From my experience, Chrome will only store those settings over HTTPS. Over HTPP Chrome will not store the settings thus it will prompt for the permissions every time

It might happen after a call before initialize twilio when you get another call . So , you must initialize first .

Related

WebRTC connection does not resume after mobile browser is backgrounded

I have a web application running on Safari on an iPad displaying a live WebRTC video stream. When the user switches away from Safari for a few seconds, and then switches back, the <video> element just shows a black rectangle.
I have added logging to the onsignalingstatechange handler, and checked the console logs for any apparent errors after resuming Safari, but there is nothing obvious indicating the failure.
How can I recover/resume/restart the stream after the user switches back to Safari?
Here is my cargo cult WebRTC code, for reference:
export default class WebRtcPlayer {
static server = "http://127.0.0.1:8083";
server = null;
stream = null;
channel = null;
webrtc = null;
mediastream = null;
video = null;
constructor(id, stream, channel) {
this.server = WebRtcPlayer.server;
this.video = document.getElementById(id);
this.stream = stream;
this.channel = channel;
this.video.addEventListener("loadeddata", () => {
this.video.play();
});
this.video.addEventListener("error", () => {
console.error("video error");
});
this.play();
}
getStreamUrl() {
// RTSPtoWeb only, not RTSPtoWebRTC
return `${this.server}/stream/${this.stream}/channel/${this.channel}/webrtc`;
}
async play() {
console.log("webrtc play");
this.mediastream = new MediaStream();
this.video.srcObject = this.mediastream;
this.webrtc = new RTCPeerConnection({
iceServers: [{
urls: ["stun:stun.l.google.com:19302"],
}],
sdpSemantics: "unified-plan"
});
this.webrtc.onnegotiationneeded = this.handleNegotiationNeeded.bind(this);
this.webrtc.onsignalingstatechange = this.handleSignalingStateChange.bind(this);
this.webrtc.ontrack = this.handleTrack.bind(this);
this.webrtc.addTransceiver("video", {
"direction": "sendrecv",
});
}
async handleNegotiationNeeded() {
console.log("handleNegotiationNeeded");
let offer = await this.webrtc.createOffer({
offerToReceiveAudio: false,
offerToReceiveVideo: true
});
await this.webrtc.setLocalDescription(offer);
}
async handleSignalingStateChange() {
console.log(`handleSignalingStateChange ${this.webrtc.signalingState}`);
switch (this.webrtc.signalingState) {
case "have-local-offer":
let formData = new FormData();
formData.append("data", btoa(this.webrtc.localDescription.sdp));
const response = await fetch(this.getStreamUrl(), {
method: "POST",
body: formData,
});
this.webrtc.setRemoteDescription(new RTCSessionDescription({
type: "answer",
sdp: atob(await response.text()),
}));
break;
case "stable":
/*
* There is no ongoing exchange of offer and answer underway.
* This may mean that the RTCPeerConnection object is new, in which case both the localDescription and remoteDescription are null;
* it may also mean that negotiation is complete and a connection has been established.
*/
break;
case "closed":
/*
* The RTCPeerConnection has been closed.
*/
break;
default:
console.log(`unhandled signalingState is ${this.webrtc.signalingState}`);
break;
}
}
handleTrack(event) {
console.log("handle track");
this.mediastream.addTrack(event.track);
}
static setServer(serv) {
this.server = serv;
}
}
I'm not sure if it's the best way, but I used the Page Visibility API to subscribe to the visibilitychange event:
constructor(id, stream, channel) {
// ...
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "visible") {
console.log("Document became visible, restarting WebRTC stream.");
this.play();
}
});
// ...
}

Twilio Voice Recording

I am using the twilio JS client to make call from the web. the client call the backend to get the token. Here the backend code that returns the token. How to record the call. mean where to set the recording url. The call is successfull. But don't know where to pass the recording url.
public function newToken(Request $request)
{
$accountSid = config('services.twilio')['accountSid'];
$applicationSid = config('services.twilio')['applicationSid'];
$apiKey = config('services.twilio')['apiKey'];
$apiSecret = config('services.twilio')['apiSecret'];
$voiceGrant = new VoiceGrant();
$voiceGrant->setOutgoingApplicationSid($applicationSid);
$voiceGrant->setIncomingAllow(true);
$this->accessToken->addGrant($voiceGrant);
$token = $this->accessToken->toJWT();
return response()->json(['token' => $token]);
}
On JS side the code which uses the client side library of twillio.
const Device = Twilio.Device;
// Store some selectors for elements we'll reuse
var callStatus = $("#call-status");
var answerButton = $(".answer-button");
var callSupportButton = $(".call-support-button");
var hangUpButton = $(".hangup-button");
var callCustomerButtons = $(".call-customer-button");
var device = null;
function updateCallStatus(status) {
callStatus.attr('placeholder', status);
}
/* Get a Twilio Client token with an AJAX request */
$(document).ready(function() {
setupClient();
});
function setupHandlers(device) {
device.on('ready', function(_device) {
updateCallStatus("Ready");
});
/* Report any errors to the call status display */
device.on('error', function(error) {
updateCallStatus("ERROR: " + error.message);
});
/* Callback for when Twilio Client initiates a new connection */
device.on('connect', function(connection) {
// Enable the hang up button and disable the call buttons
hangUpButton.prop("disabled", false);
callCustomerButtons.prop("disabled", true);
callSupportButton.prop("disabled", true);
answerButton.prop("disabled", true);
// If phoneNumber is part of the connection, this is a call from a
// support agent to a customer's phone
if ("phoneNumber" in connection.message) {
updateCallStatus("In call with " + connection.message.phoneNumber);
} else {
// This is a call from a website user to a support agent
updateCallStatus("In call with support");
}
});
/* Callback for when a call ends */
device.on('disconnect', function(connection) {
// Disable the hangup button and enable the call buttons
hangUpButton.prop("disabled", true);
callCustomerButtons.prop("disabled", false);
callSupportButton.prop("disabled", false);
updateCallStatus("Ready");
});
/* Callback for when Twilio Client receives a new incoming call */
device.on('incoming', function(connection) {
updateCallStatus("Incoming support call");
// Set a callback to be executed when the connection is accepted
connection.accept(function() {
updateCallStatus("In call with customer");
});
// Set a callback on the answer button and enable it
answerButton.click(function() {
connection.accept();
});
answerButton.prop("disabled", false);
});
};
function setupClient() {
$.post("/token", {
forPage: window.location.pathname,
_token: $('meta[name="csrf-token"]').attr('content')
}).done(function(data) {
// Set up the Twilio Client device with the token
device = new Device();
device.setup(data.token);
setupHandlers(device);
}).fail(function() {
updateCallStatus("Could not get a token from server!");
});
};
/* Call a customer from a support ticket */
window.callCustomer = function(phoneNumber) {
updateCallStatus("Calling " + phoneNumber + "...");
var params = { "phoneNumber": phoneNumber };
device.connect(params);
};
/* Call the support_agent from the home page */
window.callSupport = function() {
updateCallStatus("Calling support...");
// Our backend will assume that no params means a call to support_agent
device.connect();
};
/* End a call */
window.hangUp = function() {
device.disconnectAll();
};
I'll assume that the TwiML generated by the URL you have set on your TwiML Application includes the verb in order to connect the incoming Client call to something like a PSTN number.
If that assumption is correct then you can include the record attribute on that verb:
<Response>
<Dial record="record-from-ringing-dual"
recordingStatusCallback="https://myapp.com/recording-events"
recordingStatusCallbackEvent="in-progress completed absent">
<Number>+15558675310</Number>
</Dial>
</Response>
Use record-from-answer-dual or record-from-ringing-dual record values for dual-channel recordings, with the parent and child calls in different stereo tracks.
See the support article Recording a Phone Call with Twilio for more details.

CXCallObserverDelegate: callChanged not triggering

I'm developing a NativeScript application which will make use of listening to the call state of the phone. For this i'm using CXCallObserver and CXCallObserverDelegate which I set up as following:
module.exports = {
phoneDelegate: NSObject.extend({
initWithResolveReject: function(resolve, reject){
var self = this.super.init();
if(self){
this.resolve = resolve;
this.reject = reject;
}
return self;
},
callObserverCallChanged: function(observer, call){
console.log("This log is not triggering");
if(call.hasEnded){
// call has ended
this.resolve({phoneState: "ended"});
}
if(call.isOutgoing && !call.hasConnected){
// Dialing out
this.resolve({phoneState: "outgoing call"});
}
if(!call.isOutgoing && !call.hasConnected && !call.hasEnded){
// Call is incoming
this.resolve({phoneState: "incoming call"});
}
if(call.hasConnected && !call.hasEnded){
// Call is ongoing
this.resolve({phoneState: "ongoing call"});
}
}
}, {
protocols: [CXCallObserverDelegate]
}),
registerListener: function(){
return new Promise((resolve, reject) => {
try{
this.callObserver = new CXCallObserver();
let myCallDelegate = this.phoneDelegate.alloc().initWithResolveReject(resolve, reject);
this.callObserver.setDelegateQueue(myCallDelegate, null);
console.log("phone listener registered");
} catch(error) {
reject({error: error});
}
})
}
}
The listener is getting registered as it should, at least no errors are thrown and the last console log in "registerListener" is executed as it should.
When I try to make a phone call, either incoming or outgoing nothing happens. At least the first console log in "callObserverCallChanged" should execute on any phone state change. But nothing happens.
Any one got any suggestion what might be wrong?
let myCallDelegate =
this.phoneDelegate.alloc().initWithResolveReject(resolve, reject);
this.callObserver.setDelegateQueue(myCallDelegate, null);
Ok the solution was quite embarrassing. The myCallDelegate instance was destroyed before doing anything due to not assigning it to the class. Well here is the solution:
this.myCallDelegate = this.phoneDelegate.alloc().initWithResolveReject(resolve, reject);
this.callObserver.setDelegateQueue(this.myCallDelegate, null);
Shoutout to tsonevn for taking his time and finding this misstake.

Socket.IO checking if room contains clients with socket.io-redis

What is "efficient way" to check if some room has connected clients or even exists?
socket.on('pm', function(data) {
data.from = socket.decoded_token._id;
var to = data.to;
if (_.isUndefined(io.sockets.adapter.rooms[to])) {
debug("user [%s] is not connected", to);
} else {
socket.to(to).emit('pm', message);
}
});
Is it efficient way to do it with multiple instances of socket.io and redis adapter?
I wish something like this could be used:
socket.on('pm', function(data) {
data.from = socket.decoded_token._id;
var to = data.to;
socket.to(to).emit('pm', message, function(err){
//error callback, but callback aren't supported for broadcast messages
debug("user [%s] is not connected", to);
});
}
});

Phonegap show html if not connected

Here is my code at the minute:
Using phonegap 2.9
<head>
</head>
<body>
<script charset="utf-8" src="js/cordova.js"></script>
<script>
document.addEventListener("deviceready", onDeviceReady, false);
document.addEventListener("online", onOnline, false);
document.addEventListener("offline", onOffline, false);
// PhoneGap is loaded and it is now safe to make calls PhoneGap methods
//
function onDeviceReady() {
console.log("Device Ready");
}
// Handle the online event
//
function onOnline() {
document.location = 'http://app.dadad.com';
}
function onOffline() {
console.log("Offline");
}
</script>
</body>
However right now I just get a white screen whether i'm connected or not. Eventually what I would like is to display some html when the user is not connected.
So in conclusion:
Would like to fix the function as it is not working
Would like to show html when not connected.
The Online/Offline events do not fire onload. They are there for when you are in app (complete load) and then lose or gain connection. I have gotten around this by doing an initial connection check on load, like this:
function checkConnetcion() {
var networkState = navigator.connection.type;
var states = {};
states[Connection.UNKNOWN] = false;
states[Connection.ETHERNET] = true;
states[Connection.WIFI] = true;
states[Connection.CELL_2G] = true;
states[Connection.CELL_3G] = true;
states[Connection.CELL_4G] = true;
states[Connection.CELL] = true;
states[Connection.NONE] = false;
var connectionStatus = states[networkState];
if(connectionStatus) {
//Do something if connected
}
else{
//Do something if not connected
}
}
Then add this your onready function:
// PhoneGap is loaded and it is now safe to make calls PhoneGap methods
//
function onDeviceReady() {
console.log("Device Ready");
checkConnetcion();
}
You can make use of Jquery ajax and send a dummy request before sending actual request. If you get and Error Code as '0' it means there is no internet connectivity.
$.ajax({
url: 'TestUrl',
type: 'GET',
success: function (data) {
// Go ahead with you request
},
error: function (x, y, z) {
if (x.status == 0) {
alert("Please connect to the internet");
}
else{
alert("Other Error Occured")
}
}
});
Secondly you can also make you of HTML 5 navigator
var condition = navigator.onLine ? "ONLINE" : "OFFLINE";
But it will show ONLINE when WIFI doesn't provide Internet connection.
Cordova connection object will also show WIFI if there is no internet connection

Resources