Compare sdp to determine if transcoding is being used. (#1444)

* compare sdp for transcoding

* refactor sdp check for leading codec

* fix reference to epOther

* minor changes

* minor

* fix #1447

* fix security issue

* use convenience getter appIsUsingWebsockets in CallSession

---------

Co-authored-by: Dave Horton <daveh@beachdognet.com>
This commit is contained in:
Ed Robbins
2025-11-24 10:50:41 -06:00
committed by GitHub
co-authored by Dave Horton
parent 1984b6d3ea
commit 1a1f53aede
4 changed files with 40 additions and 13 deletions
+7 -5
View File
@@ -951,10 +951,12 @@ class CallSession extends Emitter {
} }
stopTtsStream() { stopTtsStream() {
if (this.appIsUsingWebsockets) {
this.requestor?.request('tts:streaming-event', '/streaming-event', {event_type: 'stream_closed'}) this.requestor?.request('tts:streaming-event', '/streaming-event', {event_type: 'stream_closed'})
.catch((err) => this.logger.info({err}, 'CallSession:clearTtsStream - Error sending user_interruption')); .catch((err) => this.logger.info({err}, 'CallSession:clearTtsStream - Error sending user_interruption'));
this.ttsStreamingBuffer?.stop(); this.ttsStreamingBuffer?.stop();
} }
}
async enableBotMode(gather, autoEnable) { async enableBotMode(gather, autoEnable) {
try { try {
@@ -979,7 +981,7 @@ class CallSession extends Emitter {
task.sticky = autoEnable; task.sticky = autoEnable;
// listen to the bargein-done from background manager // listen to the bargein-done from background manager
this.backgroundTaskManager.on('bargeIn-done', () => { this.backgroundTaskManager.on('bargeIn-done', () => {
if (this.requestor instanceof WsRequestor) { if (this.appIsUsingWebsockets) {
try { try {
this.kill(true); this.kill(true);
} catch (err) {} } catch (err) {}
@@ -1337,7 +1339,7 @@ class CallSession extends Emitter {
} }
if (0 === this.tasks.length && if (0 === this.tasks.length &&
this.requestor instanceof WsRequestor && this.appIsUsingWebsockets &&
!this.requestor.closedGracefully && !this.requestor.closedGracefully &&
!this.callGone && !this.callGone &&
!this.isConfirmCallSession !this.isConfirmCallSession
@@ -3023,14 +3025,14 @@ Duration=${duration} `
*/ */
_notifyTaskError(obj) { _notifyTaskError(obj) {
if (this.requestor instanceof WsRequestor) { if (this.appIsUsingWebsockets) {
this.requestor.request('jambonz:error', '/error', obj) this.requestor.request('jambonz:error', '/error', obj)
.catch((err) => this.logger.debug({err}, 'CallSession:_notifyTaskError - Error sending')); .catch((err) => this.logger.debug({err}, 'CallSession:_notifyTaskError - Error sending'));
} }
} }
_notifyTaskStatus(task, evt) { _notifyTaskStatus(task, evt) {
if (this.notifyEvents && this.requestor instanceof WsRequestor) { if (this.notifyEvents && this.appIsUsingWebsockets) {
const obj = {...evt, id: task.id, name: task.name}; const obj = {...evt, id: task.id, name: task.name};
this.requestor.request('verb:status', '/status', obj) this.requestor.request('verb:status', '/status', obj)
.catch((err) => this.logger.debug({err}, 'CallSession:_notifyTaskStatus - Error sending')); .catch((err) => this.logger.debug({err}, 'CallSession:_notifyTaskStatus - Error sending'));
@@ -3082,7 +3084,7 @@ Duration=${duration} `
} }
_clearTasks(backgroundGather, evt) { _clearTasks(backgroundGather, evt) {
if (this.requestor instanceof WsRequestor && !backgroundGather.cleared) { if (this.appIsUsingWebsockets && !backgroundGather.cleared) {
this.logger.debug({evt}, 'CallSession:_clearTasks on event from background gather'); this.logger.debug({evt}, 'CallSession:_clearTasks on event from background gather');
try { try {
backgroundGather.cleared = true; backgroundGather.cleared = true;
+9 -2
View File
@@ -21,7 +21,7 @@ const {parseUri} = require('drachtio-srf');
const {ANCHOR_MEDIA_ALWAYS, const {ANCHOR_MEDIA_ALWAYS,
JAMBONZ_DIAL_PAI_HEADER, JAMBONZ_DIAL_PAI_HEADER,
JAMBONES_DIAL_SBC_FOR_REGISTERED_USER} = require('../config'); JAMBONES_DIAL_SBC_FOR_REGISTERED_USER} = require('../config');
const { isOnhold, isOpusFirst } = require('../utils/sdp-utils'); const { isOnhold, isOpusFirst, getLeadingCodec } = require('../utils/sdp-utils');
const { normalizeJambones } = require('@jambonz/verb-specifications'); const { normalizeJambones } = require('@jambonz/verb-specifications');
const { selectHostPort } = require('../utils/network'); const { selectHostPort } = require('../utils/network');
const { sleepFor } = require('../utils/helpers'); const { sleepFor } = require('../utils/helpers');
@@ -158,6 +158,7 @@ class TaskDial extends Task {
get canReleaseMedia() { get canReleaseMedia() {
const keepAnchor = this.data.anchorMedia || const keepAnchor = this.data.anchorMedia ||
this.weAreTranscoding ||
this.cs.isBackGroundListen || this.cs.isBackGroundListen ||
this.cs.onHoldMusic || this.cs.onHoldMusic ||
ANCHOR_MEDIA_ALWAYS || ANCHOR_MEDIA_ALWAYS ||
@@ -929,7 +930,13 @@ class TaskDial extends Task {
this.logger.info({err}, 'Dial:_selectSingleDial - Error boosting audio signal'); this.logger.info({err}, 'Dial:_selectSingleDial - Error boosting audio signal');
} }
} }
/* basic determination to see if call is being transcoded */
const codecA = getLeadingCodec(this.epOther.local.sdp);
const codecB = getLeadingCodec(this.ep.remote.sdp);
this.weAreTranscoding = (codecA !== codecB);
if (this.weAreTranscoding) {
this.logger.info(`Dial:_selectSingleDial - transcoding from ${codecA} (A leg) to ${codecB} (B leg)`);
}
/* if we can release the media back to the SBC, do so now */ /* if we can release the media back to the SBC, do so now */
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);
+18 -1
View File
@@ -55,11 +55,28 @@ const extractSdpMedia = (sdp) => {
} }
}; };
const getLeadingCodec = (sdp) => {
if (!sdp) {
return null;
}
const parsed = sdpTransform.parse(sdp);
const audio = parsed.media?.find((m) => m.type === 'audio');
if (!audio) {
return null;
}
return audio.rtp?.[0]?.codec || null;
};
module.exports = { module.exports = {
isOnhold, isOnhold,
mergeSdpMedia, mergeSdpMedia,
extractSdpMedia, extractSdpMedia,
isOpusFirst, isOpusFirst,
makeOpusFirst, makeOpusFirst,
removeVideoSdp removeVideoSdp,
getLeadingCodec
}; };
+3 -2
View File
@@ -6183,9 +6183,10 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/js-yaml": { "node_modules/js-yaml": {
"version": "3.14.1", "version": "3.14.2",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
"integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"argparse": "^1.0.7", "argparse": "^1.0.7",
"esprima": "^4.0.0" "esprima": "^4.0.0"