From 77f3bd2402e3ba87c2b518123849160463e5a467 Mon Sep 17 00:00:00 2001 From: William King Date: Thu, 23 Feb 2012 16:16:55 -0800 Subject: [PATCH] Adding mod_vlc initial working version --- src/mod/formats/mod_vlc/Makefile | 4 + src/mod/formats/mod_vlc/TODO | 14 ++ src/mod/formats/mod_vlc/mod_vlc.c | 218 ++++++++++++++++++++++++++++++ 3 files changed, 236 insertions(+) create mode 100644 src/mod/formats/mod_vlc/Makefile create mode 100644 src/mod/formats/mod_vlc/TODO create mode 100644 src/mod/formats/mod_vlc/mod_vlc.c diff --git a/src/mod/formats/mod_vlc/Makefile b/src/mod/formats/mod_vlc/Makefile new file mode 100644 index 0000000000..a41d9b3109 --- /dev/null +++ b/src/mod/formats/mod_vlc/Makefile @@ -0,0 +1,4 @@ +BASE=../../../.. +include $(BASE)/build/modmake.rules + +LOCAL_LDFLAGS= -lvlc diff --git a/src/mod/formats/mod_vlc/TODO b/src/mod/formats/mod_vlc/TODO new file mode 100644 index 0000000000..ea3aacd33a --- /dev/null +++ b/src/mod/formats/mod_vlc/TODO @@ -0,0 +1,14 @@ +Following list not in priority order. + +1. Determine if a new instance is needed for each independant inbound stream, or if a global instance can handle all inbound and outbound streams +2. Look into libvlc imem and determine if it is possible to stream parts of a call at a time, or if imem requires all of the buffer to be loaded. + a. Add and confirm stream over network functionality + b. Add and confirm stream to file + c. Confirm transcode works to mp3, wav, aac, etc. +3. Test multiple concurrent listeners to the same stream. + a. Look into have a single libvlc thread stream the file and write to the audio buffer, and multiple readers + b. Test multiple input streams simultaniously + c. Load test multiple requests(both multiple to the same stream, and multiple unique streams) to look for issues +4. Enable proper seeking support +5. Add video support +6. Confirm streaming from file works on differnt sample rates. 8k, 16k, etc. diff --git a/src/mod/formats/mod_vlc/mod_vlc.c b/src/mod/formats/mod_vlc/mod_vlc.c new file mode 100644 index 0000000000..995b202b35 --- /dev/null +++ b/src/mod/formats/mod_vlc/mod_vlc.c @@ -0,0 +1,218 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2011, 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): + * + * William King william.king@quentustech.com + * + * mod_vlc.c -- VLC streaming module + * + * Examples: + * + * File: vlc:///path/to/file + * Stream: http://path.to.file.com:port/file.pls + * Stream: vlc://ftp://path.to.file.com:port/file.mp3 + * + * Notes: + * + * Requires at least libvlc version 1.2 + * + */ +#include +#include +#include + +#define VLC_BUFFER_SIZE 4096 + +static char *vlc_file_supported_formats[SWITCH_MAX_CODECS] = { 0 }; + +libvlc_instance_t *inst; + +struct vlc_file_context { + libvlc_media_player_t *mp; + libvlc_media_t *m; + switch_file_handle_t fh; + switch_memory_pool_t *pool; + switch_buffer_t *audio_buffer; + switch_mutex_t *audio_mutex; + char *path; + int samples; + int playing; + int err; +}; + +typedef struct vlc_file_context vlc_file_context_t; + +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_vlc_shutdown); +SWITCH_MODULE_LOAD_FUNCTION(mod_vlc_load); +SWITCH_MODULE_DEFINITION(mod_vlc, mod_vlc_load, mod_vlc_shutdown, NULL); + +void vlc_auto_play_callback(void *data, const void *samples, unsigned count, int64_t pts) { + vlc_file_context_t *context = (vlc_file_context_t *) data; + + switch_mutex_lock(context->audio_mutex); + if (context->audio_buffer) { + if (!switch_buffer_write(context->audio_buffer, samples, count * 2)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Buffer error\n"); + } + } + switch_mutex_unlock(context->audio_mutex); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "VLC callback for %s %d \n", context->path, count); +} + +static switch_status_t vlc_file_open(switch_file_handle_t *handle, const char *path) +{ + vlc_file_context_t *context; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "VLC open %s\n", path); + + context = switch_core_alloc(handle->memory_pool, sizeof(*context)); + context->pool = handle->memory_pool; + + context->path = switch_core_strdup(context->pool, path); + switch_buffer_create_dynamic(&(context->audio_buffer), VLC_BUFFER_SIZE, VLC_BUFFER_SIZE * 2, 0); + + /* Determine if this is a url or a path */ + /* TODO: Change this so that it tries local files first, and then if it fails try location. */ + if(! strncmp(context->path, "http", 4)){ + context->m = libvlc_media_new_location(inst, context->path); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is http %s\n", context->path); + } else if (! strncmp(context->path, "/", 1)){ + context->m = libvlc_media_new_path(inst, context->path); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is file %s\n", context->path); + } else { + context->m = libvlc_media_new_location(inst, context->path); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "VLC Path is unknown type %s\n", context->path); + } + + context->playing = 0; + context->err = 0; + + context->mp = libvlc_media_player_new_from_media(context->m); + + if ( !handle->samplerate) + handle->samplerate = 16000; + libvlc_audio_set_format(context->mp, "S16N", handle->samplerate, 1); + + libvlc_audio_set_callbacks(context->mp, vlc_auto_play_callback, NULL,NULL,NULL,NULL, (void *) context); + + switch_mutex_init(&context->audio_mutex, SWITCH_MUTEX_NESTED, context->pool); + + handle->private_info = context; + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t vlc_file_read(switch_file_handle_t *handle, void *data, size_t *len) +{ + vlc_file_context_t *context = handle->private_info; + size_t bytes = *len * sizeof(int16_t); + libvlc_state_t status; + + if (!context) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "VLC read handle context is NULL\n"); + return SWITCH_STATUS_GENERR; + } + + if (context->err) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "VLC error\n"); + return SWITCH_STATUS_GENERR; + } + + if(! context->playing ) { + context->playing = 1; + libvlc_media_player_play(context->mp); + } + + status = libvlc_media_get_state(context->m); + if (status == 6 || status == 7) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "VLC media state: %d\n", (int) status); + return SWITCH_STATUS_GENERR; + } + + switch_mutex_lock(context->audio_mutex); + switch_buffer_read(context->audio_buffer, data, bytes); + switch_mutex_unlock(context->audio_mutex); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t vlc_file_close(switch_file_handle_t *handle) +{ + vlc_file_context_t *context = handle->private_info; + + libvlc_media_player_stop(context->mp); + libvlc_media_release(context->m); + + return SWITCH_STATUS_SUCCESS; +} + +/* Macro expands to: switch_status_t mod_vlc_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */ +SWITCH_MODULE_LOAD_FUNCTION(mod_vlc_load) +{ + switch_file_interface_t *file_interface; + const char *args = "-vvv"; + + /* connect my internal structure to the blank pointer passed to me */ + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + + vlc_file_supported_formats[0] = "vlc"; + vlc_file_supported_formats[1] = "http"; + + file_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE); + file_interface->interface_name = modname; + file_interface->extens = vlc_file_supported_formats; + file_interface->file_open = vlc_file_open; + file_interface->file_close = vlc_file_close; + file_interface->file_read = vlc_file_read; + + /* load the vlc engine. */ + inst = libvlc_new(1, &args); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Initialized VLC instance\n"); + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + +/* + Called when the system shuts down + Macro expands to: switch_status_t mod_vlc_shutdown() */ +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_vlc_shutdown) +{ + libvlc_release(inst); + 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 + */