]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-5034 --resolve please document accordingly on the wiki
authorAnthony Minessale <anthm@freeswitch.org>
Wed, 17 Apr 2013 20:24:00 +0000 (15:24 -0500)
committerAnthony Minessale <anthm@freeswitch.org>
Wed, 17 Apr 2013 20:24:18 +0000 (15:24 -0500)
src/mod/formats/mod_local_stream/mod_local_stream.c

index 8402b2bfcb48105b375700172b5df1062a6709e9..040d0e5bc2a9da7363a5b3ef5ba53c58f895e316 100644 (file)
@@ -25,6 +25,7 @@
  * 
  * Anthony Minessale II <anthm@freeswitch.org>
  * Cesar Cepeda <cesar@auronix.com>
+ * Emmanuel Schmidbauer <e.schmidbauer@gmail.com>
  *
  *
  * 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,120 +559,169 @@ 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");
-
-               if (!(name && path)) {
-                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid config!\n");
-                       continue;
+       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 (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
-                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n");
-                       abort();
-               }
+       if (source->chime_max) {
+               source->chime_max *= source->rate;
+       }
 
-               source = switch_core_alloc(pool, sizeof(*source));
-               assert(source != NULL);
-               source->pool = pool;
+       if (source->chime_total) {
+               source->chime_counter = source->rate * source->chime_freq;
+       }
 
-               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;
+       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);
+}
 
-               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");
+static void launch_streams(const char *name)
+{
+       switch_xml_t cfg, xml, directory;
 
-                       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 (!(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);
+}
 
-               if (source->chime_max) {
-                       source->chime_max *= source->rate;
-               }
+static void event_handler(switch_event_t *event)
+{
+       RUNNING = 0;
+}
 
-               if (source->chime_total) {
-                       source->chime_counter = source->rate * source->chime_freq;
-               }
+#define RELOAD_LOCAL_STREAM_SYNTAX "<local_stream_name>"
+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;
 
-               source->samples = switch_samples_per_packet(source->rate, source->interval);
 
-               switch_mutex_init(&source->mutex, SWITCH_MUTEX_NESTED, source->pool);
+       if (zstr(cmd)) {
+               goto usage;
+       }
 
-               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 (!(mycmd = strdup(cmd))) {
+               goto usage;
        }
 
-       switch_xml_free(xml);
-}
+       if ((argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) < 1) {
+               goto usage;
+       }
 
-static void event_handler(switch_event_t *event)
-{
-       RUNNING = 0;
+       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 "<local_stream_name>"
@@ -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 "<local_stream_name> [<path>] [<rate>] [<shuffle>] [<prebuf>] [<channels>] [<interval>] [<timer_name>]"
+#define START_LOCAL_STREAM_SYNTAX "<local_stream_name>"
 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;
-       }
-
-       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);
+       launch_streams(local_stream_name);
+       stream->write_function(stream, "+OK stream: %s", local_stream_name);
 
-       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);