mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-20 16:50:39 +00:00
fixed transcription is not received when call is terminated (#1259)
* fixed transcription is not received when call is terminated * wip * fixed failing testcases * wip * wip * wip * wip * should not do gracefulshutdown on stopAmd
This commit is contained in:
@@ -418,7 +418,11 @@ module.exports = (logger) => {
|
||||
}
|
||||
|
||||
if (ep.connected) {
|
||||
ep.stopTranscription({vendor, bugname})
|
||||
ep.stopTranscription({
|
||||
vendor,
|
||||
bugname,
|
||||
gracefulShutdown: false
|
||||
})
|
||||
.catch((err) => logger.info(err, 'stopAmd: Error stopping transcription'));
|
||||
task.emit('amd', {type: AmdEvents.Stopped});
|
||||
ep.execute('avmd_stop').catch((err) => this.logger.info(err, 'Error stopping avmd'));
|
||||
|
||||
115
lib/utils/media-endpoint.js
Normal file
115
lib/utils/media-endpoint.js
Normal file
@@ -0,0 +1,115 @@
|
||||
const {
|
||||
JAMBONES_USE_FREESWITCH_TIMER_FD,
|
||||
JAMBONES_MEDIA_TIMEOUT_MS,
|
||||
JAMBONES_MEDIA_HOLD_TIMEOUT_MS,
|
||||
JAMBONES_TRANSCRIBE_EP_DESTROY_DELAY_MS,
|
||||
} = require('../config');
|
||||
const { sleepFor } = require('./helpers');
|
||||
|
||||
const createMediaEndpoint = async(srf, logger, {
|
||||
activeMs,
|
||||
drachtioFsmrfOptions = {},
|
||||
onHoldMusic,
|
||||
inbandDtmfEnabled,
|
||||
mediaTimeoutHandler,
|
||||
} = {}) => {
|
||||
const { getFreeswitch } = srf.locals;
|
||||
const ms = activeMs || getFreeswitch();
|
||||
if (!ms)
|
||||
throw new Error('no available Freeswitch for creating media endpoint');
|
||||
|
||||
const ep = await ms.createEndpoint(drachtioFsmrfOptions);
|
||||
|
||||
// Configure the endpoint
|
||||
const opts = {
|
||||
...(onHoldMusic && {holdMusic: `shout://${onHoldMusic.replace(/^https?:\/\//, '')}`}),
|
||||
...(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) {
|
||||
ep.set(opts);
|
||||
}
|
||||
// inbandDtmfEnabled
|
||||
if (inbandDtmfEnabled) {
|
||||
// https://developer.signalwire.com/freeswitch/FreeSWITCH-Explained/Modules/mod-dptools/6587132/#0-about
|
||||
ep.execute('start_dtmf').catch((err) => {
|
||||
logger.error('Error starting inband DTMF', { error: err });
|
||||
});
|
||||
ep.inbandDtmfEnabled = true;
|
||||
}
|
||||
// Handle Media Timeout
|
||||
if (mediaTimeoutHandler) {
|
||||
ep.once('destroy', (evt) => {
|
||||
mediaTimeoutHandler(evt, ep);
|
||||
});
|
||||
}
|
||||
// Handle graceful shutdown for endpoint if required
|
||||
if (JAMBONES_TRANSCRIBE_EP_DESTROY_DELAY_MS > 0) {
|
||||
const getEpGracefulShutdownPromise = () => {
|
||||
if (!ep.gracefulShutdownPromise) {
|
||||
ep.gracefulShutdownPromise = new Promise((resolve) => {
|
||||
// this resolver will be called when stt task received transcription.
|
||||
ep.gracefulShutdownResolver = () => {
|
||||
resolve();
|
||||
ep.gracefulShutdownPromise = null;
|
||||
};
|
||||
});
|
||||
}
|
||||
return ep.gracefulShutdownPromise;
|
||||
};
|
||||
|
||||
const gracefulShutdownHandler = async() => {
|
||||
// resolve when one of the following happens:
|
||||
// 1. stt task received transcription
|
||||
// 2. JAMBONES_TRANSCRIBE_EP_DESTROY_DELAY_MS passed
|
||||
await Promise.race([
|
||||
getEpGracefulShutdownPromise(),
|
||||
sleepFor(JAMBONES_TRANSCRIBE_EP_DESTROY_DELAY_MS)
|
||||
]);
|
||||
};
|
||||
|
||||
const origStartTranscription = ep.startTranscription.bind(ep);
|
||||
ep.startTranscription = async(...args) => {
|
||||
try {
|
||||
const result = await origStartTranscription(...args);
|
||||
ep.isTranscribeActive = true;
|
||||
return result;
|
||||
} catch (err) {
|
||||
ep.isTranscribeActive = false;
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const origStopTranscription = ep.stopTranscription.bind(ep);
|
||||
ep.stopTranscription = async(opts = {}, ...args) => {
|
||||
const { gracefulShutdown = true, ...others } = opts;
|
||||
if (ep.isTranscribeActive && gracefulShutdown) {
|
||||
// only wait for graceful shutdown if transcription is active
|
||||
await gracefulShutdownHandler();
|
||||
}
|
||||
try {
|
||||
const result = await origStopTranscription({...others}, ...args);
|
||||
ep.isTranscribeActive = false;
|
||||
return result;
|
||||
} catch (err) {
|
||||
ep.isTranscribeActive = false;
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const origDestroy = ep.destroy.bind(ep);
|
||||
ep.destroy = async() => {
|
||||
if (ep.isTranscribeActive) {
|
||||
await gracefulShutdownHandler();
|
||||
}
|
||||
return await origDestroy();
|
||||
};
|
||||
}
|
||||
|
||||
return ep;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createMediaEndpoint,
|
||||
};
|
||||
@@ -16,13 +16,7 @@ const crypto = require('crypto');
|
||||
const HttpRequestor = require('./http-requestor');
|
||||
const WsRequestor = require('./ws-requestor');
|
||||
const {makeOpusFirst, removeVideoSdp} = require('./sdp-utils');
|
||||
const {
|
||||
JAMBONES_USE_FREESWITCH_TIMER_FD,
|
||||
JAMBONES_MEDIA_TIMEOUT_MS,
|
||||
JAMBONES_MEDIA_HOLD_TIMEOUT_MS,
|
||||
JAMBONES_TRANSCRIBE_EP_DESTROY_DELAY_MS
|
||||
} = require('../config');
|
||||
const { sleepFor } = require('./helpers');
|
||||
const { createMediaEndpoint } = require('./media-endpoint');
|
||||
|
||||
class SingleDialer extends Emitter {
|
||||
constructor({logger, sbcAddress, target, opts, application, callInfo, accountInfo, rootSpan, startSpan, dialTask,
|
||||
@@ -98,6 +92,7 @@ class SingleDialer extends Emitter {
|
||||
};
|
||||
}
|
||||
this.ms = ms;
|
||||
this.srf = srf;
|
||||
let uri, to, inviteSpan;
|
||||
try {
|
||||
switch (this.target.type) {
|
||||
@@ -139,8 +134,7 @@ class SingleDialer extends Emitter {
|
||||
this.updateCallStatus = srf.locals.dbHelpers.updateCallStatus;
|
||||
this.serviceUrl = srf.locals.serviceUrl;
|
||||
|
||||
this.ep = await ms.createEndpoint();
|
||||
this._configMsEndpoint();
|
||||
this.ep = await this._createMediaEndpoint();
|
||||
this.logger.debug(`SingleDialer:exec - created endpoint ${this.ep.uuid}`);
|
||||
|
||||
/**
|
||||
@@ -352,43 +346,19 @@ class SingleDialer extends Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
_configMsEndpoint() {
|
||||
const opts = {
|
||||
...(this.onHoldMusic && {holdMusic: `shout://${this.onHoldMusic.replace(/^https?:\/\//, '')}`}),
|
||||
...(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) {
|
||||
this.ep.set(opts);
|
||||
}
|
||||
if (this.dialTask?.inbandDtmfEnabled && !this.ep.inbandDtmfEnabled) {
|
||||
// https://developer.signalwire.com/freeswitch/FreeSWITCH-Explained/Modules/mod-dptools/6587132/#0-about
|
||||
try {
|
||||
this.ep.execute('start_dtmf');
|
||||
this.ep.inbandDtmfEnabled = true;
|
||||
} catch (err) {
|
||||
this.logger.info(err, 'place-outdial:_configMsEndpoint - error enable inband DTMF');
|
||||
}
|
||||
}
|
||||
async _handleMediaTimeout(evt, ep) {
|
||||
this.logger.info({evt}, 'SingleDialer:_handleMediaTimeout - media timeout event received');
|
||||
this.dialTask.kill(this.dialTask.cs, 'media-timeout');
|
||||
}
|
||||
|
||||
const origDestroy = this.ep.destroy.bind(this.ep);
|
||||
this.ep.destroy = async() => {
|
||||
try {
|
||||
if (this.dialTask.transcribeTask && JAMBONES_TRANSCRIBE_EP_DESTROY_DELAY_MS) {
|
||||
// transcribe task is being used, wait for some time before destroy
|
||||
// if final transcription is received but endpoint is already closed,
|
||||
// freeswitch module will not be able to send the transcription
|
||||
|
||||
this.logger.info('SingleDialer:_configMsEndpoint -' +
|
||||
' Dial with transcribe task, wait for some time before destroy');
|
||||
await sleepFor(JAMBONES_TRANSCRIBE_EP_DESTROY_DELAY_MS);
|
||||
}
|
||||
await origDestroy();
|
||||
} catch (err) {
|
||||
this.logger.error(err, 'SingleDialer:_configMsEndpoint - error destroying endpoint');
|
||||
}
|
||||
};
|
||||
async _createMediaEndpoint(drachtioFsmrfOptions = {}) {
|
||||
return await createMediaEndpoint(this.srf, this.logger, {
|
||||
acactiveMs: this.ms,
|
||||
drachtioFsmrfOptions,
|
||||
onHoldMusic: this.onHoldMusic,
|
||||
inbandDtmfEnabled: this.dialTask?.inbandDtmfEnabled,
|
||||
mediaTimeoutHandler: this._handleMediaTimeout.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -528,8 +498,7 @@ class SingleDialer extends Emitter {
|
||||
assert(this.dlg && this.dlg.connected && !this.ep);
|
||||
|
||||
this.logger.debug('SingleDialer:reAnchorMedia: re-anchoring media after partial media');
|
||||
this.ep = await this.ms.createEndpoint({remoteSdp: this.dlg.remote.sdp});
|
||||
this._configMsEndpoint();
|
||||
this.ep = await this._createMediaEndpoint({remoteSdp: this.dlg.remote.sdp});
|
||||
await this.dlg.modify(this.ep.local.sdp, {
|
||||
headers: {
|
||||
'X-Reason': 'anchor-media'
|
||||
|
||||
Reference in New Issue
Block a user