oggdec filter improvements.
authorAndre Noll <maan@systemlinux.org>
Sat, 19 Sep 2009 08:38:25 +0000 (10:38 +0200)
committerAndre Noll <maan@systemlinux.org>
Sat, 19 Sep 2009 08:38:25 +0000 (10:38 +0200)
Try to open the ogg vorbis callbacks as soon as possible rather
than waiting until the input buffer reaches the given initial buffer
size. If that fails, try again later when more data is available but
fail if the input buffer size is larger than the initial buffer size
and we can still not open the ov callbacks.

Also, if a hole was detected, likely because we started streaming
in the middle of the file, add an additional delay to avoid buffer
underruns.

oggdec_filter.c

index b158a5e17444112148bd1d33d90dba87e912274e..bdfafedfbab45153a566dc953a989c22e15d097d 100644 (file)
@@ -35,6 +35,8 @@ struct private_oggdec_data {
        size_t inbuf_len;
        /** The number of bytes consumed from the input buffer. */
        size_t converted;
        size_t inbuf_len;
        /** The number of bytes consumed from the input buffer. */
        size_t converted;
+       /** When to start producing output. */
+       struct timeval stream_start;
 };
 
 static size_t cb_read(void *buf, size_t size, size_t nmemb, void *datasource)
 };
 
 static size_t cb_read(void *buf, size_t size, size_t nmemb, void *datasource)
@@ -124,24 +126,30 @@ static ssize_t ogg_convert(char *inbuffer, size_t len, struct filter_node *fn)
        pod->converted = 0;
 
        if (!pod->vf) {
        pod->converted = 0;
 
        if (!pod->vf) {
-               int ib = 1024 * conf->initial_buffer_arg; /* initial buffer */
                if (*fn->fc->input_error < 0)
                        return *fn->fc->input_error;
                if (*fn->fc->input_error < 0)
                        return *fn->fc->input_error;
-               if (len < ib) {
-                       PARA_DEBUG_LOG("initial input buffer %zd/%d, "
-                               "waiting for more data\n", len, ib);
+               if (!len)
                        return 0;
                        return 0;
-               }
                pod->vf = para_malloc(sizeof(struct OggVorbis_File));
                PARA_NOTICE_LOG("input buffer: %zd, opening ov callbacks\n", len);
                ret = ov_open_callbacks(fn, pod->vf,
                        NULL, /* no initial buffer */
                        0, /* no initial bytes */
                        ovc); /* the ov_open_callbacks */
                pod->vf = para_malloc(sizeof(struct OggVorbis_File));
                PARA_NOTICE_LOG("input buffer: %zd, opening ov callbacks\n", len);
                ret = ov_open_callbacks(fn, pod->vf,
                        NULL, /* no initial buffer */
                        0, /* no initial bytes */
                        ovc); /* the ov_open_callbacks */
+               if (ret == OV_ENOTVORBIS) {
+                       /* this might be due to the input buffer being too small */
+                       int ib = 1024 * conf->initial_buffer_arg; /* initial buffer */
+                       if (len < ib) {
+                               PARA_INFO_LOG("initial input buffer %zd/%d, "
+                                       "waiting for more data\n", len, ib);
+                               free(pod->vf);
+                               pod->vf = NULL;
+                               return 0;
+                       }
+                       return -E_OGGDEC_NOTVORBIS;
+               }
                if (ret == OV_EREAD)
                        return -E_OGGDEC_READ;
                if (ret == OV_EREAD)
                        return -E_OGGDEC_READ;
-               if (ret == OV_ENOTVORBIS)
-                       return -E_OGGDEC_NOTVORBIS;
                if (ret == OV_EVERSION)
                        return -E_OGGDEC_VERSION;
                if (ret == OV_EBADHEADER)
                if (ret == OV_EVERSION)
                        return -E_OGGDEC_VERSION;
                if (ret == OV_EBADHEADER)
@@ -152,13 +160,27 @@ static ssize_t ogg_convert(char *inbuffer, size_t len, struct filter_node *fn)
                fn->fc->samplerate = ov_info(pod->vf, 0)->rate;
                PARA_NOTICE_LOG("%d channels, %d Hz\n", fn->fc->channels,
                        fn->fc->samplerate);
                fn->fc->samplerate = ov_info(pod->vf, 0)->rate;
                PARA_NOTICE_LOG("%d channels, %d Hz\n", fn->fc->channels,
                        fn->fc->samplerate);
+               /* wait a bit to avoid buffer underruns */
+               tv_add(now, &(struct timeval){0, 500 * 1000}, &pod->stream_start);
+               return pod->converted;
+       }
+       if (tv_diff(now, &pod->stream_start, NULL) < 0) {
+               PARA_DEBUG_LOG("initial delay..\n");
+               return 0;
        }
        while (fn->loaded < fn->bufsize) {
                int length = fn->bufsize - fn->loaded;
                long read_ret = ov_read(pod->vf, fn->buf + fn->loaded, length,
                        ENDIAN, 2 /* 16 bit */, 1 /* signed */, NULL);
        }
        while (fn->loaded < fn->bufsize) {
                int length = fn->bufsize - fn->loaded;
                long read_ret = ov_read(pod->vf, fn->buf + fn->loaded, length,
                        ENDIAN, 2 /* 16 bit */, 1 /* signed */, NULL);
-               if (read_ret == OV_HOLE || !read_ret)
+               if (read_ret == 0)
+                       return pod->converted;
+               if (read_ret == OV_HOLE) {
+                       if (!fn->loaded) {
+                               PARA_INFO_LOG("hole, delaying playback\n");
+                               tv_add(now, &(struct timeval){0, 500 * 1000}, &pod->stream_start);
+                       }
                        return pod->converted;
                        return pod->converted;
+               }
                if (read_ret < 0)
                        return -E_OGGDEC_BADLINK;
                fn->loaded += read_ret;
                if (read_ret < 0)
                        return -E_OGGDEC_BADLINK;
                fn->loaded += read_ret;