mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-20 16:50:39 +00:00
Feature/siprec server (#143)
* fixes from testing * modify Task#exec to take resources as an object rather than argument list * pass 2 endpoints to Transcribe when invoked in a SipRec call session * logging * change siprec invite to sendrecv just so freeswitch does not try to reinvite (TODO: block outgoing media at rtpengine) * Config: when enabling recording, block until siprec dialog is established * missed play verb in commit 031c79d * linting * bugfix: get final transcript in siprec call
This commit is contained in:
@@ -6,7 +6,8 @@ const {
|
||||
CallStatus,
|
||||
TaskName,
|
||||
KillReason,
|
||||
RecordState
|
||||
RecordState,
|
||||
AllowedSipRecVerbs
|
||||
} = require('../utils/constants');
|
||||
const moment = require('moment');
|
||||
const assert = require('assert');
|
||||
@@ -917,9 +918,7 @@ class CallSession extends Emitter {
|
||||
}
|
||||
|
||||
if (this.isSipRecCallSession) {
|
||||
const pruned = tasks.filter((t) =>
|
||||
[TaskName.Config, TaskName.Listen, TaskName.Transcribe].includes(t.name)
|
||||
);
|
||||
const pruned = tasks.filter((t) => AllowedSipRecVerbs.includes(t.name));
|
||||
if (0 === pruned.length) {
|
||||
this.logger.info({tasks},
|
||||
'CallSession:replaceApplication - only config, transcribe and/or listen allowed on an incoming siprec call');
|
||||
@@ -1102,11 +1101,20 @@ class CallSession extends Emitter {
|
||||
if (this.callGone) new Error(`${BADPRECONDITIONS}: call gone`);
|
||||
|
||||
if (this.ep) {
|
||||
if (task.earlyMedia === true || this.dlg) return this.ep;
|
||||
const resources = {ep: this.ep};
|
||||
if (task.earlyMedia === true || this.dlg) {
|
||||
return {
|
||||
...resources,
|
||||
...(this.isSipRecCallSession && {ep2: this.ep2})
|
||||
};
|
||||
}
|
||||
|
||||
// we are going from an early media connection to answer
|
||||
await this.propagateAnswer();
|
||||
return this.ep;
|
||||
return {
|
||||
...resources,
|
||||
...(this.isSipRecCallSession && {ep2: this.ep2})
|
||||
};
|
||||
}
|
||||
|
||||
// need to allocate an endpoint
|
||||
@@ -1129,7 +1137,7 @@ class CallSession extends Emitter {
|
||||
if (this.direction === CallDirection.Inbound) {
|
||||
if (task.earlyMedia && !this.req.finalResponseSent) {
|
||||
this.res.send(183, {body: ep.local.sdp});
|
||||
return ep;
|
||||
return {ep};
|
||||
}
|
||||
this.logger.debug('propogating answer');
|
||||
await this.propagateAnswer();
|
||||
@@ -1138,7 +1146,7 @@ class CallSession extends Emitter {
|
||||
// outbound call TODO
|
||||
}
|
||||
|
||||
return ep;
|
||||
return {ep};
|
||||
} catch (err) {
|
||||
if (err === CALLER_CANCELLED_ERR_MSG) {
|
||||
this.logger.error(err, 'caller canceled quickly before we could respond, ending call');
|
||||
@@ -1163,7 +1171,7 @@ class CallSession extends Emitter {
|
||||
_evalStableCallPrecondition(task) {
|
||||
if (this.callGone) throw new Error(`${BADPRECONDITIONS}: call gone`);
|
||||
if (!this.dlg) throw new Error(`${BADPRECONDITIONS}: call was not answered`);
|
||||
return this.dlg;
|
||||
return {dlg: this.dlg};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1202,7 +1210,7 @@ class CallSession extends Emitter {
|
||||
* Hang up the call and free the media endpoint
|
||||
*/
|
||||
_clearResources() {
|
||||
for (const resource of [this.dlg, this.ep]) {
|
||||
for (const resource of [this.dlg, this.ep, this.ep2]) {
|
||||
if (resource && resource.connected) resource.destroy();
|
||||
}
|
||||
this.dlg = null;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const InboundCallSession = require('./inbound-call-session');
|
||||
const {createSipRecPayload} = require('../utils/siprec-utils');
|
||||
const {CallStatus} = require('../utils/constants');
|
||||
/**
|
||||
* @classdesc Subclass of InboundCallSession. This represents a CallSession that is
|
||||
* established for an inbound SIPREC call.
|
||||
@@ -13,44 +14,45 @@ class SipRecCallSession extends InboundCallSession {
|
||||
this.sdp1 = sdp1;
|
||||
this.sdp2 = sdp2;
|
||||
this.metadata = metadata;
|
||||
|
||||
setImmediate(this._answerSipRecCall.bind(this));
|
||||
}
|
||||
|
||||
async _answerSipRecCall() {
|
||||
async answerSipRecCall() {
|
||||
try {
|
||||
this.ms = this.getMS();
|
||||
this.ep = await this.ms.createEndpoint({remoteSdp: this.sdp1});
|
||||
this.ep2 = await this.ms.createEndpoint({remoteSdp: this.sdp2});
|
||||
let remoteSdp = this.sdp1.replace(/sendonly/, 'sendrecv');
|
||||
this.ep = await this.ms.createEndpoint({remoteSdp});
|
||||
//this.logger.debug({remoteSdp, localSdp: this.ep.local.sdp}, 'SipRecCallSession - allocated first endpoint');
|
||||
remoteSdp = this.sdp2.replace(/sendonly/, 'sendrecv');
|
||||
this.ep2 = await this.ms.createEndpoint({remoteSdp});
|
||||
//this.logger.debug({remoteSdp, localSdp: this.ep2.local.sdp}, 'SipRecCallSession - allocated second endpoint');
|
||||
await this.ep.bridge(this.ep2);
|
||||
const combinedSdp = await createSipRecPayload(this.ep.local.sdp, this.ep2.local.sdp, this.logger);
|
||||
/*
|
||||
this.logger.debug({
|
||||
sdp1: this.sdp1,
|
||||
sdp2: this.sdp2,
|
||||
combinedSdp
|
||||
}, 'SipRecCallSession:_answerSipRecCall - created SIPREC payload');
|
||||
*/
|
||||
this.dlg = await this.srf.createUAS(this.req, this.res, {
|
||||
headers: {
|
||||
'Content-Type': 'application/sdp',
|
||||
'X-Trace-ID': this.req.locals.traceId,
|
||||
'X-Call-Sid': this.req.locals.callSid
|
||||
},
|
||||
localSdp: combinedSdp
|
||||
});
|
||||
this.dlg.on('destroy', this._callerHungup.bind(this));
|
||||
this.wrapDialog(this.dlg);
|
||||
this.dlg.callSid = this.callSid;
|
||||
this.emit('callStatusChange', {sipStatus: 200, sipReason: 'OK', callStatus: CallStatus.InProgress});
|
||||
|
||||
this.dlg.on('modify', this._onReinvite.bind(this));
|
||||
this.dlg.on('refer', this._onRefer.bind(this));
|
||||
} catch (err) {
|
||||
this.logger.error({err}, 'SipRecCallSession:_answerSipRecCall error:');
|
||||
if (this.res && !this.res.finalResponseSent) this.res.send(500);
|
||||
this._callReleased();
|
||||
}
|
||||
}
|
||||
|
||||
_callReleased() {
|
||||
/* release that second endpoint we created, then call superclass implementation */
|
||||
if (this.ep2?.connected) {
|
||||
this.ep2.destroy();
|
||||
this.ep2 = null;
|
||||
}
|
||||
super._callReleased();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SipRecCallSession;
|
||||
|
||||
Reference in New Issue
Block a user