diff --git a/build/modules.conf.in b/build/modules.conf.in index c69559ec17..a33a5bdd2e 100644 --- a/build/modules.conf.in +++ b/build/modules.conf.in @@ -38,6 +38,7 @@ event_handlers/mod_event_socket formats/mod_native_file formats/mod_sndfile #formats/mod_shout +#formats/mod_local_stream #languages/mod_perl #languages/mod_python #languages/mod_spidermonkey diff --git a/conf/freeswitch.xml b/conf/freeswitch.xml index eac3d9228a..82a3d7ec3f 100644 --- a/conf/freeswitch.xml +++ b/conf/freeswitch.xml @@ -125,6 +125,7 @@ +
diff --git a/conf/modules.conf.xml b/conf/modules.conf.xml index e7e2cfcc0f..e78d42e39e 100644 --- a/conf/modules.conf.xml +++ b/conf/modules.conf.xml @@ -53,6 +53,8 @@ + + diff --git a/src/mod/formats/mod_local_stream/Makefile b/src/mod/formats/mod_local_stream/Makefile new file mode 100644 index 0000000000..09018d1801 --- /dev/null +++ b/src/mod/formats/mod_local_stream/Makefile @@ -0,0 +1,2 @@ +MODNAME=mod_native_file +include ../../../../build/modmake.rules diff --git a/src/mod/formats/mod_local_stream/mod_local_stream.c b/src/mod/formats/mod_local_stream/mod_local_stream.c new file mode 100644 index 0000000000..f892474279 --- /dev/null +++ b/src/mod/formats/mod_local_stream/mod_local_stream.c @@ -0,0 +1,380 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_local_stream.c -- Local Streaming Audio + * + */ +#include +/* for apr_pstrcat */ + + +SWITCH_MODULE_LOAD_FUNCTION(mod_local_stream_load); +SWITCH_MODULE_DEFINITION(mod_local_stream, mod_local_stream_load, NULL, NULL); +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_local_stream_shutdown); + +struct local_stream_source; + +static struct { + switch_mutex_t *mutex; + switch_hash_t *source_hash; +} globals; + +static int RUNNING = 1; + +struct local_stream_context { + struct local_stream_source *source; + switch_mutex_t *audio_mutex; + switch_buffer_t *audio_buffer; + int err; + struct local_stream_context *next; +}; +typedef struct local_stream_context local_stream_context_t; + + +struct local_stream_source { + char *name; + char *location; + int channels; + int rate; + int interval; + int samples; + char *timer_name; + local_stream_context_t *context_list; + switch_dir_t *dir_handle; + switch_mutex_t *mutex; + switch_memory_pool_t *pool; +}; +typedef struct local_stream_source local_stream_source_t; + +static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void *obj) +{ + local_stream_source_t *source = obj; + switch_file_handle_t fh = {0}; + local_stream_context_t *cp; + char file_buf[128] = "", path_buf[512] = ""; + switch_timer_t timer = {0}; + + + if (switch_core_timer_init(&timer, source->timer_name, source->interval, source->samples, source->pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Can't start timer.\n"); + return NULL; + } + + + while(RUNNING) { + const char *fname; + + if (switch_dir_open(&source->dir_handle, source->location, source->pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Can't open directory: %s\n", source->location); + return NULL; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "open directory: %s\n", source->location); + switch_yield(1000000); + + while((fname = switch_dir_next_file(source->dir_handle, file_buf, sizeof(file_buf)))) { + switch_size_t olen; + uint8_t *abuf[SWITCH_RECOMMENDED_BUFFER_SIZE] = {0}; + + snprintf(path_buf, sizeof(path_buf), "%s/%s", source->location, fname); + fname = path_buf; + if (switch_core_file_open(&fh, + (char *)fname, + source->channels, + source->rate, + SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, source->pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't open %s\n", fname); + switch_yield(1000000); + continue; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Playing %s\n", fname); + + while (RUNNING) { + switch_core_timer_next(&timer); + olen = source->samples; + if (switch_core_file_read(&fh, abuf, &olen) != SWITCH_STATUS_SUCCESS || !olen) { + switch_core_file_close(&fh); + break; + } + + switch_mutex_lock(source->mutex); + for (cp = source->context_list; cp; cp = cp->next) { + switch_mutex_lock(cp->audio_mutex); + switch_buffer_write(cp->audio_buffer, abuf, olen * 2); + switch_mutex_unlock(cp->audio_mutex); + } + switch_mutex_unlock(source->mutex); + } + + } + + switch_dir_close(source->dir_handle); + + } + + switch_core_destroy_memory_pool(&source->pool); + + return NULL; +} + +static switch_status_t local_stream_file_open(switch_file_handle_t *handle, char *path) +{ + local_stream_context_t *context; + local_stream_source_t *source; + + if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This format does not support writing!\n"); + return SWITCH_STATUS_FALSE; + } + + switch_mutex_lock(globals.mutex); + source = switch_core_hash_find(globals.source_hash, path); + switch_mutex_unlock(globals.mutex); + + if (!source) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unknown source %s\n", path); + return SWITCH_STATUS_FALSE; + } + + if ((context = switch_core_alloc(handle->memory_pool, sizeof(*context))) == 0) { + return SWITCH_STATUS_MEMERR; + } + + handle->samples = 0; + handle->samplerate = source->rate; + handle->channels = source->channels; + handle->format = 0; + handle->sections = 0; + handle->seekable = 0; + handle->speed = 0; + handle->private_info = context; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Opening Stream [%s] %dhz\n", path, handle->samplerate); + + switch_mutex_init(&context->audio_mutex, SWITCH_MUTEX_NESTED, handle->memory_pool); + if (switch_buffer_create_dynamic(&context->audio_buffer, 512, 1024, 0) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); + return SWITCH_STATUS_MEMERR; + } + + context->source = source; + + switch_mutex_lock(source->mutex); + context->next = source->context_list; + source->context_list = context; + switch_mutex_unlock(source->mutex); + + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t local_stream_file_close(switch_file_handle_t *handle) +{ + local_stream_context_t *cp, *last = NULL, *context = handle->private_info; + + switch_mutex_lock(context->source->mutex); + for (cp = context->source->context_list; cp; cp = cp->next) { + if (cp == context) { + if (last) { + last->next = cp->next; + } else { + context->source->context_list = cp->next; + } + break; + } + last = cp; + } + switch_mutex_unlock(context->source->mutex); + switch_buffer_destroy(&context->audio_buffer); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t local_stream_file_seek(switch_file_handle_t *handle, unsigned int *cur_sample, int64_t samples, int whence) +{ + return SWITCH_STATUS_FALSE; +} + +static switch_status_t local_stream_file_read(switch_file_handle_t *handle, void *data, size_t *len) +{ + local_stream_context_t *context = handle->private_info; + switch_size_t bytes = 0; + size_t need = *len * 2; + + switch_mutex_lock(context->audio_mutex); + if ((bytes = switch_buffer_read(context->audio_buffer, data, need))) { + *len = bytes / 2; + } else { + if (need > 2560) { + need = 2560; + } + memset(data, 255, need); + *len = need / 2; + } + switch_mutex_unlock(context->audio_mutex); + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t local_stream_file_write(switch_file_handle_t *handle, void *data, size_t *len) +{ + return SWITCH_STATUS_FALSE; +} + +static switch_status_t local_stream_file_set_string(switch_file_handle_t *handle, switch_audio_col_t col, const char *string) +{ + return SWITCH_STATUS_FALSE; +} + +static switch_status_t local_stream_file_get_string(switch_file_handle_t *handle, switch_audio_col_t col, const char **string) +{ + return SWITCH_STATUS_FALSE; +} + +/* Registration */ + +static char *supported_formats[SWITCH_MAX_CODECS] = { 0 }; + +static void launch_threads(void) +{ + char *cf = "local_stream.conf"; + switch_xml_t cfg, xml, directory, param; + switch_memory_pool_t *pool; + local_stream_source_t *source; + 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; + } + + 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; + } + + 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"; + + + 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 == 16000) { + source->rate = tmp; + } + } else if (!strcasecmp(var, "channels")) { + int tmp = atoi(val); + if (tmp == 1 || tmp == 2) { + source->channels = tmp; + } + } else if (!strcasecmp(var, "interval")) { + source->interval = atoi(val); + } else if (!strcasecmp(var, "timer_name")) { + source->timer_name = switch_core_strdup(source->pool, val); + } + } + + source->samples = switch_bytes_per_frame(source->rate, source->interval); + switch_core_hash_insert(globals.source_hash, source->name, source); + + 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); + + } +} + + +SWITCH_MODULE_LOAD_FUNCTION(mod_local_stream_load) +{ + switch_file_interface_t *file_interface; + supported_formats[0] = "local_stream"; + + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + file_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE); + file_interface->interface_name = modname; + file_interface->extens = supported_formats; + file_interface->file_open = local_stream_file_open; + file_interface->file_close = local_stream_file_close; + file_interface->file_read = local_stream_file_read; + file_interface->file_write = local_stream_file_write; + file_interface->file_seek = local_stream_file_seek; + file_interface->file_set_string = local_stream_file_set_string; + file_interface->file_get_string = local_stream_file_get_string; + + memset(&globals, 0, sizeof(globals)); + switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, pool); + switch_core_hash_init(&globals.source_hash, pool); + launch_threads(); + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_local_stream_shutdown) +{ + RUNNING = 0; + switch_yield(500000); + return SWITCH_STATUS_SUCCESS; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab: + */ diff --git a/src/mod/formats/mod_local_stream/mod_local_stream.vcproj b/src/mod/formats/mod_local_stream/mod_local_stream.vcproj new file mode 100644 index 0000000000..30776e903b --- /dev/null +++ b/src/mod/formats/mod_local_stream/mod_local_stream.vcproj @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/languages/mod_mono/mod_mono.c b/src/mod/languages/mod_mono/mod_mono.c index cfc7d8fb29..95943320dc 100755 --- a/src/mod/languages/mod_mono/mod_mono.c +++ b/src/mod/languages/mod_mono/mod_mono.c @@ -194,12 +194,13 @@ switch_status_t mod_mono_load_modules(const char *module_dir) iter = NULL; mono_plugin *plugin = NULL; const char *fname = NULL; + char buf[512] = ""; if (switch_dir_open(&module_dir_handle, module_dir, mono_pool) != SWITCH_STATUS_SUCCESS) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not open directory: %s\n", module_dir); /* Read the modules directory */ - while ((fname = switch_dir_next_file(module_dir_handle))) { + while ((fname = switch_dir_next_file(module_dir_handle, buf, sizeof(buf)))) { assembly = (MonoAssembly *) switch_core_alloc(mono_pool, sizeof(assembly)); image = (MonoImage *) switch_core_alloc(mono_pool, sizeof(image)); diff --git a/src/mod/timers/mod_softtimer/mod_softtimer.c b/src/mod/timers/mod_softtimer/mod_softtimer.c index 35e817eb4d..0f7cdb59c2 100644 --- a/src/mod/timers/mod_softtimer/mod_softtimer.c +++ b/src/mod/timers/mod_softtimer/mod_softtimer.c @@ -43,6 +43,7 @@ static switch_memory_pool_t *module_pool = NULL; static struct { int32_t RUNNING; + int32_t STARTED; switch_mutex_t *mutex; } globals; @@ -76,6 +77,14 @@ static timer_matrix_t TIMER_MATRIX[MAX_ELEMENTS + 1]; static inline switch_status_t timer_init(switch_timer_t *timer) { timer_private_t *private_info; + int sanity = 0; + + while(globals.STARTED == 0) { + switch_yield(100000); + if (++sanity == 10) { + break; + } + } if (globals.RUNNING != 1 || !globals.mutex) { return SWITCH_STATUS_FALSE; @@ -220,9 +229,9 @@ SWITCH_MODULE_RUNTIME_FUNCTION(mod_softtimer_runtime) memset(&globals, 0, sizeof(globals)); switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, module_pool); - - globals.RUNNING = 1; - + + globals.STARTED = globals.RUNNING = 1; + while (globals.RUNNING == 1) { reference += STEP_MIC; diff --git a/src/switch_apr.c b/src/switch_apr.c index 5648185816..069884fc06 100644 --- a/src/switch_apr.c +++ b/src/switch_apr.c @@ -391,26 +391,46 @@ struct switch_dir { SWITCH_DECLARE(switch_status_t) switch_dir_open(switch_dir_t **new_dir, const char *dirname, switch_memory_pool_t *pool) { switch_status_t status; - switch_dir_t *dir = switch_core_alloc(pool, sizeof(switch_dir_t)); - status = apr_dir_open(&(dir->dir_handle), dirname, pool); - *new_dir = dir; + switch_dir_t *dir = malloc(sizeof(*dir)); + + memset(dir, 0, sizeof(*dir)); + if ((status = apr_dir_open(&(dir->dir_handle), dirname, pool)) == APR_SUCCESS) { + *new_dir = dir; + } else { + free(dir); + *new_dir = NULL; + } + return status; } SWITCH_DECLARE(switch_status_t) switch_dir_close(switch_dir_t *thedir) { return apr_dir_close(thedir->dir_handle); + + free(thedir); } SWITCH_DECLARE(const char *) switch_dir_next_file(switch_dir_t *thedir, char *buf, switch_size_t len) { const char *fname = NULL; apr_int32_t finfo_flags = APR_FINFO_DIRENT | APR_FINFO_TYPE | APR_FINFO_NAME; + const char *name; + while (apr_dir_read(&(thedir->finfo), finfo_flags, thedir->dir_handle) == SWITCH_STATUS_SUCCESS) { - if (thedir->finfo.filetype != APR_REG) + if (thedir->finfo.filetype != APR_REG) { continue; - if (thedir->finfo.fname) { - switch_copy_string(buf, thedir->finfo.fname, len); + } + if (!(name = thedir->finfo.fname)) { + name = thedir->finfo.name; + } + + if (!name) { + continue; + } + + if (name) { + switch_copy_string(buf, name, len); fname = buf; } else { continue;