This commit is contained in:
Dave Horton
2024-11-19 09:37:53 -05:00
committed by GitHub
parent b976a62a60
commit 916d577b75
4 changed files with 69 additions and 7 deletions

4
app.js
View File

@@ -68,6 +68,8 @@ const {
decrKey,
retrieveSet,
isMemberOfSet,
addKey,
retrieveKey
} = require('@jambonz/realtimedb-helpers')({}, logger);
const activeCallIds = new Map();
@@ -101,6 +103,8 @@ srf.locals = {...srf.locals,
},
realtimeDbHelpers: {
client: redisClient,
addKey,
retrieveKey,
createHash,
retrieveHash,
incrKey,

View File

@@ -1,7 +1,15 @@
const Emitter = require('events');
const sdpTransform = require('sdp-transform');
const SrsClient = require('@jambonz/siprec-client-utils');
const {makeRtpEngineOpts, nudgeCallCounts, isPrivateVoipNetwork, isBlackListedSipGateway} = require('./utils');
const {
makeRtpEngineOpts,
nudgeCallCounts,
isPrivateVoipNetwork,
isBlackListedSipGateway,
makeFullMediaReleaseKey,
makePartnerFullMediaReleaseKey
} = require('./utils');
const { MediaPath } = require('./constants.json');
const {forwardInDialogRequests} = require('drachtio-fn-b2b-sugar');
const {SipError, stringifyUri, parseUri} = require('drachtio-srf');
const debug = require('debug')('jambonz:sbc-outbound');
@@ -111,6 +119,8 @@ class CallSession extends Emitter {
this.writeCdrs = this.srf.locals.writeCdrs;
this.decrKey = req.srf.locals.realtimeDbHelpers.decrKey;
this.addKey = req.srf.locals.realtimeDbHelpers.addKey;
this.retrieveKey = req.srf.locals.realtimeDbHelpers.retrieveKey;
const {
lookupOutboundCarrierForAccount,
@@ -123,7 +133,8 @@ class CallSession extends Emitter {
this.lookupSipGatewaysByCarrier = lookupSipGatewaysByCarrier;
this.lookupCarrierByAccountLcr = lookupCarrierByAccountLcr;
this._mediaReleased = false;
this._mediaPath = MediaPath.FullMedia;
this.recordingNoAnswerTimeout = (process.env.JAMBONES_RECORDING_NO_ANSWER_TIMEOUT || 2) * 1000;
}
@@ -144,7 +155,7 @@ class CallSession extends Emitter {
}
get isMediaReleased() {
return this._mediaReleased;
return this._mediaPath !== MediaPath.FullMedia;
}
subscribeForDTMF(dlg) {
@@ -651,6 +662,15 @@ class CallSession extends Emitter {
answered_at: callStart
};
}
/* save far end SDP for later use if we do a full media release */
if (process.env.JAMBONES_ENABLE_FULL_MEDIA_RELEASE) {
const key = makeFullMediaReleaseKey(this.req.get('X-CID'));
const sdp = uac.remote.sdp;
this.logger.info({key, sdp}, 'saving far end sdp for full media release feature');
this.addKey(key, sdp, 3600).catch((err) => this.logger.error(err, 'Error saving far end sdp'));
}
this.uas = uas;
this.uac = uac;
[uas, uac].forEach((dlg) => {
@@ -807,11 +827,32 @@ Duration=${payload.duration} `
try {
const reason = req.get('X-Reason');
const isReleasingMedia = reason && dlg.type === 'uas' && ['release-media', 'anchor-media'].includes(reason);
const isFullMediaRelease = reason === 'release-media-entirely' && process.env.JAMBONES_ENABLE_FULL_MEDIA_RELEASE;
const fromTag = dlg.type === 'uas' ? this.rtpEngineOpts.uas.tag : this.rtpEngineOpts.uac.tag;
const toTag = dlg.type === 'uas' ? this.rtpEngineOpts.uac.tag : this.rtpEngineOpts.uas.tag;
const offerMedia = dlg.type === 'uas' ? this.rtpEngineOpts.uac.mediaOpts : this.rtpEngineOpts.uas.mediaOpts;
const answerMedia = dlg.type === 'uas' ? this.rtpEngineOpts.uas.mediaOpts : this.rtpEngineOpts.uac.mediaOpts;
const direction = dlg.type === 'uas' ? ['private', 'public'] : ['public', 'private'];
if (isFullMediaRelease) {
const a_sdp = await this.retrieveKey(makePartnerFullMediaReleaseKey(this.req.get('X-CID')));
this.logger.info({a_sdp}, 'reinvite ourselves out of the media path with this reinvite offer');
const answerSdp = await dlg.other.modify(a_sdp);
this.logger.info({answerSdp}, 'far end response to full media release');
res.send(200, {
body: dlg.local.sdp,
headers: {
'Contact': this.contactHeader
}
});
/* no media going through us now we can destroy the rtpengine resource */
this.rtpEngineResource.destroy().catch((err) => {
this.logger.info({err}, 'Error destroying rtpengine resource after full media release');
});
this._mediaPath = MediaPath.NoMedia;
return;
}
if (isReleasingMedia) {
if (!offerMedia.flags.includes('port latching')) offerMedia.flags.push('port latching');
if (!offerMedia.flags.includes('asymmetric')) offerMedia.flags.push('asymmetric');
@@ -839,17 +880,18 @@ Duration=${payload.duration} `
let sdp;
//HL 2024-11-13: previously forwarded re-invites to webrtc clients but further testing has shown to be unnecessary
//if (isReleasingMedia && !this.calleeIsUsingSrtp) {
if (isReleasingMedia) {
//DH 2024-11- 18: if we are going from no-media to either partial or full media, we need reinvite the far end
if (isReleasingMedia && this._mediaPath !== MediaPath.NoMedia) {
this.logger.info(`got a reinvite from FS to ${reason}`);
sdp = dlg.other.remote.sdp;
if (!answerMedia.flags.includes('port latching')) answerMedia.flags.push('port latching');
if (!answerMedia.flags.includes('asymmetric')) answerMedia.flags.push('asymmetric');
answerMedia.flags = answerMedia.flags.filter((f) => f !== 'media handover');
this._mediaReleased = 'release-media' === reason;
this._mediaPath = 'release-media' === reason ? MediaPath.PartialMedia : MediaPath.FullMedia;
}
else {
sdp = await dlg.other.modify(response.sdp);
this.logger.info({sdp}, 'CallSession:_onReinvite: got sdp from 200 OK to invite we sent');
}
opts = {
...this.rtpEngineOpts.common,

7
lib/constants.json Normal file
View File

@@ -0,0 +1,7 @@
{
"MediaPath": {
"NoMedia": "no-media",
"PartialMedia": "partial-media",
"FullMedia": "full-media"
}
}

View File

@@ -233,6 +233,13 @@ async function isBlackListedSipGateway(client, logger, sip_gateway_sid) {
}
}
const makeFullMediaReleaseKey = (callId) => {
return `b_sdp:${callId}`;
};
const makePartnerFullMediaReleaseKey = (callId) => {
return `a_sdp:${callId}`;
};
module.exports = {
makeRtpEngineOpts,
selectHostPort,
@@ -244,5 +251,7 @@ module.exports = {
createHealthCheckApp,
nudgeCallCounts,
isPrivateVoipNetwork,
isBlackListedSipGateway
isBlackListedSipGateway,
makeFullMediaReleaseKey,
makePartnerFullMediaReleaseKey
};