1 /*
2 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
4 *
5 * Version: MPL 1.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License `
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18 *
19 * The Initial Developer of the Original Code is
20 * Anthony Minessale II <anthm@freeswitch.org>
21 * Portions created by the Initial Developer are Copyright (C)
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Anthony Minessale II <anthm@freeswitch.org>
27 * Moises Silva <moises.silva@gmail.com> (Multiple endpoints work sponsored by Comrex Corporation)
28 * Raymond Chandler <intralanman@freeswitch.org>
29 *
30 *
31 * mod_portaudio.c -- PortAudio Endpoint Module
32 *
33 */
34
35 #include "switch.h"
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <math.h>
40 #include <string.h>
41 #include "pablio.h"
42
43 #define MY_EVENT_RINGING "portaudio::ringing"
44 #define MY_EVENT_MAKE_CALL "portaudio::makecall"
45 #define MY_EVENT_CALL_HELD "portaudio::callheld"
46 #define MY_EVENT_CALL_RESUMED "portaudio::callresumed"
47 #define MY_EVENT_ERROR_AUDIO_DEV "portaudio::audio_dev_error"
48 #define SWITCH_PA_CALL_ID_VARIABLE "pa_call_id"
49
50 #define MIN_STREAM_SAMPLE_RATE 8000
51 #define STREAM_SAMPLES_PER_PACKET(stream) ((stream->codec_ms * stream->sample_rate) / 1000)
52
53 SWITCH_MODULE_LOAD_FUNCTION(mod_portaudio_load);
54 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_portaudio_shutdown);
55 //SWITCH_MODULE_RUNTIME_FUNCTION(mod_portaudio_runtime);
56 SWITCH_MODULE_DEFINITION(mod_portaudio, mod_portaudio_load, mod_portaudio_shutdown, NULL);
57
58 static switch_memory_pool_t *module_pool = NULL;
59 switch_endpoint_interface_t *portaudio_endpoint_interface;
60
61 #define SAMPLE_TYPE paInt16
62 typedef int16_t SAMPLE;
63
64 typedef switch_status_t (*pa_command_t) (char **argv, int argc, switch_stream_handle_t *stream);
65
66 typedef enum {
67 GFLAG_NONE = 0,
68 GFLAG_EAR = (1 << 0),
69 GFLAG_MOUTH = (1 << 1),
70 GFLAG_RING = (1 << 2)
71 } GFLAGS;
72
73 typedef enum {
74 TFLAG_IO = (1 << 0),
75 TFLAG_INBOUND = (1 << 1),
76 TFLAG_OUTBOUND = (1 << 2),
77 TFLAG_DTMF = (1 << 3),
78 TFLAG_VOICE = (1 << 4),
79 TFLAG_HANGUP = (1 << 5),
80 TFLAG_LINEAR = (1 << 6),
81 TFLAG_ANSWER = (1 << 7),
82 TFLAG_HUP = (1 << 8),
83 TFLAG_MASTER = (1 << 9),
84 TFLAG_AUTO_ANSWER = (1 << 10)
85 } TFLAGS;
86
87 struct audio_stream {
88 int indev;
89 int outdev;
90 PABLIO_Stream *stream;
91 switch_timer_t write_timer;
92 struct audio_stream *next;
93 };
94 typedef struct audio_stream audio_stream_t;
95
96 /* Audio stream that can be shared across endpoints */
97 typedef struct _shared_audio_stream_t {
98 /*! Friendly name for this stream */
99 char name[255];
100 /*! Sampling rate */
101 int sample_rate;
102 /*! Buffer packetization (and therefore timing) */
103 int codec_ms;
104 /*! The PA input device */
105 int indev;
106 /*! Input channels being used */
107 uint8_t inchan_used[MAX_IO_CHANNELS];
108 /*! The PA output device */
109 int outdev;
110 /*! Output channels being used */
111 uint8_t outchan_used[MAX_IO_CHANNELS];
112 /*! How many channels to create (for both indev and outdev) */
113 int channels;
114 /*! The io stream helper to buffer audio */
115 PABLIO_Stream *stream;
116 /* It can be shared after all :-) */
117 switch_mutex_t *mutex;
118 } shared_audio_stream_t;
119
120 typedef struct private_object private_t;
121 /* Endpoint that can be called via portaudio/endpoint/<endpoint-name> */
122 typedef struct _audio_endpoint {
123 /*! Friendly name for this endpoint */
124 char name[255];
125
126 /*! Input stream for this endpoint */
127 shared_audio_stream_t *in_stream;
128
129 /*! Output stream for this endpoint */
130 shared_audio_stream_t *out_stream;
131
132 /*! Channel index within the input stream where we get the audio for this endpoint */
133 int inchan;
134
135 /*! Channel index within the output stream where we get the audio for this endpoint */
136 int outchan;
137
138 /*! Associated private information if involved in a call */
139 private_t *master;
140
141 /*! For timed read and writes */
142 switch_timer_t read_timer;
143 switch_timer_t write_timer;
144
145 /* We need our own read frame */
146 switch_frame_t read_frame;
147 unsigned char read_buf[SWITCH_RECOMMENDED_BUFFER_SIZE];
148
149 /* Needed codecs for the core to read/write in the proper format */
150 switch_codec_t read_codec;
151 switch_codec_t write_codec;
152
153 /*! Let's be safe */
154 switch_mutex_t *mutex;
155 } audio_endpoint_t;
156
157 struct private_object {
158 unsigned int flags;
159 switch_core_session_t *session;
160 switch_caller_profile_t *caller_profile;
161 char call_id[50];
162 int sample_rate;
163 int codec_ms;
164 switch_mutex_t *flag_mutex;
165 char *hold_file;
166 switch_file_handle_t fh;
167 switch_file_handle_t *hfh;
168 switch_frame_t hold_frame;
169 unsigned char holdbuf[SWITCH_RECOMMENDED_BUFFER_SIZE];
170 audio_endpoint_t *audio_endpoint;
171 struct private_object *next;
172 };
173
174
175 static struct {
176 int debug;
177 int port;
178 char *cid_name;
179 char *cid_num;
180 char *dialplan;
181 char *context;
182 char *ring_file;
183 char *hold_file;
184 char *timer_name;
185 int ringdev;
186 int indev;
187 int outdev;
188 int call_id;
189 int unload_device_fail;
190 switch_hash_t *call_hash;
191 switch_mutex_t *device_lock;
192 switch_mutex_t *pvt_lock;
193 switch_mutex_t *streams_lock;
194 switch_mutex_t *flag_mutex;
195 switch_mutex_t *pa_mutex;
196 int sample_rate;
197 int codec_ms;
198 audio_stream_t *main_stream;
199 audio_stream_t *ring_stream;
200 switch_codec_t read_codec;
201 switch_codec_t write_codec;
202 switch_frame_t read_frame;
203 switch_frame_t cng_frame;
204 unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE];
205 unsigned char cngbuf[SWITCH_RECOMMENDED_BUFFER_SIZE];
206 private_t *call_list;
207 audio_stream_t *stream_list;
208 /*! Streams that can be used by multiple endpoints at the same time */
209 switch_hash_t *sh_streams;
210 /*! Endpoints configured */
211 switch_hash_t *endpoints;
212 int ring_interval;
213 GFLAGS flags;
214 switch_timer_t read_timer;
215 switch_timer_t readfile_timer;
216 switch_timer_t hold_timer;
217 int dual_streams;
218 time_t deactivate_timer;
219 int live_stream_switch;
220 int no_auto_resume_call;
221 int no_ring_during_call;
222 int codecs_inited;
223 int stream_in_use; //only really used by playdev
224 int destroying_streams;
225 } globals;
226
227
228 #define PA_MASTER 1
229 #define PA_SLAVE 0
230
231
232 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_dialplan, globals.dialplan);
233 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_context, globals.context);
234 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_cid_name, globals.cid_name);
235 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_cid_num, globals.cid_num);
236 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_ring_file, globals.ring_file);
237 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_hold_file, globals.hold_file);
238 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_timer_name, globals.timer_name);
239 #define is_master(t) switch_test_flag(t, TFLAG_MASTER)
240
241 static void add_pvt(private_t *tech_pvt, int master);
242 static void remove_pvt(private_t *tech_pvt);
243 static switch_status_t channel_on_init(switch_core_session_t *session);
244 static switch_status_t channel_on_hangup(switch_core_session_t *session);
245 static switch_status_t channel_on_destroy(switch_core_session_t *session);
246 static switch_status_t channel_on_routing(switch_core_session_t *session);
247 static switch_status_t channel_on_exchange_media(switch_core_session_t *session);
248 static switch_status_t channel_on_soft_execute(switch_core_session_t *session);
249 static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event,
250 switch_caller_profile_t *outbound_profile,
251 switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags,
252 switch_call_cause_t *cancel_cause);
253 static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id);
254 static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id);
255 static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig);
256
257 static switch_status_t create_codecs(int restart);
258 static void create_hold_event(private_t *tech_pvt, int unhold);
259 static audio_stream_t * find_audio_stream(int indev, int outdev, int already_locked);
260 static audio_stream_t * get_audio_stream(int indev, int outdev);
261 static audio_stream_t * create_audio_stream(int indev, int outdev);
262 PaError open_audio_stream(PABLIO_Stream **stream, const PaStreamParameters * inputParameters, const PaStreamParameters * outputParameters);
263 static switch_status_t switch_audio_stream();
264 static void add_stream(audio_stream_t *stream, int already_locked);
265 static void remove_stream(audio_stream_t *stream, int already_locked);
266 static switch_status_t destroy_audio_stream(int indev, int outdev);
267 static switch_status_t destroy_actual_stream(audio_stream_t *stream);
268 static void destroy_audio_streams();
269 static switch_status_t validate_main_audio_stream();
270 static switch_status_t validate_ring_audio_stream();
271
272 static int dump_info(int verbose);
273 static switch_status_t load_config(void);
274 static int get_dev_by_name(char *name, int in);
275 static int get_dev_by_number(char *numstr, int in);
276 SWITCH_STANDARD_API(pa_cmd);
277
278 /*
279 State methods they get called when the state changes to the specific state
280 returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next
281 so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it.
282 */
channel_on_init(switch_core_session_t * session)283 static switch_status_t channel_on_init(switch_core_session_t *session)
284 {
285 switch_channel_t *channel;
286
287 if (session) {
288 if ((channel = switch_core_session_get_channel(session))) {
289 switch_channel_set_flag(channel, CF_AUDIO);
290 }
291 }
292
293 return SWITCH_STATUS_SUCCESS;
294 }
295
channel_on_routing(switch_core_session_t * session)296 static switch_status_t channel_on_routing(switch_core_session_t *session)
297 {
298
299 switch_channel_t *channel = switch_core_session_get_channel(session);
300 private_t *tech_pvt = switch_core_session_get_private(session);
301 switch_time_t last;
302 int waitsec = globals.ring_interval * 1000000;
303 switch_file_handle_t fh = { 0 };
304 const char *val, *ring_file = NULL, *hold_file = NULL;
305 int16_t abuf[2048];
306
307 switch_assert(tech_pvt != NULL);
308
309 last = switch_micro_time_now() - waitsec;
310
311 if ((val = switch_channel_get_variable(channel, "pa_hold_file"))) {
312 hold_file = val;
313 } else {
314 hold_file = globals.hold_file;
315 }
316
317 if (hold_file) {
318 tech_pvt->hold_file = switch_core_session_strdup(session, hold_file);
319 }
320 if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
321 if (!tech_pvt->audio_endpoint && validate_main_audio_stream() != SWITCH_STATUS_SUCCESS) {
322 switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
323 return SWITCH_STATUS_FALSE;
324 }
325
326 if (!tech_pvt->audio_endpoint &&
327 switch_test_flag(tech_pvt, TFLAG_OUTBOUND) &&
328 !switch_test_flag(tech_pvt, TFLAG_AUTO_ANSWER)) {
329
330 add_pvt(tech_pvt, PA_SLAVE);
331
332 ring_file = globals.ring_file;
333 if ((val = switch_channel_get_variable(channel, "pa_ring_file"))) {
334 ring_file = val;
335 }
336
337 if (switch_test_flag((&globals), GFLAG_RING)) {
338 ring_file = NULL;
339 }
340 switch_set_flag_locked((&globals), GFLAG_RING);
341 if (ring_file) {
342 if (switch_core_file_open(&fh,
343 ring_file,
344 globals.read_codec.implementation->number_of_channels,
345 globals.read_codec.implementation->actual_samples_per_second,
346 SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) == SWITCH_STATUS_SUCCESS) {
347
348 if (validate_ring_audio_stream() != SWITCH_STATUS_SUCCESS) {
349 switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
350 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Ring Error!\n");
351 switch_core_file_close(&fh);
352 return SWITCH_STATUS_GENERR;
353 }
354 } else {
355 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot open %s, disabling ring file!\n", ring_file);
356 ring_file = NULL;
357 }
358 }
359 }
360
361 if (tech_pvt->audio_endpoint || switch_test_flag(tech_pvt, TFLAG_AUTO_ANSWER)) {
362 switch_mutex_lock(globals.pvt_lock);
363 add_pvt(tech_pvt, PA_MASTER);
364 if (switch_test_flag(tech_pvt, TFLAG_AUTO_ANSWER)) {
365 switch_channel_mark_answered(channel);
366 switch_set_flag(tech_pvt, TFLAG_ANSWER);
367 }
368 switch_mutex_unlock(globals.pvt_lock);
369 switch_yield(1000000);
370 } else {
371 switch_channel_mark_ring_ready(channel);
372 }
373
374 while (switch_channel_get_state(channel) == CS_ROUTING &&
375 !switch_channel_test_flag(channel, CF_ANSWERED) &&
376 !switch_test_flag(tech_pvt, TFLAG_ANSWER)) {
377 switch_size_t olen = globals.readfile_timer.samples;
378
379 if (switch_micro_time_now() - last >= waitsec) {
380 char buf[512];
381 switch_event_t *event;
382
383 switch_snprintf(buf, sizeof(buf), "BRRRRING! BRRRRING! call %s\n", tech_pvt->call_id);
384
385 if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_RINGING) == SWITCH_STATUS_SUCCESS) {
386 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_info", buf);
387 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call_id", tech_pvt->call_id); /* left behind for backwards compatability */
388 switch_channel_set_variable(channel, SWITCH_PA_CALL_ID_VARIABLE, tech_pvt->call_id);
389 switch_channel_event_set_data(channel, event);
390 switch_event_fire(&event);
391 }
392 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s\n", buf);
393 last = switch_micro_time_now();
394 }
395
396 if (ring_file) {
397 if (switch_core_timer_next(&globals.readfile_timer) != SWITCH_STATUS_SUCCESS) {
398 switch_core_file_close(&fh);
399 break;
400 }
401 switch_core_file_read(&fh, abuf, &olen);
402 if (olen == 0) {
403 unsigned int pos = 0;
404 switch_core_file_seek(&fh, &pos, 0, SEEK_SET);
405 }
406
407 if (globals.ring_stream && (! switch_test_flag(globals.call_list, TFLAG_MASTER) ||
408 ( !globals.no_ring_during_call && globals.main_stream != globals.ring_stream)) ) { //if there is a ring stream and not an active call or if there is an active call and we are allowed to ring during it AND the ring stream is not the main stream
409 WriteAudioStream(globals.ring_stream->stream, abuf, (long) olen, 0, &globals.ring_stream->write_timer);
410 }
411 } else {
412 switch_yield(10000);
413 }
414 }
415 switch_clear_flag_locked((&globals), GFLAG_RING);
416 }
417
418 if (ring_file) {
419 switch_core_file_close(&fh);
420 }
421
422 if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) {
423 if (!switch_test_flag(tech_pvt, TFLAG_ANSWER) &&
424 !switch_channel_test_flag(channel, CF_ANSWERED)) {
425 switch_channel_hangup(channel, SWITCH_CAUSE_NO_ANSWER);
426 return SWITCH_STATUS_SUCCESS;
427 }
428 switch_set_flag(tech_pvt, TFLAG_ANSWER);
429 }
430
431 switch_set_flag_locked(tech_pvt, TFLAG_IO);
432
433
434 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL ROUTING\n",
435 switch_channel_get_name(switch_core_session_get_channel(session)));
436 return SWITCH_STATUS_SUCCESS;
437 }
438
channel_on_execute(switch_core_session_t * session)439 static switch_status_t channel_on_execute(switch_core_session_t *session)
440 {
441 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL EXECUTE\n",
442 switch_channel_get_name(switch_core_session_get_channel(session)));
443 return SWITCH_STATUS_SUCCESS;
444 }
445
find_audio_stream(int indev,int outdev,int already_locked)446 static audio_stream_t* find_audio_stream(int indev, int outdev, int already_locked)
447 {
448 audio_stream_t *cur_stream;
449
450 if (! globals.stream_list) {
451 return NULL;
452 }
453
454 if (! already_locked) {
455 switch_mutex_lock(globals.streams_lock);
456 }
457 cur_stream = globals.stream_list;
458
459 while (cur_stream != NULL) {
460 if (cur_stream->outdev == outdev) {
461 if (indev == -1 || cur_stream->indev == indev) {
462 if (! already_locked) {
463 switch_mutex_unlock(globals.streams_lock);
464 }
465 return cur_stream;
466 }
467 }
468 cur_stream = cur_stream->next;
469 }
470 if (! already_locked) {
471 switch_mutex_unlock(globals.streams_lock);
472 }
473 return NULL;
474 }
475
destroy_audio_streams()476 static void destroy_audio_streams()
477 {
478 int close_wait = 4;
479 globals.destroying_streams = 1;
480
481 while (globals.stream_in_use && close_wait--) {
482 switch_yield(250000);
483 }
484 while (globals.stream_list != NULL) {
485 destroy_audio_stream(globals.stream_list->indev, globals.stream_list->outdev);
486 }
487 globals.destroying_streams = 0;
488 }
489
validate_main_audio_stream()490 static switch_status_t validate_main_audio_stream()
491 {
492 if (globals.read_timer.timer_interface) {
493 switch_core_timer_sync(&globals.read_timer);
494 }
495
496 if (globals.main_stream) {
497 if (globals.main_stream->write_timer.timer_interface) {
498 switch_core_timer_sync(&(globals.main_stream->write_timer));
499 }
500
501 return SWITCH_STATUS_SUCCESS;
502 }
503
504 globals.main_stream = get_audio_stream(globals.indev, globals.outdev);
505
506 if (globals.main_stream) {
507 return SWITCH_STATUS_SUCCESS;
508 }
509
510 return SWITCH_STATUS_FALSE;
511 }
512
validate_ring_audio_stream()513 static switch_status_t validate_ring_audio_stream()
514 {
515 if (globals.ringdev == -1) {
516 return SWITCH_STATUS_SUCCESS;
517 }
518 if (globals.ring_stream) {
519 if (globals.ring_stream->write_timer.timer_interface) {
520 switch_core_timer_sync(&(globals.ring_stream->write_timer));
521 }
522 return SWITCH_STATUS_SUCCESS;
523 }
524 globals.ring_stream = get_audio_stream(-1, globals.ringdev);
525 if (globals.ring_stream) {
526 return SWITCH_STATUS_SUCCESS;
527 }
528
529 return SWITCH_STATUS_FALSE;
530 }
531
destroy_actual_stream(audio_stream_t * stream)532 static switch_status_t destroy_actual_stream(audio_stream_t *stream)
533 {
534 if (stream == NULL) {
535 return SWITCH_STATUS_FALSE;
536 }
537
538 if (globals.main_stream == stream) {
539 globals.main_stream = NULL;
540 }
541
542 if (globals.ring_stream == stream) {
543 globals.ring_stream = NULL;
544 }
545
546 CloseAudioStream(stream->stream);
547 stream->stream = NULL;
548
549 if (stream->write_timer.timer_interface) {
550 switch_core_timer_destroy(&stream->write_timer);
551 }
552
553 switch_safe_free(stream);
554 return SWITCH_STATUS_SUCCESS;
555 }
destroy_audio_stream(int indev,int outdev)556 static switch_status_t destroy_audio_stream(int indev, int outdev)
557 {
558 audio_stream_t *stream;
559
560 switch_mutex_lock(globals.streams_lock);
561 stream = find_audio_stream(indev, outdev,1);
562 if (stream == NULL) {
563 switch_mutex_unlock(globals.streams_lock);
564 return SWITCH_STATUS_FALSE;
565 }
566
567 remove_stream(stream, 1);
568 switch_mutex_unlock(globals.streams_lock);
569
570 destroy_actual_stream(stream);
571 return SWITCH_STATUS_SUCCESS;
572 }
573
574
destroy_codecs(void)575 static void destroy_codecs(void)
576 {
577
578 if (switch_core_codec_ready(&globals.read_codec)) {
579 switch_core_codec_destroy(&globals.read_codec);
580 }
581
582 if (switch_core_codec_ready(&globals.write_codec)) {
583 switch_core_codec_destroy(&globals.write_codec);
584 }
585
586 if (globals.read_timer.timer_interface) {
587 switch_core_timer_destroy(&globals.read_timer);
588 }
589
590 if (globals.readfile_timer.timer_interface) {
591 switch_core_timer_destroy(&globals.readfile_timer);
592 }
593
594 if (globals.hold_timer.timer_interface) {
595 switch_core_timer_destroy(&globals.hold_timer);
596 }
597
598 globals.codecs_inited = 0;
599 }
600
create_hold_event(private_t * tech_pvt,int unhold)601 static void create_hold_event(private_t *tech_pvt, int unhold)
602 {
603 switch_event_t *event;
604 char * event_id;
605
606 if (unhold) {
607 event_id = MY_EVENT_CALL_RESUMED;
608 } else {
609 event_id = MY_EVENT_CALL_HELD;
610 }
611
612 if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, event_id) == SWITCH_STATUS_SUCCESS) {
613 switch_channel_event_set_data(switch_core_session_get_channel(tech_pvt->session), event);
614 switch_event_fire(&event);
615 }
616 }
617
add_stream(audio_stream_t * stream,int already_locked)618 static void add_stream(audio_stream_t * stream, int already_locked)
619 {
620 audio_stream_t *last;
621
622 if (! already_locked) {
623 switch_mutex_lock(globals.streams_lock);
624 }
625 for (last = globals.stream_list; last && last->next; last = last->next);
626 if (last == NULL) {
627 globals.stream_list = stream;
628 } else {
629 last->next = stream;
630 }
631 if (! already_locked) {
632 switch_mutex_unlock(globals.streams_lock);
633 }
634 }
remove_stream(audio_stream_t * stream,int already_locked)635 static void remove_stream(audio_stream_t * stream, int already_locked)
636 {
637 audio_stream_t *previous;
638 if (! already_locked) {
639 switch_mutex_lock(globals.streams_lock);
640 }
641 if (globals.stream_list == stream) {
642 globals.stream_list = stream->next;
643 } else {
644 for (previous = globals.stream_list; previous && previous->next && previous->next != stream; previous = previous->next) {
645 ;
646 }
647 if (previous) {
648 previous->next = stream->next;
649 }
650 }
651 if (! already_locked) {
652 switch_mutex_unlock(globals.streams_lock);
653 }
654 }
655
add_pvt(private_t * tech_pvt,int master)656 static void add_pvt(private_t *tech_pvt, int master)
657 {
658 private_t *tp;
659 uint8_t in_list = 0;
660
661 switch_mutex_lock(globals.pvt_lock);
662
663 if (*tech_pvt->call_id == '\0') {
664 switch_mutex_lock(globals.pa_mutex);
665 switch_snprintf(tech_pvt->call_id, sizeof(tech_pvt->call_id), "%d", ++globals.call_id);
666 switch_channel_set_variable(switch_core_session_get_channel(tech_pvt->session), SWITCH_PA_CALL_ID_VARIABLE, tech_pvt->call_id);
667 switch_core_hash_insert(globals.call_hash, tech_pvt->call_id, tech_pvt);
668 if (!tech_pvt->audio_endpoint) {
669 switch_core_session_set_read_codec(tech_pvt->session, &globals.read_codec);
670 switch_core_session_set_write_codec(tech_pvt->session, &globals.write_codec);
671 }
672 switch_mutex_unlock(globals.pa_mutex);
673 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Added call %s\n", tech_pvt->call_id);
674 }
675
676 for (tp = globals.call_list; tp; tp = tp->next) {
677 if (tp == tech_pvt) {
678 in_list = 1;
679 }
680 if (master && switch_test_flag(tp, TFLAG_MASTER) ) {
681 switch_clear_flag_locked(tp, TFLAG_MASTER);
682 create_hold_event(tp, 0);
683 }
684 }
685
686
687 if (master) {
688 if (!in_list) {
689 tech_pvt->next = globals.call_list;
690 globals.call_list = tech_pvt;
691 }
692 switch_set_flag_locked(tech_pvt, TFLAG_MASTER);
693
694 } else if (!in_list) {
695 for (tp = globals.call_list; tp && tp->next; tp = tp->next);
696 if (tp) {
697 tp->next = tech_pvt;
698 } else {
699 globals.call_list = tech_pvt;
700 }
701 }
702
703 switch_mutex_unlock(globals.pvt_lock);
704 }
705
remove_pvt(private_t * tech_pvt)706 static void remove_pvt(private_t *tech_pvt)
707 {
708 private_t *tp, *last = NULL;
709 int was_master = 0;
710
711 switch_mutex_lock(globals.pvt_lock);
712 for (tp = globals.call_list; tp; tp = tp->next) {
713
714 if (tp == tech_pvt) {
715 if (switch_test_flag(tp, TFLAG_MASTER)) {
716 switch_clear_flag_locked(tp, TFLAG_MASTER);
717 was_master = 1;
718 }
719 if (last) {
720 last->next = tp->next;
721 } else {
722 globals.call_list = tp->next;
723 }
724 }
725 last = tp;
726 }
727
728 if (globals.call_list) {
729 if (was_master && ! globals.no_auto_resume_call) {
730 switch_set_flag_locked(globals.call_list, TFLAG_MASTER);
731 create_hold_event(globals.call_list, 1);
732 }
733 } else {
734 globals.deactivate_timer = switch_epoch_time_now(NULL) + 2;
735 destroy_audio_streams();
736 }
737
738 switch_mutex_unlock(globals.pvt_lock);
739 }
740
tech_close_file(private_t * tech_pvt)741 static void tech_close_file(private_t *tech_pvt)
742 {
743 if (tech_pvt->hfh) {
744 tech_pvt->hfh = NULL;
745 switch_core_file_close(&tech_pvt->fh);
746 }
747 }
748
channel_on_destroy(switch_core_session_t * session)749 static switch_status_t channel_on_destroy(switch_core_session_t *session)
750 {
751 //private_t *tech_pvt = switch_core_session_get_private(session);
752 //switch_assert(tech_pvt != NULL);
753 return SWITCH_STATUS_SUCCESS;
754 }
755
756 static int release_stream_channel(shared_audio_stream_t *stream, int index, int input);
channel_on_hangup(switch_core_session_t * session)757 static switch_status_t channel_on_hangup(switch_core_session_t *session)
758 {
759 private_t *tech_pvt = switch_core_session_get_private(session);
760 switch_assert(tech_pvt != NULL);
761
762 if (tech_pvt->audio_endpoint) {
763 audio_endpoint_t *endpoint = tech_pvt->audio_endpoint;
764
765 tech_pvt->audio_endpoint = NULL;
766
767 switch_mutex_lock(endpoint->mutex);
768
769 release_stream_channel(endpoint->in_stream, endpoint->inchan, 1);
770 release_stream_channel(endpoint->out_stream, endpoint->outchan, 0);
771 switch_core_timer_destroy(&endpoint->read_timer);
772 switch_core_timer_destroy(&endpoint->write_timer);
773 switch_core_codec_destroy(&endpoint->read_codec);
774 switch_core_codec_destroy(&endpoint->write_codec);
775 endpoint->master = NULL;
776
777 switch_mutex_unlock(endpoint->mutex);
778 }
779
780 switch_mutex_lock(globals.pa_mutex);
781 switch_core_hash_delete(globals.call_hash, tech_pvt->call_id);
782 switch_mutex_unlock(globals.pa_mutex);
783
784 switch_clear_flag_locked(tech_pvt, TFLAG_IO);
785 switch_set_flag_locked(tech_pvt, TFLAG_HUP);
786
787 remove_pvt(tech_pvt);
788
789 tech_close_file(tech_pvt);
790
791 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL HANGUP\n",
792 switch_channel_get_name(switch_core_session_get_channel(session)));
793
794 return SWITCH_STATUS_SUCCESS;
795 }
796
channel_kill_channel(switch_core_session_t * session,int sig)797 static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig)
798 {
799 switch_channel_t *channel = switch_core_session_get_channel(session);
800 private_t *tech_pvt = switch_core_session_get_private(session);
801 switch_assert(tech_pvt != NULL);
802
803 switch (sig) {
804 case SWITCH_SIG_KILL:
805 switch_set_flag_locked(tech_pvt, TFLAG_HUP);
806 switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
807 break;
808 default:
809 break;
810 }
811 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s CHANNEL KILL\n", switch_channel_get_name(channel));
812
813 return SWITCH_STATUS_SUCCESS;
814 }
815
channel_on_soft_execute(switch_core_session_t * session)816 static switch_status_t channel_on_soft_execute(switch_core_session_t *session)
817 {
818 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "CHANNEL TRANSMIT\n");
819 return SWITCH_STATUS_SUCCESS;
820 }
821
channel_on_exchange_media(switch_core_session_t * session)822 static switch_status_t channel_on_exchange_media(switch_core_session_t *session)
823 {
824 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "CHANNEL LOOPBACK\n");
825 return SWITCH_STATUS_SUCCESS;
826 }
827
828
channel_send_dtmf(switch_core_session_t * session,const switch_dtmf_t * dtmf)829 static switch_status_t channel_send_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf)
830 {
831 private_t *tech_pvt = switch_core_session_get_private(session);
832 switch_assert(tech_pvt != NULL);
833
834 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "DTMF ON CALL %s [%c]\n", tech_pvt->call_id, dtmf->digit);
835
836 return SWITCH_STATUS_SUCCESS;
837 }
838
channel_endpoint_read(audio_endpoint_t * endpoint,switch_frame_t ** frame)839 static switch_status_t channel_endpoint_read(audio_endpoint_t *endpoint, switch_frame_t **frame)
840 {
841 int samples = 0;
842
843 if (!endpoint->in_stream) {
844 switch_core_timer_next(&endpoint->read_timer);
845 *frame = &globals.cng_frame;
846 return SWITCH_STATUS_SUCCESS;
847 }
848
849 endpoint->read_frame.data = endpoint->read_buf;
850 endpoint->read_frame.buflen = sizeof(endpoint->read_buf);
851 endpoint->read_frame.source = __FILE__;
852 samples = ReadAudioStream(endpoint->in_stream->stream,
853 endpoint->read_frame.data, STREAM_SAMPLES_PER_PACKET(endpoint->in_stream),
854 endpoint->inchan, &endpoint->read_timer);
855
856 if (!samples) {
857 switch_core_timer_next(&endpoint->read_timer);
858 *frame = &globals.cng_frame;
859 return SWITCH_STATUS_SUCCESS;
860 }
861
862 endpoint->read_frame.datalen = (samples * sizeof(int16_t));
863 endpoint->read_frame.samples = samples;
864 endpoint->read_frame.codec = &endpoint->read_codec;
865 *frame = &endpoint->read_frame;
866 return SWITCH_STATUS_SUCCESS;
867 }
868
channel_read_frame(switch_core_session_t * session,switch_frame_t ** frame,switch_io_flag_t flags,int stream_id)869 static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id)
870 {
871 private_t *tech_pvt = switch_core_session_get_private(session);
872 int samples = 0;
873 switch_status_t status = SWITCH_STATUS_FALSE;
874 switch_assert(tech_pvt != NULL);
875
876 if (tech_pvt->audio_endpoint) {
877 return channel_endpoint_read(tech_pvt->audio_endpoint, frame);
878 }
879
880 if (!globals.main_stream) {
881 goto normal_return;
882 }
883
884 if (switch_test_flag(tech_pvt, TFLAG_HUP)) {
885 goto normal_return;
886 }
887
888 if (!switch_test_flag(tech_pvt, TFLAG_IO)) {
889 goto cng_wait;
890 }
891
892 if (!is_master(tech_pvt)) {
893 if (tech_pvt->hold_file) {
894 switch_size_t olen = globals.read_codec.implementation->samples_per_packet;
895
896 if (!tech_pvt->hfh) {
897 int sample_rate = globals.sample_rate;
898 if (switch_core_file_open(&tech_pvt->fh,
899 tech_pvt->hold_file,
900 globals.read_codec.implementation->number_of_channels,
901 globals.read_codec.implementation->actual_samples_per_second,
902 SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) {
903 tech_pvt->hold_file = NULL;
904 goto cng_wait;
905 }
906
907 tech_pvt->hfh = &tech_pvt->fh;
908 tech_pvt->hold_frame.data = tech_pvt->holdbuf;
909 tech_pvt->hold_frame.buflen = sizeof(tech_pvt->holdbuf);
910 tech_pvt->hold_frame.rate = sample_rate;
911 tech_pvt->hold_frame.codec = &globals.write_codec;
912 }
913
914 if (switch_core_timer_next(&globals.hold_timer) != SWITCH_STATUS_SUCCESS) {
915 switch_core_file_close(&tech_pvt->fh);
916 goto cng_nowait;
917 }
918 switch_core_file_read(tech_pvt->hfh, tech_pvt->hold_frame.data, &olen);
919
920 if (olen == 0) {
921 unsigned int pos = 0;
922 switch_core_file_seek(tech_pvt->hfh, &pos, 0, SEEK_SET);
923 goto cng_nowait;
924 }
925
926 tech_pvt->hold_frame.datalen = (uint32_t) (olen * sizeof(int16_t));
927 tech_pvt->hold_frame.samples = (uint32_t) olen;
928 *frame = &tech_pvt->hold_frame;
929
930 status = SWITCH_STATUS_SUCCESS;
931 goto normal_return;
932 }
933
934 goto cng_wait;
935 }
936
937 if (tech_pvt->hfh) {
938 tech_close_file(tech_pvt);
939 }
940
941 switch_mutex_lock(globals.device_lock);
942 samples = ReadAudioStream(globals.main_stream->stream, globals.read_frame.data, globals.read_codec.implementation->samples_per_packet, 0, &globals.read_timer);
943 switch_mutex_unlock(globals.device_lock);
944
945 if (samples) {
946 globals.read_frame.datalen = samples * 2;
947 globals.read_frame.samples = samples;
948
949 *frame = &globals.read_frame;
950
951 if (!switch_test_flag((&globals), GFLAG_MOUTH)) {
952 memset(globals.read_frame.data, 255, globals.read_frame.datalen);
953 }
954 status = SWITCH_STATUS_SUCCESS;
955 } else {
956 goto cng_nowait;
957 }
958
959 normal_return:
960 return status;
961
962 cng_nowait:
963 *frame = &globals.cng_frame;
964 return SWITCH_STATUS_SUCCESS;
965
966 cng_wait:
967 switch_core_timer_next(&globals.hold_timer);
968 *frame = &globals.cng_frame;
969 return SWITCH_STATUS_SUCCESS;
970
971 }
972
channel_endpoint_write(audio_endpoint_t * endpoint,switch_frame_t * frame)973 static switch_status_t channel_endpoint_write(audio_endpoint_t *endpoint, switch_frame_t *frame)
974 {
975 if (!endpoint->out_stream) {
976 switch_core_timer_next(&endpoint->write_timer);
977 return SWITCH_STATUS_SUCCESS;
978 }
979 if (!endpoint->master) {
980 return SWITCH_STATUS_SUCCESS;
981 }
982 if (switch_test_flag(endpoint->master, TFLAG_HUP)) {
983 return SWITCH_STATUS_FALSE;
984 }
985 if (!switch_test_flag(endpoint->master, TFLAG_IO)) {
986 return SWITCH_STATUS_SUCCESS;
987 }
988 WriteAudioStream(endpoint->out_stream->stream, (short *)frame->data,
989 (int)(frame->datalen / sizeof(SAMPLE)),
990 endpoint->outchan, &(endpoint->write_timer));
991 return SWITCH_STATUS_SUCCESS;
992 }
993
channel_write_frame(switch_core_session_t * session,switch_frame_t * frame,switch_io_flag_t flags,int stream_id)994 static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id)
995 {
996 switch_status_t status = SWITCH_STATUS_FALSE;
997 private_t *tech_pvt = switch_core_session_get_private(session);
998 switch_assert(tech_pvt != NULL);
999
1000 if (tech_pvt->audio_endpoint) {
1001 return channel_endpoint_write(tech_pvt->audio_endpoint, frame);
1002 }
1003
1004 if (!globals.main_stream) {
1005 return SWITCH_STATUS_FALSE;
1006 }
1007
1008 if (switch_test_flag(tech_pvt, TFLAG_HUP)) {
1009 return SWITCH_STATUS_FALSE;
1010 }
1011
1012 if (!is_master(tech_pvt) || !switch_test_flag(tech_pvt, TFLAG_IO)) {
1013 return SWITCH_STATUS_SUCCESS;
1014 }
1015
1016 if (globals.main_stream) {
1017 if (switch_test_flag((&globals), GFLAG_EAR)) {
1018 WriteAudioStream(globals.main_stream->stream, (short *) frame->data, (int) (frame->datalen / sizeof(SAMPLE)), 0, &(globals.main_stream->write_timer));
1019 }
1020 status = SWITCH_STATUS_SUCCESS;
1021 }
1022
1023 return status;
1024 }
1025
channel_answer_channel(switch_core_session_t * session)1026 static switch_status_t channel_answer_channel(switch_core_session_t *session)
1027 {
1028 return SWITCH_STATUS_SUCCESS;
1029 }
1030
channel_receive_message(switch_core_session_t * session,switch_core_session_message_t * msg)1031 static switch_status_t channel_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg)
1032 {
1033 private_t *tech_pvt = switch_core_session_get_private(session);
1034 switch_assert(tech_pvt != NULL);
1035
1036 switch (msg->message_id) {
1037 case SWITCH_MESSAGE_INDICATE_ANSWER:
1038 channel_answer_channel(session);
1039 break;
1040 case SWITCH_MESSAGE_INDICATE_PROGRESS:
1041 {
1042 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Engage Early Media\n");
1043 switch_set_flag_locked(tech_pvt, TFLAG_IO);
1044 }
1045 default:
1046 break;
1047 }
1048 return SWITCH_STATUS_SUCCESS;
1049 }
1050
1051 switch_state_handler_table_t portaudio_event_handlers = {
1052 /*.on_init */ channel_on_init,
1053 /*.on_routing */ channel_on_routing,
1054 /*.on_execute */ channel_on_execute,
1055 /*.on_hangup */ channel_on_hangup,
1056 /*.on_exchange_media */ channel_on_exchange_media,
1057 /*.on_soft_execute */ channel_on_soft_execute,
1058 /*.on_consume_media */ NULL,
1059 /*.on_hibernate */ NULL,
1060 /*.on_reset */ NULL,
1061 /*.on_park */ NULL,
1062 /*.on_reporting */ NULL,
1063 /*.on_destroy */ channel_on_destroy
1064 };
1065
1066 switch_io_routines_t portaudio_io_routines = {
1067 /*.outgoing_channel */ channel_outgoing_channel,
1068 /*.read_frame */ channel_read_frame,
1069 /*.write_frame */ channel_write_frame,
1070 /*.kill_channel */ channel_kill_channel,
1071 /*.send_dtmf */ channel_send_dtmf,
1072 /*.receive_message */ channel_receive_message
1073 };
1074
1075 static int create_shared_audio_stream(shared_audio_stream_t *stream);
1076 static int destroy_shared_audio_stream(shared_audio_stream_t *stream);
take_stream_channel(shared_audio_stream_t * stream,int index,int input)1077 static int take_stream_channel(shared_audio_stream_t *stream, int index, int input)
1078 {
1079 int rc = 0;
1080 if (!stream) {
1081 return rc;
1082 }
1083
1084 switch_mutex_lock(stream->mutex);
1085
1086 if (!stream->stream && create_shared_audio_stream(stream)) {
1087 rc = -1;
1088 goto done;
1089 }
1090
1091 if (input) {
1092 if (stream->inchan_used[index]) {
1093 rc = -1;
1094 goto done;
1095 }
1096 stream->inchan_used[index] = 1;
1097 } else {
1098 if (!input && stream->outchan_used[index]) {
1099 rc = -1;
1100 goto done;
1101 }
1102 stream->outchan_used[index] = 1;
1103 }
1104
1105 done:
1106 switch_mutex_unlock(stream->mutex);
1107 return rc;
1108 }
1109
release_stream_channel(shared_audio_stream_t * stream,int index,int input)1110 static int release_stream_channel(shared_audio_stream_t *stream, int index, int input)
1111 {
1112 int i = 0;
1113 int destroy_stream = 1;
1114 int rc = 0;
1115
1116 if (!stream) {
1117 return rc;
1118 }
1119
1120 switch_mutex_lock(stream->mutex);
1121
1122 if (input) {
1123 stream->inchan_used[index] = 0;
1124 } else {
1125 stream->outchan_used[index] = 0;
1126 }
1127
1128 for (i = 0; i < stream->channels; i++) {
1129 if (stream->inchan_used[i] || stream->outchan_used[i]) {
1130 destroy_stream = 0;
1131 }
1132 }
1133 if (destroy_stream) {
1134 destroy_shared_audio_stream(stream);
1135 }
1136
1137 switch_mutex_unlock(stream->mutex);
1138 return rc;
1139 }
1140
1141 /* Make sure when you have 2 sessions in the same scope that you pass the appropriate one to the routines
1142 that allocate memory or you will have 1 channel with memory allocated from another channel's pool!
1143 */
channel_outgoing_channel(switch_core_session_t * session,switch_event_t * var_event,switch_caller_profile_t * outbound_profile,switch_core_session_t ** new_session,switch_memory_pool_t ** pool,switch_originate_flag_t flags,switch_call_cause_t * cancel_cause)1144 static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event,
1145 switch_caller_profile_t *outbound_profile,
1146 switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags,
1147 switch_call_cause_t *cancel_cause)
1148 {
1149 char name[128];
1150 const char *id = NULL;
1151 private_t *tech_pvt = NULL;
1152 switch_channel_t *channel = NULL;
1153 switch_caller_profile_t *caller_profile = NULL;
1154 switch_call_cause_t retcause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
1155 int codec_ms = -1;
1156 int samples_per_packet = -1;
1157 int sample_rate = 0;
1158 audio_endpoint_t *endpoint = NULL;
1159 char *endpoint_name = NULL;
1160 const char *endpoint_answer = NULL;
1161
1162 if (!outbound_profile) {
1163 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing caller profile\n");
1164 return retcause;
1165 }
1166
1167 if (!(*new_session = switch_core_session_request_uuid(portaudio_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, flags, pool, switch_event_get_header(var_event, "origination_uuid")))) {
1168 return retcause;
1169 }
1170
1171 switch_core_session_add_stream(*new_session, NULL);
1172 if ((tech_pvt = (private_t *) switch_core_session_alloc(*new_session, sizeof(private_t))) != 0) {
1173 memset(tech_pvt, 0, sizeof(*tech_pvt));
1174 switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(*new_session));
1175 channel = switch_core_session_get_channel(*new_session);
1176 switch_core_session_set_private(*new_session, tech_pvt);
1177 tech_pvt->session = *new_session;
1178 } else {
1179 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session), SWITCH_LOG_CRIT, "Hey where is my memory pool?\n");
1180 switch_core_session_destroy(new_session);
1181 return retcause;
1182 }
1183
1184 if (outbound_profile->destination_number && !strncasecmp(outbound_profile->destination_number, "endpoint", sizeof("endpoint")-1)) {
1185 endpoint = NULL;
1186 endpoint_name = switch_core_strdup(outbound_profile->pool, outbound_profile->destination_number);
1187 endpoint_name = strchr(endpoint_name, '/');
1188 if (!endpoint_name) {
1189 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session), SWITCH_LOG_CRIT, "No portaudio endpoint specified\n");
1190 goto error;
1191 }
1192 endpoint_name++;
1193 endpoint = switch_core_hash_find(globals.endpoints, endpoint_name);
1194 if (!endpoint) {
1195 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session), SWITCH_LOG_CRIT, "Invalid portaudio endpoint %s\n", endpoint_name);
1196 goto error;
1197 }
1198
1199 switch_mutex_lock(endpoint->mutex);
1200
1201 if (endpoint->master) {
1202 /* someone already has this endpoint */
1203 retcause = SWITCH_CAUSE_USER_BUSY;
1204 goto error;
1205 }
1206
1207 codec_ms = endpoint->in_stream ? endpoint->in_stream->codec_ms : endpoint->out_stream->codec_ms;
1208 samples_per_packet = endpoint->in_stream ?
1209 STREAM_SAMPLES_PER_PACKET(endpoint->in_stream) : STREAM_SAMPLES_PER_PACKET(endpoint->out_stream);
1210 sample_rate = endpoint->in_stream ? endpoint->in_stream->sample_rate : endpoint->out_stream->sample_rate;
1211
1212 if (switch_core_timer_init(&endpoint->read_timer,
1213 globals.timer_name, codec_ms,
1214 samples_per_packet, module_pool) != SWITCH_STATUS_SUCCESS) {
1215 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "failed to setup read timer for endpoint '%s'!\n", endpoint->name);
1216 goto error;
1217 }
1218
1219 /* The write timer must be setup regardless */
1220 if (switch_core_timer_init(&endpoint->write_timer,
1221 globals.timer_name, codec_ms,
1222 samples_per_packet, module_pool) != SWITCH_STATUS_SUCCESS) {
1223 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "failed to setup read timer for endpoint '%s'!\n", endpoint->name);
1224 goto error;
1225 }
1226
1227 if (switch_core_codec_init(&endpoint->read_codec,
1228 "L16", NULL, NULL, sample_rate, codec_ms, 1,
1229 SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, NULL) != SWITCH_STATUS_SUCCESS) {
1230 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n");
1231 goto error;
1232 }
1233
1234 if (switch_core_codec_init(&endpoint->write_codec,
1235 "L16", NULL, NULL, sample_rate, codec_ms, 1,
1236 SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, NULL) != SWITCH_STATUS_SUCCESS) {
1237 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n");
1238 goto error;
1239 }
1240 switch_core_session_set_read_codec(tech_pvt->session, &endpoint->read_codec);
1241 switch_core_session_set_write_codec(tech_pvt->session, &endpoint->write_codec);
1242
1243 /* try to acquire the stream */
1244 if (take_stream_channel(endpoint->in_stream, endpoint->inchan, 1)) {
1245 retcause = SWITCH_CAUSE_USER_BUSY;
1246 goto error;
1247 }
1248 if (take_stream_channel(endpoint->out_stream, endpoint->outchan, 0)) {
1249 release_stream_channel(endpoint->in_stream, endpoint->inchan, 1);
1250 retcause = SWITCH_CAUSE_USER_BUSY;
1251 goto error;
1252 }
1253 switch_snprintf(name, sizeof(name), "portaudio/endpoint-%s", endpoint_name);
1254 if (var_event && (endpoint_answer = (switch_event_get_header(var_event, "endpoint_answer")))) {
1255 if (switch_true(endpoint_answer)) {
1256 switch_set_flag(tech_pvt, TFLAG_AUTO_ANSWER);
1257 }
1258 } else {
1259 switch_set_flag(tech_pvt, TFLAG_AUTO_ANSWER);
1260 }
1261 endpoint->master = tech_pvt;
1262 tech_pvt->audio_endpoint = endpoint;
1263 switch_mutex_unlock(endpoint->mutex);
1264 } else {
1265 id = !zstr(outbound_profile->caller_id_number) ? outbound_profile->caller_id_number : "na";
1266 switch_snprintf(name, sizeof(name), "portaudio/%s", id);
1267 if (outbound_profile->destination_number && !strcasecmp(outbound_profile->destination_number, "auto_answer")) {
1268 switch_set_flag(tech_pvt, TFLAG_AUTO_ANSWER);
1269 }
1270 }
1271 switch_channel_set_name(channel, name);
1272 caller_profile = switch_caller_profile_clone(*new_session, outbound_profile);
1273 switch_channel_set_caller_profile(channel, caller_profile);
1274 tech_pvt->caller_profile = caller_profile;
1275
1276 switch_set_flag_locked(tech_pvt, TFLAG_OUTBOUND);
1277 switch_channel_set_state(channel, CS_INIT);
1278 switch_channel_set_flag(channel, CF_AUDIO);
1279 return SWITCH_CAUSE_SUCCESS;
1280
1281 error:
1282 if (endpoint) {
1283 if (!endpoint->master) {
1284 if (endpoint->read_timer.interval) {
1285 switch_core_timer_destroy(&endpoint->read_timer);
1286 }
1287 if (endpoint->write_timer.interval) {
1288 switch_core_timer_destroy(&endpoint->write_timer);
1289 }
1290 if (endpoint->read_codec.codec_interface) {
1291 switch_core_codec_destroy(&endpoint->read_codec);
1292 }
1293 if (endpoint->write_codec.codec_interface) {
1294 switch_core_codec_destroy(&endpoint->write_codec);
1295 }
1296 }
1297 switch_mutex_unlock(endpoint->mutex);
1298 }
1299 if (new_session && *new_session) {
1300 switch_core_session_destroy(new_session);
1301 }
1302 return retcause;
1303 }
1304
1305
SWITCH_MODULE_LOAD_FUNCTION(mod_portaudio_load)1306 SWITCH_MODULE_LOAD_FUNCTION(mod_portaudio_load)
1307 {
1308 switch_status_t status;
1309 switch_api_interface_t *api_interface;
1310
1311 module_pool = pool;
1312
1313 if (paNoError != Pa_Initialize()) {
1314 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot initialize port audio!\n");
1315 return SWITCH_STATUS_TERM;
1316 }
1317
1318 memset(&globals, 0, sizeof(globals));
1319
1320 switch_core_hash_init(&globals.call_hash);
1321 switch_core_hash_init(&globals.sh_streams);
1322 switch_core_hash_init(&globals.endpoints);
1323 switch_mutex_init(&globals.device_lock, SWITCH_MUTEX_NESTED, module_pool);
1324 switch_mutex_init(&globals.pvt_lock, SWITCH_MUTEX_NESTED, module_pool);
1325 switch_mutex_init(&globals.streams_lock, SWITCH_MUTEX_NESTED, module_pool);
1326 switch_mutex_init(&globals.flag_mutex, SWITCH_MUTEX_NESTED, module_pool);
1327 switch_mutex_init(&globals.pa_mutex, SWITCH_MUTEX_NESTED, module_pool);
1328 globals.codecs_inited = 0;
1329 globals.read_frame.data = globals.databuf;
1330 globals.read_frame.buflen = sizeof(globals.databuf);
1331 globals.cng_frame.data = globals.cngbuf;
1332 globals.cng_frame.buflen = sizeof(globals.cngbuf);
1333 switch_set_flag((&globals.cng_frame), SFF_CNG);
1334 globals.flags = GFLAG_EAR | GFLAG_MOUTH;
1335 /* dual streams makes portaudio on solaris choke */
1336 #if defined(sun) || defined(__sun)
1337 globals.dual_streams = 0;
1338 #endif
1339
1340 if ((status = load_config()) != SWITCH_STATUS_SUCCESS) {
1341 return status;
1342 }
1343
1344 if (dump_info(0)) {
1345 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't find any audio devices!\n");
1346 return SWITCH_STATUS_TERM;
1347 }
1348
1349 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
1350 "Input Device: %d, Output Device: %d, Ring Device: %d Sample Rate: %d MS: %d\n", globals.indev,
1351 globals.outdev, globals.ringdev, globals.sample_rate, globals.codec_ms);
1352
1353
1354 if (switch_event_reserve_subclass(MY_EVENT_RINGING) != SWITCH_STATUS_SUCCESS) {
1355 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass!\n");
1356 return SWITCH_STATUS_GENERR;
1357 }
1358
1359 if (switch_event_reserve_subclass(MY_EVENT_MAKE_CALL) != SWITCH_STATUS_SUCCESS) {
1360 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass!\n");
1361 return SWITCH_STATUS_GENERR;
1362 }
1363 if (switch_event_reserve_subclass(MY_EVENT_CALL_HELD) != SWITCH_STATUS_SUCCESS) {
1364 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass!\n");
1365 return SWITCH_STATUS_GENERR;
1366 }
1367 if (switch_event_reserve_subclass(MY_EVENT_CALL_RESUMED) != SWITCH_STATUS_SUCCESS) {
1368 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass!\n");
1369 return SWITCH_STATUS_GENERR;
1370 }
1371
1372 if (switch_event_reserve_subclass(MY_EVENT_ERROR_AUDIO_DEV) != SWITCH_STATUS_SUCCESS) {
1373 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass!\n");
1374 return SWITCH_STATUS_GENERR;
1375 }
1376
1377
1378 /* connect my internal structure to the blank pointer passed to me */
1379 *module_interface = switch_loadable_module_create_module_interface(pool, modname);
1380 portaudio_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE);
1381 portaudio_endpoint_interface->interface_name = "portaudio";
1382 portaudio_endpoint_interface->io_routines = &portaudio_io_routines;
1383 portaudio_endpoint_interface->state_handler = &portaudio_event_handlers;
1384
1385 SWITCH_ADD_API(api_interface, "pa", "PortAudio", pa_cmd, "<command> [<args>]");
1386 switch_console_set_complete("add pa help");
1387 switch_console_set_complete("add pa dump");
1388 switch_console_set_complete("add pa call");
1389 switch_console_set_complete("add pa answer");
1390 switch_console_set_complete("add pa hangup");
1391 switch_console_set_complete("add pa list");
1392 switch_console_set_complete("add pa switch");
1393 switch_console_set_complete("add pa dtmf");
1394 switch_console_set_complete("add pa flags");
1395 switch_console_set_complete("add pa devlist");
1396 switch_console_set_complete("add pa indev");
1397 switch_console_set_complete("add pa outdev");
1398 switch_console_set_complete("add pa preparestream");
1399 switch_console_set_complete("add pa switchstream");
1400 switch_console_set_complete("add pa closestreams");
1401 switch_console_set_complete("add pa ringdev");
1402 switch_console_set_complete("add pa ringfile");
1403 switch_console_set_complete("add pa play");
1404 switch_console_set_complete("add pa playdev");
1405 switch_console_set_complete("add pa looptest");
1406 switch_console_set_complete("add pa shstreams");
1407 switch_console_set_complete("add pa endpoints");
1408
1409 /* indicate that the module should continue to be loaded */
1410 return SWITCH_STATUS_SUCCESS;
1411 }
1412
check_device(char * devstr,int check_input)1413 static int check_device(char *devstr, int check_input)
1414 {
1415 int devval;
1416 if (devstr[0] == '#') {
1417 devval = get_dev_by_number(devstr + 1, check_input);
1418 } else {
1419 devval = get_dev_by_name(devstr, check_input);
1420 }
1421 return devval;
1422 }
1423
load_streams(switch_xml_t streams)1424 static switch_status_t load_streams(switch_xml_t streams)
1425 {
1426 switch_status_t status = SWITCH_STATUS_SUCCESS;
1427 switch_xml_t param, mystream;
1428 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Loading streams ...\n");
1429 for (mystream = switch_xml_child(streams, "stream"); mystream; mystream = mystream->next) {
1430 shared_audio_stream_t *stream = NULL;
1431 int devval = -1;
1432 char *stream_name = (char *) switch_xml_attr_soft(mystream, "name");
1433
1434 if (!stream_name) {
1435 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing stream name attribute, skipping ...\n");
1436 continue;
1437 }
1438
1439 /* check if that stream name is not already used */
1440 stream = switch_core_hash_find(globals.sh_streams, stream_name);
1441 if (stream) {
1442 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "A stream with name '%s' already exists\n", stream_name);
1443 continue;
1444 }
1445
1446 stream = switch_core_alloc(module_pool, sizeof(*stream));
1447 if (!stream) {
1448 continue;
1449 }
1450 switch_mutex_init(&stream->mutex, SWITCH_MUTEX_NESTED, module_pool);
1451 stream->indev = -1;
1452 stream->outdev = -1;
1453 stream->sample_rate = globals.sample_rate;
1454 stream->codec_ms = globals.codec_ms;
1455 stream->channels = 1;
1456 switch_snprintf(stream->name, sizeof(stream->name), "%s", stream_name);
1457 for (param = switch_xml_child(mystream, "param"); param; param = param->next) {
1458 char *var = (char *) switch_xml_attr_soft(param, "name");
1459 char *val = (char *) switch_xml_attr_soft(param, "value");
1460 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Parsing stream '%s' parameter %s = %s\n", stream_name, var, val);
1461 if (!strcmp(var, "indev")) {
1462 devval = check_device(val, 1);
1463 if (devval < 0) {
1464 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
1465 "Invalid indev specified for stream '%s'\n", stream_name);
1466 stream->indev = -1;
1467 continue;
1468 }
1469 stream->indev = devval;
1470 } else if (!strcmp(var, "outdev")) {
1471 devval = check_device(val, 0);
1472 if (devval < 0) {
1473 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
1474 "Invalid outdev specified for stream '%s'\n", stream_name);
1475 stream->outdev = -1;
1476 continue;
1477 }
1478 stream->outdev = devval;
1479 } else if (!strcmp(var, "sample-rate")) {
1480 stream->sample_rate = atoi(val);
1481 if (stream->sample_rate < MIN_STREAM_SAMPLE_RATE) {
1482 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
1483 "Invalid sample rate specified for stream '%s', forcing to 8000\n", stream_name);
1484 stream->sample_rate = MIN_STREAM_SAMPLE_RATE;
1485 }
1486 } else if (!strcmp(var, "codec-ms")) {
1487 int tmp = atoi(val);
1488 if (switch_check_interval(stream->sample_rate, tmp)) {
1489 stream->codec_ms = tmp;
1490 } else {
1491 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
1492 "codec-ms must be multiple of 10 and less than %d, Using default of 20\n", SWITCH_MAX_INTERVAL);
1493 }
1494 } else if (!strcmp(var, "channels")) {
1495 stream->channels = atoi(val);
1496 if (stream->channels < 1 || stream->channels > MAX_IO_CHANNELS) {
1497 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
1498 "Invalid number of channels specified for stream '%s', forcing to 1\n", stream_name);
1499 stream->channels = 1;
1500 }
1501 }
1502 }
1503 if (stream->indev < 0 && stream->outdev < 0) {
1504 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
1505 "You need at least one device for stream '%s'\n", stream_name);
1506 continue;
1507 }
1508 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE,
1509 "Created stream '%s', sample-rate = %d, codec-ms = %d\n", stream->name, stream->sample_rate, stream->codec_ms);
1510 switch_core_hash_insert(globals.sh_streams, stream->name, stream);
1511 }
1512 return status;
1513 }
1514
check_stream_compat(shared_audio_stream_t * in_stream,shared_audio_stream_t * out_stream)1515 static int check_stream_compat(shared_audio_stream_t *in_stream, shared_audio_stream_t *out_stream)
1516 {
1517 if (!in_stream || !out_stream) {
1518 /* nothing to be compatible with */
1519 return 0;
1520 }
1521 if (in_stream->sample_rate != out_stream->sample_rate) {
1522 return -1;
1523 }
1524 if (in_stream->codec_ms != out_stream->codec_ms) {
1525 return -1;
1526 }
1527 return 0;
1528 }
1529
check_stream(char * streamstr,int check_input,int * chanindex)1530 static shared_audio_stream_t *check_stream(char *streamstr, int check_input, int *chanindex)
1531 {
1532 shared_audio_stream_t *stream = NULL;
1533 int cnum = 0;
1534 char stream_name[255];
1535 char *chan = NULL;
1536
1537 *chanindex = -1;
1538
1539 switch_snprintf(stream_name, sizeof(stream_name), "%s", streamstr);
1540
1541 chan = strchr(stream_name, ':');
1542 if (!chan) {
1543 return NULL;
1544 }
1545 *chan = 0;
1546 chan++;
1547 cnum = atoi(chan);
1548
1549 stream = switch_core_hash_find(globals.sh_streams, stream_name);
1550 if (!stream) {
1551 return NULL;
1552 }
1553
1554 if (cnum < 0 || cnum > stream->channels) {
1555 return NULL;
1556 }
1557
1558 if (check_input && stream->indev < 0) {
1559 return NULL;
1560 }
1561
1562 if (!check_input && stream->outdev < 0) {
1563 return NULL;
1564 }
1565
1566 *chanindex = cnum;
1567
1568 return stream;
1569 }
1570
load_endpoints(switch_xml_t endpoints)1571 static switch_status_t load_endpoints(switch_xml_t endpoints)
1572 {
1573 switch_status_t status = SWITCH_STATUS_SUCCESS;
1574 switch_xml_t param, myendpoint;
1575 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Loading endpoints ...\n");
1576 for (myendpoint = switch_xml_child(endpoints, "endpoint"); myendpoint; myendpoint = myendpoint->next) {
1577 audio_endpoint_t *endpoint = NULL;
1578 shared_audio_stream_t *stream = NULL;
1579 char *endpoint_name = (char *) switch_xml_attr_soft(myendpoint, "name");
1580
1581 if (!endpoint_name) {
1582 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing endpoint name attribute, skipping ...\n");
1583 continue;
1584 }
1585
1586 /* check if that endpoint name is not already used */
1587 endpoint = switch_core_hash_find(globals.endpoints, endpoint_name);
1588 if (endpoint) {
1589 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "An endpoint with name '%s' already exists\n", endpoint_name);
1590 continue;
1591 }
1592
1593 endpoint = switch_core_alloc(module_pool, sizeof(*endpoint));
1594 if (!endpoint) {
1595 continue;
1596 }
1597 switch_mutex_init(&endpoint->mutex, SWITCH_MUTEX_NESTED, module_pool);
1598 endpoint->inchan = -1;
1599 endpoint->outchan = -1;
1600 switch_snprintf(endpoint->name, sizeof(endpoint->name), "%s", endpoint_name);
1601 for (param = switch_xml_child(myendpoint, "param"); param; param = param->next) {
1602 char *var = (char *) switch_xml_attr_soft(param, "name");
1603 char *val = (char *) switch_xml_attr_soft(param, "value");
1604 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Parsing endpoint '%s' parameter %s = %s\n", endpoint_name, var, val);
1605 if (!strcmp(var, "instream")) {
1606 stream = check_stream(val, 1, &endpoint->inchan) ;
1607 if (!stream) {
1608 endpoint->in_stream = NULL;
1609 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
1610 "Invalid instream specified for endpoint '%s'\n", endpoint_name);
1611 continue;
1612 }
1613 endpoint->in_stream = stream;
1614 } else if (!strcmp(var, "outstream")) {
1615 stream = check_stream(val, 0, &endpoint->outchan);
1616 if (!stream) {
1617 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
1618 "Invalid outstream specified for endpoint '%s'\n", endpoint_name);
1619 endpoint->out_stream = NULL;
1620 continue;
1621 }
1622 endpoint->out_stream = stream;
1623 }
1624 }
1625 if (!endpoint->in_stream && !endpoint->out_stream) {
1626 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
1627 "You need at least one stream for endpoint '%s'\n", endpoint_name);
1628 continue;
1629 }
1630 if (check_stream_compat(endpoint->in_stream, endpoint->out_stream)) {
1631 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
1632 "Incompatible input and output streams for endpoint '%s'\n", endpoint_name);
1633 continue;
1634 }
1635 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE,
1636 "Created endpoint '%s', instream = %s, outstream = %s\n", endpoint->name,
1637 endpoint->in_stream ? endpoint->in_stream->name : "(none)",
1638 endpoint->out_stream ? endpoint->out_stream->name : "(none)");
1639 switch_core_hash_insert(globals.endpoints, endpoint->name, endpoint);
1640 }
1641 return status;
1642 }
1643
load_config(void)1644 static switch_status_t load_config(void)
1645 {
1646 char *cf = "portaudio.conf";
1647 switch_xml_t cfg, xml, settings, streams, endpoints, param;
1648 switch_status_t status = SWITCH_STATUS_SUCCESS;
1649
1650 if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
1651 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf);
1652 return SWITCH_STATUS_TERM;
1653 }
1654 destroy_audio_streams();
1655 destroy_codecs();
1656 globals.dual_streams = 0;
1657 globals.live_stream_switch = 0;
1658 globals.no_auto_resume_call = 0;
1659 globals.no_ring_during_call = 0;
1660 globals.indev = globals.outdev = globals.ringdev = -1;
1661 globals.sample_rate = 8000;
1662 globals.unload_device_fail = 0;
1663
1664 if ((settings = switch_xml_child(cfg, "settings"))) {
1665 for (param = switch_xml_child(settings, "param"); param; param = param->next) {
1666 char *var = (char *) switch_xml_attr_soft(param, "name");
1667 char *val = (char *) switch_xml_attr_soft(param, "value");
1668
1669 if (!strcmp(var, "debug")) {
1670 globals.debug = atoi(val);
1671 } else if (!strcmp(var, "ring-interval")) {
1672 globals.ring_interval = atoi(val);
1673 } else if (!strcmp(var, "no-auto-resume-call")) {
1674 if (switch_true(val)) {
1675 globals.no_auto_resume_call = 1;
1676 } else {
1677 globals.no_auto_resume_call = 0;
1678 }
1679 } else if (!strcmp(var, "no-ring-during-call")) {
1680 if (switch_true(val)) {
1681 globals.no_ring_during_call = 1;
1682 } else {
1683 globals.no_ring_during_call = 0;
1684 }
1685 } else if (!strcmp(var, "live-stream-switch")) {
1686 if (switch_true(val)) {
1687 globals.live_stream_switch = 1;
1688 } else {
1689 globals.live_stream_switch = 0;
1690 }
1691 } else if (!strcmp(var, "ring-file")) {
1692 set_global_ring_file(val);
1693 } else if (!strcmp(var, "hold-file")) {
1694 set_global_hold_file(val);
1695 } else if (!strcmp(var, "dual-streams")) {
1696 if (switch_true(val)) {
1697 globals.dual_streams = 1;
1698 } else {
1699 globals.dual_streams = 0;
1700 }
1701 } else if (!strcmp(var, "timer-name")) {
1702 set_global_timer_name(val);
1703 } else if (!strcmp(var, "sample-rate")) {
1704 globals.sample_rate = atoi(val);
1705 } else if (!strcmp(var, "codec-ms")) {
1706 int tmp = atoi(val);
1707 if (switch_check_interval(globals.sample_rate, tmp)) {
1708 globals.codec_ms = tmp;
1709 } else {
1710 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
1711 "codec-ms must be multiple of 10 and less than %d, Using default of 20\n", SWITCH_MAX_INTERVAL);
1712 }
1713 } else if (!strcmp(var, "dialplan")) {
1714 set_global_dialplan(val);
1715 } else if (!strcmp(var, "context")) {
1716 set_global_context(val);
1717 } else if (!strcmp(var, "cid-name")) {
1718 set_global_cid_name(val);
1719 } else if (!strcmp(var, "cid-num")) {
1720 set_global_cid_num(val);
1721 } else if (!strcmp(var, "indev")) {
1722 if (*val == '#') {
1723 globals.indev = get_dev_by_number(val + 1, 1);
1724 } else {
1725 globals.indev = get_dev_by_name(val, 1);
1726 }
1727 } else if (!strcmp(var, "outdev")) {
1728 if (*val == '#') {
1729 globals.outdev = get_dev_by_number(val + 1, 0);
1730 } else {
1731 globals.outdev = get_dev_by_name(val, 0);
1732 }
1733 } else if (!strcmp(var, "ringdev")) {
1734 if (*val == '#') {
1735 globals.ringdev = get_dev_by_number(val + 1, 0);
1736 } else {
1737 globals.ringdev = get_dev_by_name(val, 0);
1738 }
1739 } else if (!strcasecmp(var, "unload-on-device-fail")) {
1740 globals.unload_device_fail = switch_true(val);
1741 }
1742 }
1743 }
1744
1745 if (!globals.dialplan) {
1746 set_global_dialplan("XML");
1747 }
1748
1749 if (!globals.context) {
1750 set_global_context("default");
1751 }
1752
1753 if (!globals.sample_rate) {
1754 globals.sample_rate = 8000;
1755 }
1756
1757 if (!globals.codec_ms) {
1758 globals.codec_ms = 20;
1759 }
1760
1761 globals.cng_frame.datalen = switch_samples_per_packet(globals.sample_rate, globals.codec_ms) * 2;
1762
1763 if (!globals.ring_interval) {
1764 globals.ring_interval = 5;
1765 }
1766
1767 if (!globals.timer_name) {
1768 set_global_timer_name("soft");
1769 }
1770
1771 if (globals.indev < 0) {
1772 globals.indev = get_dev_by_name(NULL, 1);
1773 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "global indev [%d]\n", globals.indev);
1774 if (globals.indev > -1) {
1775 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Switching to default input device\n");
1776 } else {
1777 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find an input device\n");
1778 if (globals.unload_device_fail) {
1779 status = SWITCH_STATUS_GENERR;
1780 }
1781 }
1782 }
1783
1784 if (globals.outdev < 0) {
1785 globals.outdev = get_dev_by_name(NULL, 0);
1786 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "global outdev [%d]\n", globals.outdev);
1787 if (globals.outdev > -1) {
1788 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Switching to default output device\n");
1789 } else {
1790 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find an output device\n");
1791 if (globals.unload_device_fail) {
1792 status = SWITCH_STATUS_GENERR;
1793 }
1794 }
1795 }
1796
1797 if (globals.ringdev < 0) {
1798 if (globals.outdev > -1) {
1799 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Invalid or no ring device configured, using output device as ring device\n");
1800 globals.ringdev = globals.outdev;
1801 }
1802 }
1803
1804 /* streams and endpoints must be last, some initialization depend on globals defaults */
1805 if ((streams = switch_xml_child(cfg, "streams"))) {
1806 load_streams(streams);
1807 }
1808
1809 if ((endpoints = switch_xml_child(cfg, "endpoints"))) {
1810 load_endpoints(endpoints);
1811 }
1812
1813
1814 switch_xml_free(xml);
1815
1816 return status;
1817 }
1818
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_portaudio_shutdown)1819 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_portaudio_shutdown)
1820 {
1821
1822 destroy_audio_streams();
1823 destroy_codecs();
1824
1825 Pa_Terminate();
1826 switch_core_hash_destroy(&globals.call_hash);
1827 switch_core_hash_destroy(&globals.sh_streams);
1828 switch_core_hash_destroy(&globals.endpoints);
1829
1830 switch_event_free_subclass(MY_EVENT_RINGING);
1831 switch_event_free_subclass(MY_EVENT_MAKE_CALL);
1832 switch_event_free_subclass(MY_EVENT_ERROR_AUDIO_DEV);
1833 switch_event_free_subclass(MY_EVENT_CALL_HELD);
1834 switch_event_free_subclass(MY_EVENT_CALL_RESUMED);
1835
1836
1837 switch_safe_free(globals.dialplan);
1838 switch_safe_free(globals.context);
1839 switch_safe_free(globals.cid_name);
1840 switch_safe_free(globals.cid_num);
1841 switch_safe_free(globals.ring_file);
1842 switch_safe_free(globals.hold_file);
1843 switch_safe_free(globals.timer_name);
1844
1845 return SWITCH_STATUS_SUCCESS;
1846 }
1847
get_dev_by_number(char * numstr,int in)1848 static int get_dev_by_number(char *numstr, int in)
1849 {
1850 int numDevices = Pa_GetDeviceCount();
1851 const PaDeviceInfo *pdi;
1852 char *end_ptr;
1853 int number;
1854
1855 number = (int) strtol(numstr, &end_ptr, 10);
1856
1857 if (end_ptr == numstr || number < 0) {
1858 return -1;
1859 }
1860
1861 if (number > -1 && number < numDevices && (pdi = Pa_GetDeviceInfo(number))) {
1862 if (in && pdi->maxInputChannels) {
1863 return number;
1864 } else if (!in && pdi->maxOutputChannels) {
1865 return number;
1866 }
1867 }
1868
1869 return -1;
1870 }
1871
get_dev_by_name(char * name,int in)1872 static int get_dev_by_name(char *name, int in)
1873 {
1874 int i;
1875 int numDevices;
1876 const PaDeviceInfo *pdi;
1877 numDevices = Pa_GetDeviceCount();
1878
1879 if (numDevices < 0) {
1880 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR, "ERROR: Pa_CountDevices returned 0x%x\n", numDevices);
1881 return -2;
1882 }
1883
1884 for (i = 0; i < numDevices; i++) {
1885 int match = 0;
1886 pdi = Pa_GetDeviceInfo(i);
1887
1888 if (zstr(name)) {
1889 match = 1;
1890 } else if (pdi && pdi->name && strstr(pdi->name, name)) {
1891 match = 1;
1892 }
1893
1894 if (match) {
1895 if (in && pdi->maxInputChannels) {
1896 return i;
1897 } else if (!in && pdi->maxOutputChannels) {
1898 return i;
1899 }
1900 }
1901 }
1902
1903 return -1;
1904 }
1905
1906
1907 /*******************************************************************/
PrintSupportedStandardSampleRates(const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters)1908 static void PrintSupportedStandardSampleRates(const PaStreamParameters * inputParameters, const PaStreamParameters * outputParameters)
1909 {
1910 int i, printCount, cr = 7;
1911 PaError err;
1912 static double standardSampleRates[] = { 8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0,
1913 44100.0, 48000.0, 88200.0, 96000.0, 192000.0, -1
1914 };
1915
1916 printCount = cr;
1917 for (i = 0; standardSampleRates[i] > 0; i++) {
1918 err = Pa_IsFormatSupported(inputParameters, outputParameters, standardSampleRates[i]);
1919 if (err == paFormatIsSupported) {
1920 if (printCount == cr) {
1921 printCount = 0;
1922 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_INFO, "\n\t%0.2f", standardSampleRates[i]);
1923 } else {
1924 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_INFO, ", %0.2f", standardSampleRates[i]);
1925 }
1926 printCount++;
1927 }
1928 }
1929 if (!printCount) {
1930 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_INFO, " None\n");
1931 } else {
1932 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_INFO, "\n");
1933 }
1934 }
1935
1936 /*******************************************************************/
play_dev(switch_stream_handle_t * stream,int outdev,char * file,const char * max_seconds,const char * no_close)1937 static switch_status_t play_dev(switch_stream_handle_t *stream, int outdev, char * file, const char * max_seconds, const char * no_close)
1938 {
1939 switch_file_handle_t fh = { 0 };
1940 int samples = 0;
1941 int seconds = 5;
1942 audio_stream_t * audio_stream;
1943 int created_stream = 0;
1944 int wrote = 0;
1945 switch_size_t olen;
1946 int16_t abuf[2048];
1947
1948
1949 if (!strcasecmp(file, "ringtest")) {
1950 file = globals.ring_file;
1951 }
1952 if (outdev == -1) {
1953 stream->write_function(stream, "Invalid output audio device\n");
1954 return SWITCH_STATUS_FALSE;
1955 }
1956 audio_stream = get_audio_stream(-1, outdev);
1957
1958 fh.pre_buffer_datalen = SWITCH_DEFAULT_FILE_BUFFER_LEN;
1959
1960 if (switch_core_file_open(&fh, file,
1961 globals.read_codec.implementation->number_of_channels,
1962 globals.read_codec.implementation->actual_samples_per_second,
1963 SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) {
1964 stream->write_function(stream, "Cannot play requested file %s\n", file);
1965 return SWITCH_STATUS_FALSE;
1966 }
1967
1968 olen = globals.read_codec.implementation->samples_per_packet;
1969
1970 if (max_seconds) {
1971 int i = atoi(max_seconds);
1972 if (i >= 0) {
1973 seconds = i;
1974 }
1975 }
1976
1977 if (globals.call_list) {
1978 switch_mutex_lock(globals.pvt_lock);
1979 if (!globals.main_stream) {
1980 switch_mutex_unlock(globals.pvt_lock);
1981 return SWITCH_STATUS_FALSE;
1982 }
1983
1984 if ( switch_test_flag(globals.call_list, TFLAG_MASTER) && globals.main_stream->outdev == outdev) { /*so we are the active stream so we need to dupe it basically */
1985 audio_stream = create_audio_stream(-1,outdev);
1986 created_stream=1;
1987 }
1988 switch_mutex_unlock(globals.pvt_lock);
1989 }
1990
1991 if (! audio_stream) {
1992 stream->write_function(stream, "Failed to engage audio device\n");
1993 return SWITCH_STATUS_FALSE;
1994 }
1995
1996
1997
1998 samples = globals.read_codec.implementation->actual_samples_per_second * seconds;
1999 globals.stream_in_use=1;
2000 while (switch_core_file_read(&fh, abuf, &olen) == SWITCH_STATUS_SUCCESS) {
2001 if (globals.destroying_streams || ! audio_stream->stream) {
2002 break;
2003 }
2004
2005 WriteAudioStream(audio_stream->stream, abuf, (long) olen, 0, &(audio_stream->write_timer));
2006 wrote += (int) olen;
2007 if (samples) {
2008 samples -= (int) olen;
2009 if (samples <= 0) {
2010 break;
2011 }
2012 }
2013 olen = globals.read_codec.implementation->samples_per_packet;
2014 }
2015 globals.stream_in_use = 0;
2016
2017 switch_core_file_close(&fh);
2018 if (! globals.call_list && ( ! no_close || strcasecmp(no_close, "no_close"))) {
2019 destroy_audio_streams();
2020 }
2021
2022 seconds = wrote / globals.read_codec.implementation->actual_samples_per_second;
2023 stream->write_function(stream, "playback test [%s] %d second(s) %d samples @%dkhz",
2024 file, seconds, wrote, globals.read_codec.implementation->actual_samples_per_second);
2025 if (created_stream) { /*still need this as not added to the global pool */
2026 destroy_actual_stream(audio_stream);
2027 }
2028 return SWITCH_STATUS_SUCCESS;
2029 }
devlist(char ** argv,int argc,switch_stream_handle_t * stream)2030 static switch_status_t devlist(char **argv, int argc, switch_stream_handle_t *stream)
2031 {
2032 int i, numDevices, prev;
2033 const PaDeviceInfo *deviceInfo;
2034 const PaHostApiInfo *hostApiInfo;
2035
2036 numDevices = Pa_GetDeviceCount();
2037
2038 if (numDevices < 0) {
2039 return SWITCH_STATUS_SUCCESS;
2040 }
2041
2042 if (argv[0] && !strcasecmp(argv[0], "xml")) {
2043 stream->write_function(stream, "<xml>\n\t<devices>\n");
2044
2045 for (i = 0; i < numDevices; i++) {
2046 deviceInfo = Pa_GetDeviceInfo(i);
2047 hostApiInfo = Pa_GetHostApiInfo(deviceInfo->hostApi);
2048 stream->write_function(stream, "\t\t<device id=\"%d\" name=\"%s\" hostapi=\"%s\" inputs=\"%d\" outputs=\"%d\" />\n", i, deviceInfo->name,
2049 hostApiInfo->name, deviceInfo->maxInputChannels, deviceInfo->maxOutputChannels);
2050 }
2051
2052 stream->write_function(stream, "\t</devices>\n\t<bindings>\n"
2053 "\t\t<ring device=\"%d\" />\n"
2054 "\t\t<input device=\"%d\" />\n"
2055 "\t\t<output device=\"%d\" />\n" "\t</bindings>\n</xml>\n", globals.ringdev, globals.indev, globals.outdev);
2056 } else {
2057
2058 for (i = 0; i < numDevices; i++) {
2059 deviceInfo = Pa_GetDeviceInfo(i);
2060 hostApiInfo = Pa_GetHostApiInfo(deviceInfo->hostApi);
2061
2062 stream->write_function(stream, "%d;%s(%s);%d;%d;", i, deviceInfo->name, hostApiInfo->name, deviceInfo->maxInputChannels,
2063 deviceInfo->maxOutputChannels);
2064
2065 prev = 0;
2066 if (globals.ringdev == i) {
2067 stream->write_function(stream, "r");
2068 prev = 1;
2069 }
2070
2071 if (globals.indev == i) {
2072 if (prev) {
2073 stream->write_function(stream, ",");
2074 }
2075 stream->write_function(stream, "i");
2076 prev = 1;
2077 }
2078
2079 if (globals.outdev == i) {
2080 if (prev) {
2081 stream->write_function(stream, ",");
2082 }
2083 stream->write_function(stream, "o");
2084 }
2085
2086 stream->write_function(stream, "\n");
2087
2088 }
2089 }
2090
2091 return SWITCH_STATUS_SUCCESS;
2092 }
2093
dump_info(int verbose)2094 static int dump_info(int verbose)
2095 {
2096 int i, numDevices, defaultDisplayed;
2097 const PaDeviceInfo *deviceInfo;
2098 PaStreamParameters inputParameters, outputParameters;
2099 PaError err;
2100 const char *line = "--------------------------------------------------------------------------------\n";
2101
2102 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,
2103 "PortAudio version number = %d\nPortAudio version text = '%s'\n", Pa_GetVersion(), Pa_GetVersionText());
2104 if (globals.call_list) {
2105 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "ERROR: Cannot use this command this while a call is in progress\n");
2106 return 0;
2107 }
2108
2109 if (verbose < 0) {
2110 destroy_audio_streams();
2111 destroy_codecs();
2112 Pa_Terminate();
2113 Pa_Initialize();
2114 load_config();
2115 verbose = 0;
2116 }
2117
2118 numDevices = Pa_GetDeviceCount();
2119 if (numDevices < 0) {
2120 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "ERROR: Pa_CountDevices returned 0x%x\n", numDevices);
2121 err = numDevices;
2122 goto error;
2123 }
2124
2125 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Number of devices = %d\n", numDevices);
2126
2127 if (!verbose) {
2128 return 0;
2129 }
2130
2131 for (i = 0; i < numDevices; i++) {
2132 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s", line);
2133 deviceInfo = Pa_GetDeviceInfo(i);
2134 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Device #%d ", i);
2135
2136 /* Mark global and API specific default devices */
2137 defaultDisplayed = 0;
2138 if (i == Pa_GetDefaultInputDevice()) {
2139 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "**Default Input");
2140 defaultDisplayed = 1;
2141
2142 } else if (i == Pa_GetHostApiInfo(deviceInfo->hostApi)->defaultInputDevice) {
2143
2144 const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo(deviceInfo->hostApi);
2145 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "**Default %s Input", hostInfo->name);
2146 defaultDisplayed = 1;
2147 }
2148
2149 if (i == Pa_GetDefaultOutputDevice()) {
2150 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "**Default Output");
2151 defaultDisplayed = 1;
2152
2153 } else if (i == Pa_GetHostApiInfo(deviceInfo->hostApi)->defaultOutputDevice) {
2154
2155 const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo(deviceInfo->hostApi);
2156 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "**Default %s Output", hostInfo->name);
2157 defaultDisplayed = 1;
2158 }
2159
2160 if (defaultDisplayed) {
2161 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "** | ");
2162 }
2163 /* print device info fields */
2164 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Name: %s\n", deviceInfo->name);
2165 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Host: %s | ", Pa_GetHostApiInfo(deviceInfo->hostApi)->name);
2166 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "inputs: %d | ", deviceInfo->maxInputChannels);
2167 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "outputs: %d | ", deviceInfo->maxOutputChannels);
2168
2169 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Default rate: %8.2f\n", deviceInfo->defaultSampleRate);
2170
2171 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Default input latency: %.3f | ", deviceInfo->defaultLowInputLatency);
2172 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Default output latency: %.3f\n", deviceInfo->defaultLowOutputLatency);
2173
2174 /* poll for standard sample rates */
2175 inputParameters.device = i;
2176 inputParameters.channelCount = deviceInfo->maxInputChannels;
2177 inputParameters.sampleFormat = paInt16;
2178 inputParameters.suggestedLatency = deviceInfo->defaultLowInputLatency; /* ignored by Pa_IsFormatSupported() */
2179 inputParameters.hostApiSpecificStreamInfo = NULL;
2180
2181 outputParameters.device = i;
2182 outputParameters.channelCount = deviceInfo->maxOutputChannels;
2183 outputParameters.sampleFormat = paInt16;
2184 outputParameters.suggestedLatency = deviceInfo->defaultLowOutputLatency; /* ignored by Pa_IsFormatSupported() */
2185 outputParameters.hostApiSpecificStreamInfo = NULL;
2186
2187 if (inputParameters.channelCount > 0) {
2188
2189 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "half-duplex 16 bit %d channel input rates:", inputParameters.channelCount);
2190 PrintSupportedStandardSampleRates(&inputParameters, NULL);
2191 }
2192
2193 if (outputParameters.channelCount > 0) {
2194 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "half-duplex 16 bit %d channel output rates:", outputParameters.channelCount);
2195 PrintSupportedStandardSampleRates(NULL, &outputParameters);
2196 }
2197
2198 if (inputParameters.channelCount > 0 && outputParameters.channelCount > 0) {
2199
2200 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,
2201 "full-duplex 16 bit %d channel input, %d channel output rates:", inputParameters.channelCount,
2202 outputParameters.channelCount);
2203 PrintSupportedStandardSampleRates(&inputParameters, &outputParameters);
2204 }
2205 }
2206
2207 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s", line);
2208
2209 return 0;
2210
2211 error:
2212 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR, "An error occurred while using the portaudio stream\n");
2213 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR, "Error number: %d\n", err);
2214 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR, "Error message: %s\n", Pa_GetErrorText(err));
2215 return err;
2216 }
2217
create_codecs(int restart)2218 static switch_status_t create_codecs(int restart)
2219 {
2220 int sample_rate = globals.sample_rate;
2221 int codec_ms = globals.codec_ms;
2222
2223 if (restart) {
2224 destroy_codecs();
2225 }
2226 if (globals.codecs_inited) {
2227 return SWITCH_STATUS_SUCCESS;
2228 }
2229
2230 if (!switch_core_codec_ready(&globals.read_codec)) {
2231 if (switch_core_codec_init(&globals.read_codec,
2232 "L16",
2233 NULL,
2234 NULL, sample_rate, codec_ms, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL,
2235 NULL) != SWITCH_STATUS_SUCCESS) {
2236 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n");
2237 return SWITCH_STATUS_FALSE;
2238 }
2239 }
2240
2241 switch_assert(globals.read_codec.implementation);
2242
2243 if (!switch_core_codec_ready(&globals.write_codec)) {
2244 if (switch_core_codec_init(&globals.write_codec,
2245 "L16",
2246 NULL,
2247 NULL,
2248 sample_rate, codec_ms, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, NULL) != SWITCH_STATUS_SUCCESS) {
2249 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n");
2250 switch_core_codec_destroy(&globals.read_codec);
2251 return SWITCH_STATUS_FALSE;
2252 }
2253 }
2254
2255 if (!globals.read_timer.timer_interface) {
2256 if (switch_core_timer_init(&globals.read_timer,
2257 globals.timer_name, codec_ms, globals.read_codec.implementation->samples_per_packet,
2258 module_pool) != SWITCH_STATUS_SUCCESS) {
2259 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "setup timer failed!\n");
2260 switch_core_codec_destroy(&globals.read_codec);
2261 switch_core_codec_destroy(&globals.write_codec);
2262 return SWITCH_STATUS_FALSE;
2263 }
2264 }
2265 if (!globals.readfile_timer.timer_interface) {
2266 if (switch_core_timer_init(&globals.readfile_timer,
2267 globals.timer_name, codec_ms, globals.read_codec.implementation->samples_per_packet,
2268 module_pool) != SWITCH_STATUS_SUCCESS) {
2269 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "setup timer failed!\n");
2270 switch_core_codec_destroy(&globals.read_codec);
2271 switch_core_codec_destroy(&globals.write_codec);
2272 return SWITCH_STATUS_FALSE;
2273 }
2274 }
2275
2276
2277 if (!globals.hold_timer.timer_interface) {
2278 if (switch_core_timer_init(&globals.hold_timer,
2279 globals.timer_name, codec_ms, globals.read_codec.implementation->samples_per_packet,
2280 module_pool) != SWITCH_STATUS_SUCCESS) {
2281 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "setup hold timer failed!\n");
2282 switch_core_codec_destroy(&globals.read_codec);
2283 switch_core_codec_destroy(&globals.write_codec);
2284 switch_core_timer_destroy(&globals.read_timer);
2285 switch_core_timer_destroy(&globals.readfile_timer);
2286
2287 return SWITCH_STATUS_FALSE;
2288 }
2289 }
2290
2291 globals.cng_frame.rate = globals.read_frame.rate = sample_rate;
2292 globals.cng_frame.codec = globals.read_frame.codec = &globals.read_codec;
2293
2294 globals.codecs_inited=1;
2295 return SWITCH_STATUS_SUCCESS;
2296 }
2297
switch_audio_stream()2298 static switch_status_t switch_audio_stream()
2299 {
2300 audio_stream_t *stream;
2301 if (! globals.call_list) { /* If no active calls then it will automatically switch over on next call */
2302 return SWITCH_STATUS_SUCCESS;
2303 }
2304 stream = get_audio_stream(globals.indev, globals.outdev);
2305 if (stream == NULL) {
2306 return SWITCH_STATUS_FALSE;
2307 }
2308
2309 globals.main_stream = stream;//TODO: need locks around here??
2310
2311 return SWITCH_STATUS_SUCCESS;
2312 }
2313
open_audio_stream(PABLIO_Stream ** stream,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters)2314 PaError open_audio_stream(PABLIO_Stream **stream, const PaStreamParameters * inputParameters, const PaStreamParameters * outputParameters)
2315 {
2316 if (inputParameters->device != -1) {
2317 return OpenAudioStream(stream, inputParameters, outputParameters, globals.sample_rate, paClipOff, globals.read_codec.implementation->samples_per_packet, globals.dual_streams);
2318 }
2319 return OpenAudioStream(stream, NULL, outputParameters, globals.sample_rate, paClipOff, globals.read_codec.implementation->samples_per_packet, 0);
2320 }
2321
open_shared_audio_stream(shared_audio_stream_t * shstream,const PaStreamParameters * inputParameters,const PaStreamParameters * outputParameters)2322 PaError open_shared_audio_stream(shared_audio_stream_t *shstream, const PaStreamParameters * inputParameters, const PaStreamParameters * outputParameters)
2323 {
2324 PaError err;
2325 if (inputParameters->device != -1) {
2326 err = OpenAudioStream(&shstream->stream, inputParameters->device != -1 ? inputParameters : NULL,
2327 outputParameters->device != -1 ? outputParameters : NULL, shstream->sample_rate,
2328 paClipOff, STREAM_SAMPLES_PER_PACKET(shstream), globals.dual_streams);
2329 } else {
2330 err = OpenAudioStream(&shstream->stream, NULL, outputParameters, shstream->sample_rate,
2331 paClipOff, STREAM_SAMPLES_PER_PACKET(shstream), 0);
2332 }
2333 if (err != paNoError) {
2334 shstream->stream = NULL;
2335 }
2336 return err;
2337 }
2338
create_shared_audio_stream(shared_audio_stream_t * shstream)2339 static int create_shared_audio_stream(shared_audio_stream_t *shstream)
2340 {
2341 PaStreamParameters inputParameters, outputParameters;
2342 PaError err;
2343 switch_event_t *event;
2344
2345 inputParameters.device = shstream->indev;
2346 if (shstream->indev != -1) {
2347 inputParameters.channelCount = shstream->channels;
2348 inputParameters.sampleFormat = SAMPLE_TYPE;
2349 inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
2350 inputParameters.hostApiSpecificStreamInfo = NULL;
2351 }
2352 outputParameters.device = shstream->outdev;
2353 if (shstream->outdev != -1) {
2354 outputParameters.channelCount = shstream->channels;
2355 outputParameters.sampleFormat = SAMPLE_TYPE;
2356 outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
2357 outputParameters.hostApiSpecificStreamInfo = NULL;
2358 }
2359
2360 err = open_shared_audio_stream(shstream, &inputParameters, &outputParameters);
2361 if (err != paNoError) {
2362 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
2363 "Error opening audio device retrying (indev = %d, outdev = %d, error = %s)\n",
2364 inputParameters.device, outputParameters.device, Pa_GetErrorText(err));
2365 switch_yield(1000000);
2366 err = open_shared_audio_stream(shstream, &inputParameters, &outputParameters);
2367 }
2368
2369 if (err != paNoError) {
2370 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't open audio device (indev = %d, outdev = %d, error = %s)\n",
2371 inputParameters.device, outputParameters.device, Pa_GetErrorText(err));
2372 if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_ERROR_AUDIO_DEV) == SWITCH_STATUS_SUCCESS) {
2373 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Reason", Pa_GetErrorText(err));
2374 switch_event_fire(&event);
2375 }
2376 return -1;
2377 }
2378 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created shared audio stream %s: %d channels %d\n",
2379 shstream->name, shstream->sample_rate, shstream->channels);
2380 return 0;
2381 }
2382
destroy_shared_audio_stream(shared_audio_stream_t * shstream)2383 static int destroy_shared_audio_stream(shared_audio_stream_t *shstream)
2384 {
2385 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Destroying shared audio stream %s\n", shstream->name);
2386 CloseAudioStream(shstream->stream);
2387 shstream->stream = NULL;
2388 return 0;
2389 }
2390
create_audio_stream(int indev,int outdev)2391 static audio_stream_t *create_audio_stream(int indev, int outdev)
2392 {
2393 PaStreamParameters inputParameters, outputParameters;
2394 PaError err;
2395 switch_event_t *event;
2396 audio_stream_t *stream;
2397
2398 stream = malloc(sizeof(*stream));
2399 if (stream == NULL) {
2400 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unable to alloc memory\n");
2401 return NULL;
2402 }
2403 memset(stream, 0, sizeof(*stream));
2404 stream->next = NULL;
2405 stream->stream = NULL;
2406 stream->indev = indev;
2407 stream->outdev = outdev;
2408 if (!stream->write_timer.timer_interface) {
2409 if (switch_core_timer_init(&(stream->write_timer),
2410 globals.timer_name, globals.codec_ms, globals.read_codec.implementation->samples_per_packet,
2411 module_pool) != SWITCH_STATUS_SUCCESS) {
2412 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "setup timer failed!\n");
2413 switch_safe_free(stream);
2414 return NULL;
2415 }
2416 }
2417
2418 inputParameters.device = indev;
2419 if (indev != -1) {
2420 inputParameters.channelCount = 1;
2421 inputParameters.sampleFormat = SAMPLE_TYPE;
2422 inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
2423 inputParameters.hostApiSpecificStreamInfo = NULL;
2424 }
2425 outputParameters.device = outdev;
2426
2427 if (outdev != -1) {
2428 outputParameters.channelCount = 1;
2429 outputParameters.sampleFormat = SAMPLE_TYPE;
2430 outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
2431 outputParameters.hostApiSpecificStreamInfo = NULL;
2432 }
2433
2434 err = open_audio_stream(&(stream->stream), &inputParameters, &outputParameters);
2435 if (err != paNoError) {
2436 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening audio device retrying\n");
2437 switch_yield(1000000);
2438 err = open_audio_stream(&(stream->stream), &inputParameters, &outputParameters);
2439 }
2440
2441 if (err != paNoError) {
2442 switch_safe_free(stream);
2443 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't open audio device\n");
2444 if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_ERROR_AUDIO_DEV) == SWITCH_STATUS_SUCCESS) {
2445 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Reason", Pa_GetErrorText(err));
2446 switch_event_fire(&event);
2447 }
2448 return NULL;
2449 }
2450 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created audio stream: %d channels %d\n", globals.sample_rate, outputParameters.channelCount);
2451 return stream;
2452 }
2453
get_audio_stream(int indev,int outdev)2454 audio_stream_t *get_audio_stream(int indev, int outdev)
2455 {
2456 audio_stream_t *stream = NULL;
2457 if (outdev == -1) {
2458 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error invalid output audio device\n");
2459 return NULL;
2460 }
2461 if (create_codecs(0) != SWITCH_STATUS_SUCCESS) {
2462 return NULL;
2463 }
2464
2465 stream = find_audio_stream(indev, outdev, 0);
2466 if (stream != NULL) {
2467 return stream;
2468 }
2469 stream = create_audio_stream(indev, outdev);
2470 if (stream) {
2471 add_stream(stream, 0);
2472 }
2473 return stream;
2474 }
2475
dtmf_call(char ** argv,int argc,switch_stream_handle_t * stream)2476 static switch_status_t dtmf_call(char **argv, int argc, switch_stream_handle_t *stream)
2477 {
2478 char *dtmf_str = argv[0];
2479 switch_dtmf_t dtmf = { 0, switch_core_default_dtmf_duration(0) };
2480
2481 if (zstr(dtmf_str)) {
2482 stream->write_function(stream, "No DTMF Supplied!\n");
2483 } else {
2484 switch_mutex_lock(globals.pvt_lock);
2485 if (globals.call_list) {
2486 switch_channel_t *channel = switch_core_session_get_channel(globals.call_list->session);
2487 char *p = dtmf_str;
2488 while (p && *p) {
2489 dtmf.digit = *p;
2490 switch_channel_queue_dtmf(channel, &dtmf);
2491 p++;
2492 }
2493 }
2494 switch_mutex_unlock(globals.pvt_lock);
2495 }
2496
2497 return SWITCH_STATUS_SUCCESS;
2498 }
2499
list_shared_streams(char ** argv,int argc,switch_stream_handle_t * stream)2500 static switch_status_t list_shared_streams(char **argv, int argc, switch_stream_handle_t *stream)
2501 {
2502 switch_hash_index_t *hi;
2503 int cnt = 0;
2504 for (hi = switch_core_hash_first(globals.sh_streams); hi; hi = switch_core_hash_next(&hi)) {
2505 const void *var;
2506 void *val;
2507 shared_audio_stream_t *s = NULL;
2508 switch_core_hash_this(hi, &var, NULL, &val);
2509 s = val;
2510 stream->write_function(stream, "%s> indev: %d, outdev: %d, sample-rate: %d, codec-ms: %d, channels: %d\n",
2511 s->name, s->indev, s->outdev, s->sample_rate, s->codec_ms, s->channels);
2512 cnt++;
2513 }
2514 stream->write_function(stream, "Total streams: %d\n", cnt);
2515 return SWITCH_STATUS_SUCCESS;
2516 }
2517
list_endpoints(char ** argv,int argc,switch_stream_handle_t * stream)2518 static switch_status_t list_endpoints(char **argv, int argc, switch_stream_handle_t *stream)
2519 {
2520 switch_hash_index_t *hi;
2521 int cnt = 0;
2522 for (hi = switch_core_hash_first(globals.endpoints); hi; hi = switch_core_hash_next(&hi)) {
2523 const void *var;
2524 void *val;
2525 audio_endpoint_t *e = NULL;
2526 switch_core_hash_this(hi, &var, NULL, &val);
2527 e = val;
2528 stream->write_function(stream, "%s> instream: %s, outstream: %s\n",
2529 e->name, e->in_stream ? e->in_stream->name : "(none)",
2530 e->out_stream ? e->out_stream->name : "(none)");
2531 cnt++;
2532 }
2533 stream->write_function(stream, "Total endpoints: %d\n", cnt);
2534 return SWITCH_STATUS_SUCCESS;
2535 }
2536
close_streams(char ** argv,int argc,switch_stream_handle_t * stream)2537 static switch_status_t close_streams(char **argv, int argc, switch_stream_handle_t *stream)
2538 {
2539 if (globals.call_list) {
2540 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "ERROR: Cannot use this command this while a call is in progress\n");
2541 return SWITCH_STATUS_FALSE;
2542 }
2543 destroy_audio_streams();
2544 stream->write_function(stream, "closestreams all open streams closed\n");
2545 return SWITCH_STATUS_SUCCESS;
2546 }
2547
set_indev(char ** argv,int argc,switch_stream_handle_t * stream)2548 static switch_status_t set_indev(char **argv, int argc, switch_stream_handle_t *stream)
2549 {
2550 int devval;
2551
2552 if (globals.call_list && ! globals.live_stream_switch) {
2553 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "ERROR: Cannot use this command this while a call is in progress\n");
2554 return SWITCH_STATUS_FALSE;
2555 }
2556 if (*argv[0] == '#') {
2557 devval = get_dev_by_number(argv[0] + 1, 1);
2558 } else {
2559 devval = get_dev_by_name(argv[0], 1);
2560 }
2561 if (devval < 0) {
2562 stream->write_function(stream, "indev not set (invalid value)\n");
2563 return SWITCH_STATUS_FALSE;
2564 }
2565 globals.indev = devval;
2566 switch_audio_stream();
2567 stream->write_function(stream, "indev set to %d\n", devval);
2568 return SWITCH_STATUS_SUCCESS;
2569 }
2570
set_outdev(char ** argv,int argc,switch_stream_handle_t * stream)2571 static switch_status_t set_outdev(char **argv, int argc, switch_stream_handle_t *stream)
2572 {
2573 int devval;
2574 if (globals.call_list && ! globals.live_stream_switch) {
2575 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "ERROR: Cannot use this command this while a call is in progress\n");
2576 return SWITCH_STATUS_FALSE;
2577 }
2578 if (*argv[0] == '#') {
2579 devval = get_dev_by_number(argv[0] + 1, 0);
2580 } else {
2581 devval = get_dev_by_name(argv[0], 0);
2582 }
2583 if (devval < 0) {
2584 stream->write_function(stream, "outdev not set (invalid value)\n");
2585 return SWITCH_STATUS_FALSE;
2586 }
2587
2588 globals.outdev = devval;
2589 switch_audio_stream();
2590 stream->write_function(stream, "outdev set to %d\n", devval);
2591 return SWITCH_STATUS_SUCCESS;
2592 }
2593
prepare_stream(char ** argv,int argc,switch_stream_handle_t * stream)2594 static switch_status_t prepare_stream(char **argv, int argc, switch_stream_handle_t *stream)
2595 {
2596 int devval=-2,devval2=-1;
2597 if (! strcmp(argv[0], "#-1")) {
2598 devval = -1;
2599 } else if (*argv[0] == '#') {
2600 devval = get_dev_by_number(argv[0]+1, 1);
2601 }
2602 if (devval == -2) {
2603 stream->write_function(stream, "preparestream not prepared as indev has (invalid value)\n");
2604 return SWITCH_STATUS_FALSE;
2605 }
2606 if (*argv[1] == '#') {
2607 devval2 = get_dev_by_number(argv[1]+1, 0);
2608 }
2609 if (devval2 == -1) {
2610 stream->write_function(stream, "preparestream not prepared as outdev has (invalid value)\n");
2611 return SWITCH_STATUS_FALSE;
2612 }
2613
2614 if (! get_audio_stream(devval,devval2)) {
2615 stream->write_function(stream, "preparestream not prepared received an invalid stream back\n");
2616 return SWITCH_STATUS_FALSE;
2617 }
2618 stream->write_function(stream, "preparestream prepared indev: %d outdev: %d\n", devval, devval2);
2619 return SWITCH_STATUS_SUCCESS;
2620 }
2621
switch_stream(char ** argv,int argc,switch_stream_handle_t * stream)2622 static switch_status_t switch_stream(char **argv, int argc, switch_stream_handle_t *stream)
2623 {
2624 int devval =-1, devval2 = -1;
2625 if (globals.call_list && ! globals.live_stream_switch) {
2626 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "ERROR: Cannot use this command this while a call is in progress\n");
2627 return SWITCH_STATUS_FALSE;
2628 }
2629 if (*argv[0] == '#') {
2630 devval = get_dev_by_number(argv[0]+1, 1);
2631 }
2632 if (devval == -1) {
2633 stream->write_function(stream, "switchstream not prepared as indev has (invalid value)\n");
2634 return SWITCH_STATUS_FALSE;
2635 }
2636 if (*argv[1] == '#') {
2637 devval2 = get_dev_by_number(argv[1]+1, 0);
2638 }
2639 if (devval2 == -1) {
2640 stream->write_function(stream, "switchstream not prepared as outdev has (invalid value)\n");
2641 return SWITCH_STATUS_FALSE;
2642 }
2643 globals.indev = devval;
2644 globals.outdev = devval2;
2645 if (switch_audio_stream() != SWITCH_STATUS_SUCCESS) {
2646 stream->write_function(stream, "switchstream was unable to switch\n");
2647 return SWITCH_STATUS_FALSE;
2648 }
2649 stream->write_function(stream, "switchstream switched to indev: %d outdev: %d\n", devval, devval2);
2650 return SWITCH_STATUS_SUCCESS;
2651 }
2652
set_ringdev(char ** argv,int argc,switch_stream_handle_t * stream)2653 static switch_status_t set_ringdev(char **argv, int argc, switch_stream_handle_t *stream)
2654 {
2655 int devval;
2656 if (globals.call_list && ! globals.live_stream_switch) {
2657 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "ERROR: Cannot use this command this while a call is in progress\n");
2658 return SWITCH_STATUS_FALSE;
2659 }
2660 if (! strcmp(argv[0], "#-1")) {
2661 globals.ring_stream = NULL;
2662 globals.ringdev = -1;
2663 stream->write_function(stream, "ringdev set to %d\n", globals.ringdev);
2664 return SWITCH_STATUS_SUCCESS;
2665 } else if (*argv[0] == '#') {
2666 devval = get_dev_by_number(argv[0] + 1, 0);
2667 } else {
2668 devval = get_dev_by_name(argv[0], 0);
2669 }
2670 if (devval == -1) {
2671 stream->write_function(stream, "ringdev not set as dev has (invalid value)\n");
2672 return SWITCH_STATUS_FALSE;
2673 }
2674 globals.ringdev = devval;
2675 stream->write_function(stream, "ringdev set to %d\n", globals.ringdev);
2676 return SWITCH_STATUS_SUCCESS;
2677 }
2678
looptest(char ** argv,int argc,switch_stream_handle_t * stream)2679 static switch_status_t looptest(char **argv, int argc, switch_stream_handle_t *stream)
2680 {
2681 int samples = 0;
2682 int success = 0;
2683 int i;
2684
2685 if (globals.call_list) {
2686 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "ERROR: Cannot use this command this while a call is in progress\n");
2687 return SWITCH_STATUS_FALSE;
2688 }
2689 if (validate_main_audio_stream() != SWITCH_STATUS_SUCCESS) {
2690 stream->write_function(stream, "looptest Failed to engage audio device\n");
2691 return SWITCH_STATUS_FALSE;
2692 }
2693 globals.stream_in_use = 1;
2694 for (i = 0; i < 400; i++) {
2695 if (globals.destroying_streams || ! globals.main_stream->stream) {
2696 break;
2697 }
2698 if ((samples = ReadAudioStream(globals.main_stream->stream, globals.read_frame.data, globals.read_codec.implementation->samples_per_packet, 0, &globals.read_timer))) {
2699 WriteAudioStream(globals.main_stream->stream, globals.read_frame.data, (long) samples, 0, &(globals.main_stream->write_timer));
2700 success = 1;
2701 }
2702 switch_yield(10000);
2703 }
2704 globals.stream_in_use = 0;
2705
2706 if (!success) {
2707 stream->write_function(stream, "Failed to read any bytes from indev\n");
2708 return SWITCH_STATUS_FALSE;
2709 }
2710 destroy_audio_streams();
2711 stream->write_function(stream, "looptest complete\n");
2712 return SWITCH_STATUS_SUCCESS;
2713 }
2714
set_ringfile(char ** argv,int argc,switch_stream_handle_t * stream)2715 static switch_status_t set_ringfile(char **argv, int argc, switch_stream_handle_t *stream)
2716 {
2717 if (! argv[0]) {
2718 stream->write_function(stream, "%s", globals.ring_file);
2719 return SWITCH_STATUS_SUCCESS;
2720 }
2721 if (create_codecs(0) == SWITCH_STATUS_SUCCESS) {
2722 switch_file_handle_t fh = { 0 };
2723 if (switch_core_file_open(&fh,
2724 argv[0],
2725 globals.read_codec.implementation->number_of_channels,
2726 globals.read_codec.implementation->actual_samples_per_second,
2727 SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) == SWITCH_STATUS_SUCCESS) {
2728 switch_core_file_close(&fh);
2729 set_global_ring_file(argv[0]);
2730 } else {
2731 stream->write_function(stream, "ringfile Unable to open ring file %s\n", argv[0]);
2732 return SWITCH_STATUS_FALSE;
2733 }
2734 } else {
2735 stream->write_function(stream, "ringfile Failed to init codecs device\n");
2736 return SWITCH_STATUS_FALSE;
2737 }
2738 stream->write_function(stream, "ringfile set to %s", globals.ring_file);
2739 return SWITCH_STATUS_SUCCESS;
2740 }
2741
switch_call(char ** argv,int argc,switch_stream_handle_t * stream)2742 static switch_status_t switch_call(char **argv, int argc, switch_stream_handle_t *stream)
2743 {
2744 private_t *tp, *tech_pvt = NULL;
2745 char *callid = argv[0];
2746 uint8_t one_call = 0;
2747
2748
2749 switch_mutex_lock(globals.pvt_lock);
2750 if (zstr(callid)) {
2751 if (globals.call_list) {
2752 if (globals.call_list->next) {
2753 tech_pvt = globals.call_list->next;
2754 } else {
2755 tech_pvt = globals.call_list;
2756 one_call = 1;
2757 }
2758 }
2759 } else if (!strcasecmp(callid, "none")) {
2760 for (tp = globals.call_list; tp; tp = tp->next) {
2761 if (switch_test_flag(tp, TFLAG_MASTER)) {
2762 switch_clear_flag_locked(tp, TFLAG_MASTER);
2763 create_hold_event(tp,0);
2764 }
2765 }
2766 stream->write_function(stream, "OK\n");
2767 goto done;
2768 } else {
2769 tech_pvt = switch_core_hash_find(globals.call_hash, callid);
2770 }
2771
2772 if (tech_pvt) {
2773 if (tech_pvt == globals.call_list && !tech_pvt->next) {
2774 one_call = 1;
2775 }
2776
2777 if (!one_call) {
2778 remove_pvt(tech_pvt);
2779 }
2780 add_pvt(tech_pvt, PA_MASTER);
2781 create_hold_event(tech_pvt, 1);
2782 stream->write_function(stream, "OK\n");
2783 } else {
2784 stream->write_function(stream, "NO SUCH CALL\n");
2785 }
2786
2787 done:
2788 switch_mutex_unlock(globals.pvt_lock);
2789
2790 return SWITCH_STATUS_SUCCESS;
2791 }
2792
hangup_call(char ** argv,int argc,switch_stream_handle_t * stream)2793 static switch_status_t hangup_call(char **argv, int argc, switch_stream_handle_t *stream)
2794 {
2795 private_t *tech_pvt;
2796 char *callid = argv[0];
2797
2798 switch_mutex_lock(globals.pvt_lock);
2799 if (zstr(callid)) {
2800 tech_pvt = globals.call_list;
2801 } else {
2802 tech_pvt = switch_core_hash_find(globals.call_hash, callid);
2803 }
2804
2805 if (tech_pvt) {
2806 switch_channel_hangup(switch_core_session_get_channel(tech_pvt->session), SWITCH_CAUSE_NORMAL_CLEARING);
2807 stream->write_function(stream, "OK\n");
2808 } else {
2809 stream->write_function(stream, "NO SUCH CALL\n");
2810 }
2811 switch_mutex_unlock(globals.pvt_lock);
2812
2813 return SWITCH_STATUS_SUCCESS;
2814 }
2815
answer_call(char ** argv,int argc,switch_stream_handle_t * stream)2816 static switch_status_t answer_call(char **argv, int argc, switch_stream_handle_t *stream)
2817 {
2818 private_t *tp;
2819 int x = 0;
2820 char *callid = argv[0];
2821
2822 switch_mutex_lock(globals.pvt_lock);
2823
2824 if (!zstr(callid)) {
2825 if ((tp = switch_core_hash_find(globals.call_hash, callid))) {
2826 if (switch_test_flag(tp, TFLAG_ANSWER)) {
2827 stream->write_function(stream, "CALL ALREADY ANSWERED\n");
2828 } else {
2829 switch_channel_t *channel = switch_core_session_get_channel(tp->session);
2830 switch_set_flag_locked(tp, TFLAG_ANSWER);
2831 if (tp != globals.call_list) {
2832 remove_pvt(tp);
2833 }
2834 add_pvt(tp, PA_MASTER);
2835 switch_channel_mark_answered(channel);
2836 }
2837 } else {
2838 stream->write_function(stream, "NO SUCH CALL\n");
2839 }
2840
2841 goto done;
2842 }
2843
2844 for (tp = globals.call_list; tp; tp = tp->next) {
2845 if (!switch_test_flag(tp, TFLAG_ANSWER)) {
2846 switch_channel_t *channel = switch_core_session_get_channel(tp->session);
2847 switch_set_flag_locked(tp, TFLAG_ANSWER);
2848 add_pvt(tp, PA_MASTER);
2849 switch_channel_mark_answered(channel);
2850 x++;
2851 break;
2852 }
2853 }
2854 done:
2855 switch_mutex_unlock(globals.pvt_lock);
2856 stream->write_function(stream, "Answered %d channels.\n", x);
2857
2858 return SWITCH_STATUS_SUCCESS;
2859 }
2860
do_flags(char ** argv,int argc,switch_stream_handle_t * stream)2861 static switch_status_t do_flags(char **argv, int argc, switch_stream_handle_t *stream)
2862 {
2863 char *action = argv[0];
2864 char *flag_str;
2865 GFLAGS flags = GFLAG_NONE;
2866 char *p;
2867 int x = 0;
2868
2869 if (argc < 2) {
2870 goto desc;
2871 }
2872
2873 for (x = 1; x < argc; x++) {
2874 flag_str = argv[x];
2875 for (p = flag_str; *p; p++) {
2876 *p = (char) tolower(*p);
2877 }
2878
2879 if (strstr(flag_str, "ear")) {
2880 flags |= GFLAG_EAR;
2881 }
2882 if (strstr(flag_str, "mouth")) {
2883 flags |= GFLAG_MOUTH;
2884 }
2885 }
2886
2887 if (!strcasecmp(action, "on")) {
2888 if (flags & GFLAG_EAR) {
2889 switch_set_flag((&globals), GFLAG_EAR);
2890 }
2891 if (flags & GFLAG_MOUTH) {
2892 switch_set_flag((&globals), GFLAG_MOUTH);
2893 }
2894 } else if (!strcasecmp(action, "off")) {
2895 if (flags & GFLAG_EAR) {
2896 switch_clear_flag((&globals), GFLAG_EAR);
2897 }
2898 if (flags & GFLAG_MOUTH) {
2899 switch_clear_flag((&globals), GFLAG_MOUTH);
2900 }
2901 } else {
2902 goto bad;
2903 }
2904
2905 desc:
2906 x = 0;
2907 stream->write_function(stream, "FLAGS: ");
2908 if (switch_test_flag((&globals), GFLAG_EAR)) {
2909 stream->write_function(stream, "ear");
2910 x++;
2911 }
2912 if (switch_test_flag((&globals), GFLAG_MOUTH)) {
2913 stream->write_function(stream, "%smouth", x ? "|" : "");
2914 x++;
2915 }
2916 if (!x) {
2917 stream->write_function(stream, "none");
2918 }
2919
2920 goto done;
2921
2922 bad:
2923 stream->write_function(stream, "Usage: flags [on|off] <flags>\n");
2924 done:
2925 return SWITCH_STATUS_SUCCESS;
2926 }
2927
list_calls(char ** argv,int argc,switch_stream_handle_t * stream)2928 static switch_status_t list_calls(char **argv, int argc, switch_stream_handle_t *stream)
2929 {
2930 private_t *tp;
2931 int x = 0;
2932 const char *cid_name = "n/a";
2933 const char *cid_num = "n/a";
2934
2935 switch_mutex_lock(globals.pvt_lock);
2936 for (tp = globals.call_list; tp; tp = tp->next) {
2937 switch_channel_t *channel;
2938 switch_caller_profile_t *profile;
2939 x++;
2940 channel = switch_core_session_get_channel(tp->session);
2941
2942 if ((profile = switch_channel_get_caller_profile(channel))) {
2943 if (profile->originatee_caller_profile) {
2944 cid_name = "Outbound Call";
2945 cid_num = profile->originatee_caller_profile->destination_number;
2946 } else {
2947 cid_name = profile->caller_id_name;
2948 cid_num = profile->caller_id_number;
2949 }
2950 }
2951
2952 stream->write_function(stream, "%s %s [%s (%s)] %s\n", tp->call_id, switch_channel_get_name(channel),
2953 cid_name, cid_num, switch_test_flag(tp, TFLAG_MASTER) ? "active" : "hold");
2954 }
2955 switch_mutex_unlock(globals.pvt_lock);
2956
2957 stream->write_function(stream, "\n%d call%s\n", x, x == 1 ? "" : "s");
2958
2959 return SWITCH_STATUS_SUCCESS;
2960 }
2961
place_call(char ** argv,int argc,switch_stream_handle_t * stream)2962 static switch_status_t place_call(char **argv, int argc, switch_stream_handle_t *stream)
2963 {
2964 switch_core_session_t *session;
2965 switch_status_t status = SWITCH_STATUS_SUCCESS;
2966 char *dest = NULL;
2967
2968 if (zstr(argv[0])) {
2969 stream->write_function(stream, "FAIL:Usage: call <dest>\n");
2970 return SWITCH_STATUS_SUCCESS;
2971 }
2972 dest = argv[0];
2973
2974 if ((session = switch_core_session_request(portaudio_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL)) != 0) {
2975 private_t *tech_pvt;
2976 switch_channel_t *channel;
2977 char *dialplan = globals.dialplan;
2978 char *context = globals.context;
2979 char *cid_name = globals.cid_name;
2980 char *cid_num = globals.cid_num;
2981 char ip[25] = "0.0.0.0";
2982
2983 switch_core_session_add_stream(session, NULL);
2984 if ((tech_pvt = (private_t *) switch_core_session_alloc(session, sizeof(private_t))) != 0) {
2985 memset(tech_pvt, 0, sizeof(*tech_pvt));
2986 switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
2987 channel = switch_core_session_get_channel(session);
2988 switch_core_session_set_private(session, tech_pvt);
2989 tech_pvt->session = session;
2990 } else {
2991 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Hey where is my memory pool?\n");
2992 switch_core_session_destroy(&session);
2993 return SWITCH_STATUS_MEMERR;
2994 }
2995
2996 if (!zstr(argv[1])) {
2997 dialplan = argv[1];
2998 }
2999
3000 if (!zstr(argv[2])) {
3001 cid_num = argv[2];
3002 }
3003
3004 if (!zstr(argv[3])) {
3005 cid_name = argv[3];
3006 }
3007
3008 if (!zstr(argv[4])) {
3009 tech_pvt->sample_rate = atoi(argv[4]);
3010 }
3011
3012 if (!zstr(argv[5])) {
3013 tech_pvt->codec_ms = atoi(argv[5]);
3014 }
3015
3016 switch_find_local_ip(ip, sizeof(ip), NULL, AF_INET);
3017
3018 if ((tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session),
3019 NULL, dialplan, cid_name, cid_num, ip, NULL, NULL, NULL, modname, context, dest)) != 0) {
3020 char name[128];
3021 switch_snprintf(name, sizeof(name), "portaudio/%s",
3022 tech_pvt->caller_profile->destination_number ? tech_pvt->caller_profile->destination_number : modname);
3023 switch_channel_set_name(channel, name);
3024
3025 switch_channel_set_caller_profile(channel, tech_pvt->caller_profile);
3026 }
3027 tech_pvt->session = session;
3028 if ((status = validate_main_audio_stream()) == SWITCH_STATUS_SUCCESS) {
3029 switch_set_flag_locked(tech_pvt, TFLAG_ANSWER);
3030 switch_channel_mark_answered(channel);
3031 switch_channel_set_state(channel, CS_INIT);
3032 if (switch_core_session_thread_launch(tech_pvt->session) != SWITCH_STATUS_SUCCESS) {
3033 switch_event_t *event;
3034 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Error spawning thread\n");
3035 switch_core_session_destroy(&session);
3036 stream->write_function(stream, "FAIL:Thread Error!\n");
3037 if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_MAKE_CALL) == SWITCH_STATUS_SUCCESS) {
3038 char buf[512];
3039 switch_channel_event_set_data(channel, event);
3040 switch_snprintf(buf, sizeof(buf), "Thread error!.\n");
3041 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "error", buf);
3042 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "fail", "true");
3043 switch_event_fire(&event);
3044 }
3045 } else {
3046 switch_event_t *event;
3047 add_pvt(tech_pvt, PA_MASTER);
3048 stream->write_function(stream, "SUCCESS:%s:%s\n", tech_pvt->call_id, switch_core_session_get_uuid(tech_pvt->session));
3049 if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_MAKE_CALL) == SWITCH_STATUS_SUCCESS) {
3050 switch_channel_event_set_data(channel, event);
3051 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "fail", "false");
3052 switch_event_fire(&event);
3053 }
3054 }
3055 } else {
3056 switch_event_t *event;
3057 switch_core_session_destroy(&session);
3058 stream->write_function(stream, "FAIL:Device Error!\n");
3059 if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_MAKE_CALL) == SWITCH_STATUS_SUCCESS) {
3060 char buf[512];
3061 switch_channel_event_set_data(channel, event);
3062 switch_snprintf(buf, sizeof(buf), "Device fail.\n");
3063 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "error", buf);
3064 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "fail", "true");
3065 switch_event_fire(&event);
3066 }
3067 }
3068 }
3069
3070 return SWITCH_STATUS_SUCCESS;
3071 }
3072
SWITCH_STANDARD_API(pa_cmd)3073 SWITCH_STANDARD_API(pa_cmd)
3074 {
3075 char *argv[1024] = { 0 };
3076 int argc = 0;
3077 char *mycmd = NULL;
3078 switch_status_t status = SWITCH_STATUS_SUCCESS;
3079 pa_command_t func = NULL;
3080 int devval;
3081 int lead = 1;
3082 char *wcmd = NULL, *action = NULL;
3083 char cmd_buf[1024] = "";
3084 char *http = NULL;
3085
3086 const char *usage_string = "USAGE:\n"
3087 "--------------------------------------------------------------------------------\n"
3088 "pa help\n"
3089 "pa dump\n"
3090 "pa rescan\n"
3091 "pa call <dest> [<dialplan> <cid_name> <cid_num>]\n"
3092 "pa answer [<call_id>]\n"
3093 "pa hangup [<call_id>]\n"
3094 "pa list\n"
3095 "pa switch [<call_id>|none]\n"
3096 "pa dtmf <digit string>\n"
3097 "pa flags [on|off] [ear] [mouth]\n"
3098 "pa devlist [xml]\n"
3099 "pa indev #<num>|<partial name>\n"
3100 "pa outdev #<num>|<partial name>\n"
3101 "pa preparestream #<indev_num> #<outdev_num>\n"
3102 "pa switchstream #<indev_num> #<outdev_num>\n"
3103 "pa closestreams\n"
3104 "pa ringdev #<num>|<partial name>\n"
3105 "pa play [ringtest|<filename>] [seconds] [no_close]\n"
3106 "pa playdev #<num> [ringtest|<filename>] [seconds] [no_close]\n"
3107 "pa ringfile [filename]\n"
3108 "pa looptest\n"
3109 "pa shstreams\n"
3110 "pa endpoints\n"
3111 "--------------------------------------------------------------------------------\n";
3112
3113
3114 if (stream->param_event) {
3115 http = switch_event_get_header(stream->param_event, "http-host");
3116 }
3117
3118 if (http) {
3119 stream->write_function(stream, "Content-type: text/html\n\n");
3120
3121 wcmd = switch_str_nil(switch_event_get_header(stream->param_event, "wcmd"));
3122 action = switch_event_get_header(stream->param_event, "action");
3123
3124 if (action) {
3125 if (strlen(action) == 1) {
3126 switch_snprintf(cmd_buf, sizeof(cmd_buf), "dtmf %s", action);
3127 cmd = cmd_buf;
3128 } else if (!strcmp(action, "mute")) {
3129 switch_snprintf(cmd_buf, sizeof(cmd_buf), "flags off mouth");
3130 cmd = cmd_buf;
3131 } else if (!strcmp(action, "unmute")) {
3132 switch_snprintf(cmd_buf, sizeof(cmd_buf), "flags on mouth");
3133 cmd = cmd_buf;
3134 } else if (!strcmp(action, "switch")) {
3135 switch_snprintf(cmd_buf, sizeof(cmd_buf), "switch %s", wcmd);
3136 cmd = cmd_buf;
3137 } else if (!strcmp(action, "call")) {
3138 switch_snprintf(cmd_buf, sizeof(cmd_buf), "call %s", wcmd);
3139 cmd = cmd_buf;
3140 } else if (!strcmp(action, "hangup") || !strcmp(action, "list") || !strcmp(action, "devlist") || !strcmp(action, "answer")) {
3141 cmd = action;
3142 }
3143 }
3144
3145 if (zstr(cmd)) {
3146 goto done;
3147 }
3148
3149 } else {
3150
3151 if (zstr(cmd)) {
3152 stream->write_function(stream, "%s", usage_string);
3153 goto done;
3154 }
3155 }
3156
3157 if (!(mycmd = strdup(cmd))) {
3158 status = SWITCH_STATUS_MEMERR;
3159 goto done;
3160 }
3161
3162 if (!(argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
3163 stream->write_function(stream, "%s", usage_string);
3164 goto done;
3165 }
3166
3167 if (!argv[0]) {
3168 stream->write_function(stream, "Unknown Command\n");
3169 goto done;
3170 }
3171
3172 if (!strcasecmp(argv[0], "call")) {
3173 func = place_call;
3174 } else if (!strcasecmp(argv[0], "help")) {
3175 stream->write_function(stream, "%s", usage_string);
3176 goto done;
3177 } else if (!strcasecmp(argv[0], "devlist")) {
3178 func = devlist;
3179 } else if (!strcasecmp(argv[0], "rescan")) {
3180
3181 if (globals.call_list) {
3182 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "ERROR: Cannot use this command this while a call is in progress\n");
3183 goto done;
3184 }
3185
3186 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Looking for new devices.\n");
3187 dump_info(-1);
3188 goto done;
3189 } else if (!strcasecmp(argv[0], "dump")) {
3190 dump_info(1);
3191 goto done;
3192 } else if (!strcasecmp(argv[0], "list")) {
3193 func = list_calls;
3194 } else if (!strcasecmp(argv[0], "flags")) {
3195 func = do_flags;
3196 } else if (!strcasecmp(argv[0], "hangup")) {
3197 func = hangup_call;
3198 } else if (!strcasecmp(argv[0], "answer")) {
3199 func = answer_call;
3200 } else if (!strcasecmp(argv[0], "switch")) {
3201 func = switch_call;
3202 } else if (!strcasecmp(argv[0], "dtmf")) {
3203 func = dtmf_call;
3204 } else if (!strcasecmp(argv[0], "closestreams")) {
3205 func = close_streams;
3206 } else if (argv[1] && !strcmp(argv[0], "indev")) {
3207 func = set_indev;
3208 } else if (argv[1] && !strcmp(argv[0], "outdev")) {
3209 func = set_outdev;
3210 } else if (argv[1] && argv[2] && !strcmp(argv[0], "preparestream")) {
3211 func = prepare_stream;
3212 } else if (argv[1] && argv[2] && !strcmp(argv[0], "switchstream")) {
3213 func = switch_stream;
3214 } else if (argv[1] && !strcmp(argv[0], "ringdev")) {
3215 func = set_ringdev;
3216 } else if ((argv[1] && !strcmp(argv[0], "play"))) {
3217 if (validate_main_audio_stream() == SWITCH_STATUS_SUCCESS) {
3218 play_dev(stream, globals.main_stream ? globals.main_stream->outdev : -1,argv[1],argv[2], argv[3]);
3219 }else{
3220 stream->write_function(stream, "Failed to engage audio device\n");
3221 }
3222 goto done;
3223 } else if ((argv[1] && argv[2] && !strcmp(argv[0], "playdev"))) {
3224 if (*argv[1] == '#') {
3225 devval = get_dev_by_number(argv[1] + 1, 0);
3226 } else {
3227 devval = -1;
3228 }
3229 play_dev(stream, devval,argv[2],argv[3],argv[4]);
3230 goto done;
3231 } else if (!strcasecmp(argv[0], "looptest")) {
3232 func = looptest;
3233 } else if (!strcasecmp(argv[0], "ringfile")) {
3234 func = set_ringfile;
3235 } else if (!strcasecmp(argv[0], "shstreams")) {
3236 func = list_shared_streams;
3237 } else if (!strcasecmp(argv[0], "endpoints")) {
3238 func = list_endpoints;
3239 } else {
3240 stream->write_function(stream, "Unknown Command or not enough args [%s]\n", argv[0]);
3241 }
3242
3243
3244 if (func) {
3245 if (http) {
3246 stream->write_function(stream, "<pre>");
3247 }
3248
3249 switch_mutex_lock(globals.pa_mutex);
3250 func(&argv[lead], argc - lead, stream);
3251 status = SWITCH_STATUS_SUCCESS; /*if func was defined we want to always return success as the command was found */
3252 switch_mutex_unlock(globals.pa_mutex);
3253
3254 if (http) {
3255 stream->write_function(stream, "\n\n</pre>");
3256 }
3257 }
3258
3259 done:
3260 if (http) {
3261 stream->write_function(stream,
3262 "<br><br><table align=center><tr><td><center><form method=post>\n"
3263 "<input type=text name=wcmd size=40><br><br>\n"
3264 "<input name=action type=submit value=\"call\"> "
3265 "<input name=action type=submit value=\"hangup\"> "
3266 "<input name=action type=submit value=\"list\"> "
3267 "<input name=action type=submit value=\"switch\"> "
3268 "<input name=action type=submit value=\"mute\"> "
3269 "<input name=action type=submit value=\"unmute\"> "
3270 "<input name=action type=submit value=\"indev\"> "
3271 "<input name=action type=submit value=\"outdev\"> "
3272 "<input name=action type=submit value=\"devlist\"> <br> "
3273 "<input name=action type=submit value=\"preparestream\"> "
3274 "<input name=action type=submit value=\"switchstream\"> "
3275 "<input name=action type=submit value=\"closestreams\"> "
3276 "<input name=action type=submit value=\"ringdev\"> "
3277 "<input name=action type=submit value=\"play\"> "
3278 "<input name=action type=submit value=\"playdev\"> "
3279 "<input name=action type=submit value=\"answer\"> <br><br>"
3280 "<table border=1>\n"
3281 "<tr><td><input name=action type=submit value=\"1\"></td>"
3282 "<td><input name=action type=submit value=\"2\"></td>"
3283 "<td><input name=action type=submit value=\"3\"></td>\n"
3284 "<td><input name=action type=submit value=\"A\"></td></tr>\n"
3285 "<tr><td><input name=action type=submit value=\"4\"></td>"
3286 "<td><input name=action type=submit value=\"5\"></td>"
3287 "<td><input name=action type=submit value=\"6\"></td>\n"
3288 "<td><input name=action type=submit value=\"B\"></td></tr>\n"
3289 "<tr><td><input name=action type=submit value=\"7\"></td>"
3290 "<td><input name=action type=submit value=\"8\"></td>"
3291 "<td><input name=action type=submit value=\"9\"></td>\n"
3292 "<td><input name=action type=submit value=\"C\"></td></tr>\n"
3293 "<tr><td><input name=action type=submit value=\"*\"></td>"
3294 "<td><input name=action type=submit value=\"0\"></td>"
3295 "<td><input name=action type=submit value=\"#\"></td>\n"
3296 "<td><input name=action type=submit value=\"D\"></td></tr>\n" "</table>" "</form><br></center></td></tr></table>\n");
3297 }
3298
3299 switch_safe_free(mycmd);
3300 return status;
3301 }
3302
3303
3304 /* For Emacs:
3305 * Local Variables:
3306 * mode:c
3307 * indent-tabs-mode:t
3308 * tab-width:4
3309 * c-basic-offset:4
3310 * End:
3311 * For VIM:
3312 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
3313 */
3314