support kill dial if sd ep is media timeout (#1001)

* support kill dial if sd ep is media timeout

* support kill dial if sd ep is media timeout

* support kill dial if sd ep is media timeout

* add media timeout reason header to bye message

* wip

* wip

* make configuration for freeswitch media timeout

* make configuration for freeswitch media timeout

* wip
This commit is contained in:
Hoan Luu Huu
2024-12-23 19:19:41 +07:00
committed by GitHub
parent 02f25f8343
commit 564f6c9e55
6 changed files with 51 additions and 11 deletions

View File

@@ -137,6 +137,8 @@ const JAMBONES_EAGERLY_PRE_CACHE_AUDIO = parseInt(process.env.JAMBONES_EAGERLY_P
const JAMBONES_USE_FREESWITCH_TIMER_FD = process.env.JAMBONES_USE_FREESWITCH_TIMER_FD; const JAMBONES_USE_FREESWITCH_TIMER_FD = process.env.JAMBONES_USE_FREESWITCH_TIMER_FD;
const JAMBONES_DIAL_SBC_FOR_REGISTERED_USER = process.env.JAMBONES_DIAL_SBC_FOR_REGISTERED_USER || false; const JAMBONES_DIAL_SBC_FOR_REGISTERED_USER = process.env.JAMBONES_DIAL_SBC_FOR_REGISTERED_USER || false;
const JAMBONES_MEDIA_TIMEOUT_MS = process.env.JAMBONES_MEDIA_TIMEOUT_MS || 0;
const JAMBONES_MEDIA_HOLD_TIMEOUT_MS = process.env.JAMBONES_MEDIA_HOLD_TIMEOUT_MS || 0;
module.exports = { module.exports = {
JAMBONES_MYSQL_HOST, JAMBONES_MYSQL_HOST,
@@ -223,5 +225,7 @@ module.exports = {
JAMBONZ_DISABLE_DIAL_PAI_HEADER, JAMBONZ_DISABLE_DIAL_PAI_HEADER,
JAMBONES_DISABLE_DIRECT_P2P_CALL, JAMBONES_DISABLE_DIRECT_P2P_CALL,
JAMBONES_USE_FREESWITCH_TIMER_FD, JAMBONES_USE_FREESWITCH_TIMER_FD,
JAMBONES_DIAL_SBC_FOR_REGISTERED_USER JAMBONES_DIAL_SBC_FOR_REGISTERED_USER,
JAMBONES_MEDIA_TIMEOUT_MS,
JAMBONES_MEDIA_HOLD_TIMEOUT_MS
}; };

View File

@@ -28,7 +28,9 @@ const {
JAMBONES_INJECT_CONTENT, JAMBONES_INJECT_CONTENT,
JAMBONES_EAGERLY_PRE_CACHE_AUDIO, JAMBONES_EAGERLY_PRE_CACHE_AUDIO,
AWS_REGION, AWS_REGION,
JAMBONES_USE_FREESWITCH_TIMER_FD JAMBONES_USE_FREESWITCH_TIMER_FD,
JAMBONES_MEDIA_TIMEOUT_MS,
JAMBONES_MEDIA_HOLD_TIMEOUT_MS
} = require('../config'); } = require('../config');
const bent = require('bent'); const bent = require('bent');
const BackgroundTaskManager = require('../utils/background-task-manager'); const BackgroundTaskManager = require('../utils/background-task-manager');
@@ -2796,15 +2798,25 @@ Duration=${duration} `
_configMsEndpoint() { _configMsEndpoint() {
this._enableInbandDtmfIfRequired(this.ep); this._enableInbandDtmfIfRequired(this.ep);
this.ep.once('destroy', this._handleMediaTimeout.bind(this));
const opts = { const opts = {
...(this.onHoldMusic && {holdMusic: `shout://${this.onHoldMusic.replace(/^https?:\/\//, '')}`}), ...(this.onHoldMusic && {holdMusic: `shout://${this.onHoldMusic.replace(/^https?:\/\//, '')}`}),
...(JAMBONES_USE_FREESWITCH_TIMER_FD && {timer_name: 'timerfd'}) ...(JAMBONES_USE_FREESWITCH_TIMER_FD && {timer_name: 'timerfd'}),
...(JAMBONES_MEDIA_TIMEOUT_MS && {media_timeout: JAMBONES_MEDIA_TIMEOUT_MS}),
...(JAMBONES_MEDIA_HOLD_TIMEOUT_MS && {media_hold_timeout: JAMBONES_MEDIA_HOLD_TIMEOUT_MS})
}; };
if (Object.keys(opts).length > 0) { if (Object.keys(opts).length > 0) {
this.ep.set(opts); this.ep.set(opts);
} }
} }
async _handleMediaTimeout(evt) {
if (evt.reason === 'MEDIA_TIMEOUT' && !this.callGone) {
this.logger.info('CallSession:_handleMediaTimeout: received MEDIA_TIMEOUT, hangup the call');
this._jambonzHangup('Media Timeout');
}
}
async _enableInbandDtmfIfRequired(ep) { async _enableInbandDtmfIfRequired(ep) {
if (ep.inbandDtmfEnabled) return; if (ep.inbandDtmfEnabled) return;
// only enable inband dtmf detection if voip carrier dtmf_type === tones // only enable inband dtmf detection if voip carrier dtmf_type === tones

View File

@@ -70,8 +70,12 @@ class InboundCallSession extends CallSession {
this._hangup('caller'); this._hangup('caller');
} }
_jambonzHangup() { _jambonzHangup(reason) {
this.dlg?.destroy(); this.dlg?.destroy({
headers: {
...(reason && {'X-Reason': reason})
}
});
// kill current task or wakeup the call session. // kill current task or wakeup the call session.
this._callReleased(); this._callReleased();
} }

View File

@@ -273,7 +273,9 @@ class TaskDial extends Task {
this._removeDtmfDetection(this.dlg); this._removeDtmfDetection(this.dlg);
await this._killOutdials(); await this._killOutdials();
if (this.sd) { if (this.sd) {
this.sd.kill(); const byeReasonHeader = this.killReason === KillReason.MediaTimeout ? 'Media Timeout' : undefined;
this.sd.kill(byeReasonHeader);
this.sd.ep?.removeListener('destroy', this._handleMediaTimeout.bind(this));
this.sd.removeAllListeners(); this.sd.removeAllListeners();
this.sd = null; this.sd = null;
} }
@@ -887,6 +889,14 @@ class TaskDial extends Task {
if (this.canReleaseMedia || this.shouldExitMediaPathEntirely) { if (this.canReleaseMedia || this.shouldExitMediaPathEntirely) {
setTimeout(this._releaseMedia.bind(this, cs, sd, this.shouldExitMediaPathEntirely), 200); setTimeout(this._releaseMedia.bind(this, cs, sd, this.shouldExitMediaPathEntirely), 200);
} }
this.sd.ep.once('destroy', this._handleMediaTimeout.bind(this));
}
_handleMediaTimeout(evt) {
if (evt.reason === 'MEDIA_TIMEOUT' && this.sd && this.bridged) {
this.kill(this.cs, KillReason.MediaTimeout);
}
} }
_bridgeEarlyMedia(sd) { _bridgeEarlyMedia(sd) {

View File

@@ -196,7 +196,8 @@
}, },
"KillReason": { "KillReason": {
"Hangup": "hangup", "Hangup": "hangup",
"Replaced": "replaced" "Replaced": "replaced",
"MediaTimeout": "media_timeout"
}, },
"HookMsgTypes": [ "HookMsgTypes": [
"session:new", "session:new",

View File

@@ -17,7 +17,9 @@ const HttpRequestor = require('./http-requestor');
const WsRequestor = require('./ws-requestor'); const WsRequestor = require('./ws-requestor');
const {makeOpusFirst} = require('./sdp-utils'); const {makeOpusFirst} = require('./sdp-utils');
const { const {
JAMBONES_USE_FREESWITCH_TIMER_FD JAMBONES_USE_FREESWITCH_TIMER_FD,
JAMBONES_MEDIA_TIMEOUT_MS,
JAMBONES_MEDIA_HOLD_TIMEOUT_MS
} = require('../config'); } = require('../config');
class SingleDialer extends Emitter { class SingleDialer extends Emitter {
@@ -317,14 +319,19 @@ class SingleDialer extends Emitter {
/** /**
* kill the call in progress or the stable dialog, whichever we have * kill the call in progress or the stable dialog, whichever we have
*/ */
async kill() { async kill(Reason) {
this.killed = true; this.killed = true;
if (this.inviteInProgress) await this.inviteInProgress.cancel(); if (this.inviteInProgress) await this.inviteInProgress.cancel();
else if (this.dlg && this.dlg.connected) { else if (this.dlg && this.dlg.connected) {
const duration = moment().diff(this.dlg.connectTime, 'seconds'); const duration = moment().diff(this.dlg.connectTime, 'seconds');
this.logger.debug('SingleDialer:kill hanging up called party'); this.logger.debug('SingleDialer:kill hanging up called party');
this.emit('callStatusChange', {callStatus: CallStatus.Completed, duration}); this.emit('callStatusChange', {callStatus: CallStatus.Completed, duration});
this.dlg.destroy(); const headers = {
...(Reason && {'X-Reason': Reason})
};
this.dlg.destroy({
headers
});
} }
if (this.ep) { if (this.ep) {
this.logger.debug(`SingleDialer:kill - deleting endpoint ${this.ep.uuid}`); this.logger.debug(`SingleDialer:kill - deleting endpoint ${this.ep.uuid}`);
@@ -335,7 +342,9 @@ class SingleDialer extends Emitter {
_configMsEndpoint() { _configMsEndpoint() {
const opts = { const opts = {
...(this.onHoldMusic && {holdMusic: `shout://${this.onHoldMusic.replace(/^https?:\/\//, '')}`}), ...(this.onHoldMusic && {holdMusic: `shout://${this.onHoldMusic.replace(/^https?:\/\//, '')}`}),
...(JAMBONES_USE_FREESWITCH_TIMER_FD && {timer_name: 'timerfd'}) ...(JAMBONES_USE_FREESWITCH_TIMER_FD && {timer_name: 'timerfd'}),
...(JAMBONES_MEDIA_TIMEOUT_MS && {media_timeout: JAMBONES_MEDIA_TIMEOUT_MS}),
...(JAMBONES_MEDIA_HOLD_TIMEOUT_MS && {media_hold_timeout: JAMBONES_MEDIA_HOLD_TIMEOUT_MS})
}; };
if (Object.keys(opts).length > 0) { if (Object.keys(opts).length > 0) {
this.ep.set(opts); this.ep.set(opts);