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 at
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 * Cesar Cepeda <cesar@auronix.com>
28 * Emmanuel Schmidbauer <e.schmidbauer@gmail.com>
29 *
30 *
31 * mod_local_stream.c -- Local Streaming Audio
32 *
33 */
34 #include <switch.h>
35 /* for apr_pstrcat */
36 #define DEFAULT_PREBUFFER_SIZE 1024 * 64
37
38 SWITCH_MODULE_LOAD_FUNCTION(mod_local_stream_load);
39 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_local_stream_shutdown);
40 SWITCH_MODULE_DEFINITION(mod_local_stream, mod_local_stream_load, mod_local_stream_shutdown, NULL);
41
42 static int launch_streams(const char *name);
43 static void launch_thread(const char *name, const char *path, switch_xml_t directory);
44
45 static const char *global_cf = "local_stream.conf";
46
47 struct local_stream_source;
48
49 static struct {
50 switch_mutex_t *mutex;
51 switch_hash_t *source_hash;
52 } globals;
53
54 static int RUNNING = 1;
55 static int THREADS = 0;
56
57 struct local_stream_context {
58 struct local_stream_source *source;
59 switch_mutex_t *audio_mutex;
60 switch_buffer_t *audio_buffer;
61 int err;
62 const char *file;
63 const char *func;
64 int line;
65 switch_file_handle_t *handle;
66 switch_queue_t *video_q;
67 int ready;
68 int sent_png;
69 int last_w;
70 int last_h;
71 int newres;
72 int serno;
73 int pop_count;
74 int video_flushes;
75 switch_size_t blank;
76 switch_image_t *banner_img;
77 switch_time_t banner_timeout;
78 switch_memory_pool_t *pool;
79 struct local_stream_context *next;
80 };
81
82 typedef struct local_stream_context local_stream_context_t;
83
84 #define MAX_CHIME 100
85 struct local_stream_source {
86 char *name;
87 char *location;
88 uint8_t channels;
89 int rate;
90 int interval;
91 switch_size_t samples;
92 uint32_t prebuf;
93 char *timer_name;
94 local_stream_context_t *context_list;
95 int total;
96 int vol;
97 switch_agc_t *agc;
98 int energy_avg;
99 int energy_low;
100 int first;
101 switch_dir_t *dir_handle;
102 switch_mutex_t *mutex;
103 switch_memory_pool_t *pool;
104 int shuffle;
105 switch_thread_rwlock_t *rwlock;
106 int hup;
107 int ready;
108 int stopped;
109 int part_reload;
110 int full_reload;
111 int chime_freq;
112 int chime_total;
113 int chime_max;
114 int chime_cur;
115 char *chime_list[MAX_CHIME];
116 int32_t chime_counter;
117 int32_t chime_max_counter;
118 switch_file_handle_t chime_fh;
119 switch_queue_t *video_q;
120 int has_video;
121 switch_image_t *blank_img;
122 switch_image_t *logo_img;
123 switch_image_t *cover_art;
124 char *banner_txt;
125 int serno;
126 switch_size_t abuflen;
127 switch_byte_t *abuf;
128 switch_timer_t timer;
129 int logo_always;
130 switch_img_position_t logo_pos;
131 uint8_t logo_opacity;
132 uint8_t text_opacity;
133 switch_mm_t mm;
134 };
135
136 typedef struct local_stream_source local_stream_source_t;
137
get_source(const char * path)138 local_stream_source_t *get_source(const char *path)
139 {
140 local_stream_source_t *source = NULL;
141
142 switch_mutex_lock(globals.mutex);
143 if ((source = switch_core_hash_find(globals.source_hash, path))) {
144 if (!RUNNING || source->stopped || switch_thread_rwlock_tryrdlock(source->rwlock) != SWITCH_STATUS_SUCCESS) {
145 source = NULL;
146 }
147 }
148 switch_mutex_unlock(globals.mutex);
149
150 return source;
151 }
152
153
list_streams_full(const char * line,const char * cursor,switch_console_callback_match_t ** matches,switch_bool_t show_aliases)154 switch_status_t list_streams_full(const char *line, const char *cursor, switch_console_callback_match_t **matches, switch_bool_t show_aliases)
155 {
156 local_stream_source_t *source;
157 switch_hash_index_t *hi;
158 void *val;
159 const void *vvar;
160 switch_console_callback_match_t *my_matches = NULL;
161 switch_status_t status = SWITCH_STATUS_FALSE;
162
163 switch_mutex_lock(globals.mutex);
164 for (hi = switch_core_hash_first(globals.source_hash); hi; hi = switch_core_hash_next(&hi)) {
165 switch_core_hash_this(hi, &vvar, NULL, &val);
166
167 source = (local_stream_source_t *) val;
168 if (!show_aliases && strcmp((char *)vvar, source->name)) {
169 continue;
170 }
171
172 switch_console_push_match(&my_matches, (const char *) vvar);
173 }
174 switch_mutex_unlock(globals.mutex);
175
176 if (my_matches) {
177 *matches = my_matches;
178 status = SWITCH_STATUS_SUCCESS;
179 }
180
181 return status;
182 }
183
list_streams(const char * line,const char * cursor,switch_console_callback_match_t ** matches)184 switch_status_t list_streams(const char *line, const char *cursor, switch_console_callback_match_t **matches)
185 {
186 return list_streams_full(line, cursor, matches, SWITCH_TRUE);
187 }
188
do_rand(uint32_t count)189 static int do_rand(uint32_t count)
190 {
191 int r = 0;
192
193 if (count == 0) return 0;
194
195 switch_mutex_lock(globals.mutex);
196 r = (rand() % count) + 1;
197 switch_mutex_unlock(globals.mutex);
198
199 return r;
200 }
201
flush_video_queue(switch_queue_t * q)202 static void flush_video_queue(switch_queue_t *q)
203 {
204 void *pop = NULL;
205
206 if (switch_queue_size(q) == 0) {
207 return;
208 }
209
210 while (switch_queue_trypop(q, &pop) == SWITCH_STATUS_SUCCESS) {
211 if (pop) {
212 switch_image_t *img = (switch_image_t *) pop;
213 switch_img_free(&img);
214 } else {
215 break;
216 }
217 }
218
219 }
220
read_stream_thread(switch_thread_t * thread,void * obj)221 static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void *obj)
222 {
223 volatile local_stream_source_t *s = (local_stream_source_t *) obj;
224 local_stream_source_t *source = (local_stream_source_t *) s;
225 switch_file_handle_t fh = { 0 };
226 char file_buf[128] = "", path_buf[512] = "", last_path[512] = "", png_buf[512] = "", tmp_buf[512] = "";
227 int fd = -1;
228 switch_buffer_t *audio_buffer;
229 switch_byte_t *dist_buf;
230 switch_size_t used;
231 int skip = 0;
232 switch_memory_pool_t *temp_pool = NULL;
233 uint32_t dir_count = 0, do_shuffle = 0;
234 char *p;
235
236 switch_mutex_lock(globals.mutex);
237 THREADS++;
238 switch_mutex_unlock(globals.mutex);
239
240 if (!source->prebuf) {
241 source->prebuf = DEFAULT_PREBUFFER_SIZE;
242 }
243
244 if (source->shuffle) {
245 do_shuffle = 1;
246 }
247
248 if (source->prebuf < source->abuflen) {
249 source->prebuf = source->abuflen;
250 }
251
252 switch_queue_create(&source->video_q, 500, source->pool);
253 switch_buffer_create_dynamic(&audio_buffer, 1024, source->prebuf + 10, 0);
254 dist_buf = switch_core_alloc(source->pool, source->prebuf + 10);
255
256 switch_thread_rwlock_create(&source->rwlock, source->pool);
257
258 if (switch_core_timer_init(&source->timer, source->timer_name, source->interval, (int)source->samples, source->pool) != SWITCH_STATUS_SUCCESS) {
259 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Can't start timer.\n");
260 RUNNING = 0;
261 }
262
263 if (RUNNING) {
264 source->ready = 1;
265 switch_mutex_lock(globals.mutex);
266 switch_core_hash_insert(globals.source_hash, source->name, source);
267 switch_mutex_unlock(globals.mutex);
268 }
269
270 while (RUNNING && !source->stopped && source->ready) {
271 const char *fname;
272
273 if (source->dir_handle) {
274 switch_dir_close(source->dir_handle);
275 source->dir_handle = NULL;
276 }
277
278 if (temp_pool) {
279 switch_core_destroy_memory_pool(&temp_pool);
280 }
281
282 if (switch_core_new_memory_pool(&temp_pool) != SWITCH_STATUS_SUCCESS) {
283 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error creating pool");
284 goto done;
285 }
286
287 if (switch_dir_open(&source->dir_handle, source->location, temp_pool) != SWITCH_STATUS_SUCCESS) {
288 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Can't open directory: %s\n", source->location);
289 goto done;
290 }
291
292 if (fd > -1) {
293 dir_count = 0;
294 while (switch_fd_read_line(fd, path_buf, sizeof(path_buf))) {
295 dir_count++;
296 }
297 lseek(fd, 0, SEEK_SET);
298 } else {
299 dir_count = switch_dir_count(source->dir_handle);
300 }
301
302 if (do_shuffle) {
303 skip = do_rand(dir_count);
304 do_shuffle = 0;
305 }
306
307 switch_yield(1000000);
308
309 while (RUNNING && !source->stopped) {
310 switch_size_t olen;
311 const char *artist = NULL, *title = NULL;
312 char tmp_space[128] = "";
313
314 if (fd > -1) {
315 char *pb;
316 if (switch_fd_read_line(fd, path_buf, sizeof(path_buf))) {
317 if ((pb = strchr(path_buf, '\r')) || (pb = strchr(path_buf, '\n'))) {
318 *pb = '\0';
319 }
320 } else {
321 close(fd);
322 fd = -1;
323 continue;
324 }
325 } else {
326 if (!(fname = switch_dir_next_file(source->dir_handle, file_buf, sizeof(file_buf)))) {
327 break;
328 }
329
330 switch_snprintf(path_buf, sizeof(path_buf), "%s%s%s", source->location, SWITCH_PATH_SEPARATOR, fname);
331
332 if (switch_stristr(".loc", path_buf)) {
333 if ((fd = open(path_buf, O_RDONLY)) < 0) {
334 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't open %s\n", fname);
335 switch_yield(1000000);
336 }
337 continue;
338 }
339 }
340
341
342 if (dir_count > 1 && !strcmp(last_path, path_buf)) {
343 continue;
344 }
345
346 if (skip > 0) {
347 skip--;
348 continue;
349 }
350
351 switch_set_string(last_path, path_buf);
352
353 fname = path_buf;
354 fh.prebuf = source->prebuf;
355 fh.pre_buffer_datalen = source->prebuf;
356
357 if (switch_core_file_open(&fh,
358 (char *) fname,
359 source->channels, source->rate, SWITCH_FILE_FLAG_VIDEO | SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) {
360 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't open %s\n", fname);
361 switch_yield(1000000);
362 continue;
363 }
364
365 switch_buffer_zero(audio_buffer);
366
367 if (switch_core_file_has_video(&fh, SWITCH_FALSE)) {
368 flush_video_queue(source->video_q);
369 }
370
371 switch_img_free(&source->cover_art);
372 switch_set_string(tmp_buf, path_buf);
373
374 if ((p = strrchr(tmp_buf, '/'))) {
375 *p++ = '\0';
376 switch_snprintf(png_buf, sizeof(png_buf), "%s/art/%s.png", tmp_buf, p);
377 if (switch_file_exists(png_buf, temp_pool) == SWITCH_STATUS_SUCCESS) {
378 source->cover_art = switch_img_read_png(png_buf, SWITCH_IMG_FMT_I420);
379 }
380 }
381
382 source->serno++;
383 switch_safe_free(source->banner_txt);
384 title = artist = NULL;
385
386 switch_core_file_get_string(&fh, SWITCH_AUDIO_COL_STR_ARTIST, &artist);
387 switch_core_file_get_string(&fh, SWITCH_AUDIO_COL_STR_TITLE, &title);
388
389 if (!title && !artist) {
390 char *e, *p, *args[3];
391 int argc;
392
393 switch_set_string(tmp_space, path_buf);
394 p = tmp_space;
395
396 while((e = strchr(p, '/'))) {
397 *e = '\0';
398 p = e+1;
399 }
400
401 argc = switch_split(p, '-', args);
402
403 if (argc > 0) {
404 while(*args[0] == ' ') {
405 args[0]++;
406 }
407
408 while(end_of(args[0]) == ' ') {
409 end_of(args[0]) = '\0';
410 }
411
412 artist = args[0];
413
414 if (argc > 1) {
415 while(*args[1] == ' ') {
416 args[1]++;
417 }
418 while(end_of(args[1]) == ' ') {
419 end_of(args[1]) = '\0';
420 }
421 title = args[1];
422 }
423
424 if (!title) {
425 title = artist;
426 artist = NULL;
427 }
428 } else {
429 title = p;
430 artist = NULL;
431 }
432 }
433
434 if (title && (source->cover_art || switch_core_file_has_video(&fh, SWITCH_TRUE))) {
435 const char *format = "#cccccc:#333333:FreeSans.ttf:3%:";
436
437 if (artist) {
438 source->banner_txt = switch_mprintf("%s%s (%s)", format, title, artist);
439 } else {
440 source->banner_txt = switch_mprintf("%s%s", format, title);
441 }
442 }
443
444
445 while (RUNNING && !source->stopped) {
446 int is_open;
447 switch_file_handle_t *use_fh = &fh;
448
449 switch_core_timer_next(&source->timer);
450 olen = source->samples;
451
452 if (source->chime_total) {
453
454 if (source->chime_counter > 0) {
455 source->chime_counter -= (int32_t)source->samples;
456 }
457
458 if (!switch_test_flag((&source->chime_fh), SWITCH_FILE_OPEN) && source->chime_counter <= 0) {
459 char *val;
460
461 val = source->chime_list[source->chime_cur++];
462
463 if (source->chime_cur >= source->chime_total) {
464 source->chime_cur = 0;
465 }
466
467 if (switch_core_file_open(&source->chime_fh,
468 (char *) val,
469 source->channels,
470 source->rate, SWITCH_FILE_FLAG_VIDEO | SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) {
471 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't open %s\n", val);
472 }
473
474
475 if (switch_core_file_has_video(&source->chime_fh, SWITCH_FALSE)) {
476 flush_video_queue(source->video_q);
477 }
478
479 }
480
481 if (switch_test_flag((&source->chime_fh), SWITCH_FILE_OPEN)) {
482 use_fh = &source->chime_fh;
483 }
484 }
485
486 retry:
487
488 if (switch_core_file_has_video(use_fh, SWITCH_TRUE)) {
489 source->mm = use_fh->mm;
490 }
491
492 source->has_video = switch_core_file_has_video(use_fh, SWITCH_TRUE) || source->cover_art || source->banner_txt;
493
494 is_open = switch_test_flag(use_fh, SWITCH_FILE_OPEN);
495
496 if (source->hup) {
497 source->hup = 0;
498 if (is_open) {
499
500 switch_core_file_close(use_fh);
501 flush_video_queue(source->video_q);
502 switch_buffer_zero(audio_buffer);
503 if (use_fh == &source->chime_fh) {
504 source->chime_counter = source->rate * source->chime_freq;
505 switch_core_file_close(&fh);
506 use_fh = &fh;
507 }
508 goto retry;
509 }
510 }
511
512 if (is_open) {
513 int svr = 0;
514
515 if (switch_core_has_video() && switch_core_file_has_video(use_fh, SWITCH_TRUE)) {
516 switch_frame_t vid_frame = { 0 };
517
518 if (use_fh == &source->chime_fh && switch_test_flag(&fh, SWITCH_FILE_OPEN) && switch_core_file_has_video(&fh, SWITCH_TRUE)) {
519 if (switch_core_file_read_video(&fh, &vid_frame, svr) == SWITCH_STATUS_SUCCESS) {
520 switch_img_free(&vid_frame.img);
521 }
522 }
523
524 while (switch_core_file_read_video(use_fh, &vid_frame, svr) == SWITCH_STATUS_SUCCESS) {
525 if (vid_frame.img) {
526 int flush = 1;
527
528 source->has_video = 1;
529 if (source->total) {
530 if (switch_queue_trypush(source->video_q, vid_frame.img) == SWITCH_STATUS_SUCCESS) {
531 vid_frame.img = NULL;
532 flush = 0;
533 }
534 }
535
536 if (flush) {
537 switch_img_free(&vid_frame.img);
538 flush_video_queue(source->video_q);
539 }
540 }
541 }
542 } else {
543 source->has_video = 0;
544 }
545
546 if (switch_test_flag(&fh, SWITCH_FILE_OPEN) && use_fh == &source->chime_fh) {
547 olen = source->samples;
548 if (switch_core_file_read(&fh, source->abuf, &olen) != SWITCH_STATUS_SUCCESS || !olen) {
549 switch_core_file_close(&fh);
550 }
551 olen = source->samples;
552 }
553
554 switch_assert(source->abuflen >= olen * 2 * source->channels);
555
556 if (switch_core_file_read(use_fh, source->abuf, &olen) != SWITCH_STATUS_SUCCESS || !olen) {
557 switch_core_file_close(use_fh);
558 flush_video_queue(source->video_q);
559
560 if (use_fh == &source->chime_fh) {
561 source->chime_counter = source->rate * source->chime_freq;
562 } else {
563 is_open = 0;
564 }
565 } else {
566 if (use_fh == &source->chime_fh && source->chime_max) {
567 source->chime_max_counter += (int32_t)source->samples;
568 if (source->chime_max_counter >= source->chime_max) {
569 source->chime_max_counter = 0;
570 switch_core_file_close(use_fh);
571 flush_video_queue(source->video_q);
572 source->chime_counter = source->rate * source->chime_freq;
573 use_fh = &fh;
574 goto retry;
575 }
576 }
577
578 if (source->total) {
579
580 if (source->energy_avg && source->agc) {
581 switch_agc_feed(source->agc, (int16_t *)source->abuf, olen, source->channels);
582 } else if (source->vol) {
583 switch_change_sln_volume_granular((int16_t *)source->abuf, olen * source->channels, source->vol);
584 }
585
586 switch_buffer_write(audio_buffer, source->abuf, olen * 2 * source->channels);
587 } else {
588 switch_buffer_zero(audio_buffer);
589 }
590 }
591 }
592
593 used = switch_buffer_inuse(audio_buffer);
594
595 if (!used && !is_open) {
596 break;
597 }
598
599 if (!source->total) {
600 flush_video_queue(source->video_q);
601 switch_buffer_zero(audio_buffer);
602 } else if (used && (!is_open || used >= source->abuflen)) {
603 void *pop;
604 uint32_t bused = 0;
605 local_stream_context_t *cp = NULL;
606
607 switch_assert(source->abuflen <= source->prebuf);
608 used = switch_buffer_read(audio_buffer, dist_buf, source->abuflen);
609
610 switch_mutex_lock(source->mutex);
611 for (cp = source->context_list; cp && RUNNING; cp = cp->next) {
612
613 if (!cp->ready) {
614 continue;
615 }
616
617 switch_mutex_lock(cp->audio_mutex);
618
619 if (switch_test_flag(cp->handle, SWITCH_FILE_OPEN)) {
620 if (switch_test_flag(cp->handle, SWITCH_FILE_CALLBACK)) {
621 switch_mutex_unlock(cp->audio_mutex);
622 continue;
623 }
624 }
625
626 bused = (uint32_t)switch_buffer_inuse(cp->audio_buffer);
627
628 if (bused > source->samples * 768) {
629 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Flushing Stream Handle Buffer [%s() %s:%d] size: %u samples: %ld\n",
630 cp->func, cp->file, cp->line, bused, (long)source->samples);
631 switch_buffer_zero(cp->audio_buffer);
632 } else {
633 switch_buffer_write(cp->audio_buffer, dist_buf, used);
634 }
635 switch_mutex_unlock(cp->audio_mutex);
636 }
637 switch_mutex_unlock(source->mutex);
638
639
640 while (switch_queue_trypop(source->video_q, &pop) == SWITCH_STATUS_SUCCESS) {
641 switch_image_t *img;
642 switch_image_t *imgcp = NULL;
643
644 if (!pop) break;
645
646 img = (switch_image_t *) pop;
647
648 switch_mutex_lock(source->mutex);
649 if (source->context_list) {
650 if (source->total == 1) {
651 if (!switch_test_flag(source->context_list->handle, SWITCH_FILE_FLAG_VIDEO)) {
652 flush_video_queue(source->context_list->video_q);
653 } else if (switch_queue_trypush(source->context_list->video_q, img) != SWITCH_STATUS_SUCCESS) {
654 flush_video_queue(source->context_list->video_q);
655
656 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Flushing video queue\n");
657 if (++source->context_list->video_flushes > 1) {
658 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Disconnecting file\n");
659 source->context_list->ready = 0;
660 }
661 } else {
662 source->context_list->video_flushes = 0;
663 img = NULL;
664 }
665 } else {
666 for (cp = source->context_list; cp && RUNNING; cp = cp->next) {
667
668 if (!cp->ready) {
669 continue;
670 }
671
672 if (cp->video_q) {
673 imgcp = NULL;
674 switch_img_copy(img, &imgcp);
675 if (imgcp) {
676 if (!switch_test_flag(cp->handle, SWITCH_FILE_FLAG_VIDEO)) {
677 flush_video_queue(cp->video_q);
678 } else if (switch_queue_trypush(cp->video_q, imgcp) != SWITCH_STATUS_SUCCESS) {
679 flush_video_queue(cp->video_q);
680 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Flushing video queue\n");
681 if (++cp->video_flushes > 1) {
682 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Disconnecting file\n");
683 cp->ready = 0;
684 }
685 } else {
686 cp->video_flushes = 0;
687 }
688 }
689 }
690 }
691 }
692 }
693 switch_mutex_unlock(source->mutex);
694 switch_img_free(&img);
695 }
696 }
697 }
698
699 if (RUNNING && source->shuffle) {
700 skip = do_rand(dir_count);
701 }
702 }
703
704 if (source->full_reload) {
705 if (source->rwlock && switch_thread_rwlock_trywrlock(source->rwlock) != SWITCH_STATUS_SUCCESS) {
706 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Cannot stop local_stream://%s because it is in use.\n",source->name);
707 if (source->part_reload) {
708 switch_xml_t cfg, xml, directory, param;
709 if (!(xml = switch_xml_open_cfg(global_cf, &cfg, NULL))) {
710 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", global_cf);
711 }
712 if ((directory = switch_xml_find_child(cfg, "directory", "name", source->name))) {
713 for (param = switch_xml_child(directory, "param"); param; param = param->next) {
714 char *var = (char *) switch_xml_attr_soft(param, "name");
715 char *val = (char *) switch_xml_attr_soft(param, "value");
716 if (!strcasecmp(var, "shuffle")) {
717 source->shuffle = switch_true(val);
718 } else if (!strcasecmp(var, "chime-freq")) {
719 int tmp = atoi(val);
720 if (tmp > 1) {
721 source->chime_freq = tmp;
722 }
723 } else if (!strcasecmp(var, "chime-max")) {
724 int tmp = atoi(val);
725 if (tmp > 1) {
726 source->chime_max = tmp;
727 }
728 } else if (!strcasecmp(var, "chime-list")) {
729 char *list_dup = switch_core_strdup(source->pool, val);
730 source->chime_total =
731 switch_separate_string(list_dup, ',', source->chime_list, (sizeof(source->chime_list) / sizeof(source->chime_list[0])));
732 } else if (!strcasecmp(var, "interval")) {
733 int tmp = atoi(val);
734 if (SWITCH_ACCEPTABLE_INTERVAL(tmp)) {
735 source->interval = tmp;
736 } else {
737 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
738 "Interval must be multiple of 10 and less than %d, Using default of 20\n", SWITCH_MAX_INTERVAL);
739 }
740 } else if (!strcasecmp(var, "volume")) {
741 source->vol = atoi(val);
742 switch_normalize_volume_granular(source->vol);
743 } else if (!strcasecmp(var, "auto-volume")) {
744 source->energy_avg = atoi(val);
745
746 if (!(source->energy_avg > -1 && source->energy_avg <= 20000)) {
747 source->energy_avg = 0;
748 }
749 } else if (!strcasecmp(var, "auto-volume-low-point")) {
750 source->energy_low = atoi(val);
751
752 if (!(source->energy_low > -1 && source->energy_avg <= 20000)) {
753 source->energy_low = 0;
754 }
755 }
756 if (source->chime_max) {
757 source->chime_max *= source->rate;
758 }
759 if (source->chime_total) {
760 source->chime_counter = source->rate * source->chime_freq;
761 }
762 }
763 }
764 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "local_stream://%s partially reloaded.\n",source->name);
765 source->part_reload = 0;
766 source->full_reload = 0;
767
768 if (xml) {
769 switch_xml_free(xml);
770 }
771 }
772 } else {
773 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "local_stream://%s fully reloaded.\n",source->name);
774 switch_thread_rwlock_unlock(source->rwlock);
775 source->full_reload = 0;
776 source->part_reload = 0;
777 if (source->timer.interval) {
778 switch_core_timer_destroy(&source->timer);
779 }
780 launch_streams(source->name);
781 goto done;
782 }
783 }
784 }
785
786 done:
787
788 if (source->dir_handle) {
789 switch_dir_close(source->dir_handle);
790 source->dir_handle = NULL;
791 }
792
793 if (source->timer.interval) {
794 switch_core_timer_destroy(&source->timer);
795 }
796
797 switch_safe_free(source->banner_txt);
798
799 if (switch_test_flag((&fh), SWITCH_FILE_OPEN)) {
800 switch_core_file_close(&fh);
801 }
802
803 if (switch_test_flag((&source->chime_fh), SWITCH_FILE_OPEN)) {
804 switch_core_file_close(&source->chime_fh);
805 }
806
807 switch_img_free(&source->blank_img);
808 switch_img_free(&source->logo_img);
809
810 source->ready = 0;
811 switch_mutex_lock(globals.mutex);
812 switch_core_hash_delete(globals.source_hash, source->name);
813 switch_mutex_unlock(globals.mutex);
814
815 if (source->agc) {
816 switch_agc_destroy(&source->agc);
817 }
818
819 switch_thread_rwlock_wrlock(source->rwlock);
820 switch_thread_rwlock_unlock(source->rwlock);
821
822 switch_buffer_destroy(&audio_buffer);
823
824 flush_video_queue(source->video_q);
825
826 if (fd > -1) {
827 close(fd);
828 }
829
830 if (temp_pool) {
831 switch_core_destroy_memory_pool(&temp_pool);
832 }
833
834 switch_core_destroy_memory_pool(&source->pool);
835
836 switch_mutex_lock(globals.mutex);
837 THREADS--;
838 switch_mutex_unlock(globals.mutex);
839
840 return NULL;
841 }
842
local_stream_file_open(switch_file_handle_t * handle,const char * path)843 static switch_status_t local_stream_file_open(switch_file_handle_t *handle, const char *path)
844 {
845 local_stream_context_t *context;
846 local_stream_source_t *source;
847 char *alt_path = NULL;
848 switch_status_t status = SWITCH_STATUS_SUCCESS;
849 switch_memory_pool_t *pool;
850
851 /* already buffering a step back, so always disable it */
852 handle->pre_buffer_datalen = 0;
853
854 if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
855 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This format does not support writing!\n");
856 return SWITCH_STATUS_FALSE;
857 }
858
859 top:
860
861 alt_path = switch_mprintf("%s/%d", path, handle->samplerate);
862
863 if ((source = get_source(alt_path))) {
864 path = alt_path;
865 } else {
866 source = get_source(path);
867 }
868
869
870 if (!source) {
871 if (!switch_stristr("default", alt_path) && !switch_stristr("default", path)) {
872 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown source %s, trying 'default'\n", path);
873 free(alt_path);
874 path = "default";
875 goto top;
876 }
877 }
878
879 if (!source) {
880 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown source %s\n", path);
881 status = SWITCH_STATUS_FALSE;
882 goto end;
883 }
884
885 //if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
886 // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n");
887 // abort();
888 //}
889
890 pool = handle->memory_pool;
891
892 if ((context = switch_core_alloc(pool, sizeof(*context))) == 0) {
893 abort();
894 }
895
896 context->pool = pool;
897
898 switch_queue_create(&context->video_q, 40, context->pool);
899
900 handle->samples = 0;
901 handle->samplerate = source->rate;
902 handle->channels = source->channels;
903 handle->format = 0;
904 handle->sections = 0;
905 handle->seekable = 0;
906 handle->speed = 0;
907 handle->private_info = context;
908 handle->interval = source->interval;
909 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Opening Stream [%s] %dhz\n", path, handle->samplerate);
910
911 switch_mutex_init(&context->audio_mutex, SWITCH_MUTEX_NESTED, context->pool);
912 if (switch_buffer_create_dynamic(&context->audio_buffer, 512, 1024, 0) != SWITCH_STATUS_SUCCESS) {
913 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n");
914 status = SWITCH_STATUS_MEMERR;
915 goto end;
916 }
917
918 if (!switch_core_has_video() || !source->has_video ||
919 (switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO) && !source->has_video && !source->blank_img && !source->cover_art && !source->banner_txt)) {
920 switch_clear_flag_locked(handle, SWITCH_FILE_FLAG_VIDEO);
921 }
922
923 context->source = source;
924 context->file = handle->file;
925 context->func = handle->func;
926 context->line = handle->line;
927 context->handle = handle;
928 context->ready = 1;
929 switch_mutex_lock(source->mutex);
930 context->next = source->context_list;
931 source->context_list = context;
932 source->total++;
933 if (source->total == 1) {
934 source->first = 1;
935 }
936 switch_mutex_unlock(source->mutex);
937
938 end:
939
940 switch_safe_free(alt_path);
941 return status;
942 }
943
local_stream_file_close(switch_file_handle_t * handle)944 static switch_status_t local_stream_file_close(switch_file_handle_t *handle)
945 {
946 local_stream_context_t *context = NULL, *last = NULL, *cp = NULL;
947 local_stream_source_t *source;
948
949 context = handle->private_info;
950 switch_assert(context);
951
952 //pool = context->pool;
953 source = context->source;
954
955 switch_mutex_lock(source->mutex);
956 switch_clear_flag_locked(handle, SWITCH_FILE_OPEN);
957 context->ready = 0;
958
959 for (cp = source->context_list; cp; cp = cp->next) {
960 if (cp == context) {
961 if (last) {
962 last->next = cp->next;
963 } else {
964 source->context_list = cp->next;
965 }
966 break;
967 }
968 last = cp;
969 }
970
971 switch_mutex_lock(context->audio_mutex);
972
973 if (source->has_video) {
974 flush_video_queue(context->video_q);
975 switch_queue_trypush(context->video_q, NULL);
976 switch_queue_interrupt_all(context->video_q);
977 flush_video_queue(context->video_q);
978 }
979
980 source->total--;
981
982 switch_img_free(&context->banner_img);
983 switch_buffer_destroy(&context->audio_buffer);
984 switch_mutex_unlock(context->audio_mutex);
985 //switch_core_destroy_memory_pool(&pool);
986
987 context->handle = NULL;
988 handle->private_info = NULL;
989 switch_mutex_unlock(source->mutex);
990
991 switch_thread_rwlock_unlock(source->rwlock);
992
993 return SWITCH_STATUS_SUCCESS;
994 }
995
local_stream_file_read_video(switch_file_handle_t * handle,switch_frame_t * frame,switch_video_read_flag_t flags)996 static switch_status_t local_stream_file_read_video(switch_file_handle_t *handle, switch_frame_t *frame, switch_video_read_flag_t flags)
997 {
998 void *pop;
999 local_stream_context_t *context = handle->private_info;
1000 switch_status_t status;
1001 switch_time_t now;
1002 unsigned int fps = (unsigned int)ceil(handle->mm.fps);
1003 unsigned int min_qsize = fps / 2;
1004 unsigned int buf_qsize = 5;
1005
1006 if (!(context->ready && context->source->ready)) {
1007 return SWITCH_STATUS_FALSE;
1008 }
1009
1010 if (!context->source->has_video) {
1011 if (frame) {
1012 switch_image_t *src_img = context->source->cover_art;
1013
1014 if (!src_img) {
1015 src_img = context->source->blank_img;
1016 }
1017
1018 if (src_img) {
1019 switch_image_t *img = NULL;
1020
1021 if (context->sent_png && --context->sent_png > 0) {
1022 return SWITCH_STATUS_BREAK;
1023 }
1024
1025 context->sent_png = 50;
1026 switch_img_copy(src_img, &img);
1027
1028 if (context->last_w && context->last_h) {
1029 switch_img_fit(&img, context->last_w, context->last_h, SWITCH_FIT_SIZE);
1030 }
1031
1032 frame->img = img;
1033 goto got_img;
1034 }
1035 }
1036 return SWITCH_STATUS_IGNORE;
1037 }
1038
1039 if ((flags & SVR_CHECK)) {
1040 return SWITCH_STATUS_BREAK;
1041 }
1042
1043 if (handle->mm.fps >= context->source->mm.source_fps) {
1044 min_qsize = 1;
1045 buf_qsize = 1;
1046 }
1047
1048 while(context->ready && context->source->ready && switch_queue_size(context->video_q) > min_qsize) {
1049 if (switch_queue_trypop(context->video_q, &pop) == SWITCH_STATUS_SUCCESS) {
1050 switch_image_t *img = (switch_image_t *) pop;
1051 switch_img_free(&img);
1052 }
1053 }
1054
1055 if (!(context->ready && context->source->ready)) {
1056 return SWITCH_STATUS_FALSE;
1057 }
1058
1059 while (!(flags & SVR_BLOCK) && switch_queue_size(context->video_q) < buf_qsize) {
1060 return SWITCH_STATUS_BREAK;
1061 }
1062
1063 if ((flags & SVR_BLOCK)) {
1064 status = switch_queue_pop(context->video_q, &pop);
1065 } else {
1066 status = switch_queue_trypop(context->video_q, &pop);
1067 }
1068
1069 if (status == SWITCH_STATUS_SUCCESS) {
1070 if (!pop) {
1071 return SWITCH_STATUS_FALSE;
1072 }
1073
1074 frame->img = (switch_image_t *) pop;
1075 context->sent_png = 0;
1076 if (frame->img->d_w != context->last_w || frame->img->d_h != context->last_h) {
1077 context->newres = 1;
1078 }
1079 context->last_w = frame->img->d_w;
1080 context->last_h = frame->img->d_h;
1081 goto got_img;
1082 }
1083
1084 return (flags & SVR_FLUSH) ? SWITCH_STATUS_BREAK : status;
1085
1086 got_img:
1087
1088 if (context->pop_count > 0) {
1089 switch_rgb_color_t bgcolor = { 0 };
1090 switch_color_set_rgb(&bgcolor, "#000000");
1091 switch_img_fill(frame->img, 0, 0, frame->img->d_w, frame->img->d_h, &bgcolor);
1092 context->pop_count--;
1093 }
1094
1095 now = switch_micro_time_now();
1096
1097 if (context->banner_img) {
1098 if (now >= context->banner_timeout) {
1099 switch_img_free(&context->banner_img);
1100 }
1101 }
1102
1103 if (context->serno != context->source->serno) {
1104 switch_img_free(&context->banner_img);
1105 context->banner_timeout = 0;
1106 context->serno = context->source->serno;
1107 context->pop_count = 5;
1108 }
1109
1110 if (context->source->banner_txt) {
1111 if (!context->banner_timeout || context->banner_timeout >= now) {
1112 if (context->newres) {
1113 switch_img_free(&context->banner_img);
1114 context->newres = 0;
1115 }
1116 if (!context->banner_img) {
1117 context->banner_img = switch_img_write_text_img(context->last_w, context->last_h, SWITCH_TRUE, context->source->banner_txt);
1118 context->banner_timeout = now + 5000000;
1119 }
1120 }
1121 } else {
1122 if (context->banner_img) {
1123 switch_img_free(&context->banner_img);
1124 }
1125 context->banner_timeout = 0;
1126 }
1127
1128 if (frame->img && context->banner_img && frame->img->d_w >= context->banner_img->d_w) {
1129 switch_img_overlay(frame->img, context->banner_img, 0, frame->img->d_h - context->banner_img->d_h, context->source->text_opacity);
1130 }
1131
1132 if (frame->img && context->source->logo_img &&
1133 (context->source->logo_always || context->banner_img) && frame->img->d_w >= context->source->logo_img->d_w) {
1134 int x = 0, y = 0;
1135
1136 switch_img_find_position(context->source->logo_pos,
1137 frame->img->d_w, frame->img->d_h,
1138 context->source->logo_img->d_w, context->source->logo_img->d_h,
1139 &x, &y);
1140
1141 if (context->banner_img) {
1142 y -= context->banner_img->d_h;
1143 }
1144
1145 switch_img_overlay(frame->img, context->source->logo_img, x, y, context->source->logo_opacity);
1146 }
1147
1148 return SWITCH_STATUS_SUCCESS;
1149 }
1150
local_stream_file_read(switch_file_handle_t * handle,void * data,size_t * len)1151 static switch_status_t local_stream_file_read(switch_file_handle_t *handle, void *data, size_t *len)
1152 {
1153 local_stream_context_t *context = handle->private_info;
1154 switch_size_t bytes = 0;
1155 size_t need;
1156
1157 if (!(context->ready && context->source->ready)) {
1158 *len = 0;
1159 return SWITCH_STATUS_FALSE;
1160 }
1161
1162 if (!context->source->has_video) {
1163 if (switch_test_flag(handle, SWITCH_FILE_FLAG_VIDEO)) {
1164 switch_clear_flag_locked(handle, SWITCH_FILE_FLAG_VIDEO);
1165 }
1166 }
1167
1168 switch_mutex_lock(context->audio_mutex);
1169 need = *len * 2 * context->source->channels;
1170
1171 if ((bytes = switch_buffer_read(context->audio_buffer, data, need))) {
1172 *len = bytes / 2 / context->source->channels;
1173 } else {
1174 size_t blank;
1175
1176 switch_assert(handle->samplerate <= 48000);
1177 switch_assert(handle->real_channels <= 2);
1178
1179 blank = (handle->samplerate / 4) * 2 * handle->real_channels;
1180
1181 if (need > blank) {
1182 need = blank;
1183 }
1184
1185 memset(data, 0, need);
1186 *len = need / 2 / context->source->channels;
1187 }
1188 switch_mutex_unlock(context->audio_mutex);
1189 handle->sample_count += *len;
1190
1191 return SWITCH_STATUS_SUCCESS;
1192 }
1193
1194 /* Registration */
1195
1196 static char *supported_formats[SWITCH_MAX_CODECS] = { 0 };
1197
launch_thread(const char * name,const char * path,switch_xml_t directory)1198 static void launch_thread(const char *name, const char *path, switch_xml_t directory)
1199 {
1200 local_stream_source_t *source = NULL;
1201 switch_memory_pool_t *pool;
1202 switch_xml_t param;
1203 switch_thread_t *thread;
1204 switch_threadattr_t *thd_attr = NULL;
1205
1206 if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
1207 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n");
1208 abort();
1209 }
1210 source = switch_core_alloc(pool, sizeof(*source));
1211 assert(source != NULL);
1212 source->pool = pool;
1213
1214 source->name = switch_core_strdup(source->pool, name);
1215 source->location = switch_core_strdup(source->pool, path);
1216 source->rate = 8000;
1217 source->interval = 20;
1218 source->channels = 1;
1219 source->timer_name = "soft";
1220 source->prebuf = DEFAULT_PREBUFFER_SIZE;
1221 source->stopped = 0;
1222 source->hup = 0;
1223 source->chime_freq = 30;
1224 source->logo_opacity = source->text_opacity = 100;
1225
1226 for (param = switch_xml_child(directory, "param"); param; param = param->next) {
1227 char *var = (char *) switch_xml_attr_soft(param, "name");
1228 char *val = (char *) switch_xml_attr_soft(param, "value");
1229
1230 if (!strcasecmp(var, "rate")) {
1231 int tmp = atoi(val);
1232 if (tmp == 8000 || tmp == 12000 || tmp == 16000 || tmp == 24000 || tmp == 32000 || tmp == 48000) {
1233 source->rate = tmp;
1234 }
1235 } else if (!strcasecmp(var, "shuffle")) {
1236 source->shuffle = switch_true(val);
1237 } else if (!strcasecmp(var, "prebuf")) {
1238 int tmp = atoi(val);
1239 if (tmp > 0) {
1240 source->prebuf = (uint32_t) tmp;
1241 }
1242 } else if (!strcasecmp(var, "channels")) {
1243 int tmp = atoi(val);
1244 if (tmp == 1 || tmp == 2) {
1245 source->channels = (uint8_t) tmp;
1246 }
1247 } else if (!strcasecmp(var, "chime-freq")) {
1248 int tmp = atoi(val);
1249 if (tmp > 1) {
1250 source->chime_freq = tmp;
1251 }
1252 } else if (!strcasecmp(var, "chime-max")) {
1253 int tmp = atoi(val);
1254 if (tmp > 1) {
1255 source->chime_max = tmp;
1256 }
1257 } else if (!strcasecmp(var, "chime-list")) {
1258 char *list_dup = switch_core_strdup(source->pool, val);
1259 source->chime_total =
1260 switch_separate_string(list_dup, ',', source->chime_list, (sizeof(source->chime_list) / sizeof(source->chime_list[0])));
1261 } else if (!strcasecmp(var, "interval")) {
1262 int tmp = atoi(val);
1263 if (SWITCH_ACCEPTABLE_INTERVAL(tmp)) {
1264 source->interval = tmp;
1265 } else {
1266 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
1267 "Interval must be multiple of 10 and less than %d, Using default of 20\n", SWITCH_MAX_INTERVAL);
1268 }
1269 } else if (!strcasecmp(var, "timer-name")) {
1270 source->timer_name = switch_core_strdup(source->pool, val);
1271 } else if (!strcasecmp(var, "blank-img") && !zstr(val)) {
1272 source->blank_img = switch_img_read_png(val, SWITCH_IMG_FMT_I420);
1273 } else if (!strcasecmp(var, "logo-img") && !zstr(val)) {
1274 source->logo_img = switch_img_read_png(val, SWITCH_IMG_FMT_ARGB);
1275 } else if (!strcasecmp(var, "logo-always") && !zstr(val)) {
1276 source->logo_always = switch_true(val);
1277 } else if (!strcasecmp(var, "logo-position") && !zstr(val)) {
1278 source->logo_pos = parse_img_position(val);
1279 } else if (!strcasecmp(var, "logo-opacity") && !zstr(val)) {
1280 source->logo_opacity = atoi(val);
1281 if (source->logo_opacity < 0 || source->logo_opacity > 100) {
1282 source->logo_opacity = 0;
1283 }
1284 } else if (!strcasecmp(var, "text-opacity") && !zstr(val)) {
1285 source->text_opacity = atoi(val);
1286 if (source->text_opacity < 0 || source->text_opacity > 100) {
1287 source->text_opacity = 0;
1288 }
1289 } else if (!strcasecmp(var, "volume")) {
1290 source->vol = atoi(val);
1291 switch_normalize_volume_granular(source->vol);
1292 } else if (!strcasecmp(var, "auto-volume")) {
1293 source->energy_avg = atoi(val);
1294
1295 if (!(source->energy_avg > -1 && source->energy_avg <= 20000)) {
1296 source->energy_avg = 0;
1297 }
1298 } else if (!strcasecmp(var, "auto-volume-low-point")) {
1299 source->energy_low = atoi(val);
1300
1301 if (!(source->energy_low > -1 && source->energy_avg <= 20000)) {
1302 source->energy_low = 0;
1303 }
1304 }
1305 }
1306
1307 if (source->chime_max) {
1308 source->chime_max *= source->rate;
1309 }
1310
1311 if (source->chime_total) {
1312 source->chime_counter = source->rate * source->chime_freq;
1313 }
1314
1315 source->samples = switch_samples_per_packet(source->rate, source->interval);
1316 source->abuflen = (source->samples * 2 * source->channels);
1317 source->abuf = switch_core_alloc(source->pool, source->abuflen + 1024);
1318 switch_mutex_init(&source->mutex, SWITCH_MUTEX_NESTED, source->pool);
1319 switch_threadattr_create(&thd_attr, source->pool);
1320 switch_threadattr_detach_set(thd_attr, 1);
1321 switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
1322 switch_thread_create(&thread, thd_attr, read_stream_thread, source, source->pool);
1323 }
1324
launch_streams(const char * name)1325 static int launch_streams(const char *name)
1326 {
1327 switch_xml_t cfg, xml, directory;
1328 int x = 0;
1329
1330 if (!(xml = switch_xml_open_cfg(global_cf, &cfg, NULL))) {
1331 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", global_cf);
1332 return 0;
1333 }
1334
1335 if (zstr(name)) {
1336 for (directory = switch_xml_child(cfg, "directory"); directory; directory = directory->next) {
1337 char *name_attr = (char *) switch_xml_attr(directory, "name");
1338 char *path = (char *) switch_xml_attr(directory, "path");
1339 launch_thread(name_attr, path, directory);
1340 x++;
1341 }
1342 } else if ((directory = switch_xml_find_child(cfg, "directory", "name", name))) {
1343 char *path = (char *) switch_xml_attr(directory, "path");
1344 launch_thread(name, path, directory);
1345 x++;
1346 }
1347 switch_xml_free(xml);
1348
1349 return x;
1350 }
1351
event_handler(switch_event_t * event)1352 static void event_handler(switch_event_t *event)
1353 {
1354 RUNNING = 0;
1355 }
1356
1357 #define LOCAL_STREAM_SYNTAX "<show|start|reload|stop|hup> <local_stream_name>"
SWITCH_STANDARD_API(local_stream_function)1358 SWITCH_STANDARD_API(local_stream_function)
1359 {
1360 local_stream_source_t *source = NULL;
1361 char *mycmd = NULL, *argv[5] = { 0 };
1362 char *local_stream_name = NULL;
1363 int argc = 0;
1364 int ok = 0;
1365
1366 if (zstr(cmd)) {
1367 goto usage;
1368 }
1369
1370 if (!(mycmd = strdup(cmd))) {
1371 goto usage;
1372 }
1373
1374 if ((argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) < 1) {
1375 goto usage;
1376 }
1377
1378 local_stream_name = argv[1];
1379
1380
1381 if (!strcasecmp(argv[0], "hup") && local_stream_name) {
1382 if ((source = get_source(local_stream_name))) {
1383 source->hup = 1;
1384 stream->write_function(stream, "+OK hup stream: %s", source->name);
1385 switch_thread_rwlock_unlock(source->rwlock);
1386 }
1387 } else if (!strcasecmp(argv[0], "vol") && local_stream_name) {
1388 if ((source = get_source(local_stream_name))) {
1389 if (argv[2]) {
1390 if (!strncasecmp(argv[2], "auto:", 5)) {
1391 char *p;
1392 source->energy_avg = atoi(argv[2] + 5);
1393
1394 if ((p = strchr(argv[2] + 5, ':'))) {
1395 int tmp = 0;
1396 p++;
1397 tmp = atoi(p);
1398
1399 if ((tmp > -1 && tmp <= 20000)) {
1400 source->energy_low = tmp;
1401 } else {
1402 stream->write_function(stream, "-ERR invalid auto-volume low-energy level for stream: %s\n", source->name);
1403 }
1404 }
1405
1406 if (!(source->energy_avg > -1 && source->energy_avg <= 20000)) {
1407 source->energy_avg = 0;
1408 stream->write_function(stream, "-ERR invalid auto-volume level for stream: %s\n", source->name);
1409 } else {
1410 if (!source->agc) {
1411 switch_agc_create(&source->agc, source->energy_avg, source->energy_low, 500, 3, (1000 / source->interval) * 2);
1412 } else {
1413 switch_agc_set_energy_avg(source->agc, source->energy_avg);
1414 switch_agc_set_energy_low(source->agc, source->energy_low);
1415 }
1416 }
1417 } else {
1418 source->vol = atoi(argv[2]);
1419 switch_normalize_volume_granular(source->vol);
1420 if (source->agc) {
1421 switch_agc_destroy(&source->agc);
1422 }
1423 source->energy_avg = 0;
1424 }
1425 }
1426
1427 if (source->energy_avg) {
1428 stream->write_function(stream, "+OK Auto-Volume stream: %s is %d/%d", source->name, source->energy_avg, source->energy_low);
1429 } else {
1430 stream->write_function(stream, "+OK vol stream: %s is %d", source->name, source->vol);
1431 }
1432 switch_thread_rwlock_unlock(source->rwlock);
1433 }
1434 } else if (!strcasecmp(argv[0], "stop") && local_stream_name) {
1435 if ((source = get_source(local_stream_name))) {
1436 source->stopped = 1;
1437 stream->write_function(stream, "+OK");
1438 switch_thread_rwlock_unlock(source->rwlock);
1439 } else {
1440 stream->write_function(stream, "-ERR Cannot locate local_stream %s!\n", local_stream_name);
1441 }
1442 } else if (!strcasecmp(argv[0], "reload") && local_stream_name) {
1443 if ((source = get_source(local_stream_name))) {
1444 source->full_reload = 1;
1445 source->part_reload = 1;
1446 source->hup = 1;
1447 stream->write_function(stream, "+OK");
1448 switch_thread_rwlock_unlock(source->rwlock);
1449 } else {
1450 stream->write_function(stream, "-ERR Cannot locate local_stream %s!\n", local_stream_name);
1451 }
1452 } else if (!strcasecmp(argv[0], "start") && local_stream_name) {
1453 if ((source = get_source(local_stream_name))) {
1454 source->stopped = 0;
1455 stream->write_function(stream, "+OK stream: %s", source->name);
1456 switch_thread_rwlock_unlock(source->rwlock);
1457 } else {
1458 if ((ok = launch_streams(local_stream_name))) {
1459 stream->write_function(stream, "+OK stream: %s", local_stream_name);
1460 }
1461 }
1462
1463 } else if (!strcasecmp(argv[0], "show")) {
1464 switch_hash_index_t *hi;
1465 const void *var;
1466 void *val;
1467 switch_bool_t xml = SWITCH_FALSE;
1468
1469 if (argc == 1) {
1470 switch_mutex_lock(globals.mutex);
1471 for (hi = switch_core_hash_first(globals.source_hash); hi; hi = switch_core_hash_next(&hi)) {
1472 switch_core_hash_this(hi, &var, NULL, &val);
1473 if ((source = (local_stream_source_t *) val)) {
1474 stream->write_function(stream, "%s,%s\n", source->name, source->location);
1475 }
1476 }
1477 switch_mutex_unlock(globals.mutex);
1478 } else {
1479 if (argc == 4 && !strcasecmp("xml", argv[3])) {
1480 xml = SWITCH_TRUE;
1481 }
1482
1483 if ((source = get_source(local_stream_name))) {
1484 if (xml) {
1485 stream->write_function(stream, "<?xml version=\"1.0\"?>\n<local_stream name=\"%s\">\n", source->name);
1486 stream->write_function(stream, " <location>%s</location>\n", source->location);
1487 stream->write_function(stream, " <channels>%d</channels>\n", source->channels);
1488 stream->write_function(stream, " <rate>%d</rate>\n", source->rate);
1489 stream->write_function(stream, " <interval>%d<interval>\n", source->interval);
1490 stream->write_function(stream, " <samples>%d</samples>\n", source->samples);
1491 stream->write_function(stream, " <prebuf>%d</prebuf>\n", source->prebuf);
1492 stream->write_function(stream, " <timer>%s</timer>\n", source->timer_name);
1493 stream->write_function(stream, " <total>%d</total>\n", source->total);
1494 stream->write_function(stream, " <shuffle>%s</shuffle>\n", (source->shuffle) ? "true" : "false");
1495 stream->write_function(stream, " <ready>%s</ready>\n", (source->ready) ? "true" : "false");
1496 stream->write_function(stream, " <stopped>%s</stopped>\n", (source->stopped) ? "true" : "false");
1497 stream->write_function(stream, "</local_stream>\n");
1498 } else {
1499 stream->write_function(stream, "%s\n", source->name);
1500 stream->write_function(stream, " location: %s\n", source->location);
1501 stream->write_function(stream, " channels: %d\n", source->channels);
1502 stream->write_function(stream, " rate: %d\n", source->rate);
1503 stream->write_function(stream, " interval: %d\n", source->interval);
1504 stream->write_function(stream, " samples: %d\n", source->samples);
1505 stream->write_function(stream, " prebuf: %d\n", source->prebuf);
1506 stream->write_function(stream, " timer: %s\n", source->timer_name);
1507 stream->write_function(stream, " total: %d\n", source->total);
1508 stream->write_function(stream, " shuffle: %s\n", (source->shuffle) ? "true" : "false");
1509 stream->write_function(stream, " ready: %s\n", (source->ready) ? "true" : "false");
1510 stream->write_function(stream, " stopped: %s\n", (source->stopped) ? "true" : "false");
1511 stream->write_function(stream, " reloading: %s\n", (source->full_reload) ? "true" : "false");
1512 }
1513 switch_thread_rwlock_unlock(source->rwlock);
1514 } else {
1515 stream->write_function(stream, "-ERR Cannot locate local_stream %s!\n", local_stream_name);
1516 }
1517 }
1518 }
1519
1520 goto done;
1521
1522 usage:
1523 stream->write_function(stream, "-USAGE: %s\n", LOCAL_STREAM_SYNTAX);
1524
1525 done:
1526
1527 switch_safe_free(mycmd);
1528 return SWITCH_STATUS_SUCCESS;
1529 }
1530
SWITCH_MODULE_LOAD_FUNCTION(mod_local_stream_load)1531 SWITCH_MODULE_LOAD_FUNCTION(mod_local_stream_load)
1532 {
1533 switch_api_interface_t *commands_api_interface;
1534 switch_file_interface_t *file_interface;
1535
1536 supported_formats[0] = "local_stream";
1537
1538
1539 memset(&globals, 0, sizeof(globals));
1540 switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, pool);
1541 switch_core_hash_init(&globals.source_hash);
1542 if (!launch_streams(NULL)) {
1543 return SWITCH_STATUS_GENERR;
1544 }
1545
1546 *module_interface = switch_loadable_module_create_module_interface(pool, modname);
1547 file_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE);
1548 file_interface->interface_name = modname;
1549 file_interface->extens = supported_formats;
1550 file_interface->file_open = local_stream_file_open;
1551 file_interface->file_close = local_stream_file_close;
1552 file_interface->file_read = local_stream_file_read;
1553
1554 if (switch_core_has_video()) {
1555 file_interface->file_read_video = local_stream_file_read_video;
1556 }
1557
1558 if (switch_event_bind(modname, SWITCH_EVENT_SHUTDOWN, SWITCH_EVENT_SUBCLASS_ANY, event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
1559 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind event handler!\n");
1560 }
1561
1562 SWITCH_ADD_API(commands_api_interface, "local_stream", "manage local streams", local_stream_function, LOCAL_STREAM_SYNTAX);
1563 // switch_console_set_complete("add sofia profile ::sofia::list_profiles start");
1564 switch_console_set_complete("add local_stream show ::console::list_streams as xml");
1565 switch_console_set_complete("add local_stream start");
1566 switch_console_set_complete("add local_stream reload ::console::list_streams");
1567 switch_console_set_complete("add local_stream stop ::console::list_streams");
1568 switch_console_set_complete("add local_stream hup ::console::list_streams");
1569 switch_console_add_complete_func("::console::list_streams", list_streams);
1570 /* indicate that the module should continue to be loaded */
1571
1572 return SWITCH_STATUS_SUCCESS;
1573 }
1574
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_local_stream_shutdown)1575 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_local_stream_shutdown)
1576 {
1577 RUNNING = 0;
1578 switch_event_unbind_callback(event_handler);
1579
1580 while (THREADS > 0) {
1581 switch_yield(100000);
1582 }
1583
1584 switch_core_hash_destroy(&globals.source_hash);
1585 return SWITCH_STATUS_SUCCESS;
1586 }
1587
1588 /* For Emacs:
1589 * Local Variables:
1590 * mode:c
1591 * indent-tabs-mode:t
1592 * tab-width:4
1593 * c-basic-offset:4
1594 * End:
1595 * For VIM:
1596 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
1597 */
1598