sip-lab 1.22.0 → 1.24.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.
Files changed (34) hide show
  1. package/README.md +5 -4
  2. package/binding.gyp +4 -0
  3. package/build_deps.sh +21 -1
  4. package/index.js +19 -0
  5. package/package.json +3 -2
  6. package/pocketsphinx/model/CMakeLists.txt +3 -0
  7. package/pocketsphinx/model/en-us/cmudict-en-us.dict +134782 -0
  8. package/pocketsphinx/model/en-us/en-us/README +34 -0
  9. package/pocketsphinx/model/en-us/en-us/feat.params +12 -0
  10. package/pocketsphinx/model/en-us/en-us/mdef +0 -0
  11. package/pocketsphinx/model/en-us/en-us/means +0 -0
  12. package/pocketsphinx/model/en-us/en-us/noisedict +5 -0
  13. package/pocketsphinx/model/en-us/en-us/sendump +0 -0
  14. package/pocketsphinx/model/en-us/en-us/transition_matrices +0 -0
  15. package/pocketsphinx/model/en-us/en-us/variances +0 -0
  16. package/pocketsphinx/model/en-us/en-us-phone.lm.bin +0 -0
  17. package/pocketsphinx/model/en-us/en-us.lm.bin +0 -0
  18. package/prebuilds/linux-x64/sip-lab.node +0 -0
  19. package/samples/artifacts/hello_good_morning.wav +0 -0
  20. package/samples/play_wav_and_speech_recog.bad_transcript.pcmu8000.js +182 -0
  21. package/samples/speech_synth_and_recog.speex16000.js +186 -0
  22. package/samples/start_play_wav_with_end_of_file_event.js +269 -0
  23. package/samples/start_play_wav_with_no_loop.js +257 -0
  24. package/samples/tcp_and_extra_headers.js +47 -1
  25. package/samples/text_to_speech.js +22 -3
  26. package/src/addon.cpp +72 -0
  27. package/src/event_templates.cpp +20 -7
  28. package/src/event_templates.hpp +6 -0
  29. package/src/pjmedia/include/pjmedia/flite_port.h +10 -4
  30. package/src/pjmedia/include/pjmedia/pocketsphinx_port.h +19 -0
  31. package/src/pjmedia/src/pjmedia/flite_port.c +91 -25
  32. package/src/pjmedia/src/pjmedia/pocketsphinx_port.c +273 -0
  33. package/src/sip.cpp +707 -507
  34. package/src/sip.hpp +5 -0
package/src/sip.cpp CHANGED
@@ -23,6 +23,7 @@
23
23
  #include "dtmfdet.h"
24
24
  #include "fax_port.h"
25
25
  #include "flite_port.h"
26
+ #include "pocketsphinx_port.h"
26
27
 
27
28
  #include <ctime>
28
29
 
@@ -265,14 +266,6 @@ pjsip_route_hdr route_set;
265
266
  pjsip_route_hdr *route;
266
267
  const pj_str_t hname = pj_str((char *)"Route");
267
268
 
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
269
  #define MAXDIGITS 256
277
270
 
278
271
  #define DTMF_MODE_RFC2833 0
@@ -324,6 +317,10 @@ struct AudioEndpoint {
324
317
 
325
318
  pj_str_t mode;
326
319
 
320
+ pjmedia_conf *conf;
321
+ pjmedia_master_port *master_port;
322
+ pjmedia_port *null_port;
323
+
327
324
  ConfBridgePort stream_cbp;
328
325
  ConfBridgePort wav_player_cbp;
329
326
  ConfBridgePort wav_writer_cbp;
@@ -331,6 +328,7 @@ struct AudioEndpoint {
331
328
  ConfBridgePort dtmfdet_cbp;
332
329
  ConfBridgePort fax_cbp;
333
330
  ConfBridgePort flite_cbp;
331
+ ConfBridgePort pocketsphinx_cbp;
334
332
  };
335
333
 
336
334
  struct VideoEndpoint {
@@ -433,10 +431,6 @@ struct Call {
433
431
  pjmedia_sdp_session *active_remote_sdp;
434
432
 
435
433
  bool local_sdp_answer_already_set;
436
-
437
- pjmedia_conf *conf;
438
- pjmedia_master_port *master_port;
439
- pjmedia_port *null_port;
440
434
  };
441
435
 
442
436
  #define MAX_TCP_DATA 4096
@@ -472,6 +466,10 @@ PackageSet g_PackageSet;
472
466
 
473
467
  #define DEFAULT_EXPIRES 600
474
468
 
469
+ #define CONNECTION_MODE_SOURCE 0
470
+ #define CONNECTION_MODE_SINK 1
471
+ #define CONNECTION_MODE_SOURCE_AND_SINK 2
472
+
475
473
  void handle_events() {
476
474
  pj_time_val tv = {0, 1};
477
475
  pjsip_endpt_handle_events(g_sip_endpt, &tv);
@@ -618,30 +616,31 @@ static void build_stream_stat(ostringstream &oss, pjmedia_rtcp_stat *stat,
618
616
 
619
617
  bool prepare_tonegen(Call *call, AudioEndpoint *ae);
620
618
  bool prepare_dtmfdet(Call *call, AudioEndpoint *ae);
621
- bool prepare_wav_player(Call *c, AudioEndpoint *ae, const char *file);
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);
619
+ bool prepare_wav_player(Call *call, AudioEndpoint *ae, const char *file, unsigned flags, bool end_of_file_event);
620
+ bool prepare_wav_writer(Call *call, AudioEndpoint *ae, const char *file);
621
+ bool prepare_fax(Call *call, AudioEndpoint *ae, bool is_sender, const char *file, unsigned flags);
622
+ bool prepare_flite(Call *call, AudioEndpoint *ae, const char *voice, bool end_of_speech_event);
623
+ bool prepare_pocketsphinx(Call *call, AudioEndpoint *ae);
626
624
 
627
625
  void prepare_error_event(ostringstream *oss, char *scope, char *details);
628
626
  // void prepare_pjsipcall_error_event(ostringstream *oss, char *scope, char
629
627
  // *function, pj_status_t s);
630
628
  void append_status(ostringstream *oss, pj_status_t s);
631
629
 
632
- bool is_media_active(Call *c, MediaEndpoint *me);
630
+ bool is_media_active(Call *call, MediaEndpoint *me);
633
631
  void close_media_endpoint(Call *call, MediaEndpoint *me);
634
632
 
635
- void close_media(Call *c);
633
+ void close_media(Call *call);
636
634
 
637
- bool process_media(Call *c, pjsip_dialog *dlg, Document &document, bool answer);
635
+ bool process_media(Call *call, pjsip_dialog *dlg, Document &document, bool answer);
638
636
 
639
637
  typedef pj_status_t (*audio_endpoint_stop_op_t)(Call *call, AudioEndpoint *ae);
640
638
 
641
639
  pj_status_t audio_endpoint_stop_play_wav(Call *call, AudioEndpoint *ae);
642
640
  pj_status_t audio_endpoint_stop_record_wav(Call *call, AudioEndpoint *ae);
643
641
  pj_status_t audio_endpoint_stop_fax(Call *call, AudioEndpoint *ae);
644
- pj_status_t audio_endpoint_stop_flite(Call *call, AudioEndpoint *ae);
642
+ pj_status_t audio_endpoint_stop_speech_synth(Call *call, AudioEndpoint *ae);
643
+ pj_status_t audio_endpoint_stop_speech_recog(Call *call, AudioEndpoint *ae);
645
644
 
646
645
  static pjsip_module mod_tester = {
647
646
  NULL,
@@ -716,23 +715,36 @@ find_endpoint_by_inband_dtmf_media_stream(Call *call,
716
715
  return -1;
717
716
  }
718
717
 
719
- pj_status_t setup_call_conf(Call *call) {
718
+ pj_status_t create_audio_endpoint_conf(Call *call, AudioEndpoint *ae, pjmedia_port *stream_port) {
720
719
  pj_status_t status;
720
+ int conf_ports = 16;
721
+
722
+ unsigned sampling_rate = PJMEDIA_PIA_SRATE(&stream_port->info);
723
+ unsigned channel_count = PJMEDIA_PIA_CCNT(&stream_port->info);
724
+ unsigned samples_per_frame = PJMEDIA_PIA_SPF(&stream_port->info);
725
+ unsigned bits_per_sample = PJMEDIA_PIA_BITS(&stream_port->info);
726
+
721
727
  status = pjmedia_conf_create(call->inv->pool,
722
- CONF_PORTS,
723
- CLOCK_RATE,
724
- CHANNEL_COUNT,
725
- SAMPLES_PER_FRAME,
726
- BITS_PER_SAMPLE,
728
+ conf_ports,
729
+ sampling_rate,
730
+ channel_count,
731
+ samples_per_frame,
732
+ bits_per_sample,
727
733
  PJMEDIA_CONF_NO_DEVICE,
728
- &call->conf);
734
+ &ae->conf);
729
735
 
730
736
  if (status != PJ_SUCCESS) {
731
737
  addon_log(L_DBG, "pjmedia_conf_create failed\n");
732
738
  return false;
733
739
  }
734
740
 
735
- status = pjmedia_null_port_create(call->inv->pool, CLOCK_RATE, CHANNEL_COUNT, SAMPLES_PER_FRAME, BITS_PER_SAMPLE, &call->null_port);
741
+ status = pjmedia_null_port_create(
742
+ call->inv->pool,
743
+ sampling_rate,
744
+ channel_count,
745
+ samples_per_frame,
746
+ bits_per_sample,
747
+ &ae->null_port);
736
748
  if (status != PJ_SUCCESS) {
737
749
  addon_log(L_DBG, "pjmedia_null_port_created failed\n");
738
750
  return false;
@@ -740,19 +752,19 @@ pj_status_t setup_call_conf(Call *call) {
740
752
 
741
753
  pjmedia_port *conf_port = NULL;
742
754
 
743
- conf_port = pjmedia_conf_get_master_port(call->conf);
755
+ conf_port = pjmedia_conf_get_master_port(ae->conf);
744
756
  if (conf_port == NULL) {
745
757
  addon_log(L_DBG, "pjmedia_conf_get_master_port failed\n");
746
758
  return false;
747
759
  }
748
760
 
749
- status = pjmedia_master_port_create(call->inv->pool, call->null_port, conf_port, 0, &call->master_port);
761
+ status = pjmedia_master_port_create(call->inv->pool, ae->null_port, conf_port, 0, &ae->master_port);
750
762
  if (status != PJ_SUCCESS) {
751
763
  addon_log(L_DBG, "pjmedia_master_port_create failed\n");
752
764
  return false;
753
765
  }
754
766
 
755
- status = pjmedia_master_port_start(call->master_port);
767
+ status = pjmedia_master_port_start(ae->master_port);
756
768
  if (status != PJ_SUCCESS) {
757
769
  addon_log(L_DBG, "pjmedia_master_port_start failed\n");
758
770
  return false;
@@ -761,38 +773,6 @@ pj_status_t setup_call_conf(Call *call) {
761
773
  return PJ_SUCCESS;
762
774
  }
763
775
 
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
776
  static int find_endpoint_by_inband_dtmf_media_port(Call *call,
797
777
  pjmedia_port *port) {
798
778
  for (int i = 0; i < call->media_count; i++) {
@@ -851,7 +831,7 @@ static void on_inband_dtmf(pjmedia_port *port, void *user_data, char digit) {
851
831
  ae->last_digit_timestamp[mode] = ms_timestamp();
852
832
  PJW_UNLOCK();
853
833
  } else {
854
- char evt[256];
834
+ char evt[1024];
855
835
  make_evt_dtmf(evt, sizeof(evt), call_id, 1, &d, mode, media_id);
856
836
  dispatch_event(evt);
857
837
  }
@@ -868,11 +848,60 @@ static void on_fax_result(pjmedia_port *port, void *user_data, int result) {
868
848
  return;
869
849
  }
870
850
 
871
- char evt[256];
851
+ char evt[1024];
872
852
  make_evt_fax_result(evt, sizeof(evt), call_id, result);
873
853
  dispatch_event(evt);
874
854
  }
875
855
 
856
+ static void on_end_of_file(pjmedia_port *port, void *user_data) {
857
+ if (g_shutting_down)
858
+ return;
859
+
860
+ long call_id;
861
+ if (!g_call_ids.get_id((long)user_data, call_id)) {
862
+ printf(
863
+ "on_end_of_file: Failed to get call_id. Event will not be notified.\n");
864
+ return;
865
+ }
866
+
867
+ char evt[1024];
868
+ make_evt_end_of_file(evt, sizeof(evt), call_id);
869
+ dispatch_event(evt);
870
+ }
871
+
872
+ static void on_end_of_speech(pjmedia_port *port, void *user_data) {
873
+ if (g_shutting_down)
874
+ return;
875
+
876
+ long call_id;
877
+ if (!g_call_ids.get_id((long)user_data, call_id)) {
878
+ printf(
879
+ "on_end_of_speech: Failed to get call_id. Event will not be notified.\n");
880
+ return;
881
+ }
882
+
883
+ char evt[1024];
884
+ make_evt_end_of_speech(evt, sizeof(evt), call_id);
885
+ dispatch_event(evt);
886
+ }
887
+
888
+ static void on_speech_transcript(pjmedia_port*, void *user_data, char* transcript) {
889
+ if (g_shutting_down)
890
+ return;
891
+
892
+ long call_id;
893
+ if (!g_call_ids.get_id((long)user_data, call_id)) {
894
+ addon_log(
895
+ L_DBG,
896
+ "on_speech_transcript: Failed to get call_id. Event will not be notified.\n");
897
+ return;
898
+ }
899
+
900
+ char evt[1024];
901
+ make_evt_speech_transcript(evt, sizeof(evt), call_id, transcript);
902
+ dispatch_event(evt);
903
+ }
904
+
876
905
  void dispatch_event(const char *evt) {
877
906
  addon_log(L_DBG, "dispach_event called with evt=%s\n", evt);
878
907
  // g_event_sink(evt);
@@ -3014,12 +3043,6 @@ int call_create(Transport *t, unsigned flags, pjsip_dialog *dlg,
3014
3043
  }
3015
3044
  // addon_log(L_DBG, "pjsip_dlg_add_usage OK\n");
3016
3045
 
3017
- status = setup_call_conf(call);
3018
- if (status != PJ_SUCCESS) {
3019
- set_error("setup_call_conf failed");
3020
- return -1;
3021
- }
3022
-
3023
3046
  return call_id;
3024
3047
  }
3025
3048
 
@@ -3090,7 +3113,6 @@ pj_status_t send_dtmf(Call *call, const char *digits, int mode) {
3090
3113
  return PJ_SUCCESS;
3091
3114
  }
3092
3115
 
3093
- // int pjw_call_send_dtmf(long call_id, const char *digits, int mode)
3094
3116
  int pjw_call_send_dtmf(long call_id, const char *json) {
3095
3117
  #define MAX_LENGTH \
3096
3118
  31 // pjsip allows for 31 digits (inband allows for 32 digits)
@@ -3098,32 +3120,40 @@ int pjw_call_send_dtmf(long call_id, const char *json) {
3098
3120
  PJW_LOCK();
3099
3121
  clear_error();
3100
3122
 
3123
+ Call *call;
3124
+
3125
+ pj_status_t status;
3126
+
3101
3127
  long val;
3128
+
3102
3129
  char *digits;
3103
3130
  int mode = 0;
3104
- ;
3105
3131
 
3106
- Call *call;
3132
+ MediaEndpoint *me;
3133
+ AudioEndpoint *ae;
3134
+ int res;
3135
+
3136
+ int media_id = -1;
3107
3137
 
3108
3138
  char buffer[MAX_JSON_INPUT];
3109
3139
 
3110
3140
  Document document;
3111
3141
 
3112
- const char *valid_params[] = {"digits", "mode", ""};
3142
+ const char *valid_params[] = {"digits", "mode", "media_id", ""};
3113
3143
 
3114
- if (!parse_json(document, json, buffer, MAX_JSON_INPUT)) {
3144
+ if (!g_call_ids.get(call_id, val)) {
3145
+ set_error("Invalid call_id");
3115
3146
  goto out;
3116
3147
  }
3148
+ call = (Call *)val;
3117
3149
 
3118
- if (!validate_params(document, valid_params)) {
3150
+ if (!parse_json(document, json, buffer, MAX_JSON_INPUT)) {
3119
3151
  goto out;
3120
3152
  }
3121
3153
 
3122
- if (!g_call_ids.get(call_id, val)) {
3123
- set_error("Invalid call_id");
3154
+ if (!validate_params(document, valid_params)) {
3124
3155
  goto out;
3125
3156
  }
3126
- call = (Call *)val;
3127
3157
 
3128
3158
  if (json_get_string_param(document, "digits", false, &digits) <= 0) {
3129
3159
  goto out;
@@ -3169,7 +3199,38 @@ int pjw_call_send_dtmf(long call_id, const char *json) {
3169
3199
  adjusted_digits[len] = 0;
3170
3200
  // addon_log(L_DBG, "adjusted_digits >>%s<<\n", adjusted_digits);
3171
3201
 
3172
- send_dtmf(call, adjusted_digits, mode);
3202
+ res = json_get_int_param(document, "media_id", true, &media_id);
3203
+ if (res <= 0) {
3204
+ goto out;
3205
+ }
3206
+
3207
+ if (NOT_FOUND_OPTIONAL == res) {
3208
+ // send_dtmf to all audio endpoints
3209
+ status = send_dtmf(call, adjusted_digits, mode);
3210
+ if (status != PJ_SUCCESS) {
3211
+ goto out;
3212
+ }
3213
+ } else {
3214
+ // send_dtmf to specified media_id
3215
+
3216
+ if (media_id >= call->media_count) {
3217
+ set_error("invalid media_id");
3218
+ goto out;
3219
+ }
3220
+
3221
+ me = (MediaEndpoint *)call->media[media_id];
3222
+ if (ENDPOINT_TYPE_AUDIO != me->type) {
3223
+ set_error("invalid media_id non audio");
3224
+ goto out;
3225
+ }
3226
+
3227
+ ae = (AudioEndpoint *)me->endpoint.audio;
3228
+
3229
+ status = audio_endpoint_send_dtmf(call, ae, digits, mode);
3230
+ if (status != PJ_SUCCESS) {
3231
+ goto out;
3232
+ }
3233
+ }
3173
3234
 
3174
3235
  out:
3175
3236
  PJW_UNLOCK();
@@ -3180,7 +3241,7 @@ out:
3180
3241
  return 0;
3181
3242
  }
3182
3243
 
3183
- pj_status_t audio_endpoint_remove_port(Call *call, ConfBridgePort *cbp) {
3244
+ pj_status_t audio_endpoint_remove_port(Call *call, AudioEndpoint *ae, ConfBridgePort *cbp) {
3184
3245
  printf("audio_endpoint_remove_port\n");
3185
3246
  pj_status_t status;
3186
3247
 
@@ -3191,7 +3252,7 @@ pj_status_t audio_endpoint_remove_port(Call *call, ConfBridgePort *cbp) {
3191
3252
  pjmedia_conf_disconnect_port_from_sinks(conf, port);
3192
3253
  */
3193
3254
 
3194
- status = pjmedia_conf_remove_port(call->conf, cbp->slot);
3255
+ status = pjmedia_conf_remove_port(ae->conf, cbp->slot);
3195
3256
  if (status != PJ_SUCCESS) {
3196
3257
  set_error("pjmedia_conf_remove_port failed");
3197
3258
  return false;
@@ -3443,7 +3504,15 @@ int count_media_by_type(Call *call, int type) {
3443
3504
  return total;
3444
3505
  }
3445
3506
 
3446
- // int pjw_call_start_record_wav(long call_id, const char *file)
3507
+ int get_first_media_id_by_type(Call *call, int type) {
3508
+ for (int i = 0; i < call->media_count; i++) {
3509
+ MediaEndpoint *me = (MediaEndpoint *)call->media[i];
3510
+ if (type == me->type)
3511
+ return i;
3512
+ }
3513
+ return -1;
3514
+ }
3515
+
3447
3516
  int pjw_call_start_record_wav(long call_id, const char *json) {
3448
3517
  PJW_LOCK();
3449
3518
  clear_error();
@@ -3452,7 +3521,7 @@ int pjw_call_start_record_wav(long call_id, const char *json) {
3452
3521
  Call *call;
3453
3522
  pj_status_t status;
3454
3523
 
3455
- unsigned media_id = 0;
3524
+ int media_id = -1;
3456
3525
 
3457
3526
  MediaEndpoint *me;
3458
3527
  AudioEndpoint *ae;
@@ -3497,7 +3566,13 @@ int pjw_call_start_record_wav(long call_id, const char *json) {
3497
3566
  }
3498
3567
 
3499
3568
  if (ae_count > 1) {
3500
- if (json_get_uint_param(document, "media_id", false, &media_id) <= 0) {
3569
+ if (json_get_int_param(document, "media_id", false, &media_id) <= 0) {
3570
+ goto out;
3571
+ }
3572
+ } else {
3573
+ media_id = get_first_media_id_by_type(call, ENDPOINT_TYPE_AUDIO);
3574
+ if(media_id < 0) {
3575
+ set_error("could not resolve media_id");
3501
3576
  goto out;
3502
3577
  }
3503
3578
  }
@@ -3541,7 +3616,7 @@ out:
3541
3616
  }
3542
3617
 
3543
3618
  pj_status_t audio_endpoint_start_play_wav(Call *call, AudioEndpoint *ae,
3544
- const char *file) {
3619
+ const char *file, unsigned flags, bool end_of_file_event) {
3545
3620
  pj_status_t status;
3546
3621
 
3547
3622
  if(!ae->stream_cbp.port) {
@@ -3555,14 +3630,34 @@ pj_status_t audio_endpoint_start_play_wav(Call *call, AudioEndpoint *ae,
3555
3630
  return -1;
3556
3631
  }
3557
3632
 
3558
- if (!prepare_wav_player(call, ae, file)) {
3633
+ if (!prepare_wav_player(call, ae, file, flags, end_of_file_event)) {
3634
+ return -1;
3635
+ }
3636
+
3637
+ return PJ_SUCCESS;
3638
+ }
3639
+
3640
+ pj_status_t audio_endpoint_start_fax(Call *call, AudioEndpoint *ae, bool is_sender, char *file, unsigned flags) {
3641
+ pj_status_t status;
3642
+
3643
+ if(!ae->stream_cbp.port) {
3644
+ set_error("stream port is not ready yet");
3645
+ return -1;
3646
+ }
3647
+
3648
+ // First stop and destroy existing port.
3649
+ status = audio_endpoint_stop_fax(call, ae);
3650
+ if(status != PJ_SUCCESS) {
3651
+ return -1;
3652
+ }
3653
+
3654
+ if (!prepare_fax(call, ae, is_sender, file, flags)) {
3559
3655
  return -1;
3560
3656
  }
3561
3657
 
3562
3658
  return PJ_SUCCESS;
3563
3659
  }
3564
3660
 
3565
- // int pjw_call_start_play_wav(long call_id, const char *file)
3566
3661
  int pjw_call_start_play_wav(long call_id, const char *json) {
3567
3662
  PJW_LOCK();
3568
3663
  clear_error();
@@ -3573,16 +3668,25 @@ int pjw_call_start_play_wav(long call_id, const char *json) {
3573
3668
  MediaEndpoint *me;
3574
3669
  AudioEndpoint *ae;
3575
3670
  int ae_count;
3671
+ int res;
3576
3672
 
3577
- unsigned media_id = 0;
3673
+ int media_id = -1;
3578
3674
 
3579
3675
  char *file;
3580
3676
 
3677
+ unsigned flags = 0;
3678
+
3679
+ bool end_of_file_event = false;
3680
+
3681
+ bool no_loop = false;
3682
+
3581
3683
  char buffer[MAX_JSON_INPUT];
3582
3684
 
3685
+ pj_status_t status;
3686
+
3583
3687
  Document document;
3584
3688
 
3585
- const char *valid_params[] = {"file", "media_id", ""};
3689
+ const char *valid_params[] = {"file", "media_id", "end_of_file_event", "no_loop", ""};
3586
3690
 
3587
3691
  if (!g_call_ids.get(call_id, val)) {
3588
3692
  set_error("Invalid call_id");
@@ -3614,26 +3718,49 @@ int pjw_call_start_play_wav(long call_id, const char *json) {
3614
3718
  goto out;
3615
3719
  }
3616
3720
 
3617
- if (ae_count > 1) {
3618
- if (json_get_uint_param(document, "media_id", false, &media_id) <= 0) {
3619
- goto out;
3620
- }
3721
+ if (json_get_bool_param(document, "end_of_file_event", true, &end_of_file_event) <= 0) {
3722
+ goto out;
3621
3723
  }
3622
3724
 
3623
- if ((int)media_id >= call->media_count) {
3624
- set_error("invalid media_id");
3725
+ if (json_get_bool_param(document, "no_loop", true, &no_loop) <= 0) {
3625
3726
  goto out;
3626
3727
  }
3627
3728
 
3628
- me = (MediaEndpoint *)call->media[media_id];
3629
- if (ENDPOINT_TYPE_AUDIO != me->type) {
3630
- set_error("media_endpoint is not audio endpoint");
3729
+ if(no_loop) {
3730
+ flags |= PJMEDIA_FILE_NO_LOOP;
3731
+ }
3732
+
3733
+ res = json_get_int_param(document, "media_id", true, &media_id);
3734
+ if (res <= 0) {
3631
3735
  goto out;
3632
3736
  }
3633
3737
 
3634
- ae = (AudioEndpoint *)me->endpoint.audio;
3738
+ if (NOT_FOUND_OPTIONAL == res) {
3739
+ // start on all audio media endpoints
3740
+ for (int i = 0; i < call->media_count; i++) {
3741
+ MediaEndpoint *me = (MediaEndpoint *)call->media[i];
3742
+ if (me->type == ENDPOINT_TYPE_AUDIO) {
3743
+ AudioEndpoint *ae = (AudioEndpoint *)me->endpoint.audio;
3744
+ status = audio_endpoint_start_play_wav(call, ae, file, flags, end_of_file_event);
3745
+ if (status != PJ_SUCCESS) goto out;
3746
+ }
3747
+ }
3748
+ } else {
3749
+ if (media_id >= call->media_count) {
3750
+ set_error("invalid media_id");
3751
+ goto out;
3752
+ }
3753
+
3754
+ me = (MediaEndpoint *)call->media[media_id];
3755
+ if (ENDPOINT_TYPE_AUDIO != me->type) {
3756
+ set_error("media_endpoint is not audio endpoint");
3757
+ goto out;
3758
+ }
3635
3759
 
3636
- audio_endpoint_start_play_wav(call, ae, file);
3760
+ ae = (AudioEndpoint *)me->endpoint.audio;
3761
+
3762
+ audio_endpoint_start_play_wav(call, ae, file, flags, end_of_file_event);
3763
+ }
3637
3764
 
3638
3765
  out:
3639
3766
  PJW_UNLOCK();
@@ -3644,7 +3771,7 @@ out:
3644
3771
  return 0;
3645
3772
  }
3646
3773
 
3647
- pj_status_t audio_endpoint_start_speech_synth(Call *call, AudioEndpoint *ae, const char * voice, const char *text) {
3774
+ pj_status_t audio_endpoint_start_speech_synth(Call *call, AudioEndpoint *ae, const char * voice, const char *text, unsigned flags, bool end_of_speech_event) {
3648
3775
  pj_status_t status;
3649
3776
 
3650
3777
  if(!ae->stream_cbp.port) {
@@ -3653,16 +3780,16 @@ pj_status_t audio_endpoint_start_speech_synth(Call *call, AudioEndpoint *ae, con
3653
3780
  }
3654
3781
 
3655
3782
  // First stop and destroy existing flite port.
3656
- status = audio_endpoint_stop_flite(call, ae);
3783
+ status = audio_endpoint_stop_speech_synth(call, ae);
3657
3784
  if(status != PJ_SUCCESS) {
3658
3785
  return -1;
3659
3786
  }
3660
3787
 
3661
- if (!prepare_flite(call, ae, voice)) {
3788
+ if (!prepare_flite(call, ae, voice, end_of_speech_event)) {
3662
3789
  return -1;
3663
3790
  }
3664
3791
 
3665
- pjmedia_flite_port_speak(ae->flite_cbp.port, text, 0);
3792
+ pjmedia_flite_port_speak(ae->flite_cbp.port, text, flags);
3666
3793
 
3667
3794
  return PJ_SUCCESS;
3668
3795
  }
@@ -3674,21 +3801,30 @@ int pjw_call_start_speech_synth(long call_id, const char *json) {
3674
3801
  long val;
3675
3802
  Call *call;
3676
3803
 
3804
+ pj_status_t status;
3805
+
3677
3806
  MediaEndpoint *me;
3678
3807
  AudioEndpoint *ae;
3679
3808
  int ae_count;
3809
+ int res;
3680
3810
 
3681
- unsigned media_id = 0;
3811
+ int media_id = -1;
3682
3812
 
3683
3813
  char *voice;
3684
3814
 
3685
3815
  char *text;
3686
3816
 
3817
+ bool end_of_speech_event = false;
3818
+
3819
+ unsigned flags = 0;
3820
+
3821
+ bool no_loop = false;
3822
+
3687
3823
  char buffer[MAX_JSON_INPUT];
3688
3824
 
3689
3825
  Document document;
3690
3826
 
3691
- const char *valid_params[] = {"voice", "text", "media_id", ""};
3827
+ const char *valid_params[] = {"voice", "text", "media_id", "end_of_speech_event", "no_loop", ""};
3692
3828
 
3693
3829
  if (!g_call_ids.get(call_id, val)) {
3694
3830
  set_error("Invalid call_id");
@@ -3729,26 +3865,49 @@ int pjw_call_start_speech_synth(long call_id, const char *json) {
3729
3865
  goto out;
3730
3866
  }
3731
3867
 
3732
- if (ae_count > 1) {
3733
- if (json_get_uint_param(document, "media_id", false, &media_id) <= 0) {
3734
- goto out;
3735
- }
3868
+ if (json_get_bool_param(document, "end_of_speech_event", true, &end_of_speech_event) <= 0) {
3869
+ goto out;
3736
3870
  }
3737
3871
 
3738
- if ((int)media_id >= call->media_count) {
3739
- set_error("invalid media_id");
3872
+ if (json_get_bool_param(document, "no_loop", true, &no_loop) <= 0) {
3740
3873
  goto out;
3741
3874
  }
3742
3875
 
3743
- me = (MediaEndpoint *)call->media[media_id];
3744
- if (ENDPOINT_TYPE_AUDIO != me->type) {
3745
- set_error("media_endpoint is not audio endpoint");
3876
+ if(no_loop) {
3877
+ flags |= PJMEDIA_SPEECH_NO_LOOP;
3878
+ }
3879
+
3880
+ res = json_get_int_param(document, "media_id", true, &media_id);
3881
+ if (res <= 0) {
3746
3882
  goto out;
3747
3883
  }
3748
3884
 
3749
- ae = (AudioEndpoint *)me->endpoint.audio;
3885
+ if (NOT_FOUND_OPTIONAL == res) {
3886
+ // start on all audio media endpoints
3887
+ for (int i = 0; i < call->media_count; i++) {
3888
+ MediaEndpoint *me = (MediaEndpoint *)call->media[i];
3889
+ if (me->type == ENDPOINT_TYPE_AUDIO) {
3890
+ AudioEndpoint *ae = (AudioEndpoint *)me->endpoint.audio;
3891
+ status = audio_endpoint_start_speech_synth(call, ae, voice, text, flags, end_of_speech_event);
3892
+ if (status != PJ_SUCCESS) goto out;
3893
+ }
3894
+ }
3895
+ } else {
3896
+ if ((int)media_id >= call->media_count) {
3897
+ set_error("invalid media_id");
3898
+ goto out;
3899
+ }
3900
+
3901
+ me = (MediaEndpoint *)call->media[media_id];
3902
+ if (ENDPOINT_TYPE_AUDIO != me->type) {
3903
+ set_error("media_endpoint is not audio endpoint");
3904
+ goto out;
3905
+ }
3750
3906
 
3751
- audio_endpoint_start_speech_synth(call, ae, voice, text);
3907
+ ae = (AudioEndpoint *)me->endpoint.audio;
3908
+
3909
+ audio_endpoint_start_speech_synth(call, ae, voice, text, flags, end_of_speech_event);
3910
+ }
3752
3911
 
3753
3912
  out:
3754
3913
  PJW_UNLOCK();
@@ -3759,79 +3918,96 @@ out:
3759
3918
  return 0;
3760
3919
  }
3761
3920
 
3762
- pj_status_t audio_endpoint_stop_flite(Call *call, AudioEndpoint *ae) {
3763
- return audio_endpoint_remove_port(call, &ae->flite_cbp);
3764
- }
3765
-
3766
- pj_status_t call_stop_audio_endpoints_op(Call *call,
3767
- audio_endpoint_stop_op_t op) {
3768
- addon_log(L_DBG, "call_stop_audio_endpoints_op media_count=%d\n",
3769
- call->media_count);
3921
+ pj_status_t audio_endpoint_start_speech_recog(Call *call, AudioEndpoint *ae) {
3770
3922
  pj_status_t status;
3771
- for (int i = 0; i < call->media_count; i++) {
3772
- MediaEndpoint *me = (MediaEndpoint *)call->media[i];
3773
- if (ENDPOINT_TYPE_AUDIO != me->type)
3774
- continue;
3775
3923
 
3776
- AudioEndpoint *ae = (AudioEndpoint *)me->endpoint.audio;
3924
+ if(!ae->stream_cbp.port) {
3925
+ set_error("stream port is not ready yet");
3926
+ return -1;
3927
+ }
3777
3928
 
3778
- status = op(call, ae);
3779
- if (status != PJ_SUCCESS) {
3780
- return status;
3781
- }
3929
+ // First stop and destroy existing port.
3930
+ status = audio_endpoint_stop_speech_recog(call, ae);
3931
+ if(status != PJ_SUCCESS) {
3932
+ return -1;
3782
3933
  }
3783
3934
 
3784
- return PJ_SUCCESS;
3785
- }
3935
+ if (!prepare_pocketsphinx(call, ae)) {
3936
+ return -1;
3937
+ }
3786
3938
 
3787
- pj_status_t audio_endpoint_stop_play_wav(Call *call, AudioEndpoint *ae) {
3788
- return audio_endpoint_remove_port(call, &ae->wav_player_cbp);
3939
+ return PJ_SUCCESS;
3789
3940
  }
3790
3941
 
3791
- int pjw_call_stop_play_wav(long call_id, const char *json) {
3942
+ int pjw_call_start_speech_recog(long call_id, const char *json) {
3792
3943
  PJW_LOCK();
3793
3944
  clear_error();
3794
3945
 
3946
+ long val;
3795
3947
  Call *call;
3796
3948
 
3797
3949
  pj_status_t status;
3798
3950
 
3799
- long val;
3800
-
3801
3951
  MediaEndpoint *me;
3802
3952
  AudioEndpoint *ae;
3953
+ int ae_count;
3803
3954
  int res;
3804
3955
 
3805
- unsigned media_id = 0;
3956
+ int media_id = -1;
3957
+
3958
+ char *voice;
3959
+
3960
+ char *text;
3961
+
3962
+ bool end_of_speech_event = false;
3963
+
3964
+ unsigned flags = 0;
3965
+
3966
+ bool no_loop = false;
3806
3967
 
3807
3968
  char buffer[MAX_JSON_INPUT];
3808
3969
 
3809
3970
  Document document;
3810
3971
 
3972
+ const char *valid_params[] = {"media_id", ""};
3973
+
3811
3974
  if (!g_call_ids.get(call_id, val)) {
3812
3975
  set_error("Invalid call_id");
3813
3976
  goto out;
3814
3977
  }
3815
3978
  call = (Call *)val;
3816
3979
 
3980
+ ae_count = count_media_by_type(call, ENDPOINT_TYPE_AUDIO);
3981
+
3982
+ if (ae_count == 0) {
3983
+ set_error("No audio endpoint");
3984
+ goto out;
3985
+ }
3986
+
3817
3987
  if (!parse_json(document, json, buffer, MAX_JSON_INPUT)) {
3818
3988
  goto out;
3819
3989
  }
3820
3990
 
3821
- res = json_get_uint_param(document, "media_id", true, &media_id);
3991
+ if (!validate_params(document, valid_params)) {
3992
+ goto out;
3993
+ }
3994
+
3995
+ res = json_get_int_param(document, "media_id", true, &media_id);
3822
3996
  if (res <= 0) {
3823
3997
  goto out;
3824
3998
  }
3825
3999
 
3826
4000
  if (NOT_FOUND_OPTIONAL == res) {
3827
- // Stop play wav in all audio endpoints
3828
- status = call_stop_audio_endpoints_op(call, audio_endpoint_stop_play_wav);
3829
- if (status != PJ_SUCCESS) {
3830
- goto out;
4001
+ // start on all audio media endpoints
4002
+ for (int i = 0; i < call->media_count; i++) {
4003
+ MediaEndpoint *me = (MediaEndpoint *)call->media[i];
4004
+ if (me->type == ENDPOINT_TYPE_AUDIO) {
4005
+ AudioEndpoint *ae = (AudioEndpoint *)me->endpoint.audio;
4006
+ status = audio_endpoint_start_speech_recog(call, ae);
4007
+ if (status != PJ_SUCCESS) goto out;
4008
+ }
3831
4009
  }
3832
4010
  } else {
3833
- // Stop play wav on specified media_id
3834
-
3835
4011
  if ((int)media_id >= call->media_count) {
3836
4012
  set_error("invalid media_id");
3837
4013
  goto out;
@@ -3839,16 +4015,13 @@ int pjw_call_stop_play_wav(long call_id, const char *json) {
3839
4015
 
3840
4016
  me = (MediaEndpoint *)call->media[media_id];
3841
4017
  if (ENDPOINT_TYPE_AUDIO != me->type) {
3842
- set_error("invalid media_id non audio");
4018
+ set_error("media_endpoint is not audio endpoint");
3843
4019
  goto out;
3844
4020
  }
3845
4021
 
3846
4022
  ae = (AudioEndpoint *)me->endpoint.audio;
3847
4023
 
3848
- status = audio_endpoint_stop_play_wav(call, ae);
3849
- if (status != PJ_SUCCESS) {
3850
- goto out;
3851
- }
4024
+ audio_endpoint_start_speech_recog(call, ae);
3852
4025
  }
3853
4026
 
3854
4027
  out:
@@ -3860,23 +4033,42 @@ out:
3860
4033
  return 0;
3861
4034
  }
3862
4035
 
3863
- pj_status_t audio_endpoint_stop_record_wav(Call *call, AudioEndpoint *ae) {
3864
- return audio_endpoint_remove_port(call, &ae->wav_writer_cbp);
4036
+ pj_status_t call_stop_op_on_all_audio_endpoints(Call *call,
4037
+ audio_endpoint_stop_op_t op) {
4038
+ addon_log(L_DBG, "call_stop_op_on_audio_endpoints media_count=%d\n",
4039
+ call->media_count);
4040
+ pj_status_t status;
4041
+ for (int i = 0; i < call->media_count; i++) {
4042
+ MediaEndpoint *me = (MediaEndpoint *)call->media[i];
4043
+ if (ENDPOINT_TYPE_AUDIO != me->type)
4044
+ continue;
4045
+
4046
+ AudioEndpoint *ae = (AudioEndpoint *)me->endpoint.audio;
4047
+
4048
+ status = op(call, ae);
4049
+ if (status != PJ_SUCCESS) {
4050
+ return status;
4051
+ }
4052
+ }
4053
+
4054
+ return PJ_SUCCESS;
3865
4055
  }
3866
4056
 
3867
- int pjw_call_stop_record_wav(long call_id, const char *json) {
4057
+ int audio_endpoint_stop_op(long call_id, const char *json, audio_endpoint_stop_op_t op) {
3868
4058
  PJW_LOCK();
3869
4059
  clear_error();
3870
4060
 
3871
- long val;
3872
- Call *call = (Call *)val;
4061
+ Call *call;
4062
+
3873
4063
  pj_status_t status;
3874
4064
 
4065
+ long val;
4066
+
3875
4067
  MediaEndpoint *me;
3876
4068
  AudioEndpoint *ae;
3877
4069
  int res;
3878
4070
 
3879
- unsigned media_id = 0;
4071
+ int media_id = -1;
3880
4072
 
3881
4073
  char buffer[MAX_JSON_INPUT];
3882
4074
 
@@ -3892,21 +4084,19 @@ int pjw_call_stop_record_wav(long call_id, const char *json) {
3892
4084
  goto out;
3893
4085
  }
3894
4086
 
3895
- res = json_get_uint_param(document, "media_id", true, &media_id);
4087
+ res = json_get_int_param(document, "media_id", true, &media_id);
3896
4088
  if (res <= 0) {
3897
4089
  goto out;
3898
4090
  }
3899
4091
 
3900
4092
  if (NOT_FOUND_OPTIONAL == res) {
3901
- // Stop record wav in all audio endpoints
3902
- status = call_stop_audio_endpoints_op(call, audio_endpoint_stop_record_wav);
3903
- if (status != PJ_SUCCESS) {
3904
- goto out;
3905
- }
4093
+ // Stop op on all audio endpoints
4094
+ status = call_stop_op_on_all_audio_endpoints(call, op);
4095
+ if (status != PJ_SUCCESS) goto out;
3906
4096
  } else {
3907
- // Stop record wav on specified media_id
4097
+ // Stop op on specified media
3908
4098
 
3909
- if ((int)media_id >= call->media_count) {
4099
+ if (media_id >= call->media_count) {
3910
4100
  set_error("invalid media_id");
3911
4101
  goto out;
3912
4102
  }
@@ -3919,10 +4109,8 @@ int pjw_call_stop_record_wav(long call_id, const char *json) {
3919
4109
 
3920
4110
  ae = (AudioEndpoint *)me->endpoint.audio;
3921
4111
 
3922
- status = audio_endpoint_stop_record_wav(call, ae);
3923
- if (status != PJ_SUCCESS) {
3924
- goto out;
3925
- }
4112
+ status = op(call, ae);
4113
+ if (status != PJ_SUCCESS) goto out;
3926
4114
  }
3927
4115
 
3928
4116
  out:
@@ -3934,152 +4122,74 @@ out:
3934
4122
  return 0;
3935
4123
  }
3936
4124
 
3937
- pj_status_t audio_endpoint_stop_fax(Call *call, AudioEndpoint *ae) {
3938
- return audio_endpoint_remove_port(call, &ae->fax_cbp);
4125
+ pj_status_t audio_endpoint_stop_speech_synth(Call *call, AudioEndpoint *ae) {
4126
+ return audio_endpoint_remove_port(call, ae, &ae->flite_cbp);
3939
4127
  }
3940
4128
 
3941
- int pjw_call_start_fax(long call_id, const char *json) {
3942
- PJW_LOCK();
3943
- clear_error();
3944
-
3945
- long val;
3946
- Call *call;
3947
- pj_status_t status;
3948
-
3949
- bool is_sender;
3950
- char *file;
3951
- unsigned flags = 0;
3952
- bool flag;
3953
-
3954
- MediaEndpoint *me;
3955
- AudioEndpoint *ae;
3956
- int ae_count;
3957
-
3958
- unsigned media_id = 0;
3959
-
3960
- char buffer[MAX_JSON_INPUT];
3961
-
3962
- const char *valid_params[] = {"is_sender", "file", "transmit_on_idle",
3963
- "media_id", ""};
3964
-
3965
- Document document;
3966
-
3967
- if (!g_call_ids.get(call_id, val)) {
3968
- set_error("Invalid call_id");
3969
- goto out;
3970
- }
3971
- call = (Call *)val;
3972
-
3973
- ae_count = count_media_by_type(call, ENDPOINT_TYPE_AUDIO);
3974
-
3975
- if (ae_count == 0) {
3976
- set_error("No audio endpoint");
3977
- goto out;
3978
- }
3979
-
3980
- if (!parse_json(document, json, buffer, MAX_JSON_INPUT)) {
3981
- goto out;
3982
- }
3983
-
3984
- if (!validate_params(document, valid_params)) {
3985
- goto out;
3986
- }
3987
-
3988
- if (json_get_string_param(document, "file", false, &file) <= 0) {
3989
- goto out;
3990
- }
3991
-
3992
- if (!file[0]) {
3993
- set_error("file cannot be blank string");
3994
- goto out;
3995
- }
3996
-
3997
- if (ae_count > 1) {
3998
- if (json_get_uint_param(document, "media_id", false, &media_id) <= 0) {
3999
- goto out;
4000
- }
4001
- }
4002
-
4003
- if ((int)media_id >= call->media_count) {
4004
- set_error("invalid media_id");
4005
- goto out;
4006
- }
4007
-
4008
- me = (MediaEndpoint *)call->media[media_id];
4009
- if (ENDPOINT_TYPE_AUDIO != me->type) {
4010
- set_error("media_endpoint is not audio endpoint");
4011
- goto out;
4012
- }
4013
-
4014
- ae = (AudioEndpoint *)me->endpoint.audio;
4015
-
4016
- if (!parse_json(document, json, buffer, MAX_JSON_INPUT)) {
4017
- goto out;
4018
- }
4019
-
4020
- if (!validate_params(document, valid_params)) {
4021
- goto out;
4022
- }
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
+ }
4023
4132
 
4024
- if (json_get_bool_param(document, "is_sender", false, &is_sender) <= 0) {
4025
- goto out;
4026
- }
4133
+ pj_status_t audio_endpoint_stop_play_wav(Call *call, AudioEndpoint *ae) {
4134
+ return audio_endpoint_remove_port(call, ae, &ae->wav_player_cbp);
4135
+ }
4027
4136
 
4028
- if (json_get_string_param(document, "file", false, &file) <= 0) {
4029
- goto out;
4030
- }
4137
+ pj_status_t audio_endpoint_stop_record_wav(Call *call, AudioEndpoint *ae) {
4138
+ return audio_endpoint_remove_port(call, ae, &ae->wav_writer_cbp);
4139
+ }
4031
4140
 
4032
- if (!file[0]) {
4033
- set_error("file cannot be blank string");
4034
- goto out;
4035
- }
4141
+ pj_status_t audio_endpoint_stop_fax(Call *call, AudioEndpoint *ae) {
4142
+ return audio_endpoint_remove_port(call, ae, &ae->fax_cbp);
4143
+ }
4036
4144
 
4037
- flag = false;
4038
- if (json_get_bool_param(document, "transmit_on_idle", true, &flag) <= 0) {
4039
- goto out;
4040
- } else {
4041
- if (flag)
4042
- flags |= FAX_FLAG_TRANSMIT_ON_IDLE;
4043
- }
4044
4145
 
4045
- // First stop and destroy existing fax port.
4046
- status = audio_endpoint_stop_fax(call, ae);
4047
- if(status != PJ_SUCCESS) {
4048
- set_error("audio_endpoint_stop_fax failed");
4049
- return -1;
4050
- }
4051
4146
 
4052
- if (!prepare_fax(call, ae, is_sender, file, flags)) {
4053
- set_error("prepare_fax failed");
4054
- goto out;
4055
- }
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);
4149
+ }
4056
4150
 
4057
- out:
4058
- PJW_UNLOCK();
4059
- if (pjw_errorstring[0]) {
4060
- return -1;
4061
- }
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);
4153
+ }
4062
4154
 
4063
- return 0;
4155
+ int pjw_call_stop_play_wav(long call_id, const char *json) {
4156
+ return audio_endpoint_stop_op(call_id, json, audio_endpoint_stop_play_wav);
4157
+ }
4158
+
4159
+ int pjw_call_stop_record_wav(long call_id, const char *json) {
4160
+ return audio_endpoint_stop_op(call_id, json, audio_endpoint_stop_record_wav);
4064
4161
  }
4065
4162
 
4066
4163
  int pjw_call_stop_fax(long call_id, const char *json) {
4164
+ return audio_endpoint_stop_op(call_id, json, audio_endpoint_stop_fax);
4165
+ }
4166
+
4167
+
4168
+ int pjw_call_start_fax(long call_id, const char *json) {
4067
4169
  PJW_LOCK();
4068
4170
  clear_error();
4069
4171
 
4070
4172
  long val;
4071
4173
  Call *call;
4072
-
4073
4174
  pj_status_t status;
4074
4175
 
4176
+ bool is_sender;
4177
+ char *file;
4178
+ unsigned flags = 0;
4179
+ bool flag;
4180
+
4075
4181
  MediaEndpoint *me;
4076
4182
  AudioEndpoint *ae;
4183
+ int ae_count;
4077
4184
  int res;
4078
4185
 
4079
- unsigned media_id = 0;
4186
+ int media_id = -1;
4080
4187
 
4081
4188
  char buffer[MAX_JSON_INPUT];
4082
4189
 
4190
+ const char *valid_params[] = {"is_sender", "file", "transmit_on_idle",
4191
+ "media_id", ""};
4192
+
4083
4193
  Document document;
4084
4194
 
4085
4195
  if (!g_call_ids.get(call_id, val)) {
@@ -4088,27 +4198,86 @@ int pjw_call_stop_fax(long call_id, const char *json) {
4088
4198
  }
4089
4199
  call = (Call *)val;
4090
4200
 
4201
+ ae_count = count_media_by_type(call, ENDPOINT_TYPE_AUDIO);
4202
+
4203
+ if (ae_count == 0) {
4204
+ set_error("No audio endpoint");
4205
+ goto out;
4206
+ }
4207
+
4091
4208
  if (!parse_json(document, json, buffer, MAX_JSON_INPUT)) {
4092
4209
  goto out;
4093
4210
  }
4094
4211
 
4095
- res = json_get_uint_param(document, "media_id", true, &media_id);
4096
- if (res <= 0) {
4212
+ if (!validate_params(document, valid_params)) {
4097
4213
  goto out;
4098
4214
  }
4099
4215
 
4100
- if (NOT_FOUND_OPTIONAL == res) {
4101
- // Stop fax in all audio endpoints
4102
- status = call_stop_audio_endpoints_op(call, audio_endpoint_stop_fax);
4103
- if (status != PJ_SUCCESS) {
4104
- goto out;
4105
- }
4216
+ if (json_get_string_param(document, "file", false, &file) <= 0) {
4217
+ goto out;
4218
+ }
4219
+
4220
+ if (!file[0]) {
4221
+ set_error("file cannot be blank string");
4222
+ goto out;
4223
+ }
4224
+
4225
+ if (json_get_bool_param(document, "is_sender", false, &is_sender) <= 0) {
4226
+ goto out;
4227
+ }
4228
+
4229
+ flag = false;
4230
+ if (json_get_bool_param(document, "transmit_on_idle", true, &flag) <= 0) {
4231
+ goto out;
4106
4232
  } else {
4107
- // Stop fax on specified media_id
4233
+ if (flag)
4234
+ flags |= FAX_FLAG_TRANSMIT_ON_IDLE;
4235
+ }
4108
4236
 
4109
- if ((int)media_id >= call->media_count) {
4110
- set_error("invalid media_id");
4111
- goto out;
4237
+ if(is_sender) {
4238
+ // if we are sender we can do fax operaton to all media streams
4239
+ res = json_get_int_param(document, "media_id", true, &media_id);
4240
+ if (res <= 0) goto out;
4241
+
4242
+ if (NOT_FOUND_OPTIONAL == res) {
4243
+ // start fax on all audio endpoints
4244
+ for (int i = 0; i < call->media_count; i++) {
4245
+ MediaEndpoint *me = (MediaEndpoint *)call->media[i];
4246
+ if (ENDPOINT_TYPE_AUDIO == me->type) {
4247
+ me = (MediaEndpoint *)call->media[i];
4248
+ ae = (AudioEndpoint *)me->endpoint.audio;
4249
+ status = audio_endpoint_start_fax(call, ae, is_sender, file, flags);
4250
+ if(status != PJ_SUCCESS) goto out;
4251
+ }
4252
+ }
4253
+ } else {
4254
+ if ((int)media_id >= call->media_count) {
4255
+ set_error("invalid media_id");
4256
+ goto out;
4257
+ }
4258
+
4259
+ me = (MediaEndpoint *)call->media[media_id];
4260
+ if (ENDPOINT_TYPE_AUDIO != me->type) {
4261
+ set_error("invalid media_id non audio");
4262
+ goto out;
4263
+ }
4264
+
4265
+ ae = (AudioEndpoint *)me->endpoint.audio;
4266
+
4267
+ status = audio_endpoint_start_fax(call, ae, is_sender, file, flags);
4268
+ if (status != PJ_SUCCESS) goto out;
4269
+ }
4270
+ } else {
4271
+ // if we are not sender we can only start fax operation in a single media endpoint (otherwise, there would be more than one fax process writing to the same fax file)
4272
+ res = json_get_int_param(document, "media_id", true, &media_id);
4273
+ if (res <= 0) goto out;
4274
+
4275
+ if (NOT_FOUND_OPTIONAL == res) {
4276
+ media_id = get_first_media_id_by_type(call, ENDPOINT_TYPE_AUDIO);
4277
+ if(media_id < 0) {
4278
+ set_error("could not resolve media_id");
4279
+ goto out;
4280
+ }
4112
4281
  }
4113
4282
 
4114
4283
  me = (MediaEndpoint *)call->media[media_id];
@@ -4119,10 +4288,11 @@ int pjw_call_stop_fax(long call_id, const char *json) {
4119
4288
 
4120
4289
  ae = (AudioEndpoint *)me->endpoint.audio;
4121
4290
 
4291
+ // First stop and destroy existing fax port.
4122
4292
  status = audio_endpoint_stop_fax(call, ae);
4123
- if (status != PJ_SUCCESS) {
4124
- goto out;
4125
- }
4293
+ if(status != PJ_SUCCESS) goto out;
4294
+
4295
+ if (!prepare_fax(call, ae, is_sender, file, flags)) goto out;
4126
4296
  }
4127
4297
 
4128
4298
  out:
@@ -4466,82 +4636,80 @@ bool start_tcp_media(Call *call, MediaEndpoint *me,
4466
4636
  return true;
4467
4637
  }
4468
4638
 
4639
+ void close_audio_endpoint_ports_and_conf(Call *call, AudioEndpoint *ae) {
4640
+ pj_status_t status;
4469
4641
 
4470
- bool restart_media_stream(Call *call, MediaEndpoint *me,
4471
- const pjmedia_sdp_session *local_sdp,
4472
- const pjmedia_sdp_session *remote_sdp, int idx) {
4473
- char evt[4096];
4474
- pjmedia_stream_info stream_info;
4475
-
4476
- AudioEndpoint *ae = (AudioEndpoint *)me->endpoint.audio;
4477
-
4478
- pj_status_t status;
4479
-
4480
- if(ae->stream_cbp.port) {
4481
- if(ae->tonegen_cbp.port) {
4482
- status = pjmedia_conf_disconnect_port(call->conf, ae->tonegen_cbp.slot, ae->stream_cbp.slot);
4483
- if (status != PJ_SUCCESS) {
4484
- make_evt_media_update(evt, sizeof(evt), call->id,
4485
- "setup_failed (pjmedia_conf_disconnect_port for tonegen failed)", "");
4486
- dispatch_event(evt);
4487
- return false;
4488
- }
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
+ if (ae->master_port) {
4652
+ status = pjmedia_master_port_stop(ae->master_port);
4653
+ if(status != PJ_SUCCESS) {
4654
+ addon_log(L_DBG, "pjmedia_master_port_stop failed\n");
4655
+ }
4656
+ pjmedia_master_port_destroy(ae->master_port, 0);
4657
+ if(status != PJ_SUCCESS) {
4658
+ addon_log(L_DBG, "pjmedia_master_port_destroy failed\n");
4659
+ }
4660
+ ae->master_port = NULL;
4489
4661
  }
4490
4662
 
4491
- if(ae->wav_player_cbp.port) {
4492
- status = pjmedia_conf_disconnect_port(call->conf, ae->wav_player_cbp.slot, ae->stream_cbp.slot);
4493
- if (status != PJ_SUCCESS) {
4494
- make_evt_media_update(evt, sizeof(evt), call->id,
4495
- "setup_failed (pjmedia_conf_disconnect_port for wav_player failed)", "");
4496
- dispatch_event(evt);
4497
- return false;
4498
- }
4663
+ if (ae->conf) {
4664
+ status = pjmedia_conf_destroy(ae->conf);
4665
+ if(status != PJ_SUCCESS) {
4666
+ addon_log(L_DBG, "pjmedia_conf_destroy failed\n");
4667
+ }
4668
+ ae->conf = NULL;
4499
4669
  }
4500
4670
 
4501
- if(ae->dtmfdet_cbp.port) {
4502
- status = pjmedia_conf_disconnect_port(call->conf, ae->stream_cbp.slot, ae->dtmfdet_cbp.slot);
4503
- if (status != PJ_SUCCESS) {
4504
- make_evt_media_update(evt, sizeof(evt), call->id,
4505
- "setup_failed (pjmedia_conf_disconnect_port for dtmfdet failed)", "");
4506
- dispatch_event(evt);
4507
- return false;
4508
- }
4671
+ if (ae->null_port) {
4672
+ status = pjmedia_port_destroy(ae->null_port);
4673
+ if(status != PJ_SUCCESS) {
4674
+ addon_log(L_DBG, "pjmedia_port_destroy(null_port) failed\n");
4675
+ }
4676
+ ae->null_port = NULL;
4509
4677
  }
4678
+ }
4510
4679
 
4511
- if(ae->fax_cbp.port) {
4512
- status = pjmedia_conf_disconnect_port(call->conf, ae->stream_cbp.slot, ae->fax_cbp.slot);
4513
- if (status != PJ_SUCCESS) {
4514
- make_evt_media_update(evt, sizeof(evt), call->id,
4515
- "setup_failed (pjmedia_conf_disconnect_port fax dst failed)", "");
4516
- dispatch_event(evt);
4517
- return false;
4518
- }
4519
-
4520
- status = pjmedia_conf_disconnect_port(call->conf, ae->fax_cbp.slot, ae->stream_cbp.slot);
4521
- if (status != PJ_SUCCESS) {
4522
- make_evt_media_update(evt, sizeof(evt), call->id,
4523
- "setup_failed (pjmedia_conf_disconnect_port for fax src failed)", "");
4524
- dispatch_event(evt);
4525
- return false;
4526
- }
4527
- }
4680
+ bool connect_feature_port_to_stream_port(Call *call, AudioEndpoint *ae, ConfBridgePort *cbp, int connection_mode) {
4681
+ pj_status_t status;
4528
4682
 
4529
- status = pjmedia_conf_remove_port(call->conf, ae->stream_cbp.slot);
4683
+ if(connection_mode == CONNECTION_MODE_SOURCE || connection_mode == CONNECTION_MODE_SOURCE_AND_SINK) {
4684
+ status = pjmedia_conf_connect_port(ae->conf, cbp->slot, ae->stream_cbp.slot, 0);
4530
4685
  if (status != PJ_SUCCESS) {
4531
- make_evt_media_update(evt, sizeof(evt), call->id,
4532
- "setup_failed (pjmedia_conf_remove_port failed)", "");
4686
+ set_error("pjmedia_conf_connect_port failed");
4533
4687
  return false;
4534
4688
  }
4535
- ae->stream_cbp.slot = 0;
4536
-
4537
- status = pjmedia_port_destroy(ae->stream_cbp.port);
4689
+ }
4690
+ if(connection_mode == CONNECTION_MODE_SINK || connection_mode == CONNECTION_MODE_SOURCE_AND_SINK) {
4691
+ status = pjmedia_conf_connect_port(ae->conf, ae->stream_cbp.slot, cbp->slot, 0);
4692
+ printf("status=%i\n" ,status);
4538
4693
  if (status != PJ_SUCCESS) {
4539
- make_evt_media_update(evt, sizeof(evt), call->id,
4540
- "setup_failed (pjmedia_port_destroy failed)", "");
4694
+ set_error("pjmedia_conf_connect_port failed");
4541
4695
  return false;
4542
4696
  }
4543
- ae->stream_cbp.port = NULL;
4544
- }
4697
+ }
4698
+ return true;
4699
+ }
4700
+
4701
+ bool restart_media_stream(Call *call, MediaEndpoint *me,
4702
+ const pjmedia_sdp_session *local_sdp,
4703
+ const pjmedia_sdp_session *remote_sdp, int idx) {
4704
+ char evt[4096];
4705
+ pjmedia_stream_info stream_info;
4706
+
4707
+ AudioEndpoint *ae = (AudioEndpoint *)me->endpoint.audio;
4708
+
4709
+ pj_status_t status;
4710
+
4711
+ pjmedia_port *old_port = ae->stream_cbp.port;
4712
+ pjmedia_port *new_port;
4545
4713
 
4546
4714
  status =
4547
4715
  pjmedia_stream_info_from_sdp(&stream_info, call->inv->dlg->pool,
@@ -4607,7 +4775,7 @@ bool restart_media_stream(Call *call, MediaEndpoint *me,
4607
4775
  return false;
4608
4776
  }
4609
4777
 
4610
- status = pjmedia_stream_get_port(ae->med_stream, &ae->stream_cbp.port);
4778
+ status = pjmedia_stream_get_port(ae->med_stream, &new_port);
4611
4779
  if (status != PJ_SUCCESS) {
4612
4780
  make_evt_media_update(evt, sizeof(evt), call->id,
4613
4781
  "setup_failed (pjmedia_stream_get_port failed)", "");
@@ -4615,73 +4783,112 @@ bool restart_media_stream(Call *call, MediaEndpoint *me,
4615
4783
  return false;
4616
4784
  }
4617
4785
 
4618
- status = pjmedia_conf_add_port(call->conf, call->inv->pool, ae->stream_cbp.port, NULL, &ae->stream_cbp.slot);
4619
- if (status != PJ_SUCCESS) {
4620
- make_evt_media_update(evt, sizeof(evt), call->id,
4621
- "setup_failed (pjmedia_conf_add_port failed)", "");
4622
- dispatch_event(evt);
4623
- return false;
4624
- }
4625
-
4626
- if(!ae->dtmfdet_cbp.port) {
4627
- if(!prepare_dtmfdet(call, ae)) {
4786
+ if(!old_port) {
4787
+ printf("call_id=%i restart_media_stream !old_port (first SDP negotiation)\n", call->id);
4788
+ status = create_audio_endpoint_conf(call, ae, new_port);
4789
+ if (status != PJ_SUCCESS) {
4628
4790
  make_evt_media_update(evt, sizeof(evt), call->id,
4629
- "setup_failed (prepare_dtmfdet failed)", "");
4791
+ "setup_failed (create_audio_endpoint_conf failed)", "");
4630
4792
  dispatch_event(evt);
4631
4793
  return false;
4632
4794
  }
4633
- }
4634
4795
 
4635
- if(ae->tonegen_cbp.port) {
4636
- status = pjmedia_conf_connect_port(call->conf, ae->tonegen_cbp.slot, ae->stream_cbp.slot, 0);
4796
+ ae->stream_cbp.port = new_port;
4797
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->stream_cbp.port, NULL, &ae->stream_cbp.slot);
4637
4798
  if (status != PJ_SUCCESS) {
4638
4799
  make_evt_media_update(evt, sizeof(evt), call->id,
4639
- "setup_failed (pjmedia_conf_connect_port for tonegen failed)", "");
4800
+ "setup_failed (pjmedia_conf_add_port failed)", "");
4640
4801
  dispatch_event(evt);
4641
4802
  return false;
4642
4803
  }
4643
- }
4644
4804
 
4645
- if(ae->wav_player_cbp.port) {
4646
- status = pjmedia_conf_connect_port(call->conf, ae->wav_player_cbp.slot, ae->stream_cbp.slot, 0);
4647
- if (status != PJ_SUCCESS) {
4805
+ // we always add dtmfdet to audio endpoints
4806
+ if(!prepare_dtmfdet(call, ae)) {
4648
4807
  make_evt_media_update(evt, sizeof(evt), call->id,
4649
- "setup_failed (pjmedia_conf_connect_port for wav_player failed)", "");
4808
+ "setup_failed (prepare_dtmfdet failed)", "");
4650
4809
  dispatch_event(evt);
4651
4810
  return false;
4652
4811
  }
4653
- }
4654
-
4655
- if(ae->dtmfdet_cbp.port) {
4656
- status = pjmedia_conf_connect_port(call->conf, ae->stream_cbp.slot, ae->dtmfdet_cbp.slot, 0);
4812
+ } else if(
4813
+ (PJMEDIA_PIA_SRATE(&old_port->info) != PJMEDIA_PIA_SRATE(&new_port->info)) ||
4814
+ (PJMEDIA_PIA_CCNT(&old_port->info) != PJMEDIA_PIA_CCNT(&new_port->info)) ||
4815
+ (PJMEDIA_PIA_SPF(&old_port->info) != PJMEDIA_PIA_SPF(&new_port->info)) ||
4816
+ (PJMEDIA_PIA_BITS(&old_port->info) != PJMEDIA_PIA_BITS(&new_port->info))) {
4817
+ // stream characteristics changed so we need to destroy old conf and ports
4818
+ printf("call_id=%i restart_media_stream: stream characteristics changed\n", call->id);
4819
+ close_audio_endpoint_ports_and_conf(call, ae);
4820
+
4821
+ // then create a new conf
4822
+ status = create_audio_endpoint_conf(call, ae, new_port);
4657
4823
  if (status != PJ_SUCCESS) {
4658
4824
  make_evt_media_update(evt, sizeof(evt), call->id,
4659
- "setup_failed (pjmedia_conf_connect_port for dtmfdet failed)", "");
4825
+ "setup_failed (create_audio_endpoint_conf failed)", "");
4660
4826
  dispatch_event(evt);
4661
4827
  return false;
4662
-
4663
4828
  }
4664
- }
4665
4829
 
4666
- if(ae->fax_cbp.port) {
4667
- status = pjmedia_conf_connect_port(call->conf, ae->stream_cbp.slot, ae->fax_cbp.slot, 0);
4830
+ ae->stream_cbp.port = new_port;
4831
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->stream_cbp.port, NULL, &ae->stream_cbp.slot);
4668
4832
  if (status != PJ_SUCCESS) {
4669
4833
  make_evt_media_update(evt, sizeof(evt), call->id,
4670
- "setup_failed (pjmedia_conf_connect_port for fax dst failed)", "");
4834
+ "setup_failed (pjmedia_conf_add_port failed)", "");
4671
4835
  dispatch_event(evt);
4672
4836
  return false;
4837
+ }
4673
4838
 
4839
+ // we always add dtmfdet to audio endpoints
4840
+ if(!prepare_dtmfdet(call, ae)) {
4841
+ make_evt_media_update(evt, sizeof(evt), call->id,
4842
+ "setup_failed (prepare_dtmfdet failed)", "");
4843
+ dispatch_event(evt);
4844
+ return false;
4674
4845
  }
4675
4846
 
4676
- status = pjmedia_conf_connect_port(call->conf, ae->fax_cbp.slot, ae->stream_cbp.slot, 0);
4847
+ // at this point we could try to recreate ports (see #91)
4848
+ } else {
4849
+ printf("call_id=%i restart_media_stream: stream characteristics no change\n", call->id);
4850
+ // stream characteristics didn't change so just replace the stream port
4851
+
4852
+ audio_endpoint_remove_port(call, ae, &ae->stream_cbp);
4853
+ ae->stream_cbp.port = new_port;
4854
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->stream_cbp.port, NULL, &ae->stream_cbp.slot);
4677
4855
  if (status != PJ_SUCCESS) {
4678
4856
  make_evt_media_update(evt, sizeof(evt), call->id,
4679
- "setup_failed (pjmedia_conf_connect_port for fax src failed)", "");
4857
+ "setup_failed (pjmedia_conf_add_port failed)", "");
4680
4858
  dispatch_event(evt);
4681
4859
  return false;
4682
4860
  }
4683
- }
4684
4861
 
4862
+ // 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;
4885
+ }
4886
+
4887
+ if(ae->pocketsphinx_cbp.port) {
4888
+ if(!connect_feature_port_to_stream_port(call, ae, &ae->pocketsphinx_cbp, CONNECTION_MODE_SINK)) return false;
4889
+ }
4890
+ }
4891
+
4685
4892
  return true;
4686
4893
  }
4687
4894
 
@@ -4868,8 +5075,6 @@ static void on_state_changed(pjsip_inv_session *inv, pjsip_event *e) {
4868
5075
  }
4869
5076
  }
4870
5077
 
4871
- release_call_conf(call);
4872
-
4873
5078
  long val;
4874
5079
  if (!g_call_ids.remove(call_id, val)) {
4875
5080
  addon_log(L_DBG, "g_call_ids.remove failed\n");
@@ -4889,6 +5094,7 @@ static void on_state_changed(pjsip_inv_session *inv, pjsip_event *e) {
4889
5094
  sip_msg = e->body.rx_msg.rdata->msg_info.msg_buf;
4890
5095
  }
4891
5096
 
5097
+ printf("call_id=%li sip_msg_len=%i sip_msg=%p\n", call_id, sip_msg_len, sip_msg);
4892
5098
  make_evt_call_ended(evt, sizeof(evt), call_id, sip_msg_len, sip_msg);
4893
5099
  dispatch_event(evt);
4894
5100
  }
@@ -5259,13 +5465,6 @@ static pj_bool_t on_rx_request(pjsip_rx_data *rdata) {
5259
5465
  return PJ_TRUE;
5260
5466
  }
5261
5467
 
5262
- status = setup_call_conf(call);
5263
- if (status != PJ_SUCCESS) {
5264
- printf("setup_call_conf failed\n");
5265
- pjsip_endpt_respond_stateless(g_sip_endpt, rdata, 500, &reason, NULL, NULL);
5266
- return PJ_TRUE;
5267
- }
5268
-
5269
5468
  // TODO: check if this is really necessary as we are calling
5270
5469
  // pjsip_dlg_add_usage subsequently
5271
5470
  inv->dlg->mod_data[mod_tester.id] = call;
@@ -6453,10 +6652,10 @@ bool process_media(Call *call, pjsip_dialog *dlg, Document &document, bool answe
6453
6652
  return true;
6454
6653
  }
6455
6654
 
6456
- bool is_media_active(Call *c, MediaEndpoint *me) {
6655
+ bool is_media_active(Call *call, MediaEndpoint *me) {
6457
6656
  // check if media from call->media_neg is on call->media
6458
- for (int i = 0; i < c->media_count; ++i) {
6459
- if (me == c->media[i])
6657
+ for (int i = 0; i < call->media_count; ++i) {
6658
+ if (me == call->media[i])
6460
6659
  return true;
6461
6660
  }
6462
6661
  return false;
@@ -6464,18 +6663,14 @@ bool is_media_active(Call *c, MediaEndpoint *me) {
6464
6663
 
6465
6664
  void close_media_endpoint(Call *call, MediaEndpoint *me) {
6466
6665
  printf("close_media_endpoint %p\n", (void*)me);
6666
+
6667
+ pj_status_t status;
6668
+
6467
6669
  if(!me) return;
6468
6670
 
6469
6671
  if (ENDPOINT_TYPE_AUDIO == me->type) {
6470
6672
  AudioEndpoint *ae = (AudioEndpoint *)me->endpoint.audio;
6471
-
6472
- audio_endpoint_remove_port(call, &ae->stream_cbp);
6473
- audio_endpoint_remove_port(call, &ae->wav_player_cbp);
6474
- audio_endpoint_remove_port(call, &ae->wav_writer_cbp);
6475
- audio_endpoint_remove_port(call, &ae->tonegen_cbp);
6476
- audio_endpoint_remove_port(call, &ae->dtmfdet_cbp);
6477
- audio_endpoint_remove_port(call, &ae->fax_cbp);
6478
-
6673
+ close_audio_endpoint_ports_and_conf(call, ae);
6479
6674
  close_media_transport(ae->med_transport);
6480
6675
  ae->med_transport = NULL;
6481
6676
  } else if (ENDPOINT_TYPE_MRCP == me->type) {
@@ -6499,17 +6694,17 @@ void close_media_endpoint(Call *call, MediaEndpoint *me) {
6499
6694
  me->port = 0;
6500
6695
  }
6501
6696
 
6502
- void close_media(Call *c) {
6503
- printf("close_media call_id=%li\n", c->id);
6504
- for (int i = 0; i < c->media_count; ++i) {
6505
- MediaEndpoint *me = c->media[i];
6506
- close_media_endpoint(c, me);
6697
+ void close_media(Call *call) {
6698
+ printf("close_media call_id=%i\n", call->id);
6699
+ for (int i = 0; i < call->media_count; ++i) {
6700
+ MediaEndpoint *me = call->media[i];
6701
+ close_media_endpoint(call, me);
6507
6702
  }
6508
- c->media_count = 0;
6703
+ call->media_count = 0;
6509
6704
  }
6510
6705
 
6511
- bool prepare_tonegen(Call *c, AudioEndpoint *ae) {
6512
- printf("prepare_tone_gen call.id=%i\n", c->id);
6706
+ bool prepare_tonegen(Call *call, AudioEndpoint *ae) {
6707
+ printf("prepare_tone_gen call.id=%i\n", call->id);
6513
6708
  pj_status_t status;
6514
6709
 
6515
6710
  if(ae->tonegen_cbp.port) {
@@ -6518,7 +6713,7 @@ bool prepare_tonegen(Call *c, AudioEndpoint *ae) {
6518
6713
  }
6519
6714
 
6520
6715
  status = pjmedia_tonegen_create(
6521
- c->inv->pool, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6716
+ call->inv->pool, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6522
6717
  PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info),
6523
6718
  PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6524
6719
  PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info), 0, &ae->tonegen_cbp.port);
@@ -6527,22 +6722,16 @@ bool prepare_tonegen(Call *c, AudioEndpoint *ae) {
6527
6722
  return false;
6528
6723
  }
6529
6724
 
6530
- status = pjmedia_conf_add_port(c->conf, c->inv->pool, ae->tonegen_cbp.port, NULL, &ae->tonegen_cbp.slot);
6725
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->tonegen_cbp.port, NULL, &ae->tonegen_cbp.slot);
6531
6726
  if (status != PJ_SUCCESS) {
6532
6727
  set_error("pjmedia_conf_add_port failed");
6533
6728
  return false;
6534
6729
  }
6535
6730
 
6536
- status = pjmedia_conf_connect_port(c->conf, ae->tonegen_cbp.slot, ae->stream_cbp.slot, 0);
6537
- if (status != PJ_SUCCESS) {
6538
- set_error("pjmedia_conf_connect_port failed");
6539
- return false;
6540
- }
6541
-
6542
- return true;
6731
+ return connect_feature_port_to_stream_port(call, ae, &ae->tonegen_cbp, CONNECTION_MODE_SOURCE);
6543
6732
  }
6544
6733
 
6545
- bool prepare_wav_player(Call *c, AudioEndpoint *ae, const char *file) {
6734
+ bool prepare_wav_player(Call *call, AudioEndpoint *ae, const char *file, unsigned flags, bool end_of_file_event) {
6546
6735
  pj_status_t status;
6547
6736
 
6548
6737
  unsigned wav_ptime;
@@ -6550,10 +6739,10 @@ bool prepare_wav_player(Call *c, AudioEndpoint *ae, const char *file) {
6550
6739
  PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info);
6551
6740
 
6552
6741
  status = pjmedia_wav_player_port_create(
6553
- c->inv->pool,
6742
+ call->inv->pool,
6554
6743
  file,
6555
6744
  wav_ptime,
6556
- 0, /* flags */
6745
+ flags,
6557
6746
  -1, /* buf size */
6558
6747
  &ae->wav_player_cbp.port
6559
6748
  );
@@ -6563,26 +6752,28 @@ bool prepare_wav_player(Call *c, AudioEndpoint *ae, const char *file) {
6563
6752
  return false;
6564
6753
  }
6565
6754
 
6566
- status = pjmedia_conf_add_port(c->conf, c->inv->pool, ae->wav_player_cbp.port, NULL, &ae->wav_player_cbp.slot);
6567
- if (status != PJ_SUCCESS) {
6568
- set_error("pjmedia_conf_add_port failed");
6569
- return false;
6755
+ if (end_of_file_event) {
6756
+ status = pjmedia_wav_player_set_eof_cb2(ae->wav_player_cbp.port, (void*)call, on_end_of_file);
6757
+ if (status != PJ_SUCCESS) {
6758
+ set_error("pjmedia_wav_player_set_eof_cb2 failed");
6759
+ return false;
6760
+ }
6570
6761
  }
6571
6762
 
6572
- status = pjmedia_conf_connect_port(c->conf, ae->wav_player_cbp.slot, ae->stream_cbp.slot, 0);
6763
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->wav_player_cbp.port, NULL, &ae->wav_player_cbp.slot);
6573
6764
  if (status != PJ_SUCCESS) {
6574
- set_error("pjmedia_conf_connect_port failed");
6765
+ set_error("pjmedia_conf_add_port failed");
6575
6766
  return false;
6576
6767
  }
6577
6768
 
6578
- return true;
6769
+ return connect_feature_port_to_stream_port(call, ae, &ae->wav_player_cbp, CONNECTION_MODE_SOURCE);
6579
6770
  }
6580
6771
 
6581
- bool prepare_wav_writer(Call *c, AudioEndpoint *ae, const char *file) {
6772
+ bool prepare_wav_writer(Call *call, AudioEndpoint *ae, const char *file) {
6582
6773
  pj_status_t status;
6583
6774
 
6584
6775
  status = pjmedia_wav_writer_port_create(
6585
- c->inv->pool, file, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6776
+ call->inv->pool, file, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6586
6777
  PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info), PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6587
6778
  PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info), PJMEDIA_FILE_WRITE_PCM, 0,
6588
6779
  (pjmedia_port **)&ae->wav_writer_cbp.port);
@@ -6591,122 +6782,131 @@ bool prepare_wav_writer(Call *c, AudioEndpoint *ae, const char *file) {
6591
6782
  return false;
6592
6783
  }
6593
6784
 
6594
- status = pjmedia_conf_add_port(c->conf, c->inv->pool, ae->wav_writer_cbp.port, NULL, &ae->wav_writer_cbp.slot);
6785
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->wav_writer_cbp.port, NULL, &ae->wav_writer_cbp.slot);
6595
6786
  if (status != PJ_SUCCESS) {
6596
6787
  set_error("pjmedia_conf_add_port failed");
6597
6788
  return false;
6598
6789
  }
6599
6790
 
6600
- status = pjmedia_conf_connect_port(c->conf, ae->stream_cbp.slot, ae->wav_writer_cbp.slot, 0);
6601
- if (status != PJ_SUCCESS) {
6602
- set_error("pjmedia_conf_connect_port failed");
6603
- return false;
6604
- }
6605
-
6606
- return true;
6791
+ return connect_feature_port_to_stream_port(call, ae, &ae->wav_writer_cbp, CONNECTION_MODE_SINK);
6607
6792
  }
6608
6793
 
6609
- bool prepare_dtmfdet(Call *c, AudioEndpoint *ae) {
6794
+ bool prepare_dtmfdet(Call *call, AudioEndpoint *ae) {
6610
6795
  pj_status_t status;
6611
6796
  status = pjmedia_dtmfdet_create(
6612
- c->inv->pool,
6797
+ call->inv->pool,
6613
6798
  PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6614
- PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info), PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6799
+ PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info),
6800
+ PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6615
6801
  PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info),
6616
- on_inband_dtmf, c, &ae->dtmfdet_cbp.port);
6802
+ on_inband_dtmf, call, &ae->dtmfdet_cbp.port);
6617
6803
  if (status != PJ_SUCCESS) {
6618
6804
  set_error("pjmedia_dtmfdet_create failed");
6619
6805
  return false;
6620
6806
  }
6621
6807
 
6622
- status = pjmedia_conf_add_port(c->conf, c->inv->pool, ae->dtmfdet_cbp.port, NULL, &ae->dtmfdet_cbp.slot);
6808
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->dtmfdet_cbp.port, NULL, &ae->dtmfdet_cbp.slot);
6623
6809
  if (status != PJ_SUCCESS) {
6624
6810
  set_error("pjmedia_conf_add_port failed");
6625
6811
  return false;
6626
6812
  }
6627
6813
 
6628
- status = pjmedia_conf_connect_port(c->conf, ae->stream_cbp.slot, ae->dtmfdet_cbp.slot, 0);
6629
- if (status != PJ_SUCCESS) {
6630
- set_error("pjmedia_conf_connect_port failed");
6631
- return false;
6632
- }
6633
-
6634
- return true;
6814
+ return connect_feature_port_to_stream_port(call, ae, &ae->dtmfdet_cbp, CONNECTION_MODE_SINK);
6635
6815
  }
6636
6816
 
6637
- bool prepare_fax(Call *c, AudioEndpoint *ae, bool is_sender, const char *file,
6817
+ bool prepare_fax(Call *call, AudioEndpoint *ae, bool is_sender, const char *file,
6638
6818
  unsigned flags) {
6639
6819
  pj_status_t status;
6640
6820
 
6641
6821
  status = pjmedia_fax_port_create(
6642
- c->inv->pool,
6822
+ call->inv->pool,
6643
6823
  PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6644
6824
  PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info),
6645
6825
  PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6646
6826
  PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info),
6647
- on_fax_result, c, is_sender, file,
6827
+ on_fax_result, call, is_sender, file,
6648
6828
  flags, &ae->fax_cbp.port);
6649
6829
  if (status != PJ_SUCCESS) {
6650
6830
  set_error("pjmedia_fax_port_create failed");
6651
6831
  return false;
6652
6832
  }
6653
6833
 
6654
- status = pjmedia_conf_add_port(c->conf, c->inv->pool, ae->fax_cbp.port, NULL, &ae->fax_cbp.slot);
6834
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->fax_cbp.port, NULL, &ae->fax_cbp.slot);
6655
6835
  if (status != PJ_SUCCESS) {
6656
6836
  set_error("pjmedia_conf_add_port failed");
6657
6837
  return false;
6658
6838
  }
6659
6839
 
6660
- status = pjmedia_conf_connect_port(c->conf, ae->fax_cbp.slot, ae->stream_cbp.slot, 0);
6840
+ return connect_feature_port_to_stream_port(call, ae, &ae->fax_cbp, CONNECTION_MODE_SOURCE_AND_SINK);
6841
+ }
6842
+
6843
+ 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
+ pj_status_t status;
6846
+
6847
+ if(ae->flite_cbp.port) {
6848
+ printf("already prepared\n");
6849
+ return true;
6850
+ }
6851
+
6852
+ status = pjmedia_flite_port_create(
6853
+ call->inv->pool, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6854
+ PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info),
6855
+ PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6856
+ PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info), voice, &ae->flite_cbp.port);
6661
6857
  if (status != PJ_SUCCESS) {
6662
- set_error("pjmedia_conf_connect_port failed");
6858
+ set_error("pjmedia_flite_port_create failed");
6663
6859
  return false;
6664
6860
  }
6665
6861
 
6666
- status = pjmedia_conf_connect_port(c->conf, ae->stream_cbp.slot, ae->fax_cbp.slot, 0);
6862
+ if (end_of_speech_event) {
6863
+ status = pjmedia_flite_port_set_eof_cb(ae->flite_cbp.port, (void*)call, on_end_of_speech);
6864
+ if (status != PJ_SUCCESS) {
6865
+ set_error("pjmedia_flite_port_set_eof_cb failed");
6866
+ return false;
6867
+ }
6868
+ }
6869
+
6870
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->flite_cbp.port, NULL, &ae->flite_cbp.slot);
6667
6871
  if (status != PJ_SUCCESS) {
6668
- set_error("pjmedia_conf_connect_port failed");
6872
+ set_error("pjmedia_conf_add_port failed");
6669
6873
  return false;
6670
6874
  }
6671
6875
 
6672
- return true;
6876
+ return connect_feature_port_to_stream_port(call, ae, &ae->flite_cbp, CONNECTION_MODE_SOURCE);
6673
6877
  }
6674
6878
 
6675
- bool prepare_flite(Call *c, AudioEndpoint *ae, const char *voice) {
6676
- printf("prepare_flite call.id=%i\n", c->id);
6879
+ bool prepare_pocketsphinx(Call *call, AudioEndpoint *ae) {
6880
+ printf("prepare_pocketsphinx call.id=%i\n", call->id);
6677
6881
  pj_status_t status;
6678
6882
 
6679
- if(ae->flite_cbp.port) {
6883
+ if(ae->pocketsphinx_cbp.port) {
6680
6884
  printf("already prepared\n");
6681
6885
  return true;
6682
6886
  }
6683
6887
 
6684
- status = pjmedia_flite_port_create(
6685
- c->inv->pool, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6888
+ status = pjmedia_pocketsphinx_port_create(
6889
+ call->inv->pool, PJMEDIA_PIA_SRATE(&ae->stream_cbp.port->info),
6686
6890
  PJMEDIA_PIA_CCNT(&ae->stream_cbp.port->info),
6687
6891
  PJMEDIA_PIA_SPF(&ae->stream_cbp.port->info),
6688
- PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info), NULL, 0, voice, &ae->flite_cbp.port);
6892
+ PJMEDIA_PIA_BITS(&ae->stream_cbp.port->info),
6893
+ on_speech_transcript,
6894
+ call,
6895
+ &ae->pocketsphinx_cbp.port);
6689
6896
  if (status != PJ_SUCCESS) {
6690
- set_error("pjmedia_flite_port_create failed");
6897
+ set_error("pjmedia_pocketsphinx_port_create failed");
6691
6898
  return false;
6692
6899
  }
6693
6900
 
6694
- status = pjmedia_conf_add_port(c->conf, c->inv->pool, ae->flite_cbp.port, NULL, &ae->flite_cbp.slot);
6901
+ status = pjmedia_conf_add_port(ae->conf, call->inv->pool, ae->pocketsphinx_cbp.port, NULL, &ae->pocketsphinx_cbp.slot);
6695
6902
  if (status != PJ_SUCCESS) {
6696
6903
  set_error("pjmedia_conf_add_port failed");
6697
6904
  return false;
6698
6905
  }
6699
6906
 
6700
- status = pjmedia_conf_connect_port(c->conf, ae->flite_cbp.slot, ae->stream_cbp.slot, 0);
6701
- if (status != PJ_SUCCESS) {
6702
- set_error("pjmedia_conf_connect_port failed");
6703
- return false;
6704
- }
6705
-
6706
- return true;
6907
+ return connect_feature_port_to_stream_port(call, ae, &ae->pocketsphinx_cbp, CONNECTION_MODE_SINK);
6707
6908
  }
6708
6909
 
6709
-
6710
6910
  void on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code,
6711
6911
  pj_str_t **p_st_text, pjsip_hdr *res_hdr,
6712
6912
  pjsip_msg_body **p_body) {
@@ -8012,7 +8212,7 @@ int pjw_call_send_tcp_msg(long call_id, const char *json) {
8012
8212
  MediaEndpoint *me;
8013
8213
  int res;
8014
8214
 
8015
- unsigned media_id = 0;
8215
+ int media_id = -1;
8016
8216
 
8017
8217
  char buffer[MAX_JSON_INPUT];
8018
8218
 
@@ -8036,7 +8236,7 @@ int pjw_call_send_tcp_msg(long call_id, const char *json) {
8036
8236
  }
8037
8237
  size = strlen(msg);
8038
8238
 
8039
- res = json_get_uint_param(document, "media_id", true, &media_id);
8239
+ res = json_get_int_param(document, "media_id", true, &media_id);
8040
8240
  if (res <= 0) {
8041
8241
  goto out;
8042
8242
  }
@@ -8050,7 +8250,7 @@ int pjw_call_send_tcp_msg(long call_id, const char *json) {
8050
8250
  } else {
8051
8251
  // Send msg to specified media_id
8052
8252
 
8053
- if ((int)media_id >= call->media_count) {
8253
+ if (media_id >= call->media_count) {
8054
8254
  set_error("invalid media_id");
8055
8255
  goto out;
8056
8256
  }
@@ -8093,8 +8293,8 @@ static int g_now;
8093
8293
 
8094
8294
  void check_digit_buffer(Call *call, int mode) {
8095
8295
  // addon_log(L_DBG, "check_digit_buffer g_now=%i for call_id=%i and mode=%i
8096
- // timestamp=%i len=%i\n", g_now, c->id, mode, c->last_digit_timestamp[mode],
8097
- // c->DigitBufferLength[mode]);
8296
+ // timestamp=%i len=%i\n", g_now, call->id, mode, call->last_digit_timestamp[mode],
8297
+ // call->DigitBufferLength[mode]);
8098
8298
  char evt[1024];
8099
8299
 
8100
8300
  for (int i = 0; i < call->media_count; i++) {
@@ -8118,10 +8318,10 @@ void check_digit_buffer(Call *call, int mode) {
8118
8318
  }
8119
8319
 
8120
8320
  void check_digit_buffers(long id, long val) {
8121
- Call *c = (Call *)val;
8321
+ Call *call = (Call *)val;
8122
8322
 
8123
- check_digit_buffer(c, DTMF_MODE_RFC2833);
8124
- check_digit_buffer(c, DTMF_MODE_INBAND);
8323
+ check_digit_buffer(call, DTMF_MODE_RFC2833);
8324
+ check_digit_buffer(call, DTMF_MODE_INBAND);
8125
8325
  }
8126
8326
 
8127
8327
  static int digit_buffer_thread(void *arg) {