ringcentral-softphone 1.3.4 → 1.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/call-session/inbound.cjs +41 -83
- package/dist/call-session/inbound.d.cts +2 -0
- package/dist/call-session/inbound.d.mts +2 -0
- package/dist/call-session/inbound.mjs +51 -0
- package/dist/call-session/index.cjs +212 -273
- package/dist/call-session/index.d.cts +2 -0
- package/dist/call-session/index.d.mts +2 -0
- package/dist/call-session/index.mjs +209 -0
- package/dist/call-session/outbound.cjs +56 -99
- package/dist/call-session/outbound.d.cts +2 -0
- package/dist/call-session/outbound.d.mts +2 -0
- package/dist/call-session/outbound.mjs +57 -0
- package/dist/call-session/streamer.cjs +66 -111
- package/dist/call-session/streamer.d.cts +2 -0
- package/dist/call-session/streamer.d.mts +2 -0
- package/dist/call-session/streamer.mjs +65 -0
- package/dist/chunk-CKQMccvm.cjs +28 -0
- package/dist/codec-Bh7v8J-S.d.mts +18 -0
- package/dist/codec-C-VrtVkq.d.cts +18 -0
- package/dist/codec.cjs +68 -84
- package/dist/codec.d.cts +2 -0
- package/dist/codec.d.mts +2 -0
- package/dist/codec.mjs +68 -0
- package/dist/dtmf-B13Fz2VR.d.mts +10 -0
- package/dist/dtmf-DcQ-5vSG.d.cts +10 -0
- package/dist/dtmf.cjs +37 -60
- package/dist/dtmf.d.cts +2 -0
- package/dist/dtmf.d.mts +2 -0
- package/dist/dtmf.mjs +42 -0
- package/dist/inbound--wGoGqLS.d.mts +103 -0
- package/dist/inbound-DquvTSj1.d.cts +103 -0
- package/dist/index--UjWgLK-.d.mts +8 -0
- package/dist/index-BhN2W8AV.d.mts +8 -0
- package/dist/index-CEgs-Dz2.d.cts +1 -0
- package/dist/index-Cf2Cev52.d.cts +8 -0
- package/dist/index-XMDop59x.d.cts +8 -0
- package/dist/index-q_LXL61M.d.mts +1 -0
- package/dist/index.cjs +166 -250
- package/dist/index.d.cts +2 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +172 -0
- package/dist/request-B_auLSJn.d.cts +10 -0
- package/dist/request-pBe7_mYv.d.mts +10 -0
- package/dist/response-LRRpY8lX.d.mts +9 -0
- package/dist/response-ReKvb5x9.d.cts +9 -0
- package/dist/sip-message/inbound/index.cjs +16 -50
- package/dist/sip-message/inbound/index.d.cts +2 -0
- package/dist/sip-message/inbound/index.d.mts +2 -0
- package/dist/sip-message/inbound/index.mjs +17 -0
- package/dist/sip-message/index.cjs +11 -49
- package/dist/sip-message/index.d.cts +6 -0
- package/dist/sip-message/index.d.mts +6 -0
- package/dist/sip-message/index.mjs +6 -0
- package/dist/sip-message/outbound/index.cjs +10 -40
- package/dist/sip-message/outbound/index.d.cts +2 -0
- package/dist/sip-message/outbound/index.d.mts +2 -0
- package/dist/sip-message/outbound/index.mjs +11 -0
- package/dist/sip-message/outbound/request.cjs +20 -61
- package/dist/sip-message/outbound/request.d.cts +2 -0
- package/dist/sip-message/outbound/request.d.mts +2 -0
- package/dist/sip-message/outbound/request.mjs +21 -0
- package/dist/sip-message/outbound/response.cjs +25 -54
- package/dist/sip-message/outbound/response.d.cts +2 -0
- package/dist/sip-message/outbound/response.d.mts +2 -0
- package/dist/sip-message/outbound/response.mjs +26 -0
- package/dist/sip-message/response-codes.cjs +80 -100
- package/dist/sip-message/response-codes.d.cts +5 -0
- package/dist/sip-message/response-codes.d.mts +6 -0
- package/dist/sip-message/response-codes.mjs +82 -0
- package/dist/sip-message/sip-message.cjs +25 -52
- package/dist/sip-message/sip-message.d.cts +2 -0
- package/dist/sip-message/sip-message.d.mts +2 -0
- package/dist/sip-message/sip-message.mjs +26 -0
- package/dist/sip-message-B2D5MPBI.d.cts +13 -0
- package/dist/sip-message-PaPho4qU.d.mts +13 -0
- package/dist/types-DOQ9wmX6.d.mts +12 -0
- package/dist/types-DZxCsbZE.d.cts +12 -0
- package/dist/types.cjs +0 -15
- package/dist/types.d.cts +2 -0
- package/dist/types.d.mts +2 -0
- package/dist/types.mjs +1 -0
- package/dist/utils.cjs +27 -73
- package/dist/utils.d.cts +12 -0
- package/dist/utils.d.mts +12 -0
- package/dist/utils.mjs +25 -0
- package/package.json +14 -14
- package/dist/call-session/inbound.d.ts +0 -8
- package/dist/call-session/inbound.js +0 -64
- package/dist/call-session/index.d.ts +0 -46
- package/dist/call-session/index.js +0 -248
- package/dist/call-session/outbound.d.ts +0 -11
- package/dist/call-session/outbound.js +0 -71
- package/dist/call-session/streamer.d.ts +0 -17
- package/dist/call-session/streamer.js +0 -83
- package/dist/codec.d.ts +0 -15
- package/dist/codec.js +0 -66
- package/dist/dtmf.d.ts +0 -7
- package/dist/dtmf.js +0 -47
- package/dist/index.d.ts +0 -28
- package/dist/index.js +0 -242
- package/dist/sip-message/inbound/index.d.ts +0 -5
- package/dist/sip-message/inbound/index.js +0 -22
- package/dist/sip-message/index.d.ts +0 -5
- package/dist/sip-message/index.js +0 -12
- package/dist/sip-message/outbound/index.d.ts +0 -5
- package/dist/sip-message/outbound/index.js +0 -12
- package/dist/sip-message/outbound/request.d.ts +0 -7
- package/dist/sip-message/outbound/request.js +0 -33
- package/dist/sip-message/outbound/response.d.ts +0 -6
- package/dist/sip-message/outbound/response.js +0 -26
- package/dist/sip-message/response-codes.d.ts +0 -4
- package/dist/sip-message/response-codes.js +0 -83
- package/dist/sip-message/sip-message.d.ts +0 -11
- package/dist/sip-message/sip-message.js +0 -34
- package/dist/types.d.ts +0 -9
- package/dist/types.js +0 -0
- package/dist/utils.d.ts +0 -8
- package/dist/utils.js +0 -41
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import DTMF from "../dtmf.mjs";
|
|
2
|
+
import { branch, extractAddress, localKey, randomInt } from "../utils.mjs";
|
|
3
|
+
import RequestMessage from "../sip-message/outbound/request.mjs";
|
|
4
|
+
import ResponseMessage from "../sip-message/outbound/response.mjs";
|
|
5
|
+
import "../sip-message/index.mjs";
|
|
6
|
+
import Streamer from "./streamer.mjs";
|
|
7
|
+
import { Buffer } from "node:buffer";
|
|
8
|
+
import EventEmitter from "node:events";
|
|
9
|
+
import waitFor from "wait-for-async";
|
|
10
|
+
import dgram from "node:dgram";
|
|
11
|
+
import { RtpHeader, RtpPacket, SrtpSession } from "werift-rtp";
|
|
12
|
+
//#region src/call-session/index.ts
|
|
13
|
+
const isDtmfChar = (value) => DTMF.phoneChars.includes(value);
|
|
14
|
+
var CallSession = class extends EventEmitter {
|
|
15
|
+
softphone;
|
|
16
|
+
sipMessage;
|
|
17
|
+
socket;
|
|
18
|
+
localPeer;
|
|
19
|
+
remotePeer;
|
|
20
|
+
remoteIP;
|
|
21
|
+
remotePort;
|
|
22
|
+
disposed = false;
|
|
23
|
+
srtpSession;
|
|
24
|
+
encoder;
|
|
25
|
+
decoder;
|
|
26
|
+
sdp;
|
|
27
|
+
ssrc = randomInt();
|
|
28
|
+
sequenceNumber = randomInt();
|
|
29
|
+
timestamp = randomInt();
|
|
30
|
+
constructor(softphone, sipMessage) {
|
|
31
|
+
super();
|
|
32
|
+
this.softphone = softphone;
|
|
33
|
+
this.encoder = softphone.codec.createEncoder();
|
|
34
|
+
this.decoder = softphone.codec.createDecoder();
|
|
35
|
+
this.sipMessage = sipMessage;
|
|
36
|
+
if (this.sipMessage.body.length > 0) {
|
|
37
|
+
this.remoteIP = this.sipMessage.body.match(/c=IN IP4 ([\d.]+)/)[1];
|
|
38
|
+
this.remotePort = parseInt(this.sipMessage.body.match(/m=audio (\d+) /)[1], 10);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
set remoteKey(key) {
|
|
42
|
+
const localKeyBuffer = Buffer.from(localKey, "base64");
|
|
43
|
+
const remoteKeyBuffer = Buffer.from(key, "base64");
|
|
44
|
+
this.srtpSession = new SrtpSession({
|
|
45
|
+
profile: 1,
|
|
46
|
+
keys: {
|
|
47
|
+
localMasterKey: localKeyBuffer.subarray(0, 16),
|
|
48
|
+
localMasterSalt: localKeyBuffer.subarray(16, 30),
|
|
49
|
+
remoteMasterKey: remoteKeyBuffer.subarray(0, 16),
|
|
50
|
+
remoteMasterSalt: remoteKeyBuffer.subarray(16, 30)
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
get callId() {
|
|
55
|
+
return this.sipMessage.getHeader("Call-ID");
|
|
56
|
+
}
|
|
57
|
+
send(data) {
|
|
58
|
+
this.socket.send(data, this.remotePort, this.remoteIP);
|
|
59
|
+
}
|
|
60
|
+
async hangup() {
|
|
61
|
+
const requestMessage = new RequestMessage(`BYE sip:${this.softphone.sipInfo.domain} SIP/2.0`, {
|
|
62
|
+
"Call-ID": this.callId,
|
|
63
|
+
From: this.localPeer,
|
|
64
|
+
To: this.remotePeer,
|
|
65
|
+
Via: `SIP/2.0/TLS ${this.softphone.fakeDomain};branch=${branch()}`
|
|
66
|
+
});
|
|
67
|
+
await this.softphone.send(requestMessage);
|
|
68
|
+
}
|
|
69
|
+
sendDTMF(char) {
|
|
70
|
+
const payloads = DTMF.charToPayloads(char);
|
|
71
|
+
const timestamp = this.timestamp;
|
|
72
|
+
let first = true;
|
|
73
|
+
for (const payload of payloads) {
|
|
74
|
+
const rtpPacket = new RtpPacket(new RtpHeader({
|
|
75
|
+
version: 2,
|
|
76
|
+
padding: false,
|
|
77
|
+
paddingSize: 0,
|
|
78
|
+
extension: false,
|
|
79
|
+
marker: first,
|
|
80
|
+
payloadOffset: 12,
|
|
81
|
+
payloadType: 101,
|
|
82
|
+
sequenceNumber: this.sequenceNumber,
|
|
83
|
+
timestamp,
|
|
84
|
+
ssrc: this.ssrc,
|
|
85
|
+
csrcLength: 0,
|
|
86
|
+
csrc: [],
|
|
87
|
+
extensionProfile: 48862,
|
|
88
|
+
extensionLength: void 0,
|
|
89
|
+
extensions: []
|
|
90
|
+
}), payload);
|
|
91
|
+
this.send(this.srtpSession.encrypt(rtpPacket.payload, rtpPacket.header));
|
|
92
|
+
this.sequenceNumber = (this.sequenceNumber + 1) % 65536;
|
|
93
|
+
first = false;
|
|
94
|
+
}
|
|
95
|
+
this.timestamp += 800;
|
|
96
|
+
}
|
|
97
|
+
async sendDTMFs(s, delay = 500) {
|
|
98
|
+
for (const c of s) {
|
|
99
|
+
if (!isDtmfChar(c)) throw new Error(`invalid phone char: ${c}`);
|
|
100
|
+
this.sendDTMF(c);
|
|
101
|
+
await waitFor({ interval: delay });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
streamAudio(input) {
|
|
105
|
+
const streamer = new Streamer(this, input);
|
|
106
|
+
streamer.start();
|
|
107
|
+
return streamer;
|
|
108
|
+
}
|
|
109
|
+
sendPacket(rtpPacket) {
|
|
110
|
+
if (this.disposed) return;
|
|
111
|
+
this.send(this.srtpSession.encrypt(rtpPacket.payload, rtpPacket.header));
|
|
112
|
+
}
|
|
113
|
+
startLocalServices() {
|
|
114
|
+
this.socket = dgram.createSocket("udp4");
|
|
115
|
+
this.socket.on("message", (message) => {
|
|
116
|
+
const rtpPacket = RtpPacket.deSerialize(this.srtpSession.decrypt(message));
|
|
117
|
+
this.emit("rtpPacket", rtpPacket);
|
|
118
|
+
if (rtpPacket.header.payloadType === 101) {
|
|
119
|
+
this.emit("dtmfPacket", rtpPacket);
|
|
120
|
+
const char = DTMF.payloadToChar(rtpPacket.payload);
|
|
121
|
+
if (char) this.emit("dtmf", char);
|
|
122
|
+
} else if (rtpPacket.header.payloadType === this.softphone.codec.id) {
|
|
123
|
+
if (rtpPacket.payload.length === 4 && rtpPacket.payload[0] >= 0 && rtpPacket.payload[0] < 12 && rtpPacket.payload[1] === 138 && rtpPacket.payload[2] === 3 && rtpPacket.payload[3] === 192) return;
|
|
124
|
+
try {
|
|
125
|
+
rtpPacket.payload = this.decoder.decode(rtpPacket.payload);
|
|
126
|
+
this.emit("audioPacket", rtpPacket);
|
|
127
|
+
} catch {
|
|
128
|
+
console.error("Audio packet decode failed", rtpPacket);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
this.socket.bind();
|
|
133
|
+
this.send("hello");
|
|
134
|
+
const byeHandler = (inboundMessage) => {
|
|
135
|
+
if (inboundMessage.getHeader("Call-ID") !== this.callId) return;
|
|
136
|
+
if (inboundMessage.headers.CSeq.endsWith(" BYE")) {
|
|
137
|
+
this.softphone.off("message", byeHandler);
|
|
138
|
+
this.dispose();
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
this.softphone.on("message", byeHandler);
|
|
142
|
+
}
|
|
143
|
+
dispose() {
|
|
144
|
+
this.disposed = true;
|
|
145
|
+
this.emit("disposed");
|
|
146
|
+
this.removeAllListeners();
|
|
147
|
+
this.socket?.removeAllListeners();
|
|
148
|
+
this.socket?.close();
|
|
149
|
+
}
|
|
150
|
+
async transfer(transferTo) {
|
|
151
|
+
const requestMessage = new RequestMessage(`REFER sip:${this.softphone.sipInfo.username}@${this.softphone.sipInfo.outboundProxy};transport=tls SIP/2.0`, {
|
|
152
|
+
Via: `SIP/2.0/TLS ${this.softphone.client.localAddress}:${this.softphone.client.localPort};rport;branch=${branch()};alias`,
|
|
153
|
+
"Max-Forwards": 70,
|
|
154
|
+
From: this.localPeer,
|
|
155
|
+
To: this.remotePeer,
|
|
156
|
+
Contact: `<sip:${this.softphone.sipInfo.username}@${this.softphone.client.localAddress}:${this.softphone.client.localPort};transport=TLS;ob>`,
|
|
157
|
+
"Call-ID": this.callId,
|
|
158
|
+
Event: "refer",
|
|
159
|
+
Expires: 600,
|
|
160
|
+
Supported: "replaces, 100rel, timer, norefersub",
|
|
161
|
+
Accept: "message/sipfrag;version=2.0",
|
|
162
|
+
"Allow-Events": "presence, message-summary, refer",
|
|
163
|
+
"Refer-To": `sip:${transferTo}@${this.softphone.sipInfo.domain}`,
|
|
164
|
+
"Referred-By": `<sip:${this.softphone.sipInfo.username}@${this.softphone.sipInfo.domain}>`
|
|
165
|
+
});
|
|
166
|
+
await this.softphone.send(requestMessage);
|
|
167
|
+
return new Promise((resolve) => {
|
|
168
|
+
const notifyHandler = (inboundMessage) => {
|
|
169
|
+
if (!inboundMessage.subject.startsWith("NOTIFY ")) return;
|
|
170
|
+
const responseMessage = new ResponseMessage(inboundMessage, 200);
|
|
171
|
+
this.softphone.send(responseMessage);
|
|
172
|
+
if (inboundMessage.body.trim() === "SIP/2.0 200 OK") {
|
|
173
|
+
this.softphone.off("message", notifyHandler);
|
|
174
|
+
resolve();
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
this.softphone.on("message", notifyHandler);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
async toggleReceive(toReceive) {
|
|
181
|
+
let newSDP = this.sdp;
|
|
182
|
+
if (!toReceive) newSDP = newSDP.replace(/a=sendrecv/, "a=sendonly");
|
|
183
|
+
const requestMessage = new RequestMessage(`INVITE ${extractAddress(this.remotePeer)} SIP/2.0`, {
|
|
184
|
+
"Call-Id": this.callId,
|
|
185
|
+
From: this.localPeer,
|
|
186
|
+
To: this.remotePeer,
|
|
187
|
+
Via: `SIP/2.0/TLS ${this.softphone.client.localAddress}:${this.softphone.client.localPort};rport;branch=${branch()};alias`,
|
|
188
|
+
"Content-Type": "application/sdp",
|
|
189
|
+
Contact: ` <sip:${this.softphone.sipInfo.username}@${this.softphone.client.localAddress}:${this.softphone.client.localPort};transport=TLS;ob>`
|
|
190
|
+
}, newSDP);
|
|
191
|
+
const replyMessage = await this.softphone.send(requestMessage, true);
|
|
192
|
+
const ackMessage = new RequestMessage(`ACK ${extractAddress(this.remotePeer)} SIP/2.0`, {
|
|
193
|
+
"Call-Id": this.callId,
|
|
194
|
+
From: this.localPeer,
|
|
195
|
+
To: this.remotePeer,
|
|
196
|
+
Via: replyMessage.headers.Via,
|
|
197
|
+
CSeq: replyMessage.headers.CSeq.replace(" INVITE", " ACK")
|
|
198
|
+
});
|
|
199
|
+
await this.softphone.send(ackMessage);
|
|
200
|
+
}
|
|
201
|
+
async hold() {
|
|
202
|
+
return this.toggleReceive(false);
|
|
203
|
+
}
|
|
204
|
+
async unhold() {
|
|
205
|
+
return this.toggleReceive(true);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
//#endregion
|
|
209
|
+
export { CallSession as default };
|
|
@@ -1,100 +1,57 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
const require_utils = require("../utils.cjs");
|
|
2
|
+
const require_sip_message_outbound_request = require("../sip-message/outbound/request.cjs");
|
|
3
|
+
require("../sip-message/index.cjs");
|
|
4
|
+
const require_call_session_index = require("./index.cjs");
|
|
5
|
+
//#region src/call-session/outbound.ts
|
|
6
|
+
var OutboundCallSession = class extends require_call_session_index {
|
|
7
|
+
constructor(softphone, answerMessage) {
|
|
8
|
+
super(softphone, answerMessage);
|
|
9
|
+
this.localPeer = answerMessage.headers.From;
|
|
10
|
+
this.remotePeer = answerMessage.headers.To;
|
|
11
|
+
this.remoteKey = answerMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
|
|
12
|
+
this.init();
|
|
13
|
+
}
|
|
14
|
+
init() {
|
|
15
|
+
const answerHandler = (message) => {
|
|
16
|
+
if (message.headers.CSeq !== this.sipMessage.headers.CSeq) return;
|
|
17
|
+
if (message.subject.startsWith("SIP/2.0 486")) {
|
|
18
|
+
this.softphone.off("message", answerHandler);
|
|
19
|
+
this.emit("busy");
|
|
20
|
+
this.dispose();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (message.subject.startsWith("SIP/2.0 200")) {
|
|
24
|
+
this.softphone.off("message", answerHandler);
|
|
25
|
+
this.emit("answered");
|
|
26
|
+
const ackMessage = new require_sip_message_outbound_request(`ACK ${require_utils.extractAddress(this.remotePeer)} SIP/2.0`, {
|
|
27
|
+
"Call-ID": this.callId,
|
|
28
|
+
From: this.localPeer,
|
|
29
|
+
To: this.remotePeer,
|
|
30
|
+
Via: this.sipMessage.headers.Via,
|
|
31
|
+
CSeq: this.sipMessage.headers.CSeq.replace(" INVITE", " ACK")
|
|
32
|
+
});
|
|
33
|
+
this.softphone.send(ackMessage);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
this.softphone.on("message", answerHandler);
|
|
37
|
+
this.once("answered", () => this.startLocalServices());
|
|
38
|
+
}
|
|
39
|
+
async cancel() {
|
|
40
|
+
const requestMessage = new require_sip_message_outbound_request(`CANCEL ${require_utils.extractAddress(this.remotePeer)} SIP/2.0`, {
|
|
41
|
+
"Call-ID": this.callId,
|
|
42
|
+
From: this.localPeer,
|
|
43
|
+
To: require_utils.withoutTag(this.remotePeer),
|
|
44
|
+
Via: this.sipMessage.headers.Via,
|
|
45
|
+
CSeq: this.sipMessage.headers.CSeq.replace(" INVITE", " CANCEL")
|
|
46
|
+
});
|
|
47
|
+
await this.softphone.send(requestMessage);
|
|
48
|
+
}
|
|
49
|
+
get sessionId() {
|
|
50
|
+
return this.sipMessage.headers["p-rc-api-ids"].match(/party-id=([^;]+);session-id=([^;]+)/)[2];
|
|
51
|
+
}
|
|
52
|
+
get partyId() {
|
|
53
|
+
return this.sipMessage.headers["p-rc-api-ids"].match(/party-id=([^;]+);session-id=([^;]+)/)[1];
|
|
54
|
+
}
|
|
10
55
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
for (let key of __getOwnPropNames(from))
|
|
14
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
-
}
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
-
mod
|
|
26
|
-
));
|
|
27
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
-
var outbound_exports = {};
|
|
29
|
-
__export(outbound_exports, {
|
|
30
|
-
default: () => outbound_default
|
|
31
|
-
});
|
|
32
|
-
module.exports = __toCommonJS(outbound_exports);
|
|
33
|
-
var import_sip_message = require("../sip-message/index.js");
|
|
34
|
-
var import_utils = require("../utils.js");
|
|
35
|
-
var import_index = __toESM(require("./index.js"), 1);
|
|
36
|
-
class OutboundCallSession extends import_index.default {
|
|
37
|
-
constructor(softphone, answerMessage) {
|
|
38
|
-
super(softphone, answerMessage);
|
|
39
|
-
this.localPeer = answerMessage.headers.From;
|
|
40
|
-
this.remotePeer = answerMessage.headers.To;
|
|
41
|
-
this.remoteKey = answerMessage.body.match(
|
|
42
|
-
/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/
|
|
43
|
-
)[1];
|
|
44
|
-
this.init();
|
|
45
|
-
}
|
|
46
|
-
init() {
|
|
47
|
-
const answerHandler = (message) => {
|
|
48
|
-
if (message.headers.CSeq !== this.sipMessage.headers.CSeq) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
if (message.subject.startsWith("SIP/2.0 486")) {
|
|
52
|
-
this.softphone.off("message", answerHandler);
|
|
53
|
-
this.emit("busy");
|
|
54
|
-
this.dispose();
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
if (message.subject.startsWith("SIP/2.0 200")) {
|
|
58
|
-
this.softphone.off("message", answerHandler);
|
|
59
|
-
this.emit("answered");
|
|
60
|
-
const ackMessage = new import_sip_message.RequestMessage(
|
|
61
|
-
`ACK ${(0, import_utils.extractAddress)(this.remotePeer)} SIP/2.0`,
|
|
62
|
-
{
|
|
63
|
-
"Call-ID": this.callId,
|
|
64
|
-
From: this.localPeer,
|
|
65
|
-
To: this.remotePeer,
|
|
66
|
-
Via: this.sipMessage.headers.Via,
|
|
67
|
-
CSeq: this.sipMessage.headers.CSeq.replace(" INVITE", " ACK")
|
|
68
|
-
}
|
|
69
|
-
);
|
|
70
|
-
this.softphone.send(ackMessage);
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
this.softphone.on("message", answerHandler);
|
|
74
|
-
this.once("answered", () => this.startLocalServices());
|
|
75
|
-
}
|
|
76
|
-
async cancel() {
|
|
77
|
-
const requestMessage = new import_sip_message.RequestMessage(
|
|
78
|
-
`CANCEL ${(0, import_utils.extractAddress)(this.remotePeer)} SIP/2.0`,
|
|
79
|
-
{
|
|
80
|
-
"Call-ID": this.callId,
|
|
81
|
-
From: this.localPeer,
|
|
82
|
-
To: (0, import_utils.withoutTag)(this.remotePeer),
|
|
83
|
-
Via: this.sipMessage.headers.Via,
|
|
84
|
-
CSeq: this.sipMessage.headers.CSeq.replace(" INVITE", " CANCEL")
|
|
85
|
-
}
|
|
86
|
-
);
|
|
87
|
-
await this.softphone.send(requestMessage);
|
|
88
|
-
}
|
|
89
|
-
get sessionId() {
|
|
90
|
-
const header = this.sipMessage.headers["p-rc-api-ids"];
|
|
91
|
-
const match = header.match(/party-id=([^;]+);session-id=([^;]+)/);
|
|
92
|
-
return match[2];
|
|
93
|
-
}
|
|
94
|
-
get partyId() {
|
|
95
|
-
const header = this.sipMessage.headers["p-rc-api-ids"];
|
|
96
|
-
const match = header.match(/party-id=([^;]+);session-id=([^;]+)/);
|
|
97
|
-
return match[1];
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
var outbound_default = OutboundCallSession;
|
|
56
|
+
//#endregion
|
|
57
|
+
module.exports = OutboundCallSession;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { extractAddress, withoutTag } from "../utils.mjs";
|
|
2
|
+
import RequestMessage from "../sip-message/outbound/request.mjs";
|
|
3
|
+
import "../sip-message/index.mjs";
|
|
4
|
+
import CallSession from "./index.mjs";
|
|
5
|
+
//#region src/call-session/outbound.ts
|
|
6
|
+
var OutboundCallSession = class extends CallSession {
|
|
7
|
+
constructor(softphone, answerMessage) {
|
|
8
|
+
super(softphone, answerMessage);
|
|
9
|
+
this.localPeer = answerMessage.headers.From;
|
|
10
|
+
this.remotePeer = answerMessage.headers.To;
|
|
11
|
+
this.remoteKey = answerMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
|
|
12
|
+
this.init();
|
|
13
|
+
}
|
|
14
|
+
init() {
|
|
15
|
+
const answerHandler = (message) => {
|
|
16
|
+
if (message.headers.CSeq !== this.sipMessage.headers.CSeq) return;
|
|
17
|
+
if (message.subject.startsWith("SIP/2.0 486")) {
|
|
18
|
+
this.softphone.off("message", answerHandler);
|
|
19
|
+
this.emit("busy");
|
|
20
|
+
this.dispose();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (message.subject.startsWith("SIP/2.0 200")) {
|
|
24
|
+
this.softphone.off("message", answerHandler);
|
|
25
|
+
this.emit("answered");
|
|
26
|
+
const ackMessage = new RequestMessage(`ACK ${extractAddress(this.remotePeer)} SIP/2.0`, {
|
|
27
|
+
"Call-ID": this.callId,
|
|
28
|
+
From: this.localPeer,
|
|
29
|
+
To: this.remotePeer,
|
|
30
|
+
Via: this.sipMessage.headers.Via,
|
|
31
|
+
CSeq: this.sipMessage.headers.CSeq.replace(" INVITE", " ACK")
|
|
32
|
+
});
|
|
33
|
+
this.softphone.send(ackMessage);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
this.softphone.on("message", answerHandler);
|
|
37
|
+
this.once("answered", () => this.startLocalServices());
|
|
38
|
+
}
|
|
39
|
+
async cancel() {
|
|
40
|
+
const requestMessage = new RequestMessage(`CANCEL ${extractAddress(this.remotePeer)} SIP/2.0`, {
|
|
41
|
+
"Call-ID": this.callId,
|
|
42
|
+
From: this.localPeer,
|
|
43
|
+
To: withoutTag(this.remotePeer),
|
|
44
|
+
Via: this.sipMessage.headers.Via,
|
|
45
|
+
CSeq: this.sipMessage.headers.CSeq.replace(" INVITE", " CANCEL")
|
|
46
|
+
});
|
|
47
|
+
await this.softphone.send(requestMessage);
|
|
48
|
+
}
|
|
49
|
+
get sessionId() {
|
|
50
|
+
return this.sipMessage.headers["p-rc-api-ids"].match(/party-id=([^;]+);session-id=([^;]+)/)[2];
|
|
51
|
+
}
|
|
52
|
+
get partyId() {
|
|
53
|
+
return this.sipMessage.headers["p-rc-api-ids"].match(/party-id=([^;]+);session-id=([^;]+)/)[1];
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
//#endregion
|
|
57
|
+
export { OutboundCallSession as default };
|
|
@@ -1,112 +1,67 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
var
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
const require_chunk = require("../chunk-CKQMccvm.cjs");
|
|
2
|
+
let node_buffer = require("node:buffer");
|
|
3
|
+
let node_events = require("node:events");
|
|
4
|
+
node_events = require_chunk.__toESM(node_events, 1);
|
|
5
|
+
let werift_rtp = require("werift-rtp");
|
|
6
|
+
//#region src/call-session/streamer.ts
|
|
7
|
+
var Streamer = class extends node_events.default {
|
|
8
|
+
paused = false;
|
|
9
|
+
callSession;
|
|
10
|
+
buffer;
|
|
11
|
+
originalBuffer;
|
|
12
|
+
constructor(callSession, buffer) {
|
|
13
|
+
super();
|
|
14
|
+
this.callSession = callSession;
|
|
15
|
+
this.buffer = buffer;
|
|
16
|
+
this.originalBuffer = buffer;
|
|
17
|
+
}
|
|
18
|
+
start() {
|
|
19
|
+
this.buffer = this.originalBuffer;
|
|
20
|
+
this.paused = false;
|
|
21
|
+
this.sendPacket();
|
|
22
|
+
}
|
|
23
|
+
stop() {
|
|
24
|
+
this.buffer = node_buffer.Buffer.alloc(0);
|
|
25
|
+
}
|
|
26
|
+
pause() {
|
|
27
|
+
this.paused = true;
|
|
28
|
+
}
|
|
29
|
+
resume() {
|
|
30
|
+
this.paused = false;
|
|
31
|
+
this.sendPacket();
|
|
32
|
+
}
|
|
33
|
+
get finished() {
|
|
34
|
+
return this.callSession.disposed || this.buffer.length < this.callSession.softphone.codec.packetSize;
|
|
35
|
+
}
|
|
36
|
+
sendPacket() {
|
|
37
|
+
if (!this.paused && !this.finished) {
|
|
38
|
+
const temp = this.callSession.encoder.encode(this.buffer.subarray(0, this.callSession.softphone.codec.packetSize));
|
|
39
|
+
const rtpPacket = new werift_rtp.RtpPacket(new werift_rtp.RtpHeader({
|
|
40
|
+
version: 2,
|
|
41
|
+
padding: false,
|
|
42
|
+
paddingSize: 0,
|
|
43
|
+
extension: false,
|
|
44
|
+
marker: false,
|
|
45
|
+
payloadOffset: 12,
|
|
46
|
+
payloadType: this.callSession.softphone.codec.id,
|
|
47
|
+
sequenceNumber: this.callSession.sequenceNumber,
|
|
48
|
+
timestamp: this.callSession.timestamp,
|
|
49
|
+
ssrc: this.callSession.ssrc,
|
|
50
|
+
csrcLength: 0,
|
|
51
|
+
csrc: [],
|
|
52
|
+
extensionProfile: 48862,
|
|
53
|
+
extensionLength: void 0,
|
|
54
|
+
extensions: []
|
|
55
|
+
}), temp);
|
|
56
|
+
this.callSession.send(this.callSession.srtpSession.encrypt(rtpPacket.payload, rtpPacket.header));
|
|
57
|
+
this.callSession.sequenceNumber += 1;
|
|
58
|
+
if (this.callSession.sequenceNumber > 65535) this.callSession.sequenceNumber = 0;
|
|
59
|
+
this.callSession.timestamp += this.callSession.softphone.codec.timestampInterval;
|
|
60
|
+
this.buffer = this.buffer.subarray(this.callSession.softphone.codec.packetSize);
|
|
61
|
+
if (this.finished) this.emit("finished");
|
|
62
|
+
else setTimeout(() => this.sendPacket(), 20);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
10
65
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
for (let key of __getOwnPropNames(from))
|
|
14
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
-
}
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
-
mod
|
|
26
|
-
));
|
|
27
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
-
var streamer_exports = {};
|
|
29
|
-
__export(streamer_exports, {
|
|
30
|
-
default: () => streamer_default
|
|
31
|
-
});
|
|
32
|
-
module.exports = __toCommonJS(streamer_exports);
|
|
33
|
-
var import_node_buffer = require("node:buffer");
|
|
34
|
-
var import_node_events = __toESM(require("node:events"), 1);
|
|
35
|
-
var import_werift_rtp = require("werift-rtp");
|
|
36
|
-
class Streamer extends import_node_events.default {
|
|
37
|
-
paused = false;
|
|
38
|
-
callSession;
|
|
39
|
-
buffer;
|
|
40
|
-
originalBuffer;
|
|
41
|
-
constructor(callSession, buffer) {
|
|
42
|
-
super();
|
|
43
|
-
this.callSession = callSession;
|
|
44
|
-
this.buffer = buffer;
|
|
45
|
-
this.originalBuffer = buffer;
|
|
46
|
-
}
|
|
47
|
-
start() {
|
|
48
|
-
this.buffer = this.originalBuffer;
|
|
49
|
-
this.paused = false;
|
|
50
|
-
this.sendPacket();
|
|
51
|
-
}
|
|
52
|
-
stop() {
|
|
53
|
-
this.buffer = import_node_buffer.Buffer.alloc(0);
|
|
54
|
-
}
|
|
55
|
-
pause() {
|
|
56
|
-
this.paused = true;
|
|
57
|
-
}
|
|
58
|
-
resume() {
|
|
59
|
-
this.paused = false;
|
|
60
|
-
this.sendPacket();
|
|
61
|
-
}
|
|
62
|
-
get finished() {
|
|
63
|
-
return this.callSession.disposed || this.buffer.length < this.callSession.softphone.codec.packetSize;
|
|
64
|
-
}
|
|
65
|
-
sendPacket() {
|
|
66
|
-
if (!this.paused && !this.finished) {
|
|
67
|
-
const temp = this.callSession.encoder.encode(
|
|
68
|
-
this.buffer.subarray(0, this.callSession.softphone.codec.packetSize)
|
|
69
|
-
);
|
|
70
|
-
const rtpPacket = new import_werift_rtp.RtpPacket(
|
|
71
|
-
new import_werift_rtp.RtpHeader({
|
|
72
|
-
version: 2,
|
|
73
|
-
padding: false,
|
|
74
|
-
paddingSize: 0,
|
|
75
|
-
extension: false,
|
|
76
|
-
marker: false,
|
|
77
|
-
payloadOffset: 12,
|
|
78
|
-
payloadType: this.callSession.softphone.codec.id,
|
|
79
|
-
sequenceNumber: this.callSession.sequenceNumber,
|
|
80
|
-
timestamp: this.callSession.timestamp,
|
|
81
|
-
ssrc: this.callSession.ssrc,
|
|
82
|
-
csrcLength: 0,
|
|
83
|
-
csrc: [],
|
|
84
|
-
extensionProfile: 48862,
|
|
85
|
-
extensionLength: void 0,
|
|
86
|
-
extensions: []
|
|
87
|
-
}),
|
|
88
|
-
temp
|
|
89
|
-
);
|
|
90
|
-
this.callSession.send(
|
|
91
|
-
this.callSession.srtpSession.encrypt(
|
|
92
|
-
rtpPacket.payload,
|
|
93
|
-
rtpPacket.header
|
|
94
|
-
)
|
|
95
|
-
);
|
|
96
|
-
this.callSession.sequenceNumber += 1;
|
|
97
|
-
if (this.callSession.sequenceNumber > 65535) {
|
|
98
|
-
this.callSession.sequenceNumber = 0;
|
|
99
|
-
}
|
|
100
|
-
this.callSession.timestamp += this.callSession.softphone.codec.timestampInterval;
|
|
101
|
-
this.buffer = this.buffer.subarray(
|
|
102
|
-
this.callSession.softphone.codec.packetSize
|
|
103
|
-
);
|
|
104
|
-
if (this.finished) {
|
|
105
|
-
this.emit("finished");
|
|
106
|
-
} else {
|
|
107
|
-
setTimeout(() => this.sendPacket(), 20);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
var streamer_default = Streamer;
|
|
66
|
+
//#endregion
|
|
67
|
+
module.exports = Streamer;
|