Add btr support to filter code.
authorAndre Noll <maan@systemlinux.org>
Thu, 31 Dec 2009 01:04:01 +0000 (02:04 +0100)
committerAndre Noll <maan@systemlinux.org>
Thu, 31 Dec 2009 01:04:01 +0000 (02:04 +0100)
A bit rough and only the mp3dec filter kind of works. Execute support
for mp3dec is also missing.

19 files changed:
aacdec_filter.c
amp_filter.c
audiod.c
audiod_command.c
buffer_tree.c
buffer_tree.h
compress_filter.c
error.h
fecdec_filter.c
filter.c
filter.h
filter_common.c
grab_client.c
mp3dec_filter.c
oggdec_filter.c
prebuffer_filter.c
stdin.c
wav_filter.c
wmadec_filter.c

index 59c4989..ae3a90e 100644 (file)
 /** \file aacdec_filter.c paraslash's aac (m4a) decoder. */
 
 #include <regex.h>
+#include <stdbool.h>
 
 #include "para.h"
 #include "list.h"
 #include "sched.h"
 #include "ggo.h"
+#include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
 #include "string.h"
index e180db4..b3aa270 100644 (file)
@@ -7,12 +7,14 @@
 /** \file amp_filter.c Paraslash's amplify filter. */
 
 #include <regex.h>
+#include <stdbool.h>
 
 #include "para.h"
 #include "amp_filter.cmdline.h"
 #include "list.h"
 #include "sched.h"
 #include "ggo.h"
+#include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
 #include "error.h"
index 074c261..d946093 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -10,6 +10,7 @@
 #include <dirent.h>
 #include <signal.h>
 #include <openssl/rc4.h>
+#include <stdbool.h>
 
 #include "para.h"
 #include "error.h"
@@ -19,6 +20,7 @@
 #include "sched.h"
 #include "ggo.h"
 #include "recv.h"
+#include "buffer_tree.h"
 #include "filter.h"
 #include "grab_client.h"
 #include "client.cmdline.h"
index bb91671..b790fc1 100644 (file)
@@ -9,12 +9,14 @@
 #include <regex.h>
 #include <sys/types.h>
 #include <dirent.h>
+#include <stdbool.h>
 
 #include "para.h"
 #include "audiod.cmdline.h"
 #include "list.h"
 #include "sched.h"
 #include "ggo.h"
+#include "buffer_tree.h"
 #include "filter.h"
 #include "grab_client.h"
 #include "error.h"
index 7b3355a..28999df 100644 (file)
@@ -47,7 +47,7 @@ struct btr_node {
 #define FOR_EACH_BUFFER_REF_SAFE(_br, _tmp, _btrn) \
        list_for_each_entry_safe((_br), (_tmp), &(_btrn)->input_queue, node)
 
-struct btr_node *btr_new_node(char *name, struct btr_node *parent,
+struct btr_node *btr_new_node(const char *name, struct btr_node *parent,
                btr_command_handler handler, void *context)
 {
        struct btr_node *btrn = para_malloc(sizeof(*btrn));
@@ -287,3 +287,51 @@ void *btr_context(struct btr_node *btrn)
 {
        return btrn->context;
 }
+
+/**
+ * Merge the first two input buffers into one.
+ *
+ * This is a quite expensive operation.
+ *
+ * \return The number of buffers that have been merged (zero, one or two).
+ */
+int btr_merge(struct btr_node *btrn)
+{
+       struct btr_buffer_reference *brs[2], *br;
+       char *bufs[2], *buf;
+       size_t szs[2], sz;
+       int i;
+
+       if (list_empty(&btrn->input_queue))
+               return 0;
+       if (list_is_singular(&btrn->input_queue))
+               return 1;
+       i = 0;
+       /* get references to the first two buffers */
+       FOR_EACH_BUFFER_REF(br, btrn) {
+               brs[i] = br;
+               szs[i] = btr_get_buffer_by_reference(brs[i], bufs + i);
+               i++;
+               if (i == 2)
+                       break;
+       }
+       /* make a new btrb that combines the two buffers and a br to it. */
+       sz = szs[0] + szs[1];
+       //PARA_CRIT_LOG("merging input buffers: (%zu, %zu) -> %zu\n",
+       //      szs[0], szs[1], sz);
+       buf = para_malloc(sz);
+       /* TODO: Avoid this memcopy by introducing btr buffer pool. */
+       memcpy(buf, bufs[0], szs[0]);
+       memcpy(buf + szs[0], bufs[1], szs[1]);
+
+       br = para_malloc(sizeof(*br));
+       br->btrb = new_btrb(buf, sz);
+       br->btrb->refcount = 1;
+       br->consumed = 0;
+
+       /* replace the first two refs by the new one */
+       btr_drop_buffer_reference(brs[0]);
+       btr_drop_buffer_reference(brs[1]);
+       para_list_add(&br->node, &btrn->input_queue);
+       return 2;
+}
index 27f5da2..927d3b6 100644 (file)
@@ -4,7 +4,7 @@ struct btr_node;
 typedef int (*btr_command_handler)(struct btr_node *btrn,
                const char *command, char **result);
 
-struct btr_node *btr_new_node(char *name, struct btr_node *parent,
+struct btr_node *btr_new_node(const char *name, struct btr_node *parent,
                btr_command_handler handler, void *context);
 void btr_del_node(struct btr_node *btrn);
 void btr_add_output(char *buf, size_t size, struct btr_node *btrn);
@@ -19,3 +19,4 @@ int btr_exec_up(struct btr_node *btrn, const char *command, char **value_result)
 int btr_splice_out_node(struct btr_node *btrn);
 void btr_pushdown(struct btr_node *btrn);
 void *btr_context(struct btr_node *btrn);
+int btr_merge(struct btr_node *btrn);
index 7bad4c2..90af8ab 100644 (file)
  */
 
 #include <regex.h>
+#include <stdbool.h>
 
 #include "para.h"
 #include "compress_filter.cmdline.h"
 #include "list.h"
 #include "sched.h"
 #include "ggo.h"
+#include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
 #include "error.h"
diff --git a/error.h b/error.h
index 1a3e648..92039f6 100644 (file)
--- a/error.h
+++ b/error.h
@@ -296,6 +296,7 @@ extern const char **para_errlist[];
        PARA_ERROR(MAD_FRAME_DECODE, "mad frame decode error"), \
        PARA_ERROR(MP3DEC_OVERRUN, "mp3 output buffer overrun"), \
        PARA_ERROR(MP3DEC_SYNTAX, "syntax error in mp3dec config"), \
+       PARA_ERROR(MP3DEC_EOF, "mp3dec: end of file"), \
 
 
 #define FILTER_ERRORS \
index 8acaf81..901a916 100644 (file)
@@ -7,6 +7,7 @@
 /** \file fecdec_filter.c A filter that fec-decodes an audio stream. */
 
 #include <regex.h>
+#include <stdbool.h>
 
 #include <dirent.h>
 #include "para.h"
@@ -14,6 +15,7 @@
 #include "list.h"
 #include "sched.h"
 #include "ggo.h"
+#include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
 #include "portable_io.h"
index 20ea133..45f4bff 100644 (file)
--- a/filter.c
+++ b/filter.c
@@ -14,6 +14,7 @@
 #include "list.h"
 #include "sched.h"
 #include "ggo.h"
+#include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
 #include "stdin.h"
@@ -154,9 +155,41 @@ static int parse_config(int argc, char *argv[])
 }
 
 /* TODO: support more than one filter, actually parse options */
-static int main_btr(void)
+static int __noreturn main_btr(int argc, char *argv[])
 {
-       return 42;
+       static struct sched s;
+       struct filter_node *fn = para_calloc(sizeof(*fn));
+       int ret;
+       struct filter *f;
+
+       sit->btrn = btr_new_node("stdin", NULL, NULL, NULL);
+       stdin_set_defaults(sit);
+       register_task(&sit->task);
+
+       ret = check_filter_arg("mp3dec ", &fn->conf);
+       if (ret < 0)
+               goto err;
+       fn->filter_num = ret;
+       f = filters + fn->filter_num;
+       fn->btrn = btr_new_node(f->name, sit->btrn, f->execute, fn);
+       fn->task.pre_select = f->pre_select;
+       fn->task.post_select = f->post_select;
+       sprintf(fn->task.status, "mp3dec");
+       f->open(fn);
+       register_task(&fn->task);
+
+       sot->btrn = btr_new_node("stdout", fn->btrn, NULL, NULL);
+       stdout_set_defaults(sot);
+       register_task(&sot->task);
+
+       s.default_timeout.tv_sec = 1;
+       s.default_timeout.tv_usec = 0;
+       ret = schedule(&s);
+err:
+       free(fn);
+       if (ret < 0)
+               PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+       exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
 }
 
 /**
@@ -181,7 +214,7 @@ int main(int argc, char *argv[])
        if (ret < 0)
                goto out;
        if (conf.buffer_tree_given) {
-               ret = main_btr();
+               ret = main_btr(argc, argv);
                goto out;
        }
        stdin_set_defaults(sit);
index 9f3ddd6..ab187df 100644 (file)
--- a/filter.h
+++ b/filter.h
@@ -193,6 +193,10 @@ struct filter {
 
        /** The help texts for this filter. */
        struct ggo_help help;
+
+       void (*pre_select)(struct sched *s, struct task *t);
+       void (*post_select)(struct sched *s, struct task *t);
+       btr_command_handler execute;
 };
 
 void close_filters(struct filter_chain *fc);
index 7c71ff3..94e553b 100644 (file)
@@ -9,12 +9,14 @@
 #include <regex.h>
 #include <sys/types.h>
 #include <dirent.h>
+#include <stdbool.h>
 
 #include "para.h"
 #include "list.h"
 #include "sched.h"
 #include "fd.h"
 #include "ggo.h"
+#include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
 #include "string.h"
index d1ef43f..853dfba 100644 (file)
 #include <regex.h>
 #include <sys/types.h>
 #include <dirent.h>
+#include <stdbool.h>
 
 #include "para.h"
 #include "list.h"
 #include "sched.h"
 #include "ggo.h"
+#include "buffer_tree.h"
 #include "filter.h"
 #include "grab_client.h"
 #include "audiod.h"
index bd2b4be..7574504 100644 (file)
@@ -8,12 +8,14 @@
 
 #include <mad.h>
 #include <regex.h>
+#include <stdbool.h>
 
 #include "para.h"
 #include "mp3dec_filter.cmdline.h"
 #include "list.h"
 #include "sched.h"
 #include "ggo.h"
+#include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
 #include "string.h"
@@ -28,6 +30,7 @@ enum mp3dec_flags {
        MP3DEC_FLAG_BAD_DATA = 1,
        /** Some output has already been produced. */
        MP3DEC_FLAG_DECODE_STARTED = 2,
+       MP3DEC_FLAG_NEED_MORE = 4,
 };
 
 /** Data specific to the mp3dec filter. */
@@ -44,6 +47,10 @@ struct private_mp3dec_data {
        struct timeval stream_start_barrier;
        /** Wait until this many input bytes are available. */
        size_t input_len_barrier;
+       /** The number of channels of the current stream. */
+       unsigned int channels;
+       /** Current sample rate in Hz. */
+       unsigned int samplerate;
 };
 
 static int need_bad_data_delay(struct private_mp3dec_data *pmd,
@@ -66,6 +73,7 @@ static int need_bad_data_delay(struct private_mp3dec_data *pmd,
  */
 static int handle_decode_error(struct private_mp3dec_data *pmd, size_t len)
 {
+       const struct timeval delay = {0, 60 * 1000};
        if (!MAD_RECOVERABLE(pmd->stream.error)
                        && pmd->stream.error != MAD_ERROR_BUFLEN) {
                PARA_ERROR_LOG("%s\n", mad_stream_errorstr(&pmd->stream));
@@ -82,11 +90,28 @@ static int handle_decode_error(struct private_mp3dec_data *pmd, size_t len)
         */
        pmd->flags |= MP3DEC_FLAG_BAD_DATA;
        pmd->input_len_barrier = len;
-       tv_add(now, &(struct timeval){0, 60 * 1000},
-               &pmd->stream_start_barrier);
+       tv_add(now, &delay, &pmd->stream_start_barrier);
        return 1;
 }
 
+/** 640K ought to be enough for everybody ;) */
+#define MP3DEC_MAX_PENDING (640 * 1024)
+
+static void mp3dec_pre_select(struct sched *s, struct task *t)
+{
+       struct filter_node *fn = container_of(t, struct filter_node, task);
+       size_t iqs = btr_get_input_queue_size(fn->btrn);
+       struct private_mp3dec_data *pmd = fn->private_data;
+
+       t->error = 0;
+       if (iqs <= pmd->input_len_barrier)
+               return;
+       if (btr_bytes_pending(fn->btrn) > MP3DEC_MAX_PENDING)
+               return; /* FIXME, should use reasonable bound on timeout */
+       s->timeout.tv_sec = 0;
+       s->timeout.tv_usec = 1;
+}
+
 static ssize_t mp3dec(char *inbuffer, size_t len, struct filter_node *fn)
 {
        int i, ret;
@@ -150,6 +175,18 @@ out:
        return copy;
 }
 
+static size_t used_mad_buffer_bytes(struct mad_stream *s, size_t max)
+{
+       size_t rv;
+
+       if (!s->next_frame)
+               return max;
+       /* we still have some data */
+       rv = s->next_frame - s->buffer;
+       assert(rv <= max);
+       return rv;
+}
+
 static void mp3dec_close(struct filter_node *fn)
 {
        struct private_mp3dec_data *pmd = fn->private_data;
@@ -164,6 +201,84 @@ static void mp3dec_close(struct filter_node *fn)
        fn->private_data = NULL;
 }
 
+static void mp3dec_post_select(__a_unused struct sched *s, struct task *t)
+{
+       struct filter_node *fn = container_of(t, struct filter_node, task);
+       int i, ret;
+       struct private_mp3dec_data *pmd = fn->private_data;
+       struct btr_node *btrn = fn->btrn;
+       size_t loaded, used, len = btr_get_input_queue_size(btrn);
+       char *inbuffer, *outbuffer;
+
+       pmd->stream.error = 0;
+       t->error = 0;
+       if (btr_bytes_pending(btrn) > MP3DEC_MAX_PENDING)
+               return;
+       if (need_bad_data_delay(pmd, len))
+               return;
+       if (pmd->input_len_barrier != 0 && btr_no_parent(btrn)) {
+               ret = -E_MP3DEC_EOF;
+               goto err;
+       }
+next_buffer:
+       len = btr_next_buffer(btrn, &inbuffer);
+       if (len == 0)
+               return;
+       mad_stream_buffer(&pmd->stream, (unsigned char *)inbuffer, len);
+next_frame:
+       ret = mad_header_decode(&pmd->frame.header, &pmd->stream);
+       if (ret < 0) {
+               used = used_mad_buffer_bytes(&pmd->stream, len);
+               btr_consume(btrn, used);
+               if (pmd->stream.error == MAD_ERROR_BUFLEN) {
+                       pmd->input_len_barrier = len - used;
+                       ret = btr_merge(btrn);
+                       if (ret != 2)
+                               return;
+               } else if (pmd->stream.error != MAD_ERROR_LOSTSYNC)
+                       PARA_DEBUG_LOG("header decode: %s\n",
+                               mad_stream_errorstr(&pmd->stream));
+               goto next_buffer;
+       }
+       pmd->input_len_barrier = 0;
+       pmd->samplerate = pmd->frame.header.samplerate;
+       pmd->channels = MAD_NCHANNELS(&pmd->frame.header);
+       ret = mad_frame_decode(&pmd->frame, &pmd->stream);
+       if (ret != 0) {
+               PARA_CRIT_LOG("frame decode: %d\n", ret);
+               used = used_mad_buffer_bytes(&pmd->stream, len);
+               ret = handle_decode_error(pmd, used);
+               btr_consume(btrn, used);
+               if (ret < 0)
+                       goto err;
+               if (ret == 0)
+                       goto next_buffer;
+               return;
+       }
+       mad_synth_frame(&pmd->synth, &pmd->frame);
+       pmd->flags |= MP3DEC_FLAG_DECODE_STARTED;
+
+       outbuffer = para_malloc(pmd->synth.pcm.length * 4);
+       loaded = 0;
+       for (i = 0; i < pmd->synth.pcm.length; i++) {
+               int sample = MAD_TO_SHORT(pmd->synth.pcm.samples[0][i]);
+               write_int16_host_endian(outbuffer + loaded, sample);
+               loaded += 2;
+               if (MAD_NCHANNELS(&pmd->frame.header) == 2) { /* stereo */
+                       sample = MAD_TO_SHORT(pmd->synth.pcm.samples[1][i]);
+                       write_int16_host_endian(outbuffer + loaded, sample);
+                       loaded += 2;
+               }
+       }
+       btr_add_output(outbuffer, loaded, btrn);
+       goto next_frame;
+err:
+       assert(ret < 0);
+       mp3dec_close(fn);
+       t->error = ret;
+       btr_del_node(btrn);
+}
+
 static void mp3dec_open(struct filter_node *fn)
 {
        struct private_mp3dec_data *pmd = para_calloc(sizeof(*pmd));
@@ -217,6 +332,8 @@ void mp3dec_filter_init(struct filter *f)
        f->convert = mp3dec;
        f->close = mp3dec_close;
        f->parse_config = mp3dec_parse_config;
+       f->pre_select = mp3dec_pre_select;
+       f->post_select = mp3dec_post_select;
        f->help = (struct ggo_help) {
                .short_help = mp3dec_filter_args_info_help,
                .detailed_help = mp3dec_filter_args_info_detailed_help
index c158df7..3de62cb 100644 (file)
@@ -8,12 +8,14 @@
 
 #include <regex.h>
 #include <vorbis/vorbisfile.h>
+#include <stdbool.h>
 
 #include "para.h"
 #include "oggdec_filter.cmdline.h"
 #include "list.h"
 #include "sched.h"
 #include "ggo.h"
+#include "buffer_tree.h"
 #include "filter.h"
 #include "error.h"
 #include "string.h"
index f23e4e5..357e86c 100644 (file)
@@ -7,12 +7,14 @@
 /** \file prebuffer_filter.c Paraslash's prebuffering filter. */
 
 #include <regex.h>
+#include <stdbool.h>
 
 #include "para.h"
 #include "prebuffer_filter.cmdline.h"
 #include "list.h"
 #include "sched.h"
 #include "ggo.h"
+#include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
 #include "error.h"
diff --git a/stdin.c b/stdin.c
index 8363699..d978ee9 100644 (file)
--- a/stdin.c
+++ b/stdin.c
@@ -52,7 +52,7 @@ static void stdin_pre_select_btr(struct sched *s, struct task *t)
 {
        struct stdin_task *sit = container_of(t, struct stdin_task, task);
 
-       if (btr_no_children(sit->btrn)) {
+       if (btr_no_children(sit->btrn)) { /* TODO: defer node deletion to post select */
                t->error = -E_STDIN_NO_CHILD;
                btr_del_node(sit->btrn);
                sit->btrn = NULL;
index 3c69311..eada243 100644 (file)
@@ -7,12 +7,14 @@
 /** \file wav_filter.c A filter that inserts a wave header. */
 
 #include <regex.h>
+#include <stdbool.h>
 
 #include "para.h"
 #include "error.h"
 #include "list.h"
 #include "sched.h"
 #include "ggo.h"
+#include "buffer_tree.h"
 #include "filter.h"
 #include "string.h"
 #include "portable_io.h"
index b78d762..9cc94f4 100644 (file)
@@ -25,6 +25,7 @@
 #include <string.h>
 #include <regex.h>
 #include <sys/select.h>
+#include <stdbool.h>
 
 #include "para.h"
 #include "error.h"
@@ -32,6 +33,7 @@
 #include "ggo.h"
 #include "string.h"
 #include "sched.h"
+#include "buffer_tree.h"
 #include "filter.h"
 #include "bitstream.h"
 #include "imdct.h"