sip-lab 1.38.0 → 1.40.0

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/samples/t ADDED
@@ -0,0 +1,239 @@
1
+ var sip = require ('../index.js')
2
+ var Zeq = require('@mayama/zeq')
3
+ var z = new Zeq()
4
+ var m = require('data-matching')
5
+ var sip_msg = require('sip-matching')
6
+ var sdp = require('sdp-matching')
7
+
8
+ var assert = require('assert')
9
+
10
+ async function test() {
11
+ //sip.set_log_level(6)
12
+ sip.dtmf_aggregation_on(500)
13
+
14
+ z.trap_events(sip.event_source, 'event', (evt) => {
15
+ var e = evt.args[0]
16
+ return e
17
+ })
18
+
19
+ console.log(sip.start((data) => { console.log(data)} ))
20
+
21
+ t1 = sip.transport.create({address: "127.0.0.1", type: 'udp'})
22
+ t2 = sip.transport.create({address: "127.0.0.1", type: 'udp'})
23
+
24
+ console.log("t1", t1)
25
+ console.log("t2", t2)
26
+
27
+ console.log(sip.get_codecs())
28
+ sip.set_codecs("opus/48000/2:128")
29
+
30
+ flags = 0
31
+
32
+ oc = sip.call.create(t1.id, {from_uri: 'sip:alice@test.com', to_uri: `sip:bob@${t2.address}:${t2.port}`})
33
+
34
+ await z.wait([
35
+ {
36
+ event: "incoming_call",
37
+ call_id: m.collect("call_id"),
38
+ },
39
+ {
40
+ event: 'response',
41
+ call_id: oc.id,
42
+ method: 'INVITE',
43
+ msg: sip_msg({
44
+ $rs: '100',
45
+ $rr: 'Trying',
46
+ }),
47
+ },
48
+ ], 1000)
49
+
50
+ ic = {
51
+ id: z.$call_id,
52
+ sip_call_id: z.$sip_call_id,
53
+ }
54
+
55
+ sip.call.respond(ic.id, {code: 200, reason: 'OK'})
56
+
57
+ await z.wait([
58
+ {
59
+ event: 'media_update',
60
+ call_id: oc.id,
61
+ status: 'ok',
62
+ },
63
+ {
64
+ event: 'media_update',
65
+ call_id: ic.id,
66
+ status: 'ok',
67
+ },
68
+ {
69
+ event: 'response',
70
+ call_id: oc.id,
71
+ method: 'INVITE',
72
+ msg: sip_msg({
73
+ $rs: '200',
74
+ $rr: 'OK',
75
+ 'hdr_content_type': 'application/sdp',
76
+ $rb: '!{_}a=sendrecv!{_}',
77
+ }),
78
+ },
79
+ ], 1000)
80
+
81
+ sip.call.start_record_wav(oc.id, {file: './oc.wav'})
82
+ sip.call.start_record_wav(ic.id, {file: './ic.wav'})
83
+
84
+ sip.call.start_play_wav(oc.id, {file: 'samples/artifacts/hello_good_morning.wav', end_of_file_event: true, no_loop: true})
85
+ sip.call.start_play_wav(ic.id, {file: 'samples/artifacts/hello_good_morning.wav', end_of_file_event: true, no_loop: true})
86
+
87
+ await z.wait([
88
+ {
89
+ event: 'end_of_file',
90
+ call_id: oc.id,
91
+ },
92
+ {
93
+ event: 'end_of_file',
94
+ call_id: ic.id,
95
+ },
96
+ ], 5000)
97
+
98
+ sip.call.reinvite(oc.id)
99
+
100
+ await z.wait([
101
+ {
102
+ event: 'reinvite',
103
+ call_id: ic.id
104
+ },
105
+ ], 1000)
106
+
107
+ sip.call.respond(ic.id, {code: 200, reason: 'OK'})
108
+
109
+ await z.wait([
110
+ {
111
+ event: 'response',
112
+ call_id: oc.id,
113
+ method: 'INVITE',
114
+ msg: sip_msg({
115
+ $rs: '100',
116
+ }),
117
+ },
118
+ {
119
+ event: 'response',
120
+ call_id: oc.id,
121
+ method: 'INVITE',
122
+ msg: sip_msg({
123
+ $rs: '200',
124
+ $rr: 'OK',
125
+ $rb: '!{_}a=sendrecv!{_}',
126
+ }),
127
+ },
128
+ {
129
+ event: 'media_update',
130
+ call_id: oc.id,
131
+ status: 'ok',
132
+ },
133
+ {
134
+ event: 'media_update',
135
+ call_id: ic.id,
136
+ status: 'ok',
137
+ },
138
+ ], 500)
139
+
140
+ sip.call.reinvite(oc.id, false, 0)
141
+
142
+ await z.wait([
143
+ {
144
+ event: 'reinvite',
145
+ call_id: ic.id
146
+ },
147
+ ], 1000)
148
+
149
+ sip.call.respond(ic.id, {code: 200, reason: 'OK'})
150
+
151
+ await z.wait([
152
+ {
153
+ event: 'response',
154
+ call_id: oc.id,
155
+ method: 'INVITE',
156
+ msg: sip_msg({
157
+ $rs: '100',
158
+ }),
159
+ },
160
+ {
161
+ event: 'response',
162
+ call_id: oc.id,
163
+ method: 'INVITE',
164
+ msg: sip_msg({
165
+ $rs: '200',
166
+ $rr: 'OK',
167
+ $rb: '!{_}a=sendrecv!{_}',
168
+ }),
169
+ },
170
+ {
171
+ event: 'media_update',
172
+ call_id: oc.id,
173
+ status: 'ok',
174
+ },
175
+ {
176
+ event: 'media_update',
177
+ call_id: ic.id,
178
+ status: 'ok',
179
+ },
180
+ ], 500)
181
+
182
+ oc_stat = sip.call.get_stream_stat(oc.id, {media_id: 0})
183
+ ic_stat = sip.call.get_stream_stat(ic.id, {media_id: 0})
184
+
185
+ console.log(oc_stat)
186
+ console.log(ic_stat)
187
+
188
+ oc_stat = JSON.parse(oc_stat)
189
+ ic_stat = JSON.parse(ic_stat)
190
+
191
+ assert(oc_stat.CodecInfo == 'opus/8000/1')
192
+ assert(ic_stat.CodecInfo == 'opus/8000/1')
193
+
194
+ await z.sleep(100)
195
+
196
+ sip.call.start_inband_dtmf_detection(oc.id)
197
+ sip.call.start_inband_dtmf_detection(ic.id)
198
+
199
+ sip.call.send_dtmf(oc.id, {digits: '12', mode: 1})
200
+ sip.call.send_dtmf(ic.id, {digits: '21', mode: 1})
201
+
202
+ await z.sleep(1000)
203
+
204
+ sip.call.stop_record_wav(oc.id)
205
+ sip.call.stop_record_wav(ic.id)
206
+
207
+ sip.call.terminate(oc.id)
208
+
209
+ await z.wait([
210
+ {
211
+ event: 'call_ended',
212
+ call_id: oc.id,
213
+ },
214
+ {
215
+ event: 'call_ended',
216
+ call_id: ic.id,
217
+ },
218
+ {
219
+ event: 'response',
220
+ call_id: oc.id,
221
+ method: 'BYE',
222
+ msg: sip_msg({
223
+ $rs: '200',
224
+ $rr: 'OK',
225
+ }),
226
+ },
227
+ ], 1000)
228
+
229
+ console.log("Success")
230
+
231
+ sip.stop()
232
+ process.exit(0)
233
+ }
234
+
235
+ test()
236
+ .catch(e => {
237
+ console.error(e)
238
+ process.exit(1)
239
+ })
@@ -0,0 +1,156 @@
1
+ const sip = require('../index.js')
2
+ const Zeq = require('@mayama/zeq')
3
+ const m = require('data-matching')
4
+ const sip_msg = require('sip-matching')
5
+
6
+ var z = new Zeq()
7
+
8
+ async function test() {
9
+ sip.dtmf_aggregation_on(500)
10
+
11
+ z.trap_events(sip.event_source, 'event', (evt) => {
12
+ return evt.args[0]
13
+ })
14
+
15
+ sip.set_codecs("pcmu/8000/1:128,pcma/8000/1:128")
16
+
17
+ console.log(sip.start((data) => { console.log(data) }))
18
+
19
+ // Create a WebSocket server transport (listener)
20
+ const t2 = sip.transport.create({
21
+ address: "127.0.0.1",
22
+ port: 6666,
23
+ type: "ws"
24
+ })
25
+
26
+ // Create a WebSocket client transport connecting to our server
27
+ const t1 = sip.transport.create({
28
+ address: "127.0.0.1",
29
+ type: "ws",
30
+ ws_url: "ws://127.0.0.1:6666/sip"
31
+ })
32
+
33
+ console.log("t1", t1)
34
+ console.log("t2", t2)
35
+
36
+ // Make the call from t1 to t2 over WebSocket
37
+ const oc = sip.call.create(t1.id, {
38
+ from_uri: 'sip:alice@test.com',
39
+ to_uri: 'sip:bob@127.0.0.1:8080',
40
+ })
41
+
42
+ // Wait for the call to arrive at t2 and 100 Trying response at t1
43
+ await z.wait([
44
+ {
45
+ event: "incoming_call",
46
+ call_id: m.collect("call_id"),
47
+ transport_id: t2.id,
48
+ msg: sip_msg({
49
+ $rU: 'bob',
50
+ $fU: 'alice',
51
+ $tU: 'bob',
52
+ $fd: 'test.com',
53
+ })
54
+ },
55
+ {
56
+ event: 'response',
57
+ call_id: oc.id,
58
+ method: 'INVITE',
59
+ msg: sip_msg({
60
+ $rs: '100',
61
+ $rr: 'Trying',
62
+ }),
63
+ },
64
+ ], 2000)
65
+
66
+ const ic = {
67
+ id: z.$call_id,
68
+ sip_call_id: z.$sip_call_id,
69
+ }
70
+
71
+ // Answer the call at t2 side
72
+ sip.call.respond(ic.id, {
73
+ code: 200,
74
+ reason: 'OK',
75
+ })
76
+
77
+ // Wait for 200 OK at t1 side and media setups
78
+ await z.wait([
79
+ {
80
+ event: 'response',
81
+ call_id: oc.id,
82
+ method: 'INVITE',
83
+ msg: sip_msg({
84
+ $rs: '200',
85
+ $rr: 'OK',
86
+ }),
87
+ },
88
+ {
89
+ event: 'media_update',
90
+ call_id: oc.id,
91
+ status: 'ok',
92
+ },
93
+ {
94
+ event: 'media_update',
95
+ call_id: ic.id,
96
+ status: 'ok',
97
+ },
98
+ ], 2000)
99
+
100
+ sip.call.start_inband_dtmf_detection(oc.id)
101
+ sip.call.start_inband_dtmf_detection(ic.id)
102
+
103
+ sip.call.send_dtmf(oc.id, {digits: '1234', mode: 1})
104
+ sip.call.send_dtmf(ic.id, {digits: '1234', mode: 1})
105
+
106
+ await z.wait([
107
+ {
108
+ event: 'dtmf',
109
+ call_id: ic.id,
110
+ digits: '1234',
111
+ mode: 1,
112
+ media_id: 0,
113
+ },
114
+ {
115
+ event: 'dtmf',
116
+ call_id: oc.id,
117
+ digits: '1234',
118
+ mode: 1,
119
+ media_id: 0,
120
+ },
121
+ ], 2000)
122
+
123
+ // Terminate the call from t1 side
124
+ sip.call.terminate(oc.id)
125
+
126
+ // Wait for call termination
127
+ await z.wait([
128
+ {
129
+ event: 'response',
130
+ call_id: oc.id,
131
+ method: 'BYE',
132
+ msg: sip_msg({
133
+ $rs: '200',
134
+ $rr: 'OK',
135
+ }),
136
+ },
137
+ {
138
+ event: 'call_ended',
139
+ call_id: oc.id,
140
+ },
141
+ {
142
+ event: 'call_ended',
143
+ call_id: ic.id,
144
+ },
145
+ ], 2000)
146
+
147
+ console.log("WebSocket test successful")
148
+
149
+ sip.stop()
150
+ process.exit(0)
151
+ }
152
+
153
+ test().catch(e => {
154
+ console.error(e)
155
+ process.exit(1)
156
+ })
package/src/addon.cpp CHANGED
@@ -1264,6 +1264,32 @@ Napi::Value set_codecs(const Napi::CallbackInfo &info) {
1264
1264
  return env.Null();
1265
1265
  }
1266
1266
 
1267
+ Napi::Value set_opus_config(const Napi::CallbackInfo &info) {
1268
+ Napi::Env env = info.Env();
1269
+
1270
+ if (info.Length() != 1) {
1271
+ Napi::Error::New(env, "Wrong number of arguments. Expected: config")
1272
+ .ThrowAsJavaScriptException();
1273
+ return env.Null();
1274
+ }
1275
+
1276
+ if (!info[0].IsString()) {
1277
+ Napi::TypeError::New(env, "config must be a JSON string.")
1278
+ .ThrowAsJavaScriptException();
1279
+ return env.Null();
1280
+ }
1281
+ string json = info[0].As<Napi::String>().Utf8Value();
1282
+
1283
+ int res = pjw_set_opus_config(json.c_str());
1284
+
1285
+ if (res != 0) {
1286
+ Napi::Error::New(env, pjw_get_error()).ThrowAsJavaScriptException();
1287
+ return env.Null();
1288
+ }
1289
+
1290
+ return env.Null();
1291
+ }
1292
+
1267
1293
  Napi::Value notify(const Napi::CallbackInfo &info) {
1268
1294
  Napi::Env env = info.Env();
1269
1295
 
@@ -1649,6 +1675,7 @@ Napi::Object init(Napi::Env env, Napi::Object exports) {
1649
1675
 
1650
1676
  exports.Set("get_codecs", Napi::Function::New(env, get_codecs));
1651
1677
  exports.Set("set_codecs", Napi::Function::New(env, set_codecs));
1678
+ exports.Set("_set_opus_config", Napi::Function::New(env, set_opus_config));
1652
1679
 
1653
1680
  exports.Set("notify", Napi::Function::New(env, notify));
1654
1681
  exports.Set("notify_xfer", Napi::Function::New(env, notify_xfer));
@@ -0,0 +1,89 @@
1
+ /*
2
+ * Copyright (C) 2024 MayamaTakeshi
3
+ *
4
+ * This program is free software; you can redistribute it and/or modify
5
+ * it under the terms of the GNU General Public License as published by
6
+ * the Free Software Foundation; either version 2 of the License, or
7
+ * (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful,
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ * GNU General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+ */
18
+ #ifndef __PJSIP_TRANSPORT_WS_H__
19
+ #define __PJSIP_TRANSPORT_WS_H__
20
+
21
+ /**
22
+ * @file sip_transport_ws.h
23
+ * @brief SIP WebSocket Transport.
24
+ */
25
+
26
+ #include <pjsip/sip_transport.h>
27
+ #include <websock.h>
28
+
29
+ PJ_BEGIN_DECL
30
+
31
+ /* Dynamic transport type IDs for WS and WSS */
32
+ PJ_DECL_DATA(pjsip_transport_type_e) PJSIP_TRANSPORT_WS;
33
+ PJ_DECL_DATA(pjsip_transport_type_e) PJSIP_TRANSPORT_WSS;
34
+
35
+ /**
36
+ * Initialize WebSocket transport module. Must be called once before
37
+ * any other pjsip_ws_* functions. Registers the WS and WSS transport
38
+ * types with PJSIP.
39
+ *
40
+ * @return PJ_SUCCESS on success.
41
+ */
42
+ PJ_DECL(pj_status_t) pjsip_ws_transport_init(void);
43
+
44
+ /**
45
+ * Start a WebSocket listener (server mode). Creates a WebSocket listener
46
+ * on the specified address and port. When a WebSocket connection with
47
+ * SIP sub-protocol is accepted, a new PJSIP transport is automatically
48
+ * created and registered.
49
+ *
50
+ * @param endpt The SIP endpoint.
51
+ * @param ws_endpt The WebSocket endpoint.
52
+ * @param bind_addr Address and port to listen on.
53
+ * @param secure Use TLS (WSS) if PJ_TRUE, plain WS if PJ_FALSE.
54
+ * @param p_listener_tp Optional pointer to receive the listener transport.
55
+ *
56
+ * @return PJ_SUCCESS on success.
57
+ */
58
+ PJ_DECL(pj_status_t) pjsip_ws_transport_start(
59
+ pjsip_endpoint *endpt,
60
+ pj_websock_endpoint *ws_endpt,
61
+ const pj_sockaddr *bind_addr,
62
+ pj_bool_t secure,
63
+ pjsip_transport **p_listener_tp);
64
+
65
+ /**
66
+ * Create a WebSocket connection transport (client mode). Connects to the
67
+ * specified WebSocket URL and registers a PJSIP transport for it.
68
+ *
69
+ * @param endpt The SIP endpoint.
70
+ * @param ws_endpt The WebSocket endpoint.
71
+ * @param ws_url WebSocket URL (e.g. "ws://127.0.0.1:8080/sip").
72
+ * @param hdrs Additional HTTP headers for the upgrade request
73
+ * (can be NULL).
74
+ * @param hdr_cnt Number of headers in hdrs.
75
+ * @param p_transport Pointer to receive the created transport.
76
+ *
77
+ * @return PJ_SUCCESS on success.
78
+ */
79
+ PJ_DECL(pj_status_t) pjsip_ws_transport_connect(
80
+ pjsip_endpoint *endpt,
81
+ pj_websock_endpoint *ws_endpt,
82
+ const char *ws_url,
83
+ const pj_websock_http_hdr *hdrs,
84
+ int hdr_cnt,
85
+ pjsip_transport **p_transport);
86
+
87
+ PJ_END_DECL
88
+
89
+ #endif /* __PJSIP_TRANSPORT_WS_H__ */