From 84e012c0d959c3d29096cc0f5d4dc90d1d09d4a4 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 14 Mar 2007 23:19:13 +0000 Subject: [PATCH] stick a fork in it.... needs testing and tweaks but can now read and write to icecast via shout:// and or .mp3 files the traditional way git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@4597 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- src/mod/formats/mod_shout/interface.c | 6 +- src/mod/formats/mod_shout/mod_shout.c | 456 ++++++++++++++++++-------- src/mod/formats/mod_shout/mpglib.h | 8 +- 3 files changed, 332 insertions(+), 138 deletions(-) diff --git a/src/mod/formats/mod_shout/interface.c b/src/mod/formats/mod_shout/interface.c index 7aabb1f55c..8a9ec3ddf5 100644 --- a/src/mod/formats/mod_shout/interface.c +++ b/src/mod/formats/mod_shout/interface.c @@ -13,7 +13,7 @@ void InitMP3Constants(void) } -BOOL InitMP3(struct mpstr *mp, long outscale) +BOOL InitMP3(struct mpstr *mp, long outscale, int samplerate) { /* quiet 4096 med 8192 */ @@ -26,7 +26,7 @@ BOOL InitMP3(struct mpstr *mp, long outscale) mp->fr.single = 3; /* force mono */ mp->bsnum = 0; mp->synth_bo = 1; - mp->outsamplerate = 8000; + mp->outsamplerate = samplerate; make_decode_tables_scale(mp, outscale); @@ -190,7 +190,7 @@ int decodeMP3(struct mpstr *mp,char *in,int isize,char *out, if(osize < 4608) { debug_printf("%d To less out space\n", __LINE__); - return MP3_ERR; + return MP3_TOOSMALL; } if(in) { diff --git a/src/mod/formats/mod_shout/mod_shout.c b/src/mod/formats/mod_shout/mod_shout.c index deb9ed7631..184debd5d4 100644 --- a/src/mod/formats/mod_shout/mod_shout.c +++ b/src/mod/formats/mod_shout/mod_shout.c @@ -38,13 +38,14 @@ #include -#define OUTSCALE 4096 +#define OUTSCALE 8192 #define MP3_SCACHE 16384 #define MP3_DCACHE 8192 static const char modname[] = "mod_shout"; + static char *supported_formats[SWITCH_MAX_CODECS] = {0}; struct shout_context { @@ -52,32 +53,52 @@ struct shout_context { lame_global_flags *gfp; char *stream_url; switch_mutex_t *audio_mutex; - switch_mutex_t *mp3_mutex; switch_buffer_t *audio_buffer; - switch_buffer_t *mp3_buffer; switch_memory_pool_t *memory_pool; - //char encode_buf[MP3_SCACHE]; char decode_buf[MP3_DCACHE]; struct mpstr mp; int err; + int mp3err; int dlen; + switch_file_t *fd; + FILE *fp; + int samplerate; }; typedef struct shout_context shout_context_t; + +static size_t decode_fd(shout_context_t *context, void *data, size_t bytes); + static inline void free_context(shout_context_t *context) { if (context) { + int sanity = 0; + + if (context->fd) { + switch_file_close(context->fd); + context->fd = NULL; + } + + if (context->fp) { + unsigned char mp3buffer[1024]; + int len; + + while ((len = lame_encode_flush(context->gfp, mp3buffer, sizeof(mp3buffer))) > 0) { + fwrite(mp3buffer, 1, len, context->fp); + } + + lame_mp3_tags_fid(context->gfp, context->fp); + fclose(context->fp); + context->fp = NULL; + } + if (context->audio_buffer) { switch_mutex_lock(context->audio_mutex); switch_buffer_destroy(&context->audio_buffer); switch_mutex_unlock(context->audio_mutex); } - if (context->mp3_buffer) { - switch_buffer_destroy(&context->mp3_buffer); - } - if (context->shout) { shout_close(context->shout); context->shout = NULL; @@ -87,6 +108,22 @@ static inline void free_context(shout_context_t *context) lame_close(context->gfp); context->gfp = NULL; } + if (context->stream_url) { + int err; + + switch_mutex_lock(context->audio_mutex); + err = ++context->err; + switch_mutex_unlock(context->audio_mutex); + + while(context->err == err) { + switch_yield(1000000); + if (++sanity > 10) { + break; + } + } + + ExitMP3(&context->mp); + } } } @@ -169,21 +206,119 @@ static void log_msg(char const *fmt, va_list ap) } } +static size_t decode_fd(shout_context_t *context, void *data, size_t bytes) +{ + int decode_status = 0; + int dlen = 0; + int x = 0; + char *in; + int inlen; + char *out; + int outlen; + int usedlen; + char inbuf[MP3_SCACHE]; + int done = 0; + + size_t lp; + size_t rb = 0; + + while (switch_buffer_inuse(context->audio_buffer) < bytes) { + lp = sizeof(inbuf); + if ((switch_file_read(context->fd, inbuf, &lp) != SWITCH_STATUS_SUCCESS) || lp == 0) { + goto error; + } + + inlen = (int) lp; + in = inbuf; + + out = context->decode_buf; + outlen = (int) sizeof(context->decode_buf); + usedlen = 0; + x = 0; + if (inlen < bytes) { + done = 1; + } + + do { + decode_status = decodeMP3(&context->mp, in, inlen, out, outlen, &dlen); + + if (context->err) { + goto error; + } + + if (!x) { + in = NULL; + inlen = 0; + x++; + } + + if (decode_status == MP3_TOOSMALL) { + if (context->audio_buffer) { + switch_buffer_write(context->audio_buffer, context->decode_buf, usedlen); + } else { + goto error; + } + + out = context->decode_buf; + outlen = sizeof(context->decode_buf); + usedlen = 0; + continue; + + } + + if (decode_status == MP3_ERR) { + if (++context->mp3err >= 20) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Decoder Error!\n"); + } + dlen = 0; + continue; + } + + context->mp3err = 0; + + usedlen += dlen; + out += dlen; + outlen -= dlen; + dlen = 0; + + + } while (decode_status != MP3_NEED_MORE); + + + if (context->audio_buffer) { + switch_buffer_write(context->audio_buffer, context->decode_buf, usedlen); + } else { + goto error; + } + + if (done) { + break; + } + } + + if (switch_buffer_inuse(context->audio_buffer) >= bytes) { + rb = switch_buffer_read(context->audio_buffer, data, bytes); + return rb; + } + + return 0; + + error: + switch_mutex_lock(context->audio_mutex); + context->err++; + switch_mutex_unlock(context->audio_mutex); + return 0; + +} -static int wtf = -1; static size_t stream_callback(void *ptr, size_t size, size_t nmemb, void *data) { register unsigned int realsize = (unsigned int)(size * nmemb); shout_context_t *context = data; - int rlen; - char grr[1024]; - char *ass; int decode_status = 0; int dlen = 0; - int offset = 0; int x = 0; - char *in; int inlen; char *out; @@ -200,37 +335,65 @@ static size_t stream_callback(void *ptr, size_t size, size_t nmemb, void *data) do { decode_status = decodeMP3(&context->mp, in, inlen, out, outlen, &dlen); + if (context->err) { + goto error; + } + if (!x) { in = NULL; inlen = 0; x++; } - if (decode_status == MP3_ERR) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Decoder Error!\n"); - context->err++; - return 0; + if (decode_status == MP3_TOOSMALL) { + switch_mutex_lock(context->audio_mutex); + if (context->audio_buffer) { + switch_buffer_write(context->audio_buffer, context->decode_buf, usedlen); + } else { + goto error; + } + out = context->decode_buf; + outlen = sizeof(context->decode_buf); + usedlen = 0; + switch_mutex_unlock(context->audio_mutex); + + } else if (decode_status == MP3_ERR) { + + if (++context->mp3err >= 20) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Decoder Error!\n"); + goto error; + } + + ExitMP3(&context->mp); + InitMP3(&context->mp, OUTSCALE, context->samplerate); + + return realsize; } - + + context->mp3err = 0; usedlen += dlen; out += dlen; outlen -= dlen; dlen = 0; } while (decode_status != MP3_NEED_MORE); - printf("WRITE %d\n", usedlen); + switch_mutex_lock(context->audio_mutex); - switch_buffer_write(context->audio_buffer, context->decode_buf, usedlen); + if (context->audio_buffer) { + switch_buffer_write(context->audio_buffer, context->decode_buf, usedlen); + } else { + goto error; + } switch_mutex_unlock(context->audio_mutex); - out = context->decode_buf; - outlen = sizeof(context->decode_buf); - - printf("doh\n"); - - - return realsize; + + error: + switch_mutex_lock(context->audio_mutex); + context->err++; + switch_mutex_unlock(context->audio_mutex); + return 0; + } @@ -248,7 +411,10 @@ static void *SWITCH_THREAD_FUNC stream_thread(switch_thread_t *thread, void *obj curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "FreeSWITCH(mod_shout)/1.0"); curl_easy_perform(curl_handle); curl_easy_cleanup(curl_handle); - + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Thread Done\n"); + switch_mutex_lock(context->audio_mutex); + context->err++; + switch_mutex_unlock(context->audio_mutex); return NULL; } @@ -274,115 +440,130 @@ static switch_status_t shout_file_open(switch_file_handle_t *handle, char *path) 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; - } - context->memory_pool = handle->memory_pool; - - 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); - + context->samplerate = handle->codec_imp ? handle->codec_imp->samples_per_second : 8000; + if (switch_test_flag(handle, SWITCH_FILE_FLAG_READ)) { if (switch_buffer_create_dynamic(&context->audio_buffer, MY_BLOCK_SIZE, MY_BUF_LEN, 0) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); goto error; } - if (switch_buffer_create_dynamic(&context->mp3_buffer, MY_BLOCK_SIZE, MY_BUF_LEN, 0) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); - goto error; - } - switch_mutex_init(&context->audio_mutex, SWITCH_MUTEX_NESTED, context->memory_pool); - switch_mutex_init(&context->mp3_mutex, SWITCH_MUTEX_NESTED, context->memory_pool); - InitMP3(&context->mp, OUTSCALE); - context->stream_url = switch_core_sprintf(context->memory_pool, "http://%s", path); - launch_stream_thread(context); - } else if (!switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) { - 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'; + switch_mutex_init(&context->audio_mutex, SWITCH_MUTEX_NESTED, context->memory_pool); + InitMP3(&context->mp, OUTSCALE, context->samplerate); + if (handle->handler) { + context->stream_url = switch_core_sprintf(context->memory_pool, "http://%s", path); + launch_stream_thread(context); } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid URL: %s\n", path); + if (switch_file_open(&context->fd, path, SWITCH_FOPEN_READ, + SWITCH_FPROT_UREAD|SWITCH_FPROT_UWRITE, handle->memory_pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening %s\n", path); + goto error; + } + } + } else if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) { + if (!(context->gfp = lame_init())) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not allocate lame\n"); goto error; } - 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; - } + 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_init_params(context->gfp); + lame_print_config(context->gfp); + + if (handle->handler) { + lame_set_bWriteVbrTag(context->gfp, 0); + lame_mp3_tags_fid(context->gfp, NULL); + + 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 (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 ((file = strchr(host, '/'))) { + *file++ = '\0'; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid URL: %s\n", path); + goto error; + } + + if (!(context->shout = shout_new())) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not allocate shout_t\n"); + goto error; + } + + 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_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_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_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_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_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_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_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_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_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_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_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_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_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; + 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; + } + } else { + /* lame being lame and all has FILE * coded into it's API for some functions so we gotta use it */ + if (!(context->fp = fopen(path, "wb+"))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening %s\n", path); + return SWITCH_STATUS_GENERR; + } } } @@ -424,13 +605,19 @@ static switch_status_t shout_file_read(switch_file_handle_t *handle, void *data, *len = 0; - switch_mutex_lock(context->audio_mutex); - if (context->audio_buffer) { - rb = switch_buffer_read(context->audio_buffer, data, bytes); + if (context->fd) { + rb = decode_fd(context, data, bytes); } else { - context->err++; + switch_mutex_lock(context->audio_mutex); + if (context->audio_buffer) { + rb = switch_buffer_read(context->audio_buffer, data, bytes); + } else { + switch_mutex_lock(context->audio_mutex); + context->err++; + switch_mutex_unlock(context->audio_mutex); + } + switch_mutex_unlock(context->audio_mutex); } - switch_mutex_unlock(context->audio_mutex); if (context->err) { return SWITCH_STATUS_FALSE; @@ -449,27 +636,33 @@ static switch_status_t shout_file_read(switch_file_handle_t *handle, void *data, static switch_status_t shout_file_write(switch_file_handle_t *handle, void *data, size_t *len) { shout_context_t *context = handle->private_info; - - unsigned char mp3buf[2048] = ""; long ret = 0; int rlen; int16_t *audio = data; int nsamples = *len; + + assert(context->gfp); 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)); + if (handle->handler) { + 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); + } else { + if (fwrite(mp3buf, 1, rlen, context->fp) < 0) { return SWITCH_STATUS_FALSE; } } - shout_sync(context->shout); return SWITCH_STATUS_SUCCESS; } @@ -530,6 +723,7 @@ static switch_loadable_module_interface_t shout_module_interface = { SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_module_interface_t **module_interface, char *filename) { supported_formats[0] = "shout"; + supported_formats[1] = "mp3"; shout_file_interface.extens = supported_formats; curl_global_init(CURL_GLOBAL_ALL); diff --git a/src/mod/formats/mod_shout/mpglib.h b/src/mod/formats/mod_shout/mpglib.h index 38b60f7bd1..031d33e245 100644 --- a/src/mod/formats/mod_shout/mpglib.h +++ b/src/mod/formats/mod_shout/mpglib.h @@ -1,7 +1,7 @@ -#ifndef DEBUG_MP3 +#ifdef DEBUG_MP3 #define debug_printf(fmt,...) printf(fmt, ##__VA_ARGS__); #else -#define debug_printf; +#define debug_printf(fmt,...) #endif struct buf { @@ -47,10 +47,10 @@ struct mpstr { #define MP3_ERR -1 #define MP3_OK 0 #define MP3_NEED_MORE 1 - +#define MP3_TOOSMALL 2 void InitMP3Constants(void); -BOOL InitMP3(struct mpstr *mp, long outscale); +BOOL InitMP3(struct mpstr *mp, long outscale, int samplerate); int decodeMP3(struct mpstr *mp,char *inmemory,int inmemsize, char *outmemory,int outmemsize,int *done); void ExitMP3(struct mpstr *mp);