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
This commit is contained in:
parent
504798a64a
commit
84e012c0d9
|
@ -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) {
|
||||
|
|
|
@ -38,13 +38,14 @@
|
|||
#include <curl/curl.h>
|
||||
|
||||
|
||||
#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) {
|
||||
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");
|
||||
context->err++;
|
||||
return 0;
|
||||
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);
|
||||
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,18 +440,33 @@ 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");
|
||||
context->memory_pool = handle->memory_pool;
|
||||
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;
|
||||
}
|
||||
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -295,26 +476,14 @@ static switch_status_t shout_file_open(switch_file_handle_t *handle, char *path)
|
|||
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);
|
||||
|
||||
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)) {
|
||||
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";
|
||||
|
@ -335,6 +504,11 @@ static switch_status_t shout_file_open(switch_file_handle_t *handle, char *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;
|
||||
|
@ -384,6 +558,13 @@ static switch_status_t shout_file_open(switch_file_handle_t *handle, char *path)
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handle->private_info = context;
|
||||
|
@ -424,13 +605,19 @@ static switch_status_t shout_file_read(switch_file_handle_t *handle, void *data,
|
|||
|
||||
*len = 0;
|
||||
|
||||
if (context->fd) {
|
||||
rb = decode_fd(context, data, bytes);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
if (context->err) {
|
||||
return SWITCH_STATUS_FALSE;
|
||||
|
@ -449,19 +636,20 @@ 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 (handle->handler) {
|
||||
if (rlen) {
|
||||
ret = shout_send(context->shout, mp3buf, rlen);
|
||||
if (ret != SHOUTERR_SUCCESS) {
|
||||
|
@ -470,6 +658,11 @@ static switch_status_t shout_file_write(switch_file_handle_t *handle, void *data
|
|||
}
|
||||
}
|
||||
shout_sync(context->shout);
|
||||
} else {
|
||||
if (fwrite(mp3buf, 1, rlen, context->fp) < 0) {
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue