From 76c6d4e3cb2d3739f68e8c4ec52c5549875d4092 Mon Sep 17 00:00:00 2001 From: Brian West Date: Sun, 11 Mar 2007 03:09:36 +0000 Subject: [PATCH] check in mod_shout can only write so far still gotta add read git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@4511 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- src/mod/formats/mod_shout/Makefile | 19 ++ src/mod/formats/mod_shout/mod_shout.c | 401 ++++++++++++++++++++++++++ 2 files changed, 420 insertions(+) create mode 100644 src/mod/formats/mod_shout/Makefile create mode 100644 src/mod/formats/mod_shout/mod_shout.c diff --git a/src/mod/formats/mod_shout/Makefile b/src/mod/formats/mod_shout/Makefile new file mode 100644 index 0000000000..afaacebb79 --- /dev/null +++ b/src/mod/formats/mod_shout/Makefile @@ -0,0 +1,19 @@ +LDFLAGS += -lshout -lmp3lame +MOD_CFLAGS += "-I${PREFIX}/include" +MOD_LDFLAGS += "-L${PREFIX}/lib" +all: depends $(MODNAME).$(DYNAMIC_LIB_EXTEN) + +depends: + MAKE=$(MAKE) $(BASE)/build/buildlib.sh $(BASE) install lame-3.97.tar.gz --prefix=$(PREFIX) --disable-shared --with-pic + MOD_CFLAGS="${MOD_CFLAGS}" MOD_LDFLAGS="${LD_CFLAGS}" MAKE=$(MAKE) $(BASE)/build/buildlib.sh $(BASE) install libshout-2.2.2.tar.gz --disable-shared --with-pic --prefix=$(PREFIX) + +$(MODNAME).$(DYNAMIC_LIB_EXTEN): $(MODNAME).c + $(CC) $(CFLAGS) -fPIC -c $(MODNAME).c -o $(MODNAME).o + $(CC) $(SOLINK) -o $(MODNAME).$(DYNAMIC_LIB_EXTEN) $(MODNAME).o $(LDFLAGS) + + +clean: + rm -fr *.$(DYNAMIC_LIB_EXTEN) *.o *~ + +install: + cp -f $(MODNAME).$(DYNAMIC_LIB_EXTEN) $(PREFIX)/mod diff --git a/src/mod/formats/mod_shout/mod_shout.c b/src/mod/formats/mod_shout/mod_shout.c new file mode 100644 index 0000000000..1827c4f8ca --- /dev/null +++ b/src/mod/formats/mod_shout/mod_shout.c @@ -0,0 +1,401 @@ +/* + * 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_shout.c -- Icecast Module + * + * example filename: shout://user:pass@host.com/mount.mp3 + * + */ +#include +#include +#include + +static const char modname[] = "mod_shout"; + + +static char *supported_formats[SWITCH_MAX_CODECS] = {0}; + +struct shout_context { + shout_t *shout; + lame_global_flags *gfp; +}; + +typedef struct shout_context shout_context_t; + +static inline void free_context(shout_context_t *context) +{ + if (context) { + if (context->shout) { + shout_close(context->shout); + context->shout = NULL; + } + if (context->gfp) { + lame_close(context->gfp); + context->gfp = NULL; + } + } +} + + +static void log_error(char const *fmt, va_list ap) +{ + char *data = NULL; + + if (fmt) { +#ifdef HAVE_VASPRINTF + int ret; + ret = vasprintf(&data, fmt, ap); + if ((ret == -1) || !data) { + return; + } +#else + data = (char *) malloc(2048); + if (data) { + vsnprintf(data, 2048, fmt, ap); + } else { + return; + } +#endif + } + if (data) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, (char*) "%s", data); + free(data); + } +} + +static void log_debug(char const *fmt, va_list ap) +{ + char *data = NULL; + + if (fmt) { +#ifdef HAVE_VASPRINTF + int ret; + ret = vasprintf(&data, fmt, ap); + if ((ret == -1) || !data) { + return; + } +#else + data = (char *) malloc(2048); + if (data) { + vsnprintf(data, 2048, fmt, ap); + } else { + return; + } +#endif + } + if (data) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, (char*) "%s", data); + free(data); + } +} + +static void log_msg(char const *fmt, va_list ap) +{ + char *data = NULL; + + if (fmt) { +#ifdef HAVE_VASPRINTF + int ret; + ret = vasprintf(&data, fmt, ap); + if ((ret == -1) || !data) { + return; + } +#else + data = (char *) malloc(2048); + if (data) { + vsnprintf(data, 2048, fmt, ap); + } else { + return; + } +#endif + } + if (data) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, (char*) "%s", data); + free(data); + } +} + + +static switch_status_t shout_file_open(switch_file_handle_t *handle, char *path) +{ + shout_context_t *context; + char *host, *file; + char *username, *password; + char *err = NULL; + + if (switch_test_flag(handle, SWITCH_FILE_FLAG_READ)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Read not currently supported.\n"); + return SWITCH_STATUS_GENERR; + } + + if (!switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Only write is supported.\n"); + return SWITCH_STATUS_GENERR; + } + + if ((context = switch_core_alloc(handle->memory_pool, sizeof(*context))) == 0) { + return SWITCH_STATUS_MEMERR; + } + + if (!(context->shout = shout_new())) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not allocate shout_t\n"); + goto error; + } + + if (!(context->gfp = lame_init())) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not allocate lame\n"); + goto error; + } + + lame_set_num_channels(context->gfp, handle->channels); + lame_set_in_samplerate(context->gfp, handle->samplerate); + lame_set_brate(context->gfp, 24); + lame_set_mode(context->gfp, 3); + lame_set_quality(context->gfp, 2); /* 2=high 5 = medium 7=low */ + + lame_set_errorf(context->gfp, log_error); + lame_set_debugf(context->gfp, log_debug); + lame_set_msgf(context->gfp, log_msg); + lame_set_bWriteVbrTag(context->gfp, 0); + lame_mp3_tags_fid(context->gfp, NULL); + lame_init_params(context->gfp); + lame_print_config(context->gfp); + + username = switch_core_strdup(handle->memory_pool, path); + if (!(password = strchr(username, ':'))) { + err = "invalid url"; + goto error; + } + *password++ = '\0'; + + if (!(host = strchr(password, '@'))) { + err = "invalid url"; + goto error; + } + *host++ = '\0'; + + if ((file = strchr(host, '/'))) { + *file++ = '\0'; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid URL: %s\n", path); + } + + if (shout_set_host(context->shout, host) != SHOUTERR_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error setting hostname: %s\n", shout_get_error(context->shout)); + goto error; + } + + if (shout_set_protocol(context->shout, SHOUT_PROTOCOL_HTTP) != SHOUTERR_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error setting protocol: %s\n", shout_get_error(context->shout)); + goto error; + } + + if (shout_set_port(context->shout, 8000) != SHOUTERR_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error setting port: %s\n", shout_get_error(context->shout)); + goto error; + } + + if (shout_set_password(context->shout, password) != SHOUTERR_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error setting password: %s\n", shout_get_error(context->shout)); + goto error; + } + + if (shout_set_mount(context->shout, file) != SHOUTERR_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error setting mount: %s\n", shout_get_error(context->shout)); + goto error; + } + + if (shout_set_user(context->shout, username) != SHOUTERR_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error setting user: %s\n", shout_get_error(context->shout)); + goto error; + } + + if (shout_set_url(context->shout, "mod_shout") != SHOUTERR_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error setting name: %s\n", shout_get_error(context->shout)); + goto error; + } + + if (shout_set_audio_info(context->shout, "bitrate", "24000") != SHOUTERR_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error setting user: %s\n", shout_get_error(context->shout)); + goto error; + } + + if (shout_set_format(context->shout, SHOUT_FORMAT_MP3) != SHOUTERR_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error setting user: %s\n", shout_get_error(context->shout)); + goto error; + } + + if (shout_open(context->shout) != SHOUTERR_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening stream: %s\n", shout_get_error(context->shout)); + goto error; + } + + handle->private_info = context; + + return SWITCH_STATUS_SUCCESS; + + error: + if (err) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error: %s\n", err); + } + free_context(context); + return SWITCH_STATUS_GENERR; + +} + +static switch_status_t shout_file_close(switch_file_handle_t *handle) +{ + shout_context_t *context = handle->private_info; + + + free_context(context); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t shout_file_seek(switch_file_handle_t *handle, unsigned int *cur_sample, int64_t samples, int whence) +{ + shout_context_t *context = handle->private_info; + + return SWITCH_STATUS_FALSE; + +} + +static switch_status_t shout_file_read(switch_file_handle_t *handle, void *data, size_t *len) +{ + shout_context_t *context = handle->private_info; + + return SWITCH_STATUS_FALSE; +} + +static switch_status_t shout_file_write(switch_file_handle_t *handle, void *data, size_t *len) +{ + shout_context_t *context = handle->private_info; + + + char mp3buf[2048] = ""; + long ret = 0; + int rlen; + int16_t *audio = data; + int nsamples = *len; + + if ((rlen = lame_encode_buffer(context->gfp, audio, NULL, nsamples, mp3buf, sizeof(mp3buf))) < 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "MP3 encode error %d!\n", rlen); + return SWITCH_STATUS_FALSE; + } + + if (rlen) { + ret = shout_send(context->shout, mp3buf, rlen); + if (ret != SHOUTERR_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Send error: %s\n", shout_get_error(context->shout)); + return SWITCH_STATUS_FALSE; + } + } + shout_sync(context->shout); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t shout_file_set_string(switch_file_handle_t *handle, switch_audio_col_t col, const char *string) +{ + shout_context_t *context = handle->private_info; + + switch(col) { + case SWITCH_AUDIO_COL_STR_TITLE: + if (shout_set_name(context->shout, string) != SHOUTERR_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error setting name: %s\n", shout_get_error(context->shout)); + } + break; + case SWITCH_AUDIO_COL_STR_COMMENT: + if (shout_set_url(context->shout, string) != SHOUTERR_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error setting name: %s\n", shout_get_error(context->shout)); + } + break; + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Value Ignored\n"); + break; + } + return SWITCH_STATUS_FALSE; +} + +static switch_status_t shout_file_get_string(switch_file_handle_t *handle, switch_audio_col_t col, const char **string) +{ + return SWITCH_STATUS_FALSE; +} + +static switch_file_interface_t shout_file_interface = { + /*.interface_name */ modname, + /*.file_open */ shout_file_open, + /*.file_close */ shout_file_close, + /*.file_read */ shout_file_read, + /*.file_write */ shout_file_write, + /*.file_seek */ shout_file_seek, + /*.file_set_string */ shout_file_set_string, + /*.file_get_string */ shout_file_get_string, + /*.extens */ NULL, + /*.next */ NULL, +}; + +static switch_loadable_module_interface_t shout_module_interface = { + /*.module_name */ modname, + /*.endpoint_interface */ NULL, + /*.timer_interface */ NULL, + /*.dialplan_interface */ NULL, + /*.codec_interface */ NULL, + /*.application_interface */ NULL, + /*.api_interface */ NULL, + /*.file_interface */ &shout_file_interface, + /*.speech_interface */ NULL, + /*.directory_interface */ NULL +}; + +SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_module_interface_t **module_interface, char *filename) +{ + supported_formats[0] = "shout"; + shout_file_interface.extens = supported_formats; + + /* connect my internal structure to the blank pointer passed to me */ + *module_interface = &shout_module_interface; + + shout_init(); + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:nil + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab: + */