]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
MODFORM-32 - mp3 improvements (seeking, cleanup)
authorRupa Schomaker <rupa@rupa.com>
Mon, 13 Jul 2009 19:01:03 +0000 (19:01 +0000)
committerRupa Schomaker <rupa@rupa.com>
Mon, 13 Jul 2009 19:01:03 +0000 (19:01 +0000)
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@14223 d0543943-73ff-0310-b7d9-9358b9ac24b2

src/mod/formats/mod_shout/mod_shout.c

index f0ffe1633e53eb61f7367c3bdb8bd31ce3aa0dda..d5769d2a9798b7d942cfd865f54c72ea0cd0e79a 100644 (file)
@@ -25,6 +25,7 @@
  * 
  * Anthony Minessale II <anthm@freeswitch.org>
  * Rupa Schomaker <rupa@rupa.com>
+ * John Wehle <john@feith.com>
  *
  * mod_shout.c -- Icecast Module
  *
@@ -109,7 +110,6 @@ struct shout_context {
        int err;
        int mp3err;
        int dlen;
-       switch_file_t *fd;
        FILE *fp;
        int samplerate;
        uint8_t thread_running;
@@ -129,21 +129,35 @@ struct shout_context {
 
 typedef struct shout_context shout_context_t;
 
-static size_t decode_fd(shout_context_t *context, void *data, size_t bytes);
+static void decode_fd(shout_context_t *context, void *data, size_t bytes);
 
 static inline void free_context(shout_context_t *context)
 {
        int ret;
 
        if (context) {
+               switch_mutex_lock(context->audio_mutex);
                context->err++;
-               switch_thread_rwlock_wrlock(context->rwlock);
+               switch_mutex_unlock(context->audio_mutex);
+
+               if (context->stream_url) {
+                       int sanity = 0;
 
-               if (context->fd) {
-                       switch_file_close(context->fd);
-                       context->fd = NULL;
+                       while (context->thread_running) {
+                               switch_yield(500000);
+                               if (++sanity > 10) {
+                                       break;
+                               }
+                       }
                }
 
+               switch_thread_rwlock_wrlock(context->rwlock);
+
+               if (context->mh) {
+                       mpg123_close(context->mh);
+                       mpg123_delete(context->mh);
+               }
+               
                if (context->fp) {
                        unsigned char mp3buffer[8192];
                        int len;
@@ -173,12 +187,6 @@ static inline void free_context(shout_context_t *context)
                        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->shout) {
                        shout_close(context->shout);
                        context->shout = NULL;
@@ -189,23 +197,12 @@ static inline void free_context(shout_context_t *context)
                        context->gfp = NULL;
                }
 
-               if (context->stream_url) {
-                       int sanity = 0;
-
-                       while (context->thread_running) {
-                               switch_yield(500000);
-                               if (++sanity > 10) {
-                                       break;
-                               }
-                       }
-
-                       
+               if (context->audio_buffer) {
+                       switch_buffer_destroy(&context->audio_buffer);
                }
 
-               if (context->mh) {
-                       mpg123_delete(context->mh);
-               }
-               
+               switch_mutex_destroy(context->audio_mutex);
+
                switch_thread_rwlock_unlock(context->rwlock);
                switch_thread_rwlock_destroy(context->rwlock);
        }
@@ -289,154 +286,60 @@ static void log_msg(char const *fmt, va_list ap)
        }
 }
 
-static size_t decode_fd(shout_context_t *context, void *data, size_t bytes)
+static void decode_fd(shout_context_t *context, void *data, size_t bytes)
 {
        int decode_status = 0;
-       size_t dlen = 0;
-       int x = 0;
-       unsigned char *in;
-       int inlen = 0;
-       unsigned char *out;
-       int outlen;
-       int usedlen;
-       unsigned char inbuf[MP3_SCACHE];
-       int done = 0;
-       size_t used;
-       size_t lp;
-       size_t rb = 0;
+       size_t usedlen;
        
-       while (context->eof < 2 && switch_buffer_inuse(context->audio_buffer) < bytes) {
-               lp = sizeof(inbuf);
-               if (!context->eof && ((switch_file_read(context->fd, inbuf, &lp) != SWITCH_STATUS_SUCCESS) || lp == 0)) {
-                       context->eof++;
-               }
-
-               inlen = (int) lp;
-               in = inbuf;
-
-               out = context->decode_buf;
-               outlen = (int) sizeof(context->decode_buf);
+       while (!context->err && !context->eof && switch_buffer_inuse(context->audio_buffer) < bytes) {
                usedlen = 0;
-               x = 0;
 
-               if (lp < bytes) {
-                       done = 1;
-               }
-               
-               do {
-                       if (context->eof) {
-                               decode_status = mpg123_read(context->mh, out, outlen, &dlen);
-                       } else {
-                               decode_status = mpg123_decode(context->mh, in, inlen, out, outlen, &dlen);
-                       }
-
-                       if (context->err) {
-                               goto error;
-                       }
-
-                       if (!x) {
-                               in = NULL;
-                               inlen = 0;
-                               x++;
-                       }
+               decode_status = mpg123_read(context->mh, context->decode_buf, sizeof(context->decode_buf), &usedlen);
 
-                       if (decode_status == MPG123_NEW_FORMAT) {
-                               continue;
-                       } else if (decode_status == MPG123_OK) {
-                               usedlen = dlen;
-                               break;
-                       } else if (decode_status == MPG123_DONE || (context->eof && decode_status == MPG123_NEED_MORE)) {
+               if (decode_status == MPG123_NEW_FORMAT) {
+                       continue;
+               } else if (decode_status == MPG123_OK) {
+                       ;
+               } else if (decode_status == MPG123_DONE || decode_status == MPG123_NEED_MORE) {
+                       context->eof++;
+               } else if (decode_status == MPG123_ERR || decode_status > 0) {
+                       if (++context->mp3err >= 5) {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Decoder Error!\n");
                                context->eof++;
-                               goto end;
-                       } else if (decode_status == MPG123_ERR || decode_status > 0) {
-                               if (++context->mp3err >= 5) {
-                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Decoder Error!\n");
-                                       context->eof++;
-                                       goto end;
-                               }
-                               continue;
+                               goto error;
                        }
-
-                       context->mp3err = 0;
-                       
-                       usedlen += dlen;
-                       out += dlen;
-                       outlen -= dlen;
-                       dlen = 0;
-
-
-               } while (decode_status != MPG123_NEED_MORE);
-
-               if (context->audio_buffer) {
-                       switch_buffer_write(context->audio_buffer, context->decode_buf, usedlen);
-               } else {
-                       goto error;
-               }
-
-               if (done) {
-                       break;
+                       continue;
                }
-       }
 
-  end:
-       
-       used = switch_buffer_inuse(context->audio_buffer);
+               context->mp3err = 0;
 
-       if (context->eof || done || used >= bytes) {
-               if (!(rb = switch_buffer_read(context->audio_buffer, data, bytes))) {
-                       goto error;
-               }
-               return rb;
+               switch_buffer_write(context->audio_buffer, context->decode_buf, usedlen);
        }
 
-       return 0;
+       return;
 
   error:
        switch_mutex_lock(context->audio_mutex);
        context->err++;
        switch_mutex_unlock(context->audio_mutex);
-       return 0;
 }
 
-#define error_check() if (context->err) goto error;
-
 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 decode_status = 0;
-       size_t dlen = 0;
-       int x = 0;
-       unsigned char *in;
-       int inlen;
-       unsigned char *out;
-       int outlen;
-       int usedlen;
+       size_t usedlen;
        uint32_t used, buf_size = 1024 * 128; /* do not make this 64 or less, stutter will ensue after 
                                                 first 64k buffer is dry */
-       
-       in = ptr;
-       inlen = realsize;
-       out = context->decode_buf;
-       outlen = sizeof(context->decode_buf);
-       usedlen = 0;
-
-       error_check();
 
        if (context->prebuf) {
                buf_size = context->prebuf;
        }
 
        /* make sure we aren't over zealous by slowing down the stream when the buffer is too full */
-       for (;;) {
-               error_check();
-
+       while (!context->err) {
                switch_mutex_lock(context->audio_mutex);
-               if (!context->audio_buffer) {
-                       context->err++;
-                       break;
-               }
-
                used = switch_buffer_inuse(context->audio_buffer);
                switch_mutex_unlock(context->audio_mutex);
 
@@ -448,55 +351,36 @@ static size_t stream_callback(void *ptr, size_t size, size_t nmemb, void *data)
                switch_yield(500000);
        }
 
-       error_check();
+       if (mpg123_feed(context->mh, ptr, realsize) != MPG123_OK) {
+               goto error;
+       }
 
        do {
-               decode_status = mpg123_decode(context->mh, in, inlen, out, outlen, &dlen);
-
-               error_check();
+               usedlen = 0;
 
-               if (!x) {
-                       in = NULL;
-                       inlen = 0;
-                       x++;
-               }
+               decode_status = mpg123_read(context->mh, context->decode_buf, sizeof(context->decode_buf), &usedlen);
 
                if (decode_status == MPG123_NEW_FORMAT) {
                        continue;
-               } else if (decode_status == MPG123_OK) {
-                       usedlen = dlen;
-                       break;
-               } else if (decode_status == MPG123_ERR) {
-
-                       if (++context->mp3err >= 20) {
+               } else if (decode_status == MPG123_OK || decode_status == MPG123_NEED_MORE) {
+                       ;
+               } else if (decode_status == MPG123_DONE) {
+                       context->eof++;
+               } else if (decode_status == MPG123_ERR || decode_status > 0) {
+                       if (++context->mp3err >= 5) {
                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Decoder Error!\n");
+                               context->eof++;
                                goto error;
                        }
-
-                       mpg123_delete(context->mh);
-                       context->mh = our_mpg123_new(NULL, NULL);
-                       mpg123_open_feed(context->mh);
-                       mpg123_param(context->mh, MPG123_FORCE_RATE, context->samplerate, 0);
-                       mpg123_param(context->mh, MPG123_FLAGS, MPG123_MONO_MIX, 0);
-                       mpg123_param(context->mh, MPG123_FLAGS, MPG123_SEEKBUFFER|MPG123_MONO_MIX, 0);
-                       return realsize;
+                       continue;
                }
 
                context->mp3err = 0;
-               usedlen += dlen;
-               out += dlen;
-               outlen -= dlen;
-               dlen = 0;
-       } while (decode_status != MPG123_NEED_MORE);
 
-
-       switch_mutex_lock(context->audio_mutex);
-       if (context->audio_buffer) {
+               switch_mutex_lock(context->audio_mutex);
                switch_buffer_write(context->audio_buffer, context->decode_buf, usedlen);
-       } else {
-               goto error;
-       }
-       switch_mutex_unlock(context->audio_mutex);
+               switch_mutex_unlock(context->audio_mutex);
+       } while (!context->err && !context->eof && decode_status != MPG123_NEED_MORE);
 
        return realsize;
 
@@ -537,9 +421,7 @@ static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void
        curl_easy_cleanup(curl_handle);
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Read Thread Done\n");
 
-       switch_mutex_lock(context->audio_mutex);
-       context->err++;
-       switch_mutex_unlock(context->audio_mutex);
+       context->eof++;
        context->thread_running = 0;
        switch_thread_rwlock_unlock(context->rwlock);
        return NULL;
@@ -549,18 +431,31 @@ static void launch_read_stream_thread(shout_context_t *context)
 {
        switch_thread_t *thread;
        switch_threadattr_t *thd_attr = NULL;
-
-       if (context->err) {
-               return;
-       }
+       int sanity = 10;
+       size_t used;
 
        context->thread_running = 1;
        switch_threadattr_create(&thd_attr, context->memory_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, context, context->memory_pool);
+
+       while (context->thread_running && --sanity) {
+               /* at least 1s of audio and up to 5s initialize */
+               switch_mutex_lock(context->audio_mutex);
+               used = switch_buffer_inuse(context->audio_buffer);
+               switch_mutex_unlock(context->audio_mutex);
+
+               if (used >= (2 * context->samplerate)) {
+                        break;
+               }
+
+               switch_yield(500000);
+       }
 }
 
+#define error_check() if (context->err) goto error;
+
 static void *SWITCH_THREAD_FUNC write_stream_thread(switch_thread_t *thread, void *obj)
 {
        shout_context_t *context = (shout_context_t *) obj;
@@ -677,7 +572,6 @@ static switch_status_t shout_file_open(switch_file_handle_t *handle, const char
        char *username, *password, *port;
        char *err = NULL;
        int portno = 0;
-       int sanity = 0;
 
        if ((context = switch_core_alloc(handle->memory_pool, sizeof(*context))) == 0) {
                return SWITCH_STATUS_MEMERR;
@@ -695,35 +589,35 @@ static switch_status_t shout_file_open(switch_file_handle_t *handle, const char
        
        switch_thread_rwlock_rdlock(context->rwlock);
 
+       switch_mutex_init(&context->audio_mutex, SWITCH_MUTEX_NESTED, context->memory_pool);
+
        if (switch_test_flag(handle, SWITCH_FILE_FLAG_READ)) {
                if (switch_buffer_create_dynamic(&context->audio_buffer, TC_BUFFER_SIZE, TC_BUFFER_SIZE * 2, 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);
                context->mh = our_mpg123_new(NULL, NULL);
-               mpg123_open_feed(context->mh);
                mpg123_format_all(context->mh);
                mpg123_param(context->mh, MPG123_FORCE_RATE, context->samplerate, 0);           
 
                if (handle->handler) {
                        mpg123_param(context->mh, MPG123_FLAGS, MPG123_SEEKBUFFER|MPG123_MONO_MIX, 0);
+                       if (mpg123_open_feed(context->mh) != MPG123_OK) {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening mpg feed\n");
+                               goto error;
+                       }
                        context->stream_url = switch_core_sprintf(context->memory_pool, "http://%s", path);
                        context->prebuf = handle->prebuf;
                        launch_read_stream_thread(context);
-                       while((switch_buffer_inuse(context->audio_buffer) < (switch_size_t)(2 * context->samplerate)) && ++sanity < 10) { 
-                               /* at least 1s of audio and up to 5s initialize */
-                               switch_yield(500000);
-                       }
                } else {
+                       handle->seekable = 1;
                        mpg123_param(context->mh, MPG123_FLAGS, MPG123_MONO_MIX, 0);
-                       if (switch_file_open(&context->fd, path, SWITCH_FOPEN_READ, SWITCH_FPROT_UREAD | SWITCH_FPROT_UWRITE, handle->memory_pool) != 
-                               SWITCH_STATUS_SUCCESS) {
+                       if (mpg123_open(context->mh, path) != MPG123_OK) {
                                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())) {
@@ -759,7 +653,6 @@ static switch_status_t shout_file_open(switch_file_handle_t *handle, const char
                                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);
                        lame_set_bWriteVbrTag(context->gfp, 0);
                        lame_mp3_tags_fid(context->gfp, NULL);
 
@@ -850,7 +743,6 @@ static switch_status_t shout_file_open(switch_file_handle_t *handle, const char
                        }
 
                } else {
-                       handle->seekable = 1;
                        /* 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);
@@ -891,29 +783,17 @@ static switch_status_t shout_file_seek(switch_file_handle_t *handle, unsigned in
 {
        shout_context_t *context = handle->private_info;
 
-       if (handle->handler) {
+       if (handle->handler || switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
                return SWITCH_STATUS_FALSE;
        } else {
-               switch_mutex_lock(context->audio_mutex);
-               if (context->audio_buffer) {
-                       if (context->fd) {
-                               switch_file_seek(context->fd, whence, &samples);
-                       } else if (context->fp) {
-                               *cur_sample = fseek(context->fp, *cur_sample, whence);
-                       }
+               if (whence == SWITCH_SEEK_CUR) {
+                       samples -= switch_buffer_inuse(context->audio_buffer) / sizeof(int16_t);
+               }
 
-                       mpg123_delete(context->mh);
-                       context->mh = our_mpg123_new(NULL, NULL);
-                       mpg123_open_feed(context->mh);
-                       mpg123_param(context->mh, MPG123_FORCE_RATE, context->samplerate, 0);
-                       mpg123_param(context->mh, MPG123_FLAGS, MPG123_MONO_MIX, 0);
-                       switch_buffer_zero(context->audio_buffer);
+               switch_buffer_zero(context->audio_buffer);
+               *cur_sample = mpg123_seek (context->mh, samples, whence);
 
-               } else {
-                       context->err++;
-               }
-               switch_mutex_unlock(context->audio_mutex);
-               return SWITCH_STATUS_SUCCESS;
+               return *cur_sample >= 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
        }
 }
 
@@ -924,26 +804,18 @@ static switch_status_t shout_file_read(switch_file_handle_t *handle, void *data,
 
        *len = 0;
 
-       if (!context || context->err) {
+       if (!context) {
                return SWITCH_STATUS_FALSE;
        }
 
-       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 (!handle->handler)
+               decode_fd(context, data, bytes);
 
-       if (context->err) {
+       switch_mutex_lock(context->audio_mutex);
+       rb = switch_buffer_read(context->audio_buffer, data, bytes);
+       switch_mutex_unlock(context->audio_mutex);
+
+       if (!rb && (context->eof || context->err)) {
                return SWITCH_STATUS_FALSE;
        }