sip-lab 1.23.0 → 1.27.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/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
 
@@ -23,6 +24,7 @@
23
24
  #include "dtmfdet.h"
24
25
  #include "fax_port.h"
25
26
  #include "flite_port.h"
27
+ #include "pocketsphinx_port.h"
26
28
 
27
29
  #include <ctime>
28
30
 
@@ -265,14 +267,6 @@ pjsip_route_hdr route_set;
265
267
  pjsip_route_hdr *route;
266
268
  const pj_str_t hname = pj_str((char *)"Route");
267
269
 
268
- #define CONF_PORTS 1024
269
- //#define CLOCK_RATE 16000
270
- #define CLOCK_RATE 8000
271
- #define CHANNEL_COUNT 1
272
- #define PTIME 20
273
- #define SAMPLES_PER_FRAME (CLOCK_RATE*PTIME/1000)
274
- #define BITS_PER_SAMPLE 16
275
-
276
270
  #define MAXDIGITS 256
277
271
 
278
272
  #define DTMF_MODE_RFC2833 0
@@ -312,8 +306,18 @@ struct Subscription {
312
306
  struct ConfBridgePort {
313
307
  unsigned slot;
314
308
  pjmedia_port *port;
309
+ short connection_mode;
315
310
  };
316
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
+
317
321
  struct AudioEndpoint {
318
322
  pjmedia_transport *med_transport;
319
323
  pjmedia_stream *med_stream;
@@ -324,13 +328,12 @@ struct AudioEndpoint {
324
328
 
325
329
  pj_str_t mode;
326
330
 
331
+ pjmedia_conf *conf;
332
+ pjmedia_master_port *master_port;
333
+ pjmedia_port *null_port;
334
+
327
335
  ConfBridgePort stream_cbp;
328
- ConfBridgePort wav_player_cbp;
329
- ConfBridgePort wav_writer_cbp;
330
- ConfBridgePort tonegen_cbp;
331
- ConfBridgePort dtmfdet_cbp;
332
- ConfBridgePort fax_cbp;
333
- ConfBridgePort flite_cbp;
336
+ ConfBridgePort feature_cbps[MAX_FP];
334
337
  };
335
338
 
336
339
  struct VideoEndpoint {
@@ -433,10 +436,6 @@ struct Call {
433
436
  pjmedia_sdp_session *active_remote_sdp;
434
437
 
435
438
  bool local_sdp_answer_already_set;
436
-
437
- pjmedia_conf *conf;
438
- pjmedia_master_port *master_port;
439
- pjmedia_port *null_port;
440
439
  };
441
440
 
442
441
  #define MAX_TCP_DATA 4096
@@ -472,6 +471,10 @@ PackageSet g_PackageSet;
472
471
 
473
472
  #define DEFAULT_EXPIRES 600
474
473
 
474
+ #define CONNECTION_MODE_SOURCE 0
475
+ #define CONNECTION_MODE_SINK 1
476
+ #define CONNECTION_MODE_SOURCE_AND_SINK 2
477
+
475
478
  void handle_events() {
476
479
  pj_time_val tv = {0, 1};
477
480
  pjsip_endpt_handle_events(g_sip_endpt, &tv);
@@ -618,23 +621,23 @@ static void build_stream_stat(ostringstream &oss, pjmedia_rtcp_stat *stat,
618
621
 
619
622
  bool prepare_tonegen(Call *call, AudioEndpoint *ae);
620
623
  bool prepare_dtmfdet(Call *call, AudioEndpoint *ae);
621
- bool prepare_wav_player(Call *c, AudioEndpoint *ae, const char *file, unsigned flags, bool end_of_file_event);
622
- bool prepare_wav_writer(Call *c, AudioEndpoint *ae, const char *file);
623
- bool prepare_fax(Call *c, AudioEndpoint *ae, bool is_sender, const char *file,
624
- unsigned flags);
625
- bool prepare_flite(Call *c, AudioEndpoint *ae, const char *voice, bool end_of_speech_event);
624
+ bool prepare_wav_player(Call *call, AudioEndpoint *ae, const char *file, unsigned flags, bool end_of_file_event);
625
+ bool prepare_wav_writer(Call *call, AudioEndpoint *ae, const char *file);
626
+ bool prepare_fax(Call *call, AudioEndpoint *ae, bool is_sender, const char *file, unsigned flags);
627
+ bool prepare_flite(Call *call, AudioEndpoint *ae, const char *voice, bool end_of_speech_event);
628
+ bool prepare_pocketsphinx(Call *call, AudioEndpoint *ae);
626
629
 
627
630
  void prepare_error_event(ostringstream *oss, char *scope, char *details);
628
631
  // void prepare_pjsipcall_error_event(ostringstream *oss, char *scope, char
629
632
  // *function, pj_status_t s);
630
633
  void append_status(ostringstream *oss, pj_status_t s);
631
634
 
632
- bool is_media_active(Call *c, MediaEndpoint *me);
635
+ bool is_media_active(Call *call, MediaEndpoint *me);
633
636
  void close_media_endpoint(Call *call, MediaEndpoint *me);
634
637
 
635
- void close_media(Call *c);
638
+ void close_media(Call *call);
636
639
 
637
- bool process_media(Call *c, pjsip_dialog *dlg, Document &document, bool answer);
640
+ bool process_media(Call *call, pjsip_dialog *dlg, Document &document, bool answer);
638
641
 
639
642
  typedef pj_status_t (*audio_endpoint_stop_op_t)(Call *call, AudioEndpoint *ae);
640
643
 
@@ -642,6 +645,7 @@ pj_status_t audio_endpoint_stop_play_wav(Call *call, AudioEndpoint *ae);
642
645
  pj_status_t audio_endpoint_stop_record_wav(Call *call, AudioEndpoint *ae);
643
646
  pj_status_t audio_endpoint_stop_fax(Call *call, AudioEndpoint *ae);
644
647
  pj_status_t audio_endpoint_stop_speech_synth(Call *call, AudioEndpoint *ae);
648
+ pj_status_t audio_endpoint_stop_speech_recog(Call *call, AudioEndpoint *ae);
645
649
 
646
650
  static pjsip_module mod_tester = {
647
651
  NULL,
@@ -716,23 +720,36 @@ find_endpoint_by_inband_dtmf_media_stream(Call *call,
716
720
  return -1;
717
721
  }
718
722
 
719
- pj_status_t setup_call_conf(Call *call) {
723
+ pj_status_t create_audio_endpoint_conf(Call *call, AudioEndpoint *ae, pjmedia_port *stream_port) {
720
724
  pj_status_t status;
725
+ int conf_ports = 16;
726
+
727
+ unsigned sampling_rate = PJMEDIA_PIA_SRATE(&stream_port->info);
728
+ unsigned channel_count = PJMEDIA_PIA_CCNT(&stream_port->info);
729
+ unsigned samples_per_frame = PJMEDIA_PIA_SPF(&stream_port->info);
730
+ unsigned bits_per_sample = PJMEDIA_PIA_BITS(&stream_port->info);
731
+
721
732
  status = pjmedia_conf_create(call->inv->pool,
722
- CONF_PORTS,
723
- CLOCK_RATE,
724
- CHANNEL_COUNT,
725
- SAMPLES_PER_FRAME,
726
- BITS_PER_SAMPLE,
733
+ conf_ports,
734
+ sampling_rate,
735
+ channel_count,
736
+ samples_per_frame,
737
+ bits_per_sample,
727
738
  PJMEDIA_CONF_NO_DEVICE,
728
- &call->conf);
739
+ &ae->conf);
729
740
 
730
741
  if (status != PJ_SUCCESS) {
731
742
  addon_log(L_DBG, "pjmedia_conf_create failed\n");
732
743
  return false;
733
744
  }
734
745
 
735
- status = pjmedia_null_port_create(call->inv->pool, CLOCK_RATE, CHANNEL_COUNT, SAMPLES_PER_FRAME, BITS_PER_SAMPLE, &call->null_port);
746
+ status = pjmedia_null_port_create(
747
+ call->inv->pool,
748
+ sampling_rate,
749
+ channel_count,
750
+ samples_per_frame,
751
+ bits_per_sample,
752
+ &ae->null_port);
736
753
  if (status != PJ_SUCCESS) {
737
754
  addon_log(L_DBG, "pjmedia_null_port_created failed\n");
738
755
  return false;
@@ -740,19 +757,19 @@ pj_status_t setup_call_conf(Call *call) {
740
757
 
741
758
  pjmedia_port *conf_port = NULL;
742
759
 
743
- conf_port = pjmedia_conf_get_master_port(call->conf);
760
+ conf_port = pjmedia_conf_get_master_port(ae->conf);
744
761
  if (conf_port == NULL) {
745
762
  addon_log(L_DBG, "pjmedia_conf_get_master_port failed\n");
746
763
  return false;
747
764
  }
748
765
 
749
- status = pjmedia_master_port_create(call->inv->pool, call->null_port, conf_port, 0, &call->master_port);
766
+ status = pjmedia_master_port_create(call->inv->pool, ae->null_port, conf_port, 0, &ae->master_port);
750
767
  if (status != PJ_SUCCESS) {
751
768
  addon_log(L_DBG, "pjmedia_master_port_create failed\n");
752
769
  return false;
753
770
  }
754
771
 
755
- status = pjmedia_master_port_start(call->master_port);
772
+ status = pjmedia_master_port_start(ae->master_port);
756
773
  if (status != PJ_SUCCESS) {
757
774
  addon_log(L_DBG, "pjmedia_master_port_start failed\n");
758
775
  return false;
@@ -761,45 +778,13 @@ pj_status_t setup_call_conf(Call *call) {
761
778
  return PJ_SUCCESS;
762
779
  }
763
780
 
764
- void release_call_conf(Call *call) {
765
- pj_status_t status;
766
-
767
- if (call->master_port) {
768
- status = pjmedia_master_port_stop(call->master_port);
769
- if(status != PJ_SUCCESS) {
770
- addon_log(L_DBG, "pjmedia_master_port_stop failed\n");
771
- }
772
- pjmedia_master_port_destroy(call->master_port, 0);
773
- if(status != PJ_SUCCESS) {
774
- addon_log(L_DBG, "pjmedia_master_port_destroy failed\n");
775
- }
776
- call->master_port = NULL;
777
- }
778
-
779
- if (call->conf) {
780
- status = pjmedia_conf_destroy(call->conf);
781
- if(status != PJ_SUCCESS) {
782
- addon_log(L_DBG, "pjmedia_conf_destroy failed\n");
783
- }
784
- call->conf = NULL;
785
- }
786
-
787
- if (call->null_port) {
788
- status = pjmedia_port_destroy(call->null_port);
789
- if(status != PJ_SUCCESS) {
790
- addon_log(L_DBG, "pjmedia_port_destroy(null_port) failed\n");
791
- }
792
- call->null_port = NULL;
793
- }
794
- }
795
-
796
781
  static int find_endpoint_by_inband_dtmf_media_port(Call *call,
797
782
  pjmedia_port *port) {
798
783
  for (int i = 0; i < call->media_count; i++) {
799
784
  MediaEndpoint *me = (MediaEndpoint *)call->media[i];
800
785
  if (ENDPOINT_TYPE_AUDIO == me->type) {
801
786
  AudioEndpoint *ae = (AudioEndpoint *)me->endpoint.audio;
802
- 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) {
803
788
  return i;
804
789
  }
805
790
  }
@@ -905,6 +890,22 @@ static void on_end_of_speech(pjmedia_port *port, void *user_data) {
905
890
  dispatch_event(evt);
906
891
  }
907
892
 
893
+ static void on_speech_transcript(pjmedia_port*, void *user_data, char* transcript) {
894
+ if (g_shutting_down)
895
+ return;
896
+
897
+ long call_id;
898
+ if (!g_call_ids.get_id((long)user_data, call_id)) {
899
+ addon_log(
900
+ L_DBG,
901
+ "on_speech_transcript: Failed to get call_id. Event will not be notified.\n");
902
+ return;
903
+ }
904
+
905
+ char evt[1024];
906
+ make_evt_speech_transcript(evt, sizeof(evt), call_id, transcript);
907
+ dispatch_event(evt);
908
+ }
908
909
 
909
910
  void dispatch_event(const char *evt) {
910
911
  addon_log(L_DBG, "dispach_event called with evt=%s\n", evt);
@@ -2800,9 +2801,47 @@ bool dlg_set_transport_by_t(Transport *t, pjsip_dialog *dlg) {
2800
2801
  return true;
2801
2802
  }
2802
2803
 
2803
- 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,
2804
2810
  int port) {
2805
- 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);
2806
2845
  }
2807
2846
 
2808
2847
  void build_transport_tag_from_pjsip_transport(char *dest, pjsip_transport *t) {
@@ -2814,6 +2853,7 @@ void build_transport_tag_from_pjsip_transport(char *dest, pjsip_transport *t) {
2814
2853
  assert(t->local_name.host.slen < 16);
2815
2854
  strncpy(address, t->local_name.host.ptr, t->local_name.host.slen);
2816
2855
  address[t->local_name.host.slen] = 0;
2856
+ printf("build_transport_tag_from_pjsip_transport address=%s\n", address);
2817
2857
 
2818
2858
  if (t->key.type == PJSIP_TRANSPORT_UDP) {
2819
2859
  type = "udp";
@@ -3047,12 +3087,6 @@ int call_create(Transport *t, unsigned flags, pjsip_dialog *dlg,
3047
3087
  }
3048
3088
  // addon_log(L_DBG, "pjsip_dlg_add_usage OK\n");
3049
3089
 
3050
- status = setup_call_conf(call);
3051
- if (status != PJ_SUCCESS) {
3052
- set_error("setup_call_conf failed");
3053
- return -1;
3054
- }
3055
-
3056
3090
  return call_id;
3057
3091
  }
3058
3092
 
@@ -3092,7 +3126,7 @@ pj_status_t audio_endpoint_send_dtmf(Call *call, AudioEndpoint *ae,
3092
3126
  tone.on_msec = ON_DURATION;
3093
3127
  tone.off_msec = OFF_DURATION;
3094
3128
  tone.volume = 0;
3095
- 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,
3096
3130
  &tone, 0);
3097
3131
  if (status != PJ_SUCCESS) {
3098
3132
  set_error("pjmedia_tonegen_play_digits failed.");
@@ -3251,7 +3285,7 @@ out:
3251
3285
  return 0;
3252
3286
  }
3253
3287
 
3254
- pj_status_t audio_endpoint_remove_port(Call *call, ConfBridgePort *cbp) {
3288
+ pj_status_t audio_endpoint_remove_port(Call *call, AudioEndpoint *ae, ConfBridgePort *cbp) {
3255
3289
  printf("audio_endpoint_remove_port\n");
3256
3290
  pj_status_t status;
3257
3291
 
@@ -3262,7 +3296,7 @@ pj_status_t audio_endpoint_remove_port(Call *call, ConfBridgePort *cbp) {
3262
3296
  pjmedia_conf_disconnect_port_from_sinks(conf, port);
3263
3297
  */
3264
3298
 
3265
- status = pjmedia_conf_remove_port(call->conf, cbp->slot);
3299
+ status = pjmedia_conf_remove_port(ae->conf, cbp->slot);
3266
3300
  if (status != PJ_SUCCESS) {
3267
3301
  set_error("pjmedia_conf_remove_port failed");
3268
3302
  return false;
@@ -3799,7 +3833,7 @@ pj_status_t audio_endpoint_start_speech_synth(Call *call, AudioEndpoint *ae, con
3799
3833
  return -1;
3800
3834
  }
3801
3835
 
3802
- pjmedia_flite_port_speak(ae->flite_cbp.port, text, flags);
3836
+ pjmedia_flite_port_speak(ae->feature_cbps[FP_SPEECH_SYNTH].port, text, flags);
3803
3837
 
3804
3838
  return PJ_SUCCESS;
3805
3839
  }
@@ -3928,6 +3962,121 @@ out:
3928
3962
  return 0;
3929
3963
  }
3930
3964
 
3965
+ pj_status_t audio_endpoint_start_speech_recog(Call *call, AudioEndpoint *ae) {
3966
+ pj_status_t status;
3967
+
3968
+ if(!ae->stream_cbp.port) {
3969
+ set_error("stream port is not ready yet");
3970
+ return -1;
3971
+ }
3972
+
3973
+ // First stop and destroy existing port.
3974
+ status = audio_endpoint_stop_speech_recog(call, ae);
3975
+ if(status != PJ_SUCCESS) {
3976
+ return -1;
3977
+ }
3978
+
3979
+ if (!prepare_pocketsphinx(call, ae)) {
3980
+ return -1;
3981
+ }
3982
+
3983
+ return PJ_SUCCESS;
3984
+ }
3985
+
3986
+ int pjw_call_start_speech_recog(long call_id, const char *json) {
3987
+ PJW_LOCK();
3988
+ clear_error();
3989
+
3990
+ long val;
3991
+ Call *call;
3992
+
3993
+ pj_status_t status;
3994
+
3995
+ MediaEndpoint *me;
3996
+ AudioEndpoint *ae;
3997
+ int ae_count;
3998
+ int res;
3999
+
4000
+ int media_id = -1;
4001
+
4002
+ char *voice;
4003
+
4004
+ char *text;
4005
+
4006
+ bool end_of_speech_event = false;
4007
+
4008
+ unsigned flags = 0;
4009
+
4010
+ bool no_loop = false;
4011
+
4012
+ char buffer[MAX_JSON_INPUT];
4013
+
4014
+ Document document;
4015
+
4016
+ const char *valid_params[] = {"media_id", ""};
4017
+
4018
+ if (!g_call_ids.get(call_id, val)) {
4019
+ set_error("Invalid call_id");
4020
+ goto out;
4021
+ }
4022
+ call = (Call *)val;
4023
+
4024
+ ae_count = count_media_by_type(call, ENDPOINT_TYPE_AUDIO);
4025
+
4026
+ if (ae_count == 0) {
4027
+ set_error("No audio endpoint");
4028
+ goto out;
4029
+ }
4030
+
4031
+ if (!parse_json(document, json, buffer, MAX_JSON_INPUT)) {
4032
+ goto out;
4033
+ }
4034
+
4035
+ if (!validate_params(document, valid_params)) {
4036
+ goto out;
4037
+ }
4038
+
4039
+ res = json_get_int_param(document, "media_id", true, &media_id);
4040
+ if (res <= 0) {
4041
+ goto out;
4042
+ }
4043
+
4044
+ if (NOT_FOUND_OPTIONAL == res) {
4045
+ // start on all audio media endpoints
4046
+ for (int i = 0; i < call->media_count; i++) {
4047
+ MediaEndpoint *me = (MediaEndpoint *)call->media[i];
4048
+ if (me->type == ENDPOINT_TYPE_AUDIO) {
4049
+ AudioEndpoint *ae = (AudioEndpoint *)me->endpoint.audio;
4050
+ status = audio_endpoint_start_speech_recog(call, ae);
4051
+ if (status != PJ_SUCCESS) goto out;
4052
+ }
4053
+ }
4054
+ } else {
4055
+ if ((int)media_id >= call->media_count) {
4056
+ set_error("invalid media_id");
4057
+ goto out;
4058
+ }
4059
+
4060
+ me = (MediaEndpoint *)call->media[media_id];
4061
+ if (ENDPOINT_TYPE_AUDIO != me->type) {
4062
+ set_error("media_endpoint is not audio endpoint");
4063
+ goto out;
4064
+ }
4065
+
4066
+ ae = (AudioEndpoint *)me->endpoint.audio;
4067
+
4068
+ audio_endpoint_start_speech_recog(call, ae);
4069
+ }
4070
+
4071
+ out:
4072
+ PJW_UNLOCK();
4073
+ if (pjw_errorstring[0]) {
4074
+ return -1;
4075
+ }
4076
+
4077
+ return 0;
4078
+ }
4079
+
3931
4080
  pj_status_t call_stop_op_on_all_audio_endpoints(Call *call,
3932
4081
  audio_endpoint_stop_op_t op) {
3933
4082
  addon_log(L_DBG, "call_stop_op_on_audio_endpoints media_count=%d\n",
@@ -4017,28 +4166,27 @@ out:
4017
4166
  return 0;
4018
4167
  }
4019
4168
 
4020
- pj_status_t audio_endpoint_stop_speech_synth(Call *call, AudioEndpoint *ae) {
4021
- return audio_endpoint_remove_port(call, &ae->flite_cbp);
4022
- }
4023
-
4024
4169
  pj_status_t audio_endpoint_stop_play_wav(Call *call, AudioEndpoint *ae) {
4025
- return audio_endpoint_remove_port(call, &ae->wav_player_cbp);
4170
+ return audio_endpoint_remove_port(call, ae, &ae->feature_cbps[FP_WAV_PLAYER]);
4026
4171
  }
4027
4172
 
4028
4173
  pj_status_t audio_endpoint_stop_record_wav(Call *call, AudioEndpoint *ae) {
4029
- return audio_endpoint_remove_port(call, &ae->wav_writer_cbp);
4174
+ return audio_endpoint_remove_port(call, ae, &ae->feature_cbps[FP_WAV_WRITER]);
4030
4175
  }
4031
4176
 
4032
4177
  pj_status_t audio_endpoint_stop_fax(Call *call, AudioEndpoint *ae) {
4033
- return audio_endpoint_remove_port(call, &ae->fax_cbp);
4178
+ return audio_endpoint_remove_port(call, ae, &ae->feature_cbps[FP_FAX]);
4034
4179
  }
4035
4180
 
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]);
4183
+ }
4036
4184
 
4037
-
4038
- int pjw_call_stop_speech_synth(long call_id, const char *json) {
4039
- return audio_endpoint_stop_op(call_id, json, audio_endpoint_stop_speech_synth);
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]);
4040
4187
  }
4041
4188
 
4189
+
4042
4190
  int pjw_call_stop_play_wav(long call_id, const char *json) {
4043
4191
  return audio_endpoint_stop_op(call_id, json, audio_endpoint_stop_play_wav);
4044
4192
  }
@@ -4051,6 +4199,14 @@ int pjw_call_stop_fax(long call_id, const char *json) {
4051
4199
  return audio_endpoint_stop_op(call_id, json, audio_endpoint_stop_fax);
4052
4200
  }
4053
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
+
4054
4210
 
4055
4211
  int pjw_call_start_fax(long call_id, const char *json) {
4056
4212
  PJW_LOCK();
@@ -4523,82 +4679,77 @@ bool start_tcp_media(Call *call, MediaEndpoint *me,
4523
4679
  return true;
4524
4680
  }
4525
4681
 
4682
+ void close_audio_endpoint_ports_and_conf(Call *call, AudioEndpoint *ae) {
4683
+ pj_status_t status;
4526
4684
 
4527
- bool restart_media_stream(Call *call, MediaEndpoint *me,
4528
- const pjmedia_sdp_session *local_sdp,
4529
- const pjmedia_sdp_session *remote_sdp, int idx) {
4530
- char evt[4096];
4531
- pjmedia_stream_info stream_info;
4532
-
4533
- AudioEndpoint *ae = (AudioEndpoint *)me->endpoint.audio;
4534
-
4535
- pj_status_t status;
4685
+ audio_endpoint_remove_port(call, ae, &ae->stream_cbp);
4536
4686
 
4537
- if(ae->stream_cbp.port) {
4538
- if(ae->tonegen_cbp.port) {
4539
- status = pjmedia_conf_disconnect_port(call->conf, ae->tonegen_cbp.slot, ae->stream_cbp.slot);
4540
- if (status != PJ_SUCCESS) {
4541
- make_evt_media_update(evt, sizeof(evt), call->id,
4542
- "setup_failed (pjmedia_conf_disconnect_port for tonegen failed)", "");
4543
- dispatch_event(evt);
4544
- return false;
4545
- }
4687
+ for(int i=0 ; i<MAX_FP ; i++) {
4688
+ audio_endpoint_remove_port(call, ae, &ae->feature_cbps[i]);
4546
4689
  }
4547
4690
 
4548
- if(ae->wav_player_cbp.port) {
4549
- status = pjmedia_conf_disconnect_port(call->conf, ae->wav_player_cbp.slot, ae->stream_cbp.slot);
4550
- if (status != PJ_SUCCESS) {
4551
- make_evt_media_update(evt, sizeof(evt), call->id,
4552
- "setup_failed (pjmedia_conf_disconnect_port for wav_player failed)", "");
4553
- dispatch_event(evt);
4554
- return false;
4555
- }
4691
+ if (ae->master_port) {
4692
+ status = pjmedia_master_port_stop(ae->master_port);
4693
+ if(status != PJ_SUCCESS) {
4694
+ addon_log(L_DBG, "pjmedia_master_port_stop failed\n");
4695
+ }
4696
+ pjmedia_master_port_destroy(ae->master_port, 0);
4697
+ if(status != PJ_SUCCESS) {
4698
+ addon_log(L_DBG, "pjmedia_master_port_destroy failed\n");
4699
+ }
4700
+ ae->master_port = NULL;
4556
4701
  }
4557
4702
 
4558
- if(ae->dtmfdet_cbp.port) {
4559
- status = pjmedia_conf_disconnect_port(call->conf, ae->stream_cbp.slot, ae->dtmfdet_cbp.slot);
4560
- if (status != PJ_SUCCESS) {
4561
- make_evt_media_update(evt, sizeof(evt), call->id,
4562
- "setup_failed (pjmedia_conf_disconnect_port for dtmfdet failed)", "");
4563
- dispatch_event(evt);
4564
- return false;
4565
- }
4703
+ if (ae->conf) {
4704
+ status = pjmedia_conf_destroy(ae->conf);
4705
+ if(status != PJ_SUCCESS) {
4706
+ addon_log(L_DBG, "pjmedia_conf_destroy failed\n");
4707
+ }
4708
+ ae->conf = NULL;
4566
4709
  }
4567
4710
 
4568
- if(ae->fax_cbp.port) {
4569
- status = pjmedia_conf_disconnect_port(call->conf, ae->stream_cbp.slot, ae->fax_cbp.slot);
4570
- if (status != PJ_SUCCESS) {
4571
- make_evt_media_update(evt, sizeof(evt), call->id,
4572
- "setup_failed (pjmedia_conf_disconnect_port fax dst failed)", "");
4573
- dispatch_event(evt);
4574
- return false;
4575
- }
4576
-
4577
- status = pjmedia_conf_disconnect_port(call->conf, ae->fax_cbp.slot, ae->stream_cbp.slot);
4578
- if (status != PJ_SUCCESS) {
4579
- make_evt_media_update(evt, sizeof(evt), call->id,
4580
- "setup_failed (pjmedia_conf_disconnect_port for fax src failed)", "");
4581
- dispatch_event(evt);
4582
- return false;
4583
- }
4711
+ if (ae->null_port) {
4712
+ status = pjmedia_port_destroy(ae->null_port);
4713
+ if(status != PJ_SUCCESS) {
4714
+ addon_log(L_DBG, "pjmedia_port_destroy(null_port) failed\n");
4715
+ }
4716
+ ae->null_port = NULL;
4584
4717
  }
4718
+ }
4719
+
4720
+ bool connect_feature_port_to_stream_port(Call *call, AudioEndpoint *ae, ConfBridgePort *cbp) {
4721
+ pj_status_t status;
4585
4722
 
4586
- status = pjmedia_conf_remove_port(call->conf, ae->stream_cbp.slot);
4723
+ if(cbp->connection_mode == CONNECTION_MODE_SOURCE || cbp->connection_mode == CONNECTION_MODE_SOURCE_AND_SINK) {
4724
+ status = pjmedia_conf_connect_port(ae->conf, cbp->slot, ae->stream_cbp.slot, 0);
4587
4725
  if (status != PJ_SUCCESS) {
4588
- make_evt_media_update(evt, sizeof(evt), call->id,
4589
- "setup_failed (pjmedia_conf_remove_port failed)", "");
4726
+ set_error("pjmedia_conf_connect_port failed");
4590
4727
  return false;
4591
4728
  }
4592
- ae->stream_cbp.slot = 0;
4593
-
4594
- status = pjmedia_port_destroy(ae->stream_cbp.port);
4729
+ }
4730
+ if(cbp->connection_mode == CONNECTION_MODE_SINK || cbp->connection_mode == CONNECTION_MODE_SOURCE_AND_SINK) {
4731
+ status = pjmedia_conf_connect_port(ae->conf, ae->stream_cbp.slot, cbp->slot, 0);
4732
+ printf("status=%i\n" ,status);
4595
4733
  if (status != PJ_SUCCESS) {
4596
- make_evt_media_update(evt, sizeof(evt), call->id,
4597
- "setup_failed (pjmedia_port_destroy failed)", "");
4734
+ set_error("pjmedia_conf_connect_port failed");
4598
4735
  return false;
4599
4736
  }
4600
- ae->stream_cbp.port = NULL;
4601
- }
4737
+ }
4738
+ return true;
4739
+ }
4740
+
4741
+ bool restart_media_stream(Call *call, MediaEndpoint *me,
4742
+ const pjmedia_sdp_session *local_sdp,
4743
+ const pjmedia_sdp_session *remote_sdp, int idx) {
4744
+ char evt[4096];
4745
+ pjmedia_stream_info stream_info;
4746
+
4747
+ AudioEndpoint *ae = (AudioEndpoint *)me->endpoint.audio;
4748
+
4749
+ pj_status_t status;
4750
+
4751
+ pjmedia_port *old_port = ae->stream_cbp.port;
4752
+ pjmedia_port *new_port;
4602
4753
 
4603
4754
  status =
4604
4755
  pjmedia_stream_info_from_sdp(&stream_info, call->inv->dlg->pool,
@@ -4664,7 +4815,7 @@ bool restart_media_stream(Call *call, MediaEndpoint *me,
4664
4815
  return false;
4665
4816
  }
4666
4817
 
4667
- status = pjmedia_stream_get_port(ae->med_stream, &ae->stream_cbp.port);
4818
+ status = pjmedia_stream_get_port(ae->med_stream, &new_port);
4668
4819
  if (status != PJ_SUCCESS) {
4669
4820
  make_evt_media_update(evt, sizeof(evt), call->id,
4670
4821
  "setup_failed (pjmedia_stream_get_port failed)", "");
@@ -4672,73 +4823,90 @@ bool restart_media_stream(Call *call, MediaEndpoint *me,
4672
4823
  return false;
4673
4824
  }
4674
4825
 
4675
- status = pjmedia_conf_add_port(call->conf, call->inv->pool, ae->stream_cbp.port, NULL, &ae->stream_cbp.slot);
4676
- if (status != PJ_SUCCESS) {
4677
- make_evt_media_update(evt, sizeof(evt), call->id,
4678
- "setup_failed (pjmedia_conf_add_port failed)", "");
4679
- dispatch_event(evt);
4680
- return false;
4681
- }
4682
-
4683
- if(!ae->dtmfdet_cbp.port) {
4684
- if(!prepare_dtmfdet(call, ae)) {
4826
+ if(!old_port) {
4827
+ printf("call_id=%i restart_media_stream !old_port (first SDP negotiation)\n", call->id);
4828
+ status = create_audio_endpoint_conf(call, ae, new_port);
4829
+ if (status != PJ_SUCCESS) {
4685
4830
  make_evt_media_update(evt, sizeof(evt), call->id,
4686
- "setup_failed (prepare_dtmfdet failed)", "");
4831
+ "setup_failed (create_audio_endpoint_conf failed)", "");
4687
4832
  dispatch_event(evt);
4688
4833
  return false;
4689
4834
  }
4690
- }
4691
4835
 
4692
- if(ae->tonegen_cbp.port) {
4693
- status = pjmedia_conf_connect_port(call->conf, ae->tonegen_cbp.slot, ae->stream_cbp.slot, 0);
4836
+ ae->stream_cbp.port = new_port;
4837
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->stream_cbp.port, NULL, &ae->stream_cbp.slot);
4694
4838
  if (status != PJ_SUCCESS) {
4695
4839
  make_evt_media_update(evt, sizeof(evt), call->id,
4696
- "setup_failed (pjmedia_conf_connect_port for tonegen failed)", "");
4840
+ "setup_failed (pjmedia_conf_add_port failed)", "");
4697
4841
  dispatch_event(evt);
4698
4842
  return false;
4699
4843
  }
4700
- }
4701
4844
 
4702
- if(ae->wav_player_cbp.port) {
4703
- status = pjmedia_conf_connect_port(call->conf, ae->wav_player_cbp.slot, ae->stream_cbp.slot, 0);
4704
- if (status != PJ_SUCCESS) {
4845
+ // we always add dtmfdet to audio endpoints
4846
+ if(!prepare_dtmfdet(call, ae)) {
4705
4847
  make_evt_media_update(evt, sizeof(evt), call->id,
4706
- "setup_failed (pjmedia_conf_connect_port for wav_player failed)", "");
4848
+ "setup_failed (prepare_dtmfdet failed)", "");
4707
4849
  dispatch_event(evt);
4708
4850
  return false;
4709
4851
  }
4710
- }
4711
-
4712
- if(ae->dtmfdet_cbp.port) {
4713
- status = pjmedia_conf_connect_port(call->conf, ae->stream_cbp.slot, ae->dtmfdet_cbp.slot, 0);
4852
+ } else if(
4853
+ (PJMEDIA_PIA_SRATE(&old_port->info) != PJMEDIA_PIA_SRATE(&new_port->info)) ||
4854
+ (PJMEDIA_PIA_CCNT(&old_port->info) != PJMEDIA_PIA_CCNT(&new_port->info)) ||
4855
+ (PJMEDIA_PIA_SPF(&old_port->info) != PJMEDIA_PIA_SPF(&new_port->info)) ||
4856
+ (PJMEDIA_PIA_BITS(&old_port->info) != PJMEDIA_PIA_BITS(&new_port->info))) {
4857
+ // stream characteristics changed so we need to destroy old conf and ports
4858
+ printf("call_id=%i restart_media_stream: stream characteristics changed\n", call->id);
4859
+ close_audio_endpoint_ports_and_conf(call, ae);
4860
+
4861
+ // then create a new conf
4862
+ status = create_audio_endpoint_conf(call, ae, new_port);
4714
4863
  if (status != PJ_SUCCESS) {
4715
4864
  make_evt_media_update(evt, sizeof(evt), call->id,
4716
- "setup_failed (pjmedia_conf_connect_port for dtmfdet failed)", "");
4865
+ "setup_failed (create_audio_endpoint_conf failed)", "");
4717
4866
  dispatch_event(evt);
4718
4867
  return false;
4719
-
4720
4868
  }
4721
- }
4722
4869
 
4723
- if(ae->fax_cbp.port) {
4724
- status = pjmedia_conf_connect_port(call->conf, ae->stream_cbp.slot, ae->fax_cbp.slot, 0);
4870
+ ae->stream_cbp.port = new_port;
4871
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->stream_cbp.port, NULL, &ae->stream_cbp.slot);
4725
4872
  if (status != PJ_SUCCESS) {
4726
4873
  make_evt_media_update(evt, sizeof(evt), call->id,
4727
- "setup_failed (pjmedia_conf_connect_port for fax dst failed)", "");
4874
+ "setup_failed (pjmedia_conf_add_port failed)", "");
4728
4875
  dispatch_event(evt);
4729
4876
  return false;
4877
+ }
4730
4878
 
4879
+ // we always add dtmfdet to audio endpoints
4880
+ if(!prepare_dtmfdet(call, ae)) {
4881
+ make_evt_media_update(evt, sizeof(evt), call->id,
4882
+ "setup_failed (prepare_dtmfdet failed)", "");
4883
+ dispatch_event(evt);
4884
+ return false;
4731
4885
  }
4732
4886
 
4733
- status = pjmedia_conf_connect_port(call->conf, ae->fax_cbp.slot, ae->stream_cbp.slot, 0);
4887
+ // at this point we could try to recreate ports (see #91)
4888
+ } else {
4889
+ printf("call_id=%i restart_media_stream: stream characteristics no change\n", call->id);
4890
+ // stream characteristics didn't change so just replace the stream port
4891
+
4892
+ audio_endpoint_remove_port(call, ae, &ae->stream_cbp);
4893
+ ae->stream_cbp.port = new_port;
4894
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->stream_cbp.port, NULL, &ae->stream_cbp.slot);
4734
4895
  if (status != PJ_SUCCESS) {
4735
4896
  make_evt_media_update(evt, sizeof(evt), call->id,
4736
- "setup_failed (pjmedia_conf_connect_port for fax src failed)", "");
4897
+ "setup_failed (pjmedia_conf_add_port failed)", "");
4737
4898
  dispatch_event(evt);
4738
4899
  return false;
4739
4900
  }
4740
- }
4741
4901
 
4902
+ // Need to connect ports to new stream port
4903
+ for(int i=0 ; i<MAX_FP ; i++) {
4904
+ if(ae->feature_cbps[i].port) {
4905
+ if(!connect_feature_port_to_stream_port(call, ae, &ae->feature_cbps[i])) return false;
4906
+ }
4907
+ }
4908
+ }
4909
+
4742
4910
  return true;
4743
4911
  }
4744
4912
 
@@ -4925,8 +5093,6 @@ static void on_state_changed(pjsip_inv_session *inv, pjsip_event *e) {
4925
5093
  }
4926
5094
  }
4927
5095
 
4928
- release_call_conf(call);
4929
-
4930
5096
  long val;
4931
5097
  if (!g_call_ids.remove(call_id, val)) {
4932
5098
  addon_log(L_DBG, "g_call_ids.remove failed\n");
@@ -5231,7 +5397,7 @@ static pj_bool_t on_rx_request(pjsip_rx_data *rdata) {
5231
5397
 
5232
5398
  long transport_id;
5233
5399
 
5234
- // printf("tag=%s\n", tag);
5400
+ //printf("on_rx_request transport_tag=%s\n", tag);
5235
5401
 
5236
5402
  TransportMap::iterator iter = g_TransportMap.find(tag);
5237
5403
  if (iter != g_TransportMap.end()) {
@@ -5317,13 +5483,6 @@ static pj_bool_t on_rx_request(pjsip_rx_data *rdata) {
5317
5483
  return PJ_TRUE;
5318
5484
  }
5319
5485
 
5320
- status = setup_call_conf(call);
5321
- if (status != PJ_SUCCESS) {
5322
- printf("setup_call_conf failed\n");
5323
- pjsip_endpt_respond_stateless(g_sip_endpt, rdata, 500, &reason, NULL, NULL);
5324
- return PJ_TRUE;
5325
- }
5326
-
5327
5486
  // TODO: check if this is really necessary as we are calling
5328
5487
  // pjsip_dlg_add_usage subsequently
5329
5488
  inv->dlg->mod_data[mod_tester.id] = call;
@@ -5349,7 +5508,7 @@ static pj_bool_t on_rx_request(pjsip_rx_data *rdata) {
5349
5508
 
5350
5509
  long transport_id;
5351
5510
 
5352
- // printf("tag=%s\n", tag);
5511
+ printf("on_rx_request INVITE transport_tag=%s\n", tag);
5353
5512
 
5354
5513
  TransportMap::iterator iter = g_TransportMap.find(tag);
5355
5514
  if (iter != g_TransportMap.end()) {
@@ -6511,10 +6670,10 @@ bool process_media(Call *call, pjsip_dialog *dlg, Document &document, bool answe
6511
6670
  return true;
6512
6671
  }
6513
6672
 
6514
- bool is_media_active(Call *c, MediaEndpoint *me) {
6673
+ bool is_media_active(Call *call, MediaEndpoint *me) {
6515
6674
  // check if media from call->media_neg is on call->media
6516
- for (int i = 0; i < c->media_count; ++i) {
6517
- if (me == c->media[i])
6675
+ for (int i = 0; i < call->media_count; ++i) {
6676
+ if (me == call->media[i])
6518
6677
  return true;
6519
6678
  }
6520
6679
  return false;
@@ -6522,19 +6681,14 @@ bool is_media_active(Call *c, MediaEndpoint *me) {
6522
6681
 
6523
6682
  void close_media_endpoint(Call *call, MediaEndpoint *me) {
6524
6683
  printf("close_media_endpoint %p\n", (void*)me);
6684
+
6685
+ pj_status_t status;
6686
+
6525
6687
  if(!me) return;
6526
6688
 
6527
6689
  if (ENDPOINT_TYPE_AUDIO == me->type) {
6528
6690
  AudioEndpoint *ae = (AudioEndpoint *)me->endpoint.audio;
6529
-
6530
- audio_endpoint_remove_port(call, &ae->stream_cbp);
6531
- audio_endpoint_remove_port(call, &ae->wav_player_cbp);
6532
- audio_endpoint_remove_port(call, &ae->wav_writer_cbp);
6533
- audio_endpoint_remove_port(call, &ae->tonegen_cbp);
6534
- audio_endpoint_remove_port(call, &ae->dtmfdet_cbp);
6535
- audio_endpoint_remove_port(call, &ae->fax_cbp);
6536
- audio_endpoint_remove_port(call, &ae->flite_cbp);
6537
-
6691
+ close_audio_endpoint_ports_and_conf(call, ae);
6538
6692
  close_media_transport(ae->med_transport);
6539
6693
  ae->med_transport = NULL;
6540
6694
  } else if (ENDPOINT_TYPE_MRCP == me->type) {
@@ -6558,63 +6712,63 @@ void close_media_endpoint(Call *call, MediaEndpoint *me) {
6558
6712
  me->port = 0;
6559
6713
  }
6560
6714
 
6561
- void close_media(Call *c) {
6562
- printf("close_media call_id=%i\n", c->id);
6563
- for (int i = 0; i < c->media_count; ++i) {
6564
- MediaEndpoint *me = c->media[i];
6565
- close_media_endpoint(c, me);
6715
+ void close_media(Call *call) {
6716
+ printf("close_media call_id=%i\n", call->id);
6717
+ for (int i = 0; i < call->media_count; ++i) {
6718
+ MediaEndpoint *me = call->media[i];
6719
+ close_media_endpoint(call, me);
6566
6720
  }
6567
- c->media_count = 0;
6721
+ call->media_count = 0;
6568
6722
  }
6569
6723
 
6570
- bool prepare_tonegen(Call *c, AudioEndpoint *ae) {
6571
- printf("prepare_tone_gen call.id=%i\n", c->id);
6724
+ bool prepare_tonegen(Call *call, AudioEndpoint *ae) {
6725
+ printf("prepare_tone_gen call.id=%i\n", call->id);
6572
6726
  pj_status_t status;
6573
6727
 
6574
- if(ae->tonegen_cbp.port) {
6728
+ ConfBridgePort *fp = &ae->feature_cbps[FP_TONEGEN];
6729
+
6730
+ if(fp->port) {
6575
6731
  printf("already prepared\n");
6576
6732
  return true;
6577
6733
  }
6578
6734
 
6579
6735
  status = pjmedia_tonegen_create(
6580
- c->inv->pool, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6736
+ call->inv->pool, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6581
6737
  PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info),
6582
6738
  PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6583
- PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info), 0, &ae->tonegen_cbp.port);
6739
+ PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info), 0, &fp->port);
6584
6740
  if (status != PJ_SUCCESS) {
6585
6741
  set_error("pjmedia_tonegen_create failed");
6586
6742
  return false;
6587
6743
  }
6588
6744
 
6589
- status = pjmedia_conf_add_port(c->conf, c->inv->pool, ae->tonegen_cbp.port, NULL, &ae->tonegen_cbp.slot);
6745
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, fp->port, NULL, &fp->slot);
6590
6746
  if (status != PJ_SUCCESS) {
6591
6747
  set_error("pjmedia_conf_add_port failed");
6592
6748
  return false;
6593
6749
  }
6594
6750
 
6595
- status = pjmedia_conf_connect_port(c->conf, ae->tonegen_cbp.slot, ae->stream_cbp.slot, 0);
6596
- if (status != PJ_SUCCESS) {
6597
- set_error("pjmedia_conf_connect_port failed");
6598
- return false;
6599
- }
6751
+ fp->connection_mode = CONNECTION_MODE_SOURCE;
6600
6752
 
6601
- return true;
6753
+ return connect_feature_port_to_stream_port(call, ae, fp);
6602
6754
  }
6603
6755
 
6604
- bool prepare_wav_player(Call *c, AudioEndpoint *ae, const char *file, unsigned flags, bool end_of_file_event) {
6756
+ bool prepare_wav_player(Call *call, AudioEndpoint *ae, const char *file, unsigned flags, bool end_of_file_event) {
6605
6757
  pj_status_t status;
6606
6758
 
6759
+ ConfBridgePort *fp = &ae->feature_cbps[FP_WAV_PLAYER];
6760
+
6607
6761
  unsigned wav_ptime;
6608
6762
  wav_ptime = PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info) * 1000 /
6609
6763
  PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info);
6610
6764
 
6611
6765
  status = pjmedia_wav_player_port_create(
6612
- c->inv->pool,
6766
+ call->inv->pool,
6613
6767
  file,
6614
6768
  wav_ptime,
6615
6769
  flags,
6616
6770
  -1, /* buf size */
6617
- &ae->wav_player_cbp.port
6771
+ &fp->port
6618
6772
  );
6619
6773
 
6620
6774
  if (status != PJ_SUCCESS) {
@@ -6623,164 +6777,181 @@ bool prepare_wav_player(Call *c, AudioEndpoint *ae, const char *file, unsigned f
6623
6777
  }
6624
6778
 
6625
6779
  if (end_of_file_event) {
6626
- status = pjmedia_wav_player_set_eof_cb2(ae->wav_player_cbp.port, (void*)c, on_end_of_file);
6780
+ status = pjmedia_wav_player_set_eof_cb2(fp->port, (void*)call, on_end_of_file);
6627
6781
  if (status != PJ_SUCCESS) {
6628
6782
  set_error("pjmedia_wav_player_set_eof_cb2 failed");
6629
6783
  return false;
6630
6784
  }
6631
6785
  }
6632
6786
 
6633
- status = pjmedia_conf_add_port(c->conf, c->inv->pool, ae->wav_player_cbp.port, NULL, &ae->wav_player_cbp.slot);
6787
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, fp->port, NULL, &fp->slot);
6634
6788
  if (status != PJ_SUCCESS) {
6635
6789
  set_error("pjmedia_conf_add_port failed");
6636
6790
  return false;
6637
6791
  }
6638
6792
 
6639
- status = pjmedia_conf_connect_port(c->conf, ae->wav_player_cbp.slot, ae->stream_cbp.slot, 0);
6640
- if (status != PJ_SUCCESS) {
6641
- set_error("pjmedia_conf_connect_port failed");
6642
- return false;
6643
- }
6793
+ fp->connection_mode = CONNECTION_MODE_SOURCE;
6644
6794
 
6645
- return true;
6795
+ return connect_feature_port_to_stream_port(call, ae, fp);
6646
6796
  }
6647
6797
 
6648
- bool prepare_wav_writer(Call *c, AudioEndpoint *ae, const char *file) {
6798
+ bool prepare_wav_writer(Call *call, AudioEndpoint *ae, const char *file) {
6649
6799
  pj_status_t status;
6650
6800
 
6801
+ ConfBridgePort *fp = &ae->feature_cbps[FP_WAV_WRITER];
6802
+
6651
6803
  status = pjmedia_wav_writer_port_create(
6652
- c->inv->pool, file, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6804
+ call->inv->pool, file, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6653
6805
  PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info), PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6654
6806
  PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info), PJMEDIA_FILE_WRITE_PCM, 0,
6655
- (pjmedia_port **)&ae->wav_writer_cbp.port);
6807
+ (pjmedia_port **)&fp->port);
6656
6808
  if (status != PJ_SUCCESS) {
6657
6809
  set_error("pjmedia_wav_write_port_create failed");
6658
6810
  return false;
6659
6811
  }
6660
6812
 
6661
- status = pjmedia_conf_add_port(c->conf, c->inv->pool, ae->wav_writer_cbp.port, NULL, &ae->wav_writer_cbp.slot);
6813
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, fp->port, NULL, &fp->slot);
6662
6814
  if (status != PJ_SUCCESS) {
6663
6815
  set_error("pjmedia_conf_add_port failed");
6664
6816
  return false;
6665
6817
  }
6666
6818
 
6667
- status = pjmedia_conf_connect_port(c->conf, ae->stream_cbp.slot, ae->wav_writer_cbp.slot, 0);
6668
- if (status != PJ_SUCCESS) {
6669
- set_error("pjmedia_conf_connect_port failed");
6670
- return false;
6671
- }
6819
+ fp->connection_mode = CONNECTION_MODE_SINK;
6672
6820
 
6673
- return true;
6821
+ return connect_feature_port_to_stream_port(call, ae, fp);
6674
6822
  }
6675
6823
 
6676
- bool prepare_dtmfdet(Call *c, AudioEndpoint *ae) {
6824
+ bool prepare_dtmfdet(Call *call, AudioEndpoint *ae) {
6677
6825
  pj_status_t status;
6826
+
6827
+ ConfBridgePort *fp = &ae->feature_cbps[FP_DTMFDET];
6828
+
6678
6829
  status = pjmedia_dtmfdet_create(
6679
- c->inv->pool,
6830
+ call->inv->pool,
6680
6831
  PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6681
- PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info), PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6832
+ PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info),
6833
+ PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6682
6834
  PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info),
6683
- on_inband_dtmf, c, &ae->dtmfdet_cbp.port);
6835
+ on_inband_dtmf, call, &fp->port);
6684
6836
  if (status != PJ_SUCCESS) {
6685
6837
  set_error("pjmedia_dtmfdet_create failed");
6686
6838
  return false;
6687
6839
  }
6688
6840
 
6689
- status = pjmedia_conf_add_port(c->conf, c->inv->pool, ae->dtmfdet_cbp.port, NULL, &ae->dtmfdet_cbp.slot);
6841
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, fp->port, NULL, &fp->slot);
6690
6842
  if (status != PJ_SUCCESS) {
6691
6843
  set_error("pjmedia_conf_add_port failed");
6692
6844
  return false;
6693
6845
  }
6694
6846
 
6695
- status = pjmedia_conf_connect_port(c->conf, ae->stream_cbp.slot, ae->dtmfdet_cbp.slot, 0);
6696
- if (status != PJ_SUCCESS) {
6697
- set_error("pjmedia_conf_connect_port failed");
6698
- return false;
6699
- }
6847
+ fp->connection_mode = CONNECTION_MODE_SINK;
6700
6848
 
6701
- return true;
6849
+ return connect_feature_port_to_stream_port(call, ae, fp);
6702
6850
  }
6703
6851
 
6704
- bool prepare_fax(Call *c, AudioEndpoint *ae, bool is_sender, const char *file,
6705
- unsigned flags) {
6852
+ bool prepare_fax(Call *call, AudioEndpoint *ae, bool is_sender, const char *file, unsigned flags) {
6706
6853
  pj_status_t status;
6707
6854
 
6855
+ ConfBridgePort *fp = &ae->feature_cbps[FP_FAX];
6856
+
6708
6857
  status = pjmedia_fax_port_create(
6709
- c->inv->pool,
6858
+ call->inv->pool,
6710
6859
  PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6711
6860
  PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info),
6712
6861
  PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6713
6862
  PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info),
6714
- on_fax_result, c, is_sender, file,
6715
- flags, &ae->fax_cbp.port);
6863
+ on_fax_result, call, is_sender, file,
6864
+ flags, &fp->port);
6716
6865
  if (status != PJ_SUCCESS) {
6717
6866
  set_error("pjmedia_fax_port_create failed");
6718
6867
  return false;
6719
6868
  }
6720
6869
 
6721
- status = pjmedia_conf_add_port(c->conf, c->inv->pool, ae->fax_cbp.port, NULL, &ae->fax_cbp.slot);
6870
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, fp->port, NULL, &fp->slot);
6722
6871
  if (status != PJ_SUCCESS) {
6723
6872
  set_error("pjmedia_conf_add_port failed");
6724
6873
  return false;
6725
6874
  }
6726
6875
 
6727
- status = pjmedia_conf_connect_port(c->conf, ae->fax_cbp.slot, ae->stream_cbp.slot, 0);
6728
- if (status != PJ_SUCCESS) {
6729
- set_error("pjmedia_conf_connect_port failed");
6730
- return false;
6731
- }
6876
+ fp->connection_mode = CONNECTION_MODE_SOURCE_AND_SINK;
6732
6877
 
6733
- status = pjmedia_conf_connect_port(c->conf, ae->stream_cbp.slot, ae->fax_cbp.slot, 0);
6734
- if (status != PJ_SUCCESS) {
6735
- set_error("pjmedia_conf_connect_port failed");
6736
- return false;
6737
- }
6738
-
6739
- return true;
6878
+ return connect_feature_port_to_stream_port(call, ae, fp);
6740
6879
  }
6741
6880
 
6742
- bool prepare_flite(Call *c, AudioEndpoint *ae, const char *voice, bool end_of_speech_event) {
6743
- printf("prepare_flite call.id=%i\n", c->id);
6881
+ bool prepare_flite(Call *call, AudioEndpoint *ae, const char *voice, bool end_of_speech_event) {
6744
6882
  pj_status_t status;
6745
6883
 
6746
- if(ae->flite_cbp.port) {
6884
+ ConfBridgePort *fp = &ae->feature_cbps[FP_SPEECH_SYNTH];
6885
+
6886
+ if(fp->port) {
6747
6887
  printf("already prepared\n");
6748
6888
  return true;
6749
6889
  }
6750
6890
 
6751
6891
  status = pjmedia_flite_port_create(
6752
- c->inv->pool, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6892
+ call->inv->pool, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6753
6893
  PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info),
6754
6894
  PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6755
- PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info), voice, &ae->flite_cbp.port);
6895
+ PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info),
6896
+ voice,
6897
+ &fp->port);
6756
6898
  if (status != PJ_SUCCESS) {
6757
6899
  set_error("pjmedia_flite_port_create failed");
6758
6900
  return false;
6759
6901
  }
6760
6902
 
6761
6903
  if (end_of_speech_event) {
6762
- status = pjmedia_flite_port_set_eof_cb(ae->flite_cbp.port, (void*)c, on_end_of_speech);
6904
+ status = pjmedia_flite_port_set_eof_cb(fp->port, (void*)call, on_end_of_speech);
6763
6905
  if (status != PJ_SUCCESS) {
6764
6906
  set_error("pjmedia_flite_port_set_eof_cb failed");
6765
6907
  return false;
6766
6908
  }
6767
6909
  }
6768
6910
 
6769
- status = pjmedia_conf_add_port(c->conf, c->inv->pool, ae->flite_cbp.port, NULL, &ae->flite_cbp.slot);
6911
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, fp->port, NULL, &fp->slot);
6770
6912
  if (status != PJ_SUCCESS) {
6771
6913
  set_error("pjmedia_conf_add_port failed");
6772
6914
  return false;
6773
6915
  }
6774
6916
 
6775
- status = pjmedia_conf_connect_port(c->conf, ae->flite_cbp.slot, ae->stream_cbp.slot, 0);
6917
+ fp->connection_mode = CONNECTION_MODE_SOURCE;
6918
+
6919
+ return connect_feature_port_to_stream_port(call, ae, fp);
6920
+ }
6921
+
6922
+ bool prepare_pocketsphinx(Call *call, AudioEndpoint *ae) {
6923
+ pj_status_t status;
6924
+
6925
+ ConfBridgePort *fp = &ae->feature_cbps[FP_SPEECH_RECOG];
6926
+
6927
+ if(fp->port) {
6928
+ printf("already prepared\n");
6929
+ return true;
6930
+ }
6931
+
6932
+ status = pjmedia_pocketsphinx_port_create(
6933
+ call->inv->pool, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6934
+ PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info),
6935
+ PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6936
+ PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info),
6937
+ on_speech_transcript,
6938
+ call,
6939
+ &fp->port);
6776
6940
  if (status != PJ_SUCCESS) {
6777
- set_error("pjmedia_conf_connect_port failed");
6941
+ set_error("pjmedia_pocketsphinx_port_create failed");
6778
6942
  return false;
6779
6943
  }
6780
6944
 
6781
- return true;
6782
- }
6945
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, fp->port, NULL, &fp->slot);
6946
+ if (status != PJ_SUCCESS) {
6947
+ set_error("pjmedia_conf_add_port failed");
6948
+ return false;
6949
+ }
6783
6950
 
6951
+ fp->connection_mode = CONNECTION_MODE_SINK;
6952
+
6953
+ return connect_feature_port_to_stream_port(call, ae, fp);
6954
+ }
6784
6955
 
6785
6956
  void on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code,
6786
6957
  pj_str_t **p_st_text, pjsip_hdr *res_hdr,
@@ -8168,8 +8339,8 @@ static int g_now;
8168
8339
 
8169
8340
  void check_digit_buffer(Call *call, int mode) {
8170
8341
  // addon_log(L_DBG, "check_digit_buffer g_now=%i for call_id=%i and mode=%i
8171
- // timestamp=%i len=%i\n", g_now, c->id, mode, c->last_digit_timestamp[mode],
8172
- // c->DigitBufferLength[mode]);
8342
+ // timestamp=%i len=%i\n", g_now, call->id, mode, call->last_digit_timestamp[mode],
8343
+ // call->DigitBufferLength[mode]);
8173
8344
  char evt[1024];
8174
8345
 
8175
8346
  for (int i = 0; i < call->media_count; i++) {
@@ -8193,10 +8364,10 @@ void check_digit_buffer(Call *call, int mode) {
8193
8364
  }
8194
8365
 
8195
8366
  void check_digit_buffers(long id, long val) {
8196
- Call *c = (Call *)val;
8367
+ Call *call = (Call *)val;
8197
8368
 
8198
- check_digit_buffer(c, DTMF_MODE_RFC2833);
8199
- check_digit_buffer(c, DTMF_MODE_INBAND);
8369
+ check_digit_buffer(call, DTMF_MODE_RFC2833);
8370
+ check_digit_buffer(call, DTMF_MODE_INBAND);
8200
8371
  }
8201
8372
 
8202
8373
  static int digit_buffer_thread(void *arg) {