Merge branch 'refs/heads/t/blob'
[paraslash.git] / opusdec_filter.c
index 482108a6c98df3e7f4891ea52fa81f3405353064..10ed394d295072d909441fe211fbcdc695ff62d0 100644 (file)
@@ -2,9 +2,9 @@
  * Copyright (c) 2002-2007 Jean-Marc Valin
  * Copyright (c) 2008 CSIRO
  * Copyright (c) 2007-2012 Xiph.Org Foundation
- * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2012 Andre Noll <maan@tuebingen.mpg.de>
  *
- * Licensed under the GPL v2. For licencing details see COPYING.
+ * Licensed under the GPL v2, see file COPYING.
  */
 
 /** \file opusdec_filter.c The ogg/opus decoder. */
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+
+/* Silence gcc warning caused by including opus.h */
+#if !defined(__STDC_VERSION__)
+#define __STDC_VERSION__ 0
+#endif
+
 #include <regex.h>
 #include <opus/opus.h>
 #include <opus/opus_multistream.h>
@@ -44,7 +50,6 @@
 #include "para.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
@@ -64,8 +69,9 @@ struct opusdec_context {
        ogg_page ogg_page;
        bool eos;
        int channels;
-       int preskip;
+       uint16_t preskip;
        bool have_opus_stream;
+       bool have_more;
        ogg_int32_t opus_serialno;
 };
 
@@ -135,9 +141,10 @@ static int opusdec_init(ogg_packet *op, struct opusdec_context *ctx)
 static void opusdec_add_output(short *pcm, int frames_available,
                struct btr_node *btrn, struct opusdec_context *ctx)
 {
-       int tmp_skip, num_frames, bytes;
+       int num_frames, bytes;
+       uint16_t tmp_skip;
 
-       tmp_skip = PARA_MIN(ctx->preskip, frames_available);
+       tmp_skip = PARA_MIN((int)ctx->preskip, frames_available);
        ctx->preskip -= tmp_skip;
        num_frames = frames_available - tmp_skip;
        if (num_frames <= 0)
@@ -198,85 +205,88 @@ static int decode_packet(struct opusdec_context *ctx, ogg_packet *op,
        return 1;
 }
 
-static void opusdec_pre_select(struct sched *s, struct task *t)
-{
-       struct filter_node *fn = container_of(t, struct filter_node, task);
-       struct btr_node *btrn = fn->btrn;
-       int ns;
-
-       ns = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
-       if (ns != 0)
-               return sched_min_delay(s);
-       sched_request_timeout_ms(100, s);
-}
+#define OPUSDEC_MAX_OUTPUT_SIZE (1024 * 1024)
 
-static int opusdec_post_select(__a_unused struct sched *s, struct task *t)
+static int opusdec_post_select(__a_unused struct sched *s, void *context)
 {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        struct opusdec_context *ctx = fn->private_data;
        struct btr_node *btrn = fn->btrn;
        int ret;
-       char *btr_buf, *data;
-       size_t nbytes;
        ogg_packet op;
 
        ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
-       if (ret <= 0)
-               goto out;
-       btr_merge(btrn, fn->min_iqs);
-       nbytes = btr_next_buffer(btrn, &btr_buf);
-       nbytes = PARA_MIN(nbytes, (size_t)32768);
-       ret = 0;
-       if (nbytes == 0)
-               goto out;
-       data = ogg_sync_buffer(&ctx->oy, nbytes);
-       memcpy(data, btr_buf, nbytes);
-       btr_consume(btrn, nbytes);
-       ogg_sync_wrote(&ctx->oy, nbytes);
-       for (;;) { /* loop over all ogg pages we got */
-               ret = 0;
-               if (ogg_sync_pageout(&ctx->oy, &ctx->ogg_page) != 1)
+       if (ret < 0) {
+               if (ret != -E_BTR_EOF) /* fatal error */
                        goto out;
-               if (!ctx->stream_init) {
-                       ogg_stream_init(&ctx->os, ogg_page_serialno(&ctx->ogg_page));
+               if (!ctx->have_more) /* EOF */
+                       goto out;
+       } else if (ret == 0 && !ctx->have_more) /* nothing to do */
+               goto out;
+       if (btr_get_output_queue_size(btrn) > OPUSDEC_MAX_OUTPUT_SIZE)
+               return 0;
+       for (;;) {
+               int serial;
+               if (ctx->stream_init) {
+                       ret = ogg_stream_packetout(&ctx->os, &op);
+                       if (ret == 1)
+                               break;
+               }
+               while (ogg_sync_pageout(&ctx->oy, &ctx->ogg_page) != 1) {
+                       char *btr_buf, *data;
+                       size_t nbytes = btr_next_buffer(btrn, &btr_buf);
+                       nbytes = PARA_MIN(nbytes, (size_t)24 * 1024);
+                       //PARA_CRIT_LOG("nbytes: %d\n", nbytes);
+                       ctx->have_more = false;
+                       if (nbytes == 0)
+                               return 0;
+                       data = ogg_sync_buffer(&ctx->oy, nbytes);
+                       memcpy(data, btr_buf, nbytes);
+                       btr_consume(btrn, nbytes);
+                       ogg_sync_wrote(&ctx->oy, nbytes);
+               }
+               ctx->have_more = true;
+               serial = ogg_page_serialno(&ctx->ogg_page);
+               if (ctx->stream_init) {
+                       if (serial != ctx->os.serialno)
+                               ogg_stream_reset_serialno(&ctx->os, serial);
+               } else {
+                       ogg_stream_init(&ctx->os, serial);
                        ctx->stream_init = true;
                }
-               if (ogg_page_serialno(&ctx->ogg_page) != ctx->os.serialno)
-                       ogg_stream_reset_serialno(&ctx->os,
-                               ogg_page_serialno(&ctx->ogg_page));
                /* Add page to the bitstream */
                ogg_stream_pagein(&ctx->os, &ctx->ogg_page);
-               for (;;) { /* loop over all opus packets */
-                       ret = ogg_stream_packetout(&ctx->os, &op);
-                       if (ret != 1)
-                               break;
-                       ret = decode_packet(ctx, &op, btrn);
-                       if (ret < 0)
-                               goto out;
-                       ctx->packet_count++;
-                       if (ctx->eos)
-                               ctx->have_opus_stream = false;
-               }
        }
+       ret = decode_packet(ctx, &op, btrn);
+       if (ret < 0)
+               goto out;
+       ctx->packet_count++;
+       if (ctx->eos)
+               ctx->have_opus_stream = false;
 out:
        if (ret < 0)
                btr_remove_node(&fn->btrn);
        return ret;
 }
 
-/**
- * The init function of the opusdec filter.
- *
- * \param f Pointer to the filter struct to initialize.
- *
- * \sa filter::init.
- */
-void opusdec_filter_init(struct filter *f)
+static void opusdec_pre_select(struct sched *s, void *context)
 {
-       f->open = opusdec_open;
-       f->close = opusdec_close;
-       f->pre_select = generic_filter_pre_select;
-       f->pre_select = opusdec_pre_select;
-       f->post_select = opusdec_post_select;
-       f->execute = opusdec_execute;
+       struct filter_node *fn = context;
+       struct opusdec_context *ctx = fn->private_data;
+       int ret = btr_node_status(fn->btrn, fn->min_iqs, BTR_NT_INTERNAL);
+
+       if (ret != 0)
+               return sched_min_delay(s);
+       if (!ctx->have_more)
+               return;
+       if (btr_get_output_queue_size(fn->btrn) <= OPUSDEC_MAX_OUTPUT_SIZE)
+               return sched_min_delay(s);
 }
+
+const struct filter lsg_filter_cmd_com_opusdec_user_data = {
+       .open = opusdec_open,
+       .close = opusdec_close,
+       .pre_select = opusdec_pre_select,
+       .post_select = opusdec_post_select,
+       .execute = opusdec_execute,
+};