sip-lab 1.24.0 → 1.27.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sip-lab",
3
- "version": "1.24.0",
3
+ "version": "1.27.1",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "engines": {
@@ -45,6 +45,7 @@
45
45
  "DEV.md",
46
46
  "samples",
47
47
  "prebuilds",
48
- "pocketsphinx"
48
+ "pocketsphinx",
49
+ "runtests"
49
50
  ]
50
51
  }
Binary file
package/runtests ADDED
@@ -0,0 +1,80 @@
1
+ #!/bin/bash
2
+
3
+ set -o nounset
4
+
5
+ function usage() {
6
+ cat <<EOF
7
+ Usage: $0 [-g]
8
+
9
+ Details:
10
+ -g : run each test inside gdb
11
+ EOF
12
+ }
13
+
14
+ use_gdb=0
15
+
16
+ while getopts "gh" o; do
17
+ case "${o}" in
18
+ g)
19
+ use_gdb=1
20
+ ;;
21
+ h)
22
+ usage
23
+ exit 0
24
+ ;;
25
+ *)
26
+ usage
27
+ exit 1
28
+ ;;
29
+ esac
30
+ done
31
+ shift $((OPTIND-1))
32
+
33
+ successful_tests=()
34
+
35
+ function output_successful_tests() {
36
+ echo "Successful tests:"
37
+ for t in "${successful_tests[@]}"
38
+ do
39
+ echo " - $t"
40
+ done
41
+ }
42
+
43
+ echo
44
+
45
+ for i in $(ls samples/*.js)
46
+ do
47
+ start_time=$(date +%s.%N)
48
+
49
+ if [[ $use_gdb -eq 0 ]]
50
+ then
51
+ node $i
52
+ else
53
+ gdb -ex "handle SIGSEGV stop" -ex "run" -ex "bt" -ex "quit" --args node $i
54
+ fi
55
+
56
+ if [[ $? -ne 0 ]]
57
+ then
58
+ echo "$i failed"
59
+ echo
60
+ output_successful_tests
61
+ exit 1
62
+ else
63
+ end_time=$(date +%s.%N)
64
+ duration=$(echo "$end_time - $start_time" | bc)
65
+ formatted_duration=$(printf "%.2f seconds" $duration)
66
+ successful_tests+=("$i: duration=$formatted_duration")
67
+
68
+ echo
69
+ echo "$i failed"
70
+ fi
71
+ done
72
+
73
+ echo
74
+
75
+ echo "Success. All tests passed"
76
+ echo
77
+ output_successful_tests
78
+ echo
79
+ echo "Everything OK"
80
+ echo
@@ -0,0 +1,217 @@
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
+ sip.set_codecs("PCMA/8000/1:128")
28
+
29
+ flags = 0
30
+
31
+ oc = sip.call.create(t1.id, {from_uri: 'sip:alice@test.com', to_uri: `sip:bob@${t2.address}:${t2.port}`})
32
+
33
+ await z.wait([
34
+ {
35
+ event: "incoming_call",
36
+ call_id: m.collect("call_id"),
37
+ },
38
+ {
39
+ event: 'response',
40
+ call_id: oc.id,
41
+ method: 'INVITE',
42
+ msg: sip_msg({
43
+ $rs: '100',
44
+ $rr: 'Trying',
45
+ '$(hdrcnt(via))': 1,
46
+ 'hdr_call_id': m.collect('sip_call_id'),
47
+ $fU: 'alice',
48
+ $fd: 'test.com',
49
+ $tU: 'bob',
50
+ 'hdr_l': '0',
51
+ }),
52
+ },
53
+ ], 1000)
54
+
55
+ ic = {
56
+ id: z.store.call_id,
57
+ sip_call_id: z.store.sip_call_id,
58
+ }
59
+
60
+ sip.call.respond(ic.id, {code: 200, reason: 'OK'})
61
+
62
+ await z.wait([
63
+ {
64
+ event: 'media_update',
65
+ call_id: oc.id,
66
+ status: 'ok',
67
+ },
68
+ {
69
+ event: 'media_update',
70
+ call_id: ic.id,
71
+ status: 'ok',
72
+ },
73
+ {
74
+ event: 'response',
75
+ call_id: oc.id,
76
+ method: 'INVITE',
77
+ msg: sip_msg({
78
+ $rs: '200',
79
+ $rr: 'OK',
80
+ '$(hdrcnt(VIA))': 1,
81
+ $fU: 'alice',
82
+ $fd: 'test.com',
83
+ $tU: 'bob',
84
+ 'hdr_content_type': 'application/sdp',
85
+ $rb: '!{_}a=sendrecv',
86
+ }),
87
+ },
88
+ ], 1000)
89
+
90
+ sip.call.reinvite(oc.id)
91
+
92
+ await z.wait([
93
+ {
94
+ event: 'reinvite',
95
+ call_id: ic.id
96
+ },
97
+ ], 1000)
98
+
99
+ sip.call.respond(ic.id, {code: 200, reason: 'OK'})
100
+
101
+ await z.wait([
102
+ {
103
+ event: 'response',
104
+ call_id: oc.id,
105
+ method: 'INVITE',
106
+ msg: sip_msg({
107
+ $rs: '100',
108
+ }),
109
+ },
110
+ {
111
+ event: 'response',
112
+ call_id: oc.id,
113
+ method: 'INVITE',
114
+ msg: sip_msg({
115
+ $rs: '200',
116
+ $rr: 'OK',
117
+ $rb: '!{_}a=sendrecv',
118
+ }),
119
+ },
120
+ {
121
+ event: 'media_update',
122
+ call_id: oc.id,
123
+ status: 'ok',
124
+ },
125
+ {
126
+ event: 'media_update',
127
+ call_id: ic.id,
128
+ status: 'ok',
129
+ },
130
+ ], 500)
131
+
132
+ sip.call.reinvite(oc.id, false, 0)
133
+
134
+ await z.wait([
135
+ {
136
+ event: 'reinvite',
137
+ call_id: ic.id
138
+ },
139
+ ], 1000)
140
+
141
+ sip.call.respond(ic.id, {code: 200, reason: 'OK'})
142
+
143
+ await z.wait([
144
+ {
145
+ event: 'response',
146
+ call_id: oc.id,
147
+ method: 'INVITE',
148
+ msg: sip_msg({
149
+ $rs: '100',
150
+ }),
151
+ },
152
+ {
153
+ event: 'response',
154
+ call_id: oc.id,
155
+ method: 'INVITE',
156
+ msg: sip_msg({
157
+ $rs: '200',
158
+ $rr: 'OK',
159
+ $rb: '!{_}a=sendrecv',
160
+ }),
161
+ },
162
+ {
163
+ event: 'media_update',
164
+ call_id: oc.id,
165
+ status: 'ok',
166
+ },
167
+ {
168
+ event: 'media_update',
169
+ call_id: ic.id,
170
+ status: 'ok',
171
+ },
172
+ ], 500)
173
+
174
+ oc_stat = sip.call.get_stream_stat(oc.id, {media_id: 0})
175
+ ic_stat = sip.call.get_stream_stat(ic.id, {media_id: 0})
176
+
177
+ console.log(oc_stat)
178
+ console.log(ic_stat)
179
+
180
+ oc_stat = JSON.parse(oc_stat)
181
+ ic_stat = JSON.parse(ic_stat)
182
+
183
+ assert(oc_stat.CodecInfo == 'PCMA/8000/1')
184
+ assert(ic_stat.CodecInfo == 'PCMA/8000/1')
185
+
186
+ sip.call.terminate(oc.id)
187
+
188
+ await z.wait([
189
+ {
190
+ event: 'call_ended',
191
+ call_id: oc.id,
192
+ },
193
+ {
194
+ event: 'call_ended',
195
+ call_id: ic.id,
196
+ },
197
+ {
198
+ event: 'response',
199
+ call_id: oc.id,
200
+ method: 'BYE',
201
+ msg: sip_msg({
202
+ $rs: '200',
203
+ $rr: 'OK',
204
+ }),
205
+ },
206
+ ], 1000)
207
+
208
+ console.log("Success")
209
+
210
+ sip.stop()
211
+ }
212
+
213
+ test()
214
+ .catch(e => {
215
+ console.error(e)
216
+ process.exit(1)
217
+ })
package/src/sip.cpp CHANGED
@@ -1,4 +1,5 @@
1
1
  #include <arpa/inet.h>
2
+ #include <netdb.h>
2
3
 
3
4
  #include "sip.hpp"
4
5
 
@@ -305,8 +306,18 @@ struct Subscription {
305
306
  struct ConfBridgePort {
306
307
  unsigned slot;
307
308
  pjmedia_port *port;
309
+ short connection_mode;
308
310
  };
309
311
 
312
+ #define FP_DTMFDET 0
313
+ #define FP_WAV_PLAYER 1
314
+ #define FP_WAV_WRITER 2
315
+ #define FP_TONEGEN 3
316
+ #define FP_FAX 4
317
+ #define FP_SPEECH_SYNTH 5
318
+ #define FP_SPEECH_RECOG 6
319
+ #define MAX_FP 7
320
+
310
321
  struct AudioEndpoint {
311
322
  pjmedia_transport *med_transport;
312
323
  pjmedia_stream *med_stream;
@@ -322,13 +333,7 @@ struct AudioEndpoint {
322
333
  pjmedia_port *null_port;
323
334
 
324
335
  ConfBridgePort stream_cbp;
325
- ConfBridgePort wav_player_cbp;
326
- ConfBridgePort wav_writer_cbp;
327
- ConfBridgePort tonegen_cbp;
328
- ConfBridgePort dtmfdet_cbp;
329
- ConfBridgePort fax_cbp;
330
- ConfBridgePort flite_cbp;
331
- ConfBridgePort pocketsphinx_cbp;
336
+ ConfBridgePort feature_cbps[MAX_FP];
332
337
  };
333
338
 
334
339
  struct VideoEndpoint {
@@ -779,7 +784,7 @@ static int find_endpoint_by_inband_dtmf_media_port(Call *call,
779
784
  MediaEndpoint *me = (MediaEndpoint *)call->media[i];
780
785
  if (ENDPOINT_TYPE_AUDIO == me->type) {
781
786
  AudioEndpoint *ae = (AudioEndpoint *)me->endpoint.audio;
782
- if (ae->dtmfdet_cbp.port && (pjmedia_port *)ae->dtmfdet_cbp.port == port) {
787
+ if (ae->feature_cbps[FP_DTMFDET].port && (pjmedia_port *)ae->feature_cbps[FP_DTMFDET].port == port) {
783
788
  return i;
784
789
  }
785
790
  }
@@ -1505,8 +1510,8 @@ pjsip_transport *create_udp_transport(pjsip_endpoint *sip_endpt,
1505
1510
  // pj_status_t status;
1506
1511
  pjsip_transport *transport;
1507
1512
 
1508
- int port = 5060;
1509
1513
  for (int i = 0; i < 1000; ++i) {
1514
+ int port = 5060;
1510
1515
  port += i;
1511
1516
  transport = allocate_udp_transport(sip_endpt, ipaddr, port);
1512
1517
  if (transport) {
@@ -1553,8 +1558,8 @@ pjsip_tpfactory *create_tcp_tpfactory(pjsip_endpoint *sip_endpt,
1553
1558
  // pj_status_t status;
1554
1559
  pjsip_tpfactory *tpfactory;
1555
1560
 
1556
- int port = 6060;
1557
1561
  for (int i = 0; i < 1000; ++i) {
1562
+ int port = 6060;
1558
1563
  port += i;
1559
1564
  tpfactory = allocate_tcp_tpfactory(sip_endpt, ipaddr, port);
1560
1565
  if (tpfactory) {
@@ -1604,8 +1609,8 @@ pjsip_tpfactory *create_tls_tpfactory(pjsip_endpoint *sip_endpt,
1604
1609
  // pj_status_t status;
1605
1610
  pjsip_tpfactory *tpfactory;
1606
1611
 
1607
- int port = 6060;
1608
1612
  for (int i = 0; i < 1000; ++i) {
1613
+ int port = 6060;
1609
1614
  port += i;
1610
1615
  tpfactory = allocate_tls_tpfactory(sip_endpt, ipaddr, port);
1611
1616
  if (tpfactory) {
@@ -2796,9 +2801,47 @@ bool dlg_set_transport_by_t(Transport *t, pjsip_dialog *dlg) {
2796
2801
  return true;
2797
2802
  }
2798
2803
 
2799
- void build_transport_tag(char *dest, const char *type, const char *address,
2804
+ bool is_ip_address(const char *hostname) {
2805
+ struct sockaddr_in sa;
2806
+ return inet_pton(AF_INET, hostname, &(sa.sin_addr)) != 0;
2807
+ }
2808
+
2809
+ void build_transport_tag(char *dest, const char *type, const char *hostname,
2800
2810
  int port) {
2801
- sprintf(dest, "%s:%s:%d", type, address, port);
2811
+ struct addrinfo hints, * res, *p;
2812
+
2813
+ if(is_ip_address(hostname)) {
2814
+ sprintf(dest, "%s:%s:%d", type, hostname, port);
2815
+ return;
2816
+ }
2817
+
2818
+ memset(&hints, 0, sizeof(hints));
2819
+ hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
2820
+ hints.ai_socktype = SOCK_STREAM;
2821
+
2822
+ // Resolve the domain name
2823
+ int status = getaddrinfo(hostname, NULL, &hints, &res);
2824
+ if (status != 0) {
2825
+ printf("build_transport_tag getaddrinfo: %s\n", gai_strerror(status));
2826
+ sprintf(dest, "%s:%s:%d", type, hostname, port);
2827
+ } else {
2828
+ for (p = res; p != NULL; p = p->ai_next) {
2829
+ void *addr;
2830
+ if (p->ai_family == AF_INET) { // IPv4
2831
+ struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
2832
+ addr = &(ipv4->sin_addr);
2833
+ } else { // IPv6
2834
+ struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
2835
+ addr = &(ipv6->sin6_addr);
2836
+ }
2837
+ char ipstr[INET6_ADDRSTRLEN];
2838
+ inet_ntop(p->ai_family, addr, ipstr, sizeof(ipstr));
2839
+ sprintf(dest, "%s:%s:%d", type, ipstr, port);
2840
+ break;
2841
+ }
2842
+ }
2843
+
2844
+ freeaddrinfo(res);
2802
2845
  }
2803
2846
 
2804
2847
  void build_transport_tag_from_pjsip_transport(char *dest, pjsip_transport *t) {
@@ -2810,6 +2853,7 @@ void build_transport_tag_from_pjsip_transport(char *dest, pjsip_transport *t) {
2810
2853
  assert(t->local_name.host.slen < 16);
2811
2854
  strncpy(address, t->local_name.host.ptr, t->local_name.host.slen);
2812
2855
  address[t->local_name.host.slen] = 0;
2856
+ printf("build_transport_tag_from_pjsip_transport address=%s\n", address);
2813
2857
 
2814
2858
  if (t->key.type == PJSIP_TRANSPORT_UDP) {
2815
2859
  type = "udp";
@@ -3082,7 +3126,7 @@ pj_status_t audio_endpoint_send_dtmf(Call *call, AudioEndpoint *ae,
3082
3126
  tone.on_msec = ON_DURATION;
3083
3127
  tone.off_msec = OFF_DURATION;
3084
3128
  tone.volume = 0;
3085
- status = pjmedia_tonegen_play_digits((pjmedia_port *)ae->tonegen_cbp.port, 1,
3129
+ status = pjmedia_tonegen_play_digits((pjmedia_port *)ae->feature_cbps[FP_TONEGEN].port, 1,
3086
3130
  &tone, 0);
3087
3131
  if (status != PJ_SUCCESS) {
3088
3132
  set_error("pjmedia_tonegen_play_digits failed.");
@@ -3789,7 +3833,7 @@ pj_status_t audio_endpoint_start_speech_synth(Call *call, AudioEndpoint *ae, con
3789
3833
  return -1;
3790
3834
  }
3791
3835
 
3792
- pjmedia_flite_port_speak(ae->flite_cbp.port, text, flags);
3836
+ pjmedia_flite_port_speak(ae->feature_cbps[FP_SPEECH_SYNTH].port, text, flags);
3793
3837
 
3794
3838
  return PJ_SUCCESS;
3795
3839
  }
@@ -4122,36 +4166,27 @@ out:
4122
4166
  return 0;
4123
4167
  }
4124
4168
 
4125
- pj_status_t audio_endpoint_stop_speech_synth(Call *call, AudioEndpoint *ae) {
4126
- return audio_endpoint_remove_port(call, ae, &ae->flite_cbp);
4127
- }
4128
-
4129
- pj_status_t audio_endpoint_stop_speech_recog(Call *call, AudioEndpoint *ae) {
4130
- return audio_endpoint_remove_port(call, ae, &ae->pocketsphinx_cbp);
4131
- }
4132
-
4133
4169
  pj_status_t audio_endpoint_stop_play_wav(Call *call, AudioEndpoint *ae) {
4134
- return audio_endpoint_remove_port(call, ae, &ae->wav_player_cbp);
4170
+ return audio_endpoint_remove_port(call, ae, &ae->feature_cbps[FP_WAV_PLAYER]);
4135
4171
  }
4136
4172
 
4137
4173
  pj_status_t audio_endpoint_stop_record_wav(Call *call, AudioEndpoint *ae) {
4138
- return audio_endpoint_remove_port(call, ae, &ae->wav_writer_cbp);
4174
+ return audio_endpoint_remove_port(call, ae, &ae->feature_cbps[FP_WAV_WRITER]);
4139
4175
  }
4140
4176
 
4141
4177
  pj_status_t audio_endpoint_stop_fax(Call *call, AudioEndpoint *ae) {
4142
- return audio_endpoint_remove_port(call, ae, &ae->fax_cbp);
4178
+ return audio_endpoint_remove_port(call, ae, &ae->feature_cbps[FP_FAX]);
4143
4179
  }
4144
4180
 
4145
-
4146
-
4147
- int pjw_call_stop_speech_synth(long call_id, const char *json) {
4148
- return audio_endpoint_stop_op(call_id, json, audio_endpoint_stop_speech_synth);
4181
+ pj_status_t audio_endpoint_stop_speech_synth(Call *call, AudioEndpoint *ae) {
4182
+ return audio_endpoint_remove_port(call, ae, &ae->feature_cbps[FP_SPEECH_SYNTH]);
4149
4183
  }
4150
4184
 
4151
- int pjw_call_stop_speech_recog(long call_id, const char *json) {
4152
- return audio_endpoint_stop_op(call_id, json, audio_endpoint_stop_speech_recog);
4185
+ pj_status_t audio_endpoint_stop_speech_recog(Call *call, AudioEndpoint *ae) {
4186
+ return audio_endpoint_remove_port(call, ae, &ae->feature_cbps[FP_SPEECH_RECOG]);
4153
4187
  }
4154
4188
 
4189
+
4155
4190
  int pjw_call_stop_play_wav(long call_id, const char *json) {
4156
4191
  return audio_endpoint_stop_op(call_id, json, audio_endpoint_stop_play_wav);
4157
4192
  }
@@ -4164,6 +4199,14 @@ int pjw_call_stop_fax(long call_id, const char *json) {
4164
4199
  return audio_endpoint_stop_op(call_id, json, audio_endpoint_stop_fax);
4165
4200
  }
4166
4201
 
4202
+ int pjw_call_stop_speech_synth(long call_id, const char *json) {
4203
+ return audio_endpoint_stop_op(call_id, json, audio_endpoint_stop_speech_synth);
4204
+ }
4205
+
4206
+ int pjw_call_stop_speech_recog(long call_id, const char *json) {
4207
+ return audio_endpoint_stop_op(call_id, json, audio_endpoint_stop_speech_recog);
4208
+ }
4209
+
4167
4210
 
4168
4211
  int pjw_call_start_fax(long call_id, const char *json) {
4169
4212
  PJW_LOCK();
@@ -4639,15 +4682,6 @@ bool start_tcp_media(Call *call, MediaEndpoint *me,
4639
4682
  void close_audio_endpoint_ports_and_conf(Call *call, AudioEndpoint *ae) {
4640
4683
  pj_status_t status;
4641
4684
 
4642
- audio_endpoint_remove_port(call, ae, &ae->stream_cbp);
4643
- audio_endpoint_remove_port(call, ae, &ae->wav_player_cbp);
4644
- audio_endpoint_remove_port(call, ae, &ae->wav_writer_cbp);
4645
- audio_endpoint_remove_port(call, ae, &ae->tonegen_cbp);
4646
- audio_endpoint_remove_port(call, ae, &ae->dtmfdet_cbp);
4647
- audio_endpoint_remove_port(call, ae, &ae->fax_cbp);
4648
- audio_endpoint_remove_port(call, ae, &ae->flite_cbp);
4649
- audio_endpoint_remove_port(call, ae, &ae->pocketsphinx_cbp);
4650
-
4651
4685
  if (ae->master_port) {
4652
4686
  status = pjmedia_master_port_stop(ae->master_port);
4653
4687
  if(status != PJ_SUCCESS) {
@@ -4660,6 +4694,12 @@ void close_audio_endpoint_ports_and_conf(Call *call, AudioEndpoint *ae) {
4660
4694
  ae->master_port = NULL;
4661
4695
  }
4662
4696
 
4697
+ audio_endpoint_remove_port(call, ae, &ae->stream_cbp);
4698
+
4699
+ for(int i=0 ; i<MAX_FP ; i++) {
4700
+ audio_endpoint_remove_port(call, ae, &ae->feature_cbps[i]);
4701
+ }
4702
+
4663
4703
  if (ae->conf) {
4664
4704
  status = pjmedia_conf_destroy(ae->conf);
4665
4705
  if(status != PJ_SUCCESS) {
@@ -4677,17 +4717,17 @@ void close_audio_endpoint_ports_and_conf(Call *call, AudioEndpoint *ae) {
4677
4717
  }
4678
4718
  }
4679
4719
 
4680
- bool connect_feature_port_to_stream_port(Call *call, AudioEndpoint *ae, ConfBridgePort *cbp, int connection_mode) {
4720
+ bool connect_feature_port_to_stream_port(Call *call, AudioEndpoint *ae, ConfBridgePort *cbp) {
4681
4721
  pj_status_t status;
4682
4722
 
4683
- if(connection_mode == CONNECTION_MODE_SOURCE || connection_mode == CONNECTION_MODE_SOURCE_AND_SINK) {
4723
+ if(cbp->connection_mode == CONNECTION_MODE_SOURCE || cbp->connection_mode == CONNECTION_MODE_SOURCE_AND_SINK) {
4684
4724
  status = pjmedia_conf_connect_port(ae->conf, cbp->slot, ae->stream_cbp.slot, 0);
4685
4725
  if (status != PJ_SUCCESS) {
4686
4726
  set_error("pjmedia_conf_connect_port failed");
4687
4727
  return false;
4688
4728
  }
4689
4729
  }
4690
- if(connection_mode == CONNECTION_MODE_SINK || connection_mode == CONNECTION_MODE_SOURCE_AND_SINK) {
4730
+ if(cbp->connection_mode == CONNECTION_MODE_SINK || cbp->connection_mode == CONNECTION_MODE_SOURCE_AND_SINK) {
4691
4731
  status = pjmedia_conf_connect_port(ae->conf, ae->stream_cbp.slot, cbp->slot, 0);
4692
4732
  printf("status=%i\n" ,status);
4693
4733
  if (status != PJ_SUCCESS) {
@@ -4711,6 +4751,19 @@ bool restart_media_stream(Call *call, MediaEndpoint *me,
4711
4751
  pjmedia_port *old_port = ae->stream_cbp.port;
4712
4752
  pjmedia_port *new_port;
4713
4753
 
4754
+ bool master_port_was_stopped = false;
4755
+
4756
+ if(ae->master_port) {
4757
+ status = pjmedia_master_port_stop(ae->master_port);
4758
+ if(status != PJ_SUCCESS) {
4759
+ make_evt_media_update(evt, sizeof(evt), call->id,
4760
+ "setup_failed (pjmedia_master_port_stop failed)", "");
4761
+ dispatch_event(evt);
4762
+ return false;
4763
+ }
4764
+ master_port_was_stopped = true;
4765
+ }
4766
+
4714
4767
  status =
4715
4768
  pjmedia_stream_info_from_sdp(&stream_info, call->inv->dlg->pool,
4716
4769
  g_med_endpt, local_sdp, remote_sdp, idx);
@@ -4860,35 +4913,23 @@ bool restart_media_stream(Call *call, MediaEndpoint *me,
4860
4913
  }
4861
4914
 
4862
4915
  // Need to connect ports to new stream port
4863
- if(ae->dtmfdet_cbp.port) {
4864
- if(!connect_feature_port_to_stream_port(call, ae, &ae->dtmfdet_cbp, CONNECTION_MODE_SINK)) return false;
4865
- }
4866
-
4867
- if(ae->wav_writer_cbp.port) {
4868
- if(!connect_feature_port_to_stream_port(call, ae, &ae->wav_writer_cbp, CONNECTION_MODE_SINK)) return false;
4869
- }
4870
-
4871
- if(ae->wav_player_cbp.port) {
4872
- if(!connect_feature_port_to_stream_port(call, ae, &ae->wav_player_cbp, CONNECTION_MODE_SOURCE)) return false;
4873
- }
4874
-
4875
- if(ae->tonegen_cbp.port) {
4876
- if(!connect_feature_port_to_stream_port(call, ae, &ae->tonegen_cbp, CONNECTION_MODE_SOURCE)) return false;
4877
- }
4878
-
4879
- if(ae->fax_cbp.port) {
4880
- if(!connect_feature_port_to_stream_port(call, ae, &ae->fax_cbp, CONNECTION_MODE_SOURCE_AND_SINK)) return false;
4881
- }
4882
-
4883
- if(ae->flite_cbp.port) {
4884
- if(!connect_feature_port_to_stream_port(call, ae, &ae->flite_cbp, CONNECTION_MODE_SOURCE)) return false;
4916
+ for(int i=0 ; i<MAX_FP ; i++) {
4917
+ if(ae->feature_cbps[i].port) {
4918
+ if(!connect_feature_port_to_stream_port(call, ae, &ae->feature_cbps[i])) return false;
4919
+ }
4885
4920
  }
4921
+ }
4886
4922
 
4887
- if(ae->pocketsphinx_cbp.port) {
4888
- if(!connect_feature_port_to_stream_port(call, ae, &ae->pocketsphinx_cbp, CONNECTION_MODE_SINK)) return false;
4923
+ if(master_port_was_stopped) {
4924
+ status = pjmedia_master_port_start(ae->master_port);
4925
+ if(status != PJ_SUCCESS) {
4926
+ make_evt_media_update(evt, sizeof(evt), call->id,
4927
+ "setup_failed (pjmedia_master_port_start failed)", "");
4928
+ dispatch_event(evt);
4929
+ return false;
4889
4930
  }
4890
4931
  }
4891
-
4932
+
4892
4933
  return true;
4893
4934
  }
4894
4935
 
@@ -5379,7 +5420,7 @@ static pj_bool_t on_rx_request(pjsip_rx_data *rdata) {
5379
5420
 
5380
5421
  long transport_id;
5381
5422
 
5382
- // printf("tag=%s\n", tag);
5423
+ //printf("on_rx_request transport_tag=%s\n", tag);
5383
5424
 
5384
5425
  TransportMap::iterator iter = g_TransportMap.find(tag);
5385
5426
  if (iter != g_TransportMap.end()) {
@@ -5490,7 +5531,7 @@ static pj_bool_t on_rx_request(pjsip_rx_data *rdata) {
5490
5531
 
5491
5532
  long transport_id;
5492
5533
 
5493
- // printf("tag=%s\n", tag);
5534
+ printf("on_rx_request INVITE transport_tag=%s\n", tag);
5494
5535
 
5495
5536
  TransportMap::iterator iter = g_TransportMap.find(tag);
5496
5537
  if (iter != g_TransportMap.end()) {
@@ -6707,7 +6748,9 @@ bool prepare_tonegen(Call *call, AudioEndpoint *ae) {
6707
6748
  printf("prepare_tone_gen call.id=%i\n", call->id);
6708
6749
  pj_status_t status;
6709
6750
 
6710
- if(ae->tonegen_cbp.port) {
6751
+ ConfBridgePort *fp = &ae->feature_cbps[FP_TONEGEN];
6752
+
6753
+ if(fp->port) {
6711
6754
  printf("already prepared\n");
6712
6755
  return true;
6713
6756
  }
@@ -6716,24 +6759,28 @@ bool prepare_tonegen(Call *call, AudioEndpoint *ae) {
6716
6759
  call->inv->pool, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6717
6760
  PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info),
6718
6761
  PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6719
- PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info), 0, &ae->tonegen_cbp.port);
6762
+ PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info), 0, &fp->port);
6720
6763
  if (status != PJ_SUCCESS) {
6721
6764
  set_error("pjmedia_tonegen_create failed");
6722
6765
  return false;
6723
6766
  }
6724
6767
 
6725
- status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->tonegen_cbp.port, NULL, &ae->tonegen_cbp.slot);
6768
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, fp->port, NULL, &fp->slot);
6726
6769
  if (status != PJ_SUCCESS) {
6727
6770
  set_error("pjmedia_conf_add_port failed");
6728
6771
  return false;
6729
6772
  }
6730
6773
 
6731
- return connect_feature_port_to_stream_port(call, ae, &ae->tonegen_cbp, CONNECTION_MODE_SOURCE);
6774
+ fp->connection_mode = CONNECTION_MODE_SOURCE;
6775
+
6776
+ return connect_feature_port_to_stream_port(call, ae, fp);
6732
6777
  }
6733
6778
 
6734
6779
  bool prepare_wav_player(Call *call, AudioEndpoint *ae, const char *file, unsigned flags, bool end_of_file_event) {
6735
6780
  pj_status_t status;
6736
6781
 
6782
+ ConfBridgePort *fp = &ae->feature_cbps[FP_WAV_PLAYER];
6783
+
6737
6784
  unsigned wav_ptime;
6738
6785
  wav_ptime = PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info) * 1000 /
6739
6786
  PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info);
@@ -6744,7 +6791,7 @@ bool prepare_wav_player(Call *call, AudioEndpoint *ae, const char *file, unsigne
6744
6791
  wav_ptime,
6745
6792
  flags,
6746
6793
  -1, /* buf size */
6747
- &ae->wav_player_cbp.port
6794
+ &fp->port
6748
6795
  );
6749
6796
 
6750
6797
  if (status != PJ_SUCCESS) {
@@ -6753,71 +6800,83 @@ bool prepare_wav_player(Call *call, AudioEndpoint *ae, const char *file, unsigne
6753
6800
  }
6754
6801
 
6755
6802
  if (end_of_file_event) {
6756
- status = pjmedia_wav_player_set_eof_cb2(ae->wav_player_cbp.port, (void*)call, on_end_of_file);
6803
+ status = pjmedia_wav_player_set_eof_cb2(fp->port, (void*)call, on_end_of_file);
6757
6804
  if (status != PJ_SUCCESS) {
6758
6805
  set_error("pjmedia_wav_player_set_eof_cb2 failed");
6759
6806
  return false;
6760
6807
  }
6761
6808
  }
6762
6809
 
6763
- status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->wav_player_cbp.port, NULL, &ae->wav_player_cbp.slot);
6810
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, fp->port, NULL, &fp->slot);
6764
6811
  if (status != PJ_SUCCESS) {
6765
6812
  set_error("pjmedia_conf_add_port failed");
6766
6813
  return false;
6767
6814
  }
6768
6815
 
6769
- return connect_feature_port_to_stream_port(call, ae, &ae->wav_player_cbp, CONNECTION_MODE_SOURCE);
6816
+ fp->connection_mode = CONNECTION_MODE_SOURCE;
6817
+
6818
+ return connect_feature_port_to_stream_port(call, ae, fp);
6770
6819
  }
6771
6820
 
6772
6821
  bool prepare_wav_writer(Call *call, AudioEndpoint *ae, const char *file) {
6773
6822
  pj_status_t status;
6774
6823
 
6824
+ ConfBridgePort *fp = &ae->feature_cbps[FP_WAV_WRITER];
6825
+
6775
6826
  status = pjmedia_wav_writer_port_create(
6776
6827
  call->inv->pool, file, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6777
6828
  PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info), PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6778
6829
  PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info), PJMEDIA_FILE_WRITE_PCM, 0,
6779
- (pjmedia_port **)&ae->wav_writer_cbp.port);
6830
+ (pjmedia_port **)&fp->port);
6780
6831
  if (status != PJ_SUCCESS) {
6781
6832
  set_error("pjmedia_wav_write_port_create failed");
6782
6833
  return false;
6783
6834
  }
6784
6835
 
6785
- status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->wav_writer_cbp.port, NULL, &ae->wav_writer_cbp.slot);
6836
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, fp->port, NULL, &fp->slot);
6786
6837
  if (status != PJ_SUCCESS) {
6787
6838
  set_error("pjmedia_conf_add_port failed");
6788
6839
  return false;
6789
6840
  }
6790
6841
 
6791
- return connect_feature_port_to_stream_port(call, ae, &ae->wav_writer_cbp, CONNECTION_MODE_SINK);
6842
+ fp->connection_mode = CONNECTION_MODE_SINK;
6843
+
6844
+ return connect_feature_port_to_stream_port(call, ae, fp);
6792
6845
  }
6793
6846
 
6794
6847
  bool prepare_dtmfdet(Call *call, AudioEndpoint *ae) {
6795
6848
  pj_status_t status;
6849
+
6850
+ ConfBridgePort *fp = &ae->feature_cbps[FP_DTMFDET];
6851
+
6796
6852
  status = pjmedia_dtmfdet_create(
6797
6853
  call->inv->pool,
6798
6854
  PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6799
6855
  PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info),
6800
6856
  PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6801
6857
  PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info),
6802
- on_inband_dtmf, call, &ae->dtmfdet_cbp.port);
6858
+ on_inband_dtmf, call, &fp->port);
6803
6859
  if (status != PJ_SUCCESS) {
6804
6860
  set_error("pjmedia_dtmfdet_create failed");
6805
6861
  return false;
6806
6862
  }
6807
6863
 
6808
- status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->dtmfdet_cbp.port, NULL, &ae->dtmfdet_cbp.slot);
6864
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, fp->port, NULL, &fp->slot);
6809
6865
  if (status != PJ_SUCCESS) {
6810
6866
  set_error("pjmedia_conf_add_port failed");
6811
6867
  return false;
6812
6868
  }
6813
6869
 
6814
- return connect_feature_port_to_stream_port(call, ae, &ae->dtmfdet_cbp, CONNECTION_MODE_SINK);
6870
+ fp->connection_mode = CONNECTION_MODE_SINK;
6871
+
6872
+ return connect_feature_port_to_stream_port(call, ae, fp);
6815
6873
  }
6816
6874
 
6817
- bool prepare_fax(Call *call, AudioEndpoint *ae, bool is_sender, const char *file,
6818
- unsigned flags) {
6875
+ bool prepare_fax(Call *call, AudioEndpoint *ae, bool is_sender, const char *file, unsigned flags) {
6819
6876
  pj_status_t status;
6820
6877
 
6878
+ ConfBridgePort *fp = &ae->feature_cbps[FP_FAX];
6879
+
6821
6880
  status = pjmedia_fax_port_create(
6822
6881
  call->inv->pool,
6823
6882
  PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
@@ -6825,26 +6884,29 @@ bool prepare_fax(Call *call, AudioEndpoint *ae, bool is_sender, const char *file
6825
6884
  PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6826
6885
  PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info),
6827
6886
  on_fax_result, call, is_sender, file,
6828
- flags, &ae->fax_cbp.port);
6887
+ flags, &fp->port);
6829
6888
  if (status != PJ_SUCCESS) {
6830
6889
  set_error("pjmedia_fax_port_create failed");
6831
6890
  return false;
6832
6891
  }
6833
6892
 
6834
- status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->fax_cbp.port, NULL, &ae->fax_cbp.slot);
6893
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, fp->port, NULL, &fp->slot);
6835
6894
  if (status != PJ_SUCCESS) {
6836
6895
  set_error("pjmedia_conf_add_port failed");
6837
6896
  return false;
6838
6897
  }
6839
6898
 
6840
- return connect_feature_port_to_stream_port(call, ae, &ae->fax_cbp, CONNECTION_MODE_SOURCE_AND_SINK);
6899
+ fp->connection_mode = CONNECTION_MODE_SOURCE_AND_SINK;
6900
+
6901
+ return connect_feature_port_to_stream_port(call, ae, fp);
6841
6902
  }
6842
6903
 
6843
6904
  bool prepare_flite(Call *call, AudioEndpoint *ae, const char *voice, bool end_of_speech_event) {
6844
- printf("prepare_flite call.id=%i\n", call->id);
6845
6905
  pj_status_t status;
6846
6906
 
6847
- if(ae->flite_cbp.port) {
6907
+ ConfBridgePort *fp = &ae->feature_cbps[FP_SPEECH_SYNTH];
6908
+
6909
+ if(fp->port) {
6848
6910
  printf("already prepared\n");
6849
6911
  return true;
6850
6912
  }
@@ -6853,34 +6915,39 @@ bool prepare_flite(Call *call, AudioEndpoint *ae, const char *voice, bool end_of
6853
6915
  call->inv->pool, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6854
6916
  PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info),
6855
6917
  PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6856
- PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info), voice, &ae->flite_cbp.port);
6918
+ PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info),
6919
+ voice,
6920
+ &fp->port);
6857
6921
  if (status != PJ_SUCCESS) {
6858
6922
  set_error("pjmedia_flite_port_create failed");
6859
6923
  return false;
6860
6924
  }
6861
6925
 
6862
6926
  if (end_of_speech_event) {
6863
- status = pjmedia_flite_port_set_eof_cb(ae->flite_cbp.port, (void*)call, on_end_of_speech);
6927
+ status = pjmedia_flite_port_set_eof_cb(fp->port, (void*)call, on_end_of_speech);
6864
6928
  if (status != PJ_SUCCESS) {
6865
6929
  set_error("pjmedia_flite_port_set_eof_cb failed");
6866
6930
  return false;
6867
6931
  }
6868
6932
  }
6869
6933
 
6870
- status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->flite_cbp.port, NULL, &ae->flite_cbp.slot);
6934
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, fp->port, NULL, &fp->slot);
6871
6935
  if (status != PJ_SUCCESS) {
6872
6936
  set_error("pjmedia_conf_add_port failed");
6873
6937
  return false;
6874
6938
  }
6875
6939
 
6876
- return connect_feature_port_to_stream_port(call, ae, &ae->flite_cbp, CONNECTION_MODE_SOURCE);
6940
+ fp->connection_mode = CONNECTION_MODE_SOURCE;
6941
+
6942
+ return connect_feature_port_to_stream_port(call, ae, fp);
6877
6943
  }
6878
6944
 
6879
6945
  bool prepare_pocketsphinx(Call *call, AudioEndpoint *ae) {
6880
- printf("prepare_pocketsphinx call.id=%i\n", call->id);
6881
6946
  pj_status_t status;
6882
6947
 
6883
- if(ae->pocketsphinx_cbp.port) {
6948
+ ConfBridgePort *fp = &ae->feature_cbps[FP_SPEECH_RECOG];
6949
+
6950
+ if(fp->port) {
6884
6951
  printf("already prepared\n");
6885
6952
  return true;
6886
6953
  }
@@ -6892,19 +6959,21 @@ bool prepare_pocketsphinx(Call *call, AudioEndpoint *ae) {
6892
6959
  PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info),
6893
6960
  on_speech_transcript,
6894
6961
  call,
6895
- &ae->pocketsphinx_cbp.port);
6962
+ &fp->port);
6896
6963
  if (status != PJ_SUCCESS) {
6897
6964
  set_error("pjmedia_pocketsphinx_port_create failed");
6898
6965
  return false;
6899
6966
  }
6900
6967
 
6901
- status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->pocketsphinx_cbp.port, NULL, &ae->pocketsphinx_cbp.slot);
6968
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, fp->port, NULL, &fp->slot);
6902
6969
  if (status != PJ_SUCCESS) {
6903
6970
  set_error("pjmedia_conf_add_port failed");
6904
6971
  return false;
6905
6972
  }
6906
6973
 
6907
- return connect_feature_port_to_stream_port(call, ae, &ae->pocketsphinx_cbp, CONNECTION_MODE_SINK);
6974
+ fp->connection_mode = CONNECTION_MODE_SINK;
6975
+
6976
+ return connect_feature_port_to_stream_port(call, ae, fp);
6908
6977
  }
6909
6978
 
6910
6979
  void on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code,