diff --git a/src/mod/formats/mod_local_stream/mod_local_stream.c b/src/mod/formats/mod_local_stream/mod_local_stream.c index 8402b2bfcb..040d0e5bc2 100644 --- a/src/mod/formats/mod_local_stream/mod_local_stream.c +++ b/src/mod/formats/mod_local_stream/mod_local_stream.c @@ -25,6 +25,7 @@ * * Anthony Minessale II * Cesar Cepeda + * Emmanuel Schmidbauer * * * mod_local_stream.c -- Local Streaming Audio @@ -38,6 +39,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_local_stream_load); SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_local_stream_shutdown); SWITCH_MODULE_DEFINITION(mod_local_stream, mod_local_stream_load, mod_local_stream_shutdown, NULL); +static void launch_streams(const char *name); +static void launch_thread(const char *name, const char *path, switch_xml_t directory); + +static const char *global_cf = "local_stream.conf"; struct local_stream_source; @@ -82,6 +87,9 @@ struct local_stream_source { switch_thread_rwlock_t *rwlock; int ready; int stopped; + int stop_request; + int part_reload; + int full_reload; int chime_freq; int chime_total; int chime_max; @@ -315,6 +323,64 @@ static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void switch_dir_close(source->dir_handle); source->dir_handle = NULL; + + if(source->stop_request||source->full_reload) { + if (source->rwlock && switch_thread_rwlock_trywrlock(source->rwlock) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Cannot stop local_stream://%s because it is in use.\n",source->name); + if (source->part_reload) { + switch_xml_t cfg, xml, directory, param; + if (!(xml = switch_xml_open_cfg(global_cf, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", global_cf); + } + if ((directory = switch_xml_find_child(cfg, "directory", "name", source->name))) { + for (param = switch_xml_child(directory, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + if (!strcasecmp(var, "shuffle")) { + source->shuffle = switch_true(val); + } else if (!strcasecmp(var, "chime-freq")) { + int tmp = atoi(val); + if (tmp > 1) { + source->chime_freq = tmp; + } + } else if (!strcasecmp(var, "chime-max")) { + int tmp = atoi(val); + if (tmp > 1) { + source->chime_max = tmp; + } + } else if (!strcasecmp(var, "chime-list")) { + char *list_dup = switch_core_strdup(source->pool, val); + source->chime_total = + switch_separate_string(list_dup, ',', source->chime_list, (sizeof(source->chime_list) / sizeof(source->chime_list[0]))); + } else if (!strcasecmp(var, "interval")) { + int tmp = atoi(val); + if (SWITCH_ACCEPTABLE_INTERVAL(tmp)) { + source->interval = tmp; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, + "Interval must be multiple of 10 and less than %d, Using default of 20\n", SWITCH_MAX_INTERVAL); + } + } + if (source->chime_max) { + source->chime_max *= source->rate; + } + if (source->chime_total) { + source->chime_counter = source->rate * source->chime_freq; + } + } + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "local_stream://%s partially reloaded.\n",source->name); + source->part_reload = 0; + } + } else if(source->stop_request) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "local_stream://%s stopped.\n",source->name); + source->stopped = 1; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "local_stream://%s fully reloaded.\n",source->name); + launch_streams(source->name); + goto done; + } + } } done: @@ -493,114 +559,113 @@ static switch_status_t local_stream_file_read(switch_file_handle_t *handle, void static char *supported_formats[SWITCH_MAX_CODECS] = { 0 }; -static void launch_threads(void) +static void launch_thread(const char *name, const char *path, switch_xml_t directory) { - char *cf = "local_stream.conf"; - switch_xml_t cfg, xml, directory, param; + local_stream_source_t *source = NULL; switch_memory_pool_t *pool; - local_stream_source_t *source; + switch_xml_t param; switch_thread_t *thread; switch_threadattr_t *thd_attr = NULL; - if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf); - return; + if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n"); + abort(); } + source = switch_core_alloc(pool, sizeof(*source)); + assert(source != NULL); + source->pool = pool; - for (directory = switch_xml_child(cfg, "directory"); directory; directory = directory->next) { - char *path = (char *) switch_xml_attr(directory, "path"); - char *name = (char *) switch_xml_attr(directory, "name"); + source->name = switch_core_strdup(source->pool, name); + source->location = switch_core_strdup(source->pool, path); + source->rate = 8000; + source->interval = 20; + source->channels = 1; + source->timer_name = "soft"; + source->prebuf = DEFAULT_PREBUFFER_SIZE; + source->stopped = 0; + source->chime_freq = 30; + for (param = switch_xml_child(directory, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); - if (!(name && path)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid config!\n"); - continue; - } - - if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n"); - abort(); - } - - source = switch_core_alloc(pool, sizeof(*source)); - assert(source != NULL); - source->pool = pool; - - source->name = switch_core_strdup(source->pool, name); - source->location = switch_core_strdup(source->pool, path); - source->rate = 8000; - source->interval = 20; - source->channels = 1; - source->timer_name = "soft"; - source->prebuf = DEFAULT_PREBUFFER_SIZE; - source->stopped = 0; - source->chime_freq = 30; - - for (param = switch_xml_child(directory, "param"); param; param = param->next) { - char *var = (char *) switch_xml_attr_soft(param, "name"); - char *val = (char *) switch_xml_attr_soft(param, "value"); - - if (!strcasecmp(var, "rate")) { - int tmp = atoi(val); - if (tmp == 8000 || tmp == 12000 || tmp == 16000 || tmp == 24000 || tmp == 32000 || tmp == 48000) { - source->rate = tmp; - } - } else if (!strcasecmp(var, "shuffle")) { - source->shuffle = switch_true(val); - } else if (!strcasecmp(var, "prebuf")) { - int tmp = atoi(val); - if (tmp > 0) { - source->prebuf = (uint32_t) tmp; - } - } else if (!strcasecmp(var, "channels")) { - int tmp = atoi(val); - if (tmp == 1 || tmp == 2) { - source->channels = (uint8_t) tmp; - } - } else if (!strcasecmp(var, "chime-freq")) { - int tmp = atoi(val); - if (tmp > 1) { - source->chime_freq = tmp; - } - } else if (!strcasecmp(var, "chime-max")) { - int tmp = atoi(val); - if (tmp > 1) { - source->chime_max = tmp; - } - } else if (!strcasecmp(var, "chime-list")) { - char *list_dup = switch_core_strdup(source->pool, val); - source->chime_total = - switch_separate_string(list_dup, ',', source->chime_list, (sizeof(source->chime_list) / sizeof(source->chime_list[0]))); - } else if (!strcasecmp(var, "interval")) { - int tmp = atoi(val); - if (SWITCH_ACCEPTABLE_INTERVAL(tmp)) { - source->interval = tmp; - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, - "Interval must be multiple of 10 and less than %d, Using default of 20\n", SWITCH_MAX_INTERVAL); - } - } else if (!strcasecmp(var, "timer-name")) { - source->timer_name = switch_core_strdup(source->pool, val); + if (!strcasecmp(var, "rate")) { + int tmp = atoi(val); + if (tmp == 8000 || tmp == 12000 || tmp == 16000 || tmp == 24000 || tmp == 32000 || tmp == 48000) { + source->rate = tmp; } + } else if (!strcasecmp(var, "shuffle")) { + source->shuffle = switch_true(val); + } else if (!strcasecmp(var, "prebuf")) { + int tmp = atoi(val); + if (tmp > 0) { + source->prebuf = (uint32_t) tmp; + } + } else if (!strcasecmp(var, "channels")) { + int tmp = atoi(val); + if (tmp == 1 || tmp == 2) { + source->channels = (uint8_t) tmp; + } + } else if (!strcasecmp(var, "chime-freq")) { + int tmp = atoi(val); + if (tmp > 1) { + source->chime_freq = tmp; + } + } else if (!strcasecmp(var, "chime-max")) { + int tmp = atoi(val); + if (tmp > 1) { + source->chime_max = tmp; + } + } else if (!strcasecmp(var, "chime-list")) { + char *list_dup = switch_core_strdup(source->pool, val); + source->chime_total = + switch_separate_string(list_dup, ',', source->chime_list, (sizeof(source->chime_list) / sizeof(source->chime_list[0]))); + } else if (!strcasecmp(var, "interval")) { + int tmp = atoi(val); + if (SWITCH_ACCEPTABLE_INTERVAL(tmp)) { + source->interval = tmp; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, + "Interval must be multiple of 10 and less than %d, Using default of 20\n", SWITCH_MAX_INTERVAL); + } + } else if (!strcasecmp(var, "timer-name")) { + source->timer_name = switch_core_strdup(source->pool, val); } - - if (source->chime_max) { - source->chime_max *= source->rate; - } - - if (source->chime_total) { - source->chime_counter = source->rate * source->chime_freq; - } - - source->samples = switch_samples_per_packet(source->rate, source->interval); - - switch_mutex_init(&source->mutex, SWITCH_MUTEX_NESTED, source->pool); - - switch_threadattr_create(&thd_attr, source->pool); - switch_threadattr_detach_set(thd_attr, 1); - switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); - switch_thread_create(&thread, thd_attr, read_stream_thread, source, source->pool); } + if (source->chime_max) { + source->chime_max *= source->rate; + } + + if (source->chime_total) { + source->chime_counter = source->rate * source->chime_freq; + } + + source->samples = switch_samples_per_packet(source->rate, source->interval); + switch_mutex_init(&source->mutex, SWITCH_MUTEX_NESTED, source->pool); + switch_threadattr_create(&thd_attr, source->pool); + switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&thread, thd_attr, read_stream_thread, source, source->pool); +} + +static void launch_streams(const char *name) +{ + switch_xml_t cfg, xml, directory; + + if (!(xml = switch_xml_open_cfg(global_cf, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", global_cf); + abort(); + } + if (zstr(name)) { + for (directory = switch_xml_child(cfg, "directory"); directory; directory = directory->next) { + char *name = (char *) switch_xml_attr(directory, "name"); + char *path = (char *) switch_xml_attr(directory, "path"); + launch_thread(name, path, directory); + } + } else if ((directory = switch_xml_find_child(cfg, "directory", "name", name))) { + char *path = (char *) switch_xml_attr(directory, "path"); + launch_thread(name, path, directory); + } switch_xml_free(xml); } @@ -609,6 +674,56 @@ static void event_handler(switch_event_t *event) RUNNING = 0; } +#define RELOAD_LOCAL_STREAM_SYNTAX "" +SWITCH_STANDARD_API(reload_local_stream_function) +{ + local_stream_source_t *source = NULL; + char *mycmd = NULL, *argv[1] = { 0 }; + char *local_stream_name = NULL; + int argc = 0; + + + if (zstr(cmd)) { + goto usage; + } + + if (!(mycmd = strdup(cmd))) { + goto usage; + } + + if ((argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) < 1) { + goto usage; + } + + local_stream_name = argv[0]; + if (zstr(local_stream_name)) { + goto usage; + } + + switch_mutex_lock(globals.mutex); + source = switch_core_hash_find(globals.source_hash, local_stream_name); + switch_mutex_unlock(globals.mutex); + + if (!source) { + stream->write_function(stream, "-ERR Cannot locate local_stream %s!\n", local_stream_name); + goto done; + } + + source->full_reload = 1; + source->part_reload = 1; + stream->write_function(stream, "+OK"); + goto done; + + usage: + stream->write_function(stream, "-USAGE: %s\n", RELOAD_LOCAL_STREAM_SYNTAX); + switch_safe_free(mycmd); + + done: + + switch_safe_free(mycmd); + return SWITCH_STATUS_SUCCESS; +} + #define STOP_LOCAL_STREAM_SYNTAX "" SWITCH_STANDARD_API(stop_local_stream_function) { @@ -644,7 +759,7 @@ SWITCH_STANDARD_API(stop_local_stream_function) goto done; } - source->stopped = 1; + source->stop_request = 1; stream->write_function(stream, "+OK"); goto done; @@ -723,7 +838,8 @@ SWITCH_STANDARD_API(show_local_stream_function) stream->write_function(stream, " total: %d\n", source->total); stream->write_function(stream, " shuffle: %s\n", (source->shuffle) ? "true" : "false"); stream->write_function(stream, " ready: %s\n", (source->ready) ? "true" : "false"); - stream->write_function(stream, " stopped: %s\n", (source->stopped) ? "true" : "false"); + stream->write_function(stream, " stopping: %s\n", (source->stop_request) ? "true" : "false"); + stream->write_function(stream, " reloading: %s\n", (source->full_reload) ? "true" : "false"); } } else { stream->write_function(stream, "-ERR Cannot locate local_stream %s!\n", local_stream_name); @@ -744,23 +860,13 @@ SWITCH_STANDARD_API(show_local_stream_function) return SWITCH_STATUS_SUCCESS; } -#define START_LOCAL_STREAM_SYNTAX " [] [] [] [] [] [] []" +#define START_LOCAL_STREAM_SYNTAX "" SWITCH_STANDARD_API(start_local_stream_function) { local_stream_source_t *source = NULL; - switch_memory_pool_t *pool; - switch_thread_t *thread; - switch_threadattr_t *thd_attr = NULL; char *mycmd = NULL, *argv[8] = { 0 }; - char *local_stream_name = NULL, *path = NULL, *timer_name = NULL, *chime_list = NULL, *list_dup = NULL; - uint32_t prebuf = 1; - int rate = 8000, shuffle = 1, interval = 20, chime_freq = 30; - uint8_t channels = 1; - uint32_t chime_max = 0; + char *local_stream_name = NULL; int argc = 0; - char *cf = "local_stream.conf"; - switch_xml_t cfg, xml, directory, param; - int tmp; if (zstr(cmd)) { goto usage; @@ -776,159 +882,19 @@ SWITCH_STANDARD_API(start_local_stream_function) local_stream_name = argv[0]; - if (argv[1]) { - path = strdup(argv[1]); - } - - if (argv[2]) { - tmp = atoi(argv[2]); - if (tmp == 8000 || tmp == 16000 || tmp == 32000) { - rate = tmp; - } - } - - shuffle = argv[3] ? switch_true(argv[3]) : 1; - prebuf = argv[4] ? atoi(argv[4]) : DEFAULT_PREBUFFER_SIZE; - - if (argv[5]) { - tmp = atoi(argv[5]); - if (tmp == 1 || tmp == 2) { - channels = (uint8_t) tmp; - } - } - - interval = argv[6] ? atoi(argv[6]) : 20; - - if (!SWITCH_ACCEPTABLE_INTERVAL(interval)) { - interval = 20; - } - - if (!path) { - if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf); - stream->write_function(stream, "-ERR unable to open file %s!\n", cf); - goto done; - } - - for (directory = switch_xml_child(cfg, "directory"); directory; directory = directory->next) { - char *name = (char *) switch_xml_attr(directory, "name"); - if (!name || !local_stream_name || strcasecmp(name, local_stream_name)) { - continue; - } else { - path = (char *) switch_xml_attr(directory, "path"); - if (!(name && path)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid config!\n"); - continue; - } - - for (param = switch_xml_child(directory, "param"); param; param = param->next) { - char *var = (char *) switch_xml_attr_soft(param, "name"); - char *val = (char *) switch_xml_attr_soft(param, "value"); - - if (!strcasecmp(var, "rate")) { - tmp = atoi(val); - if (tmp == 8000 || tmp == 16000 || tmp == 32000) { - rate = tmp; - } - } else if (!strcasecmp(var, "shuffle")) { - shuffle = switch_true(val); - } else if (!strcasecmp(var, "prebuf")) { - tmp = atoi(val); - if (tmp > 0) { - prebuf = (uint32_t) tmp; - } - } else if (!strcasecmp(var, "channels")) { - tmp = atoi(val); - if (tmp == 1 || tmp == 2) { - channels = (uint8_t) tmp; - } - } else if (!strcasecmp(var, "interval")) { - tmp = atoi(val); - if (SWITCH_ACCEPTABLE_INTERVAL(tmp)) { - interval = tmp; - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, - "Interval must be multiple of 10 and less than %d, Using default of 20\n", SWITCH_MAX_INTERVAL); - } - } else if (!strcasecmp(var, "timer-name")) { - timer_name = strdup(val); - } else if (!strcasecmp(var, "chime-freq")) { - tmp = atoi(val); - if (tmp > 1) { - chime_freq = (uint32_t) tmp; - } - } else if (!strcasecmp(var, "chime-max")) { - tmp = atoi(val); - if (tmp > 1) { - chime_max = (uint32_t) tmp; - } - } else if (!strcasecmp(var, "chime-list")) { - chime_list = val; - } - } - break; - } - - } - - if (path) { - path = strdup(path); - } - switch_xml_free(xml); - } - - if (zstr(local_stream_name) || zstr(path)) { - goto usage; - } - switch_mutex_lock(globals.mutex); source = switch_core_hash_find(globals.source_hash, local_stream_name); switch_mutex_unlock(globals.mutex); if (source) { source->stopped = 0; - stream->write_function(stream, "+OK stream: %s[%s] %s", source->name, source->location, source->shuffle ? "shuffle" : "no shuffle"); + source->stop_request = 0; + stream->write_function(stream, "+OK stream: %s", source->name); goto done; } - if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool for new local_stream\n"); - stream->write_function(stream, "-ERR unable to allocate memory for local_stream %s!\n", local_stream_name); - goto done; - } + launch_streams(local_stream_name); + stream->write_function(stream, "+OK stream: %s", local_stream_name); - source = switch_core_alloc(pool, sizeof(*source)); - assert(source != NULL); - source->pool = pool; - - source->name = switch_core_strdup(source->pool, local_stream_name); - source->location = switch_core_strdup(source->pool, path); - source->rate = rate; - source->interval = interval; - source->channels = channels; - source->timer_name = switch_core_strdup(source->pool, timer_name ? timer_name : (argv[7] ? argv[7] : "soft")); - list_dup = switch_core_strdup(source->pool, chime_list); - source->chime_total = switch_separate_string(list_dup, ',', source->chime_list, (sizeof(source->chime_list) / sizeof(source->chime_list[0]))); - if (source->chime_total) { - source->chime_freq = chime_freq; - - if (chime_max) { - source->chime_max = chime_max * source->rate; - } - } - source->chime_counter = source->rate * source->chime_freq; - source->prebuf = prebuf; - source->stopped = 0; - source->shuffle = shuffle; - source->samples = switch_samples_per_packet(source->rate, source->interval); - - switch_mutex_init(&source->mutex, SWITCH_MUTEX_NESTED, source->pool); - - switch_threadattr_create(&thd_attr, source->pool); - switch_threadattr_detach_set(thd_attr, 1); - switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); - switch_thread_create(&thread, thd_attr, read_stream_thread, source, source->pool); - - stream->write_function(stream, "+OK stream: %s[%s] %s", source->name, source->location, source->shuffle ? "shuffle" : "no shuffle"); goto done; usage: @@ -936,8 +902,6 @@ SWITCH_STANDARD_API(start_local_stream_function) done: - switch_safe_free(path); - switch_safe_free(timer_name); switch_safe_free(mycmd); return SWITCH_STATUS_SUCCESS; } @@ -963,8 +927,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_local_stream_load) memset(&globals, 0, sizeof(globals)); switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, pool); switch_core_hash_init(&globals.source_hash, pool); - launch_threads(); + launch_streams(NULL); + SWITCH_ADD_API(commands_api_interface, "reload_local_stream", "Reloads a local_stream", reload_local_stream_function, RELOAD_LOCAL_STREAM_SYNTAX); SWITCH_ADD_API(commands_api_interface, "stop_local_stream", "Stops and unloads a local_stream", stop_local_stream_function, STOP_LOCAL_STREAM_SYNTAX); SWITCH_ADD_API(commands_api_interface, "start_local_stream", "Starts a new local_stream", start_local_stream_function, START_LOCAL_STREAM_SYNTAX); SWITCH_ADD_API(commands_api_interface, "show_local_stream", "Shows a local stream", show_local_stream_function, SHOW_LOCAL_STREAM_SYNTAX);