mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-20 16:50:39 +00:00
fix onholdHOok (#540)
* fix onholdHOok * wip * wip * wip * wip * adding more debug log * wip * wip * wip
This commit is contained in:
@@ -1744,7 +1744,7 @@ Duration=${duration} `
|
|||||||
res.send(200, {body: this.ep.local.sdp});
|
res.send(200, {body: this.ep.local.sdp});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (this.currentTask.name === TaskName.Dial && this.currentTask.isOnHold) {
|
if (this.currentTask.name === TaskName.Dial && this.currentTask.isOnHoldEnabled) {
|
||||||
this.logger.info('onholdMusic reINVITE after media has been released');
|
this.logger.info('onholdMusic reINVITE after media has been released');
|
||||||
await this.currentTask.handleReinviteAfterMediaReleased(req, res);
|
await this.currentTask.handleReinviteAfterMediaReleased(req, res);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -138,13 +138,14 @@ class TaskDial extends Task {
|
|||||||
|
|
||||||
get name() { return TaskName.Dial; }
|
get name() { return TaskName.Dial; }
|
||||||
|
|
||||||
get isOnHold() {
|
get isOnHoldEnabled() {
|
||||||
return this.isIncomingLegHold || this.isOutgoingLegHold;
|
return !!this.data.onHoldHook;
|
||||||
}
|
}
|
||||||
|
|
||||||
get canReleaseMedia() {
|
get canReleaseMedia() {
|
||||||
const keepAnchor = this.data.anchorMedia ||
|
const keepAnchor = this.data.anchorMedia ||
|
||||||
this.cs.isBackGroundListen ||
|
this.cs.isBackGroundListen ||
|
||||||
|
this.cs.onHoldMusic ||
|
||||||
ANCHOR_MEDIA_ALWAYS ||
|
ANCHOR_MEDIA_ALWAYS ||
|
||||||
this.listenTask ||
|
this.listenTask ||
|
||||||
this.transcribeTask ||
|
this.transcribeTask ||
|
||||||
@@ -570,7 +571,8 @@ class TaskDial extends Task {
|
|||||||
accountInfo: cs.accountInfo,
|
accountInfo: cs.accountInfo,
|
||||||
rootSpan: cs.rootSpan,
|
rootSpan: cs.rootSpan,
|
||||||
startSpan: this.startSpan.bind(this),
|
startSpan: this.startSpan.bind(this),
|
||||||
dialTask: this
|
dialTask: this,
|
||||||
|
onHoldMusic: this.cs.onHoldMusic
|
||||||
});
|
});
|
||||||
this.dials.set(sd.callSid, sd);
|
this.dials.set(sd.callSid, sd);
|
||||||
|
|
||||||
@@ -679,22 +681,43 @@ class TaskDial extends Task {
|
|||||||
async _onReinvite(req, res) {
|
async _onReinvite(req, res) {
|
||||||
try {
|
try {
|
||||||
let isHandled = false;
|
let isHandled = false;
|
||||||
if (this.cs.onHoldMusic) {
|
if (this.isOnHoldEnabled) {
|
||||||
if (isOnhold(req.body) && !this.epOther && !this.ep) {
|
if (isOnhold(req.body)) {
|
||||||
await this.cs.handleReinviteAfterMediaReleased(req, res);
|
this.logger.debug('Dial: _onReinvite receive hold Request');
|
||||||
// Onhold but media is already released
|
if (!this.epOther && !this.ep) {
|
||||||
// reconnect A Leg and Response B leg
|
this.logger.debug(`Dial: _onReinvite receive hold Request,
|
||||||
await this.reAnchorMedia(this.cs, this.sd);
|
media already released, reconnect media server`);
|
||||||
this.isOutgoingLegHold = true;
|
// update caller leg for new SDP from callee.
|
||||||
|
await this.cs.handleReinviteAfterMediaReleased(req, res);
|
||||||
|
// Freeswitch media is released, reconnect
|
||||||
|
await this.reAnchorMedia(this.cs, this.sd);
|
||||||
|
this.isOutgoingLegHold = true;
|
||||||
|
} else {
|
||||||
|
this.logger.debug('Dial: _onReinvite receive hold Request, update SDP');
|
||||||
|
const newSdp = await this.ep.modify(req.body);
|
||||||
|
res.send(200, {body: newSdp});
|
||||||
|
}
|
||||||
isHandled = true;
|
isHandled = true;
|
||||||
this._onHoldHook();
|
// Media already connected, ask for onHoldHook
|
||||||
} else if (!isOnhold(req.body) && this.epOther && this.ep && this.isOutgoingLegHold && this.canReleaseMedia) {
|
this._onHoldHook(req);
|
||||||
// Offhold, time to release media
|
} else if (!isOnhold(req.body)) {
|
||||||
const newSdp = await this.ep.modify(req.body);
|
this.logger.debug('Dial: _onReinvite receive unhold Request');
|
||||||
await res.send(200, {body: newSdp});
|
if (this.epOther && this.ep && this.isOutgoingLegHold && this.canReleaseMedia) {
|
||||||
await this._releaseMedia(this.cs, this.sd);
|
this.logger.debug('Dial: _onReinvite receive unhold Request, release media');
|
||||||
|
// Offhold, time to release media
|
||||||
|
const newSdp = await this.ep.modify(req.body);
|
||||||
|
await res.send(200, {body: newSdp});
|
||||||
|
await this._releaseMedia(this.cs, this.sd);
|
||||||
|
this.isOutgoingLegHold = false;
|
||||||
|
} else {
|
||||||
|
this.logger.debug('Dial: _onReinvite receive unhold Request, update media server');
|
||||||
|
const newSdp = await this.ep.modify(req.body);
|
||||||
|
res.send(200, {body: newSdp});
|
||||||
|
}
|
||||||
|
if (this._onHoldSession) {
|
||||||
|
this._onHoldSession.kill();
|
||||||
|
}
|
||||||
isHandled = true;
|
isHandled = true;
|
||||||
this.isOutgoingLegHold = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isHandled) {
|
if (!isHandled) {
|
||||||
@@ -811,23 +834,34 @@ class TaskDial extends Task {
|
|||||||
this.epOther = cs.ep;
|
this.epOther = cs.ep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle RE-INVITE hold from caller leg.
|
||||||
async handleReinviteAfterMediaReleased(req, res) {
|
async handleReinviteAfterMediaReleased(req, res) {
|
||||||
let isHandled = false;
|
let isHandled = false;
|
||||||
if (isOnhold(req.body) && !this.epOther && !this.ep) {
|
if (this.isOnHoldEnabled) {
|
||||||
const sdp = await this.dlg.modify(req.body);
|
if (isOnhold(req.body)) {
|
||||||
res.send(200, {body: sdp});
|
if (!this.epOther && !this.ep) {
|
||||||
// Onhold but media is already released
|
// update callee leg for new SDP from caller.
|
||||||
await this.reAnchorMedia(this.cs, this.sd);
|
const sdp = await this.dlg.modify(req.body);
|
||||||
isHandled = true;
|
res.send(200, {body: sdp});
|
||||||
this.isIncomingLegHold = true;
|
// Onhold but media is already released, reconnect
|
||||||
this._onHoldHook();
|
await this.reAnchorMedia(this.cs, this.sd);
|
||||||
} else if (!isOnhold(req.body) && this.epOther && this.ep && this.isIncomingLegHold && this.canReleaseMedia) {
|
isHandled = true;
|
||||||
// Offhold, time to release media
|
this.isIncomingLegHold = true;
|
||||||
const newSdp = await this.epOther.modify(req.body);
|
}
|
||||||
await res.send(200, {body: newSdp});
|
this._onHoldHook(req);
|
||||||
await this._releaseMedia(this.cs, this.sd);
|
} else if (!isOnhold(req.body)) {
|
||||||
isHandled = true;
|
if (this.epOther && this.ep && this.isIncomingLegHold && this.canReleaseMedia) {
|
||||||
this.isIncomingLegHold = false;
|
// Offhold, time to release media
|
||||||
|
const newSdp = await this.epOther.modify(req.body);
|
||||||
|
await res.send(200, {body: newSdp});
|
||||||
|
await this._releaseMedia(this.cs, this.sd);
|
||||||
|
isHandled = true;
|
||||||
|
}
|
||||||
|
this.isIncomingLegHold = false;
|
||||||
|
if (this._onHoldSession) {
|
||||||
|
this._onHoldSession.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isHandled) {
|
if (!isHandled) {
|
||||||
@@ -846,7 +880,7 @@ class TaskDial extends Task {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onHoldHook(allowed = [TaskName.Play, TaskName.Say, TaskName.Pause]) {
|
async _onHoldHook(req, allowed = [TaskName.Play, TaskName.Say, TaskName.Pause]) {
|
||||||
if (this.data.onHoldHook) {
|
if (this.data.onHoldHook) {
|
||||||
// send silence for keep Voice quality
|
// send silence for keep Voice quality
|
||||||
await this.epOther.play('silence_stream://500');
|
await this.epOther.play('silence_stream://500');
|
||||||
@@ -856,7 +890,13 @@ class TaskDial extends Task {
|
|||||||
const b3 = this.getTracingPropagation();
|
const b3 = this.getTracingPropagation();
|
||||||
const httpHeaders = b3 && {b3};
|
const httpHeaders = b3 && {b3};
|
||||||
const json = await this.cs.application.requestor.
|
const json = await this.cs.application.requestor.
|
||||||
request('verb:hook', this.data.onHoldHook, this.cs.callInfo.toJSON(), httpHeaders);
|
request('verb:hook', this.data.onHoldHook, {
|
||||||
|
...this.cs.callInfo.toJSON(),
|
||||||
|
hold_detail: {
|
||||||
|
from: req.get('From'),
|
||||||
|
to: req.get('To')
|
||||||
|
}
|
||||||
|
}, httpHeaders);
|
||||||
const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));
|
const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));
|
||||||
allowedTasks = tasks.filter((t) => allowed.includes(t.name));
|
allowedTasks = tasks.filter((t) => allowed.includes(t.name));
|
||||||
if (tasks.length !== allowedTasks.length) {
|
if (tasks.length !== allowedTasks.length) {
|
||||||
@@ -865,7 +905,7 @@ class TaskDial extends Task {
|
|||||||
}
|
}
|
||||||
this.logger.debug(`DialTask:_onHoldHook: executing ${tasks.length} tasks`);
|
this.logger.debug(`DialTask:_onHoldHook: executing ${tasks.length} tasks`);
|
||||||
if (tasks.length) {
|
if (tasks.length) {
|
||||||
this._playSession = new ConfirmCallSession({
|
this._onHoldSession = new ConfirmCallSession({
|
||||||
logger: this.logger,
|
logger: this.logger,
|
||||||
application: this.cs.application,
|
application: this.cs.application,
|
||||||
dlg: this.isIncomingLegHold ? this.dlg : this.cs.dlg,
|
dlg: this.isIncomingLegHold ? this.dlg : this.cs.dlg,
|
||||||
@@ -875,12 +915,12 @@ class TaskDial extends Task {
|
|||||||
tasks,
|
tasks,
|
||||||
rootSpan: this.cs.rootSpan
|
rootSpan: this.cs.rootSpan
|
||||||
});
|
});
|
||||||
await this._playSession.exec();
|
await this._onHoldSession.exec();
|
||||||
this._playSession = null;
|
this._onHoldSession = null;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.info(error, 'DialTask:_onHoldHook: failed retrieving waitHook');
|
this.logger.info(error, 'DialTask:_onHoldHook: failed retrieving waitHook');
|
||||||
this._playSession = null;
|
this._onHoldSession = null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (allowedTasks && allowedTasks.length > 0 && !this.killed && this.isOnHold);
|
} while (allowedTasks && allowedTasks.length > 0 && !this.killed && this.isOnHold);
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ const WsRequestor = require('./ws-requestor');
|
|||||||
const {makeOpusFirst} = require('./sdp-utils');
|
const {makeOpusFirst} = require('./sdp-utils');
|
||||||
|
|
||||||
class SingleDialer extends Emitter {
|
class SingleDialer extends Emitter {
|
||||||
constructor({logger, sbcAddress, target, opts, application, callInfo, accountInfo, rootSpan, startSpan, dialTask}) {
|
constructor({logger, sbcAddress, target, opts, application, callInfo, accountInfo, rootSpan, startSpan, dialTask,
|
||||||
|
onHoldMusic}) {
|
||||||
super();
|
super();
|
||||||
assert(target.type);
|
assert(target.type);
|
||||||
|
|
||||||
@@ -41,6 +42,7 @@ class SingleDialer extends Emitter {
|
|||||||
|
|
||||||
this.callSid = uuidv4();
|
this.callSid = uuidv4();
|
||||||
this.dialTask = dialTask;
|
this.dialTask = dialTask;
|
||||||
|
this.onHoldMusic = onHoldMusic;
|
||||||
|
|
||||||
this.on('callStatusChange', this._notifyCallStatusChange.bind(this));
|
this.on('callStatusChange', this._notifyCallStatusChange.bind(this));
|
||||||
}
|
}
|
||||||
@@ -131,6 +133,7 @@ class SingleDialer extends Emitter {
|
|||||||
this.serviceUrl = srf.locals.serviceUrl;
|
this.serviceUrl = srf.locals.serviceUrl;
|
||||||
|
|
||||||
this.ep = await ms.createEndpoint();
|
this.ep = await ms.createEndpoint();
|
||||||
|
this._configMsEndpoint();
|
||||||
this.logger.debug(`SingleDialer:exec - created endpoint ${this.ep.uuid}`);
|
this.logger.debug(`SingleDialer:exec - created endpoint ${this.ep.uuid}`);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -253,7 +256,7 @@ class SingleDialer extends Emitter {
|
|||||||
.on('modify', async(req, res) => {
|
.on('modify', async(req, res) => {
|
||||||
try {
|
try {
|
||||||
if (this.ep) {
|
if (this.ep) {
|
||||||
if (this.dialTask && this.dialTask.isOnHold) {
|
if (this.dialTask && this.dialTask.isOnHoldEnabled) {
|
||||||
this.logger.info('dial is onhold, emit event');
|
this.logger.info('dial is onhold, emit event');
|
||||||
this.emit('reinvite', req, res);
|
this.emit('reinvite', req, res);
|
||||||
} else {
|
} else {
|
||||||
@@ -320,6 +323,12 @@ class SingleDialer extends Emitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_configMsEndpoint() {
|
||||||
|
if (this.onHoldMusic) {
|
||||||
|
this.ep.set({hold_music: `shout://${this.onHoldMusic.replace(/^https?:\/\//, '')}`});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run an application on the call after answer, e.g. call screening.
|
* Run an application on the call after answer, e.g. call screening.
|
||||||
* Once the application completes in some fashion, emit an 'accepted' event
|
* Once the application completes in some fashion, emit an 'accepted' event
|
||||||
@@ -436,6 +445,7 @@ class SingleDialer extends Emitter {
|
|||||||
async reAnchorMedia() {
|
async reAnchorMedia() {
|
||||||
assert(this.dlg && this.dlg.connected && !this.ep);
|
assert(this.dlg && this.dlg.connected && !this.ep);
|
||||||
this.ep = await this.ms.createEndpoint({remoteSdp: this.dlg.remote.sdp});
|
this.ep = await this.ms.createEndpoint({remoteSdp: this.dlg.remote.sdp});
|
||||||
|
this._configMsEndpoint();
|
||||||
await this.dlg.modify(this.ep.local.sdp, {
|
await this.dlg.modify(this.ep.local.sdp, {
|
||||||
headers: {
|
headers: {
|
||||||
'X-Reason': 'anchor-media'
|
'X-Reason': 'anchor-media'
|
||||||
@@ -466,11 +476,12 @@ class SingleDialer extends Emitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function placeOutdial({
|
function placeOutdial({
|
||||||
logger, srf, ms, sbcAddress, target, opts, application, callInfo, accountInfo, rootSpan, startSpan, dialTask
|
logger, srf, ms, sbcAddress, target, opts, application, callInfo, accountInfo, rootSpan, startSpan, dialTask,
|
||||||
|
onHoldMusic
|
||||||
}) {
|
}) {
|
||||||
const myOpts = deepcopy(opts);
|
const myOpts = deepcopy(opts);
|
||||||
const sd = new SingleDialer({
|
const sd = new SingleDialer({
|
||||||
logger, sbcAddress, target, myOpts, application, callInfo, accountInfo, rootSpan, startSpan, dialTask
|
logger, sbcAddress, target, myOpts, application, callInfo, accountInfo, rootSpan, startSpan, dialTask, onHoldMusic
|
||||||
});
|
});
|
||||||
sd.exec(srf, ms, myOpts);
|
sd.exec(srf, ms, myOpts);
|
||||||
return sd;
|
return sd;
|
||||||
|
|||||||
Reference in New Issue
Block a user