Implement the flac decoding filter.
[paraslash.git] / flacdec_filter.c
index 40795023d7531ac58081ed1198cea528d4489d57..027596438b6a7d61ea31c30facffdb2932bdd9f3 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <regex.h>
 #include <stdbool.h>
+#include <FLAC/stream_decoder.h>
 
 #include "para.h"
 #include "list.h"
 #include "filter.h"
 #include "error.h"
 #include "string.h"
+
+struct private_flacdec_data {
+       FLAC__StreamDecoder *decoder;
+       bool have_more;
+       /*
+        * We can not consume directly what was copied by the read callback
+        * because we might need to feed unconsumend bytes to the decoder again
+        * after the read callback ran out of data and returned ABORT. So we
+        * track how many bytes are unconsumed so far.
+        */
+       size_t unconsumed;
+};
+
+static FLAC__StreamDecoderReadStatus read_cb(
+               __a_unused const FLAC__StreamDecoder *decoder,
+               FLAC__byte buffer[], size_t *bytes, void *client_data)
+{
+       struct filter_node *fn = client_data;
+       struct private_flacdec_data *pfd = fn->private_data;
+       struct btr_node *btrn = fn->btrn;
+       char *btr_buf;
+       size_t copy, want = *bytes, have;
+       int ns;
+
+       *bytes = 0;
+       assert(want > 0);
+       ns = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
+       if (ns < 0)
+               return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+       for (;;) {
+               have = btr_next_buffer_omit(btrn, pfd->unconsumed, &btr_buf);
+               if (have == 0)
+                       break;
+               copy = PARA_MIN(want, have);
+               //PARA_CRIT_LOG("want: %zu, have: %zu, unconsumed %zu\n",
+               //      want, have, pfd->unconsumed);
+               memcpy(buffer, btr_buf, copy);
+               pfd->unconsumed += copy;
+               *bytes += copy;
+               buffer += copy;
+               want -= copy;
+               if (want == 0)
+                       break;
+       }
+       if (*bytes > 0)
+               return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+       /*
+        * We are kind of screwed here. Returning CONTINUE with a byte count of
+        * zero leads to an endless loop, so we must return either EOF or
+        * ABORT. Unfortunately, both options require to flush the decoder
+        * afterwards because libFLAC refuses to resume decoding if the decoder
+        * is in EOF or ABORT state. But flushing implies dropping the decoder
+        * input queue, so buffered data is lost.
+        *
+        * We work around this shortcoming by remembering the number of
+        * unconsumed bytes in pfd->unconsumed. In the write/meta callbacks,
+        * this number is decreased whenever a frame has been decoded
+        * successfully and btr_consume() has been called to consume the bytes
+        * corresponding to the decoded frame.  After returning ABORT here, the
+        * decoder can be flushed, and we will feed the unconsumed bytes again.
+        */
+       return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+}
+
+/*
+ * The exact value does not really matter. It just has to be larger than the
+ * size of the input buffer of the bitstream reader of libflac.
+ */
+#define TELL_CB_DUMMY_VAL 1000000
+
+/*
+ * FLAC__stream_decoder_get_decode_position() invokes this callback. The flac
+ * library then gets the number of unconsumed bytes from the bitstream reader,
+ * subtracts this number from the offset returned here and returns the
+ * difference as the decode position.
+ */
+static FLAC__StreamDecoderTellStatus tell_cb(__a_unused const FLAC__StreamDecoder *decoder,
+               FLAC__uint64 *absolute_byte_offset, __a_unused void *client_data)
+{
+       *absolute_byte_offset = TELL_CB_DUMMY_VAL;
+       return FLAC__STREAM_DECODER_TELL_STATUS_OK;
+}
+
+/*
+ * There is no API function that returns the number of unconsumed bytes
+ * directly. The trick is to define a tell callback which always returns a
+ * fixed dummy value and compute the number of unconsumed bytes from the return
+ * value of FLAC__stream_decoder_get_decode_position().
+ */
+static void flac_consume(struct filter_node *fn)
+{
+       struct private_flacdec_data *pfd = fn->private_data;
+       struct btr_node *btrn = fn->btrn;
+       FLAC__uint64 x;
+
+       FLAC__stream_decoder_get_decode_position(pfd->decoder, &x);
+       assert(x <= TELL_CB_DUMMY_VAL);
+       x = TELL_CB_DUMMY_VAL - x; /* number of unconsumed bytes */
+       assert(x <= pfd->unconsumed);
+       btr_consume(btrn, pfd->unconsumed - x);
+       pfd->unconsumed = x;
+}
+
+static FLAC__StreamDecoderWriteStatus write_cb(
+               const FLAC__StreamDecoder *decoder,
+               const FLAC__Frame *frame,
+               const FLAC__int32 *const buffer[],
+               void *client_data)
+{
+       struct filter_node *fn = client_data;
+       struct btr_node *btrn = fn->btrn;
+       size_t k, n = frame->header.blocksize;
+       unsigned channels = FLAC__stream_decoder_get_channels(decoder);
+       char *outbuffer = para_malloc(n * channels * 2);
+
+       if (channels == 1) {
+               for (k = 0; k < n; k++) {
+                       int sample = buffer[0][k];
+                       write_int16_host_endian(outbuffer + 2 * k, sample);
+               }
+       } else {
+               for (k = 0; k < n; k++) {
+                       int left = buffer[0][k], right = buffer[1][k];
+                       write_int16_host_endian(outbuffer + 4 * k, left);
+                       write_int16_host_endian(outbuffer + 4 * k + 2, right);
+               }
+       }
+       btr_add_output(outbuffer, n * 4, btrn);
+       flac_consume(fn);
+       return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+static void meta_cb (__a_unused const FLAC__StreamDecoder *decoder,
+               __a_unused const FLAC__StreamMetadata *metadata,
+               void *client_data)
+{
+       flac_consume(client_data);
+}
+
+static void error_cb( __a_unused const FLAC__StreamDecoder *decoder,
+               FLAC__StreamDecoderErrorStatus status,
+               __a_unused void *client_data)
+{
+       PARA_ERROR_LOG("%s\n", FLAC__StreamDecoderErrorStatusString[status]);
+}
+
+static int flacdec_init(struct filter_node *fn)
+{
+       struct private_flacdec_data *pfd = fn->private_data;
+       FLAC__StreamDecoderInitStatus init_status;
+
+       PARA_INFO_LOG("initializing flac decoder\n");
+       pfd->decoder = FLAC__stream_decoder_new();
+       if (!pfd->decoder)
+               return -E_FLACDEC_DECODER_ALLOC;
+       FLAC__stream_decoder_set_metadata_respond_all(pfd->decoder);
+       init_status = FLAC__stream_decoder_init_stream(pfd->decoder, read_cb,
+               NULL /* seek */, tell_cb, NULL /* length_cb */, NULL /* eof_cb */,
+               write_cb, meta_cb, error_cb, fn);
+       if (init_status == FLAC__STREAM_DECODER_INIT_STATUS_OK)
+               return 1;
+       FLAC__stream_decoder_delete(pfd->decoder);
+       return -E_FLACDEC_DECODER_INIT;
+}
+
 static int flacdec_execute(struct btr_node *btrn, const char *cmd,
                char **result)
 {
-       return 0;
+       struct filter_node *fn = btr_context(btrn);
+       struct private_flacdec_data *pfd = fn->private_data;
+       unsigned sample_rate = FLAC__stream_decoder_get_sample_rate(pfd->decoder);
+       unsigned channels = FLAC__stream_decoder_get_channels(pfd->decoder);
+
+       return decoder_execute(cmd, sample_rate, channels, result);
+}
+
+#define FLACDEC_MAX_OUTPUT_SIZE (640 * 1024)
+
+static bool output_queue_full(struct btr_node *btrn)
+{
+       return btr_get_output_queue_size(btrn) > FLACDEC_MAX_OUTPUT_SIZE;
+}
+
+static void flacdec_pre_select(struct sched *s, struct task *t)
+{
+       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct private_flacdec_data *pfd = fn->private_data;
+       struct btr_node *btrn = fn->btrn;
+       int ret;
+
+       ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
+       if (ret < 0)
+               return sched_min_delay(s);
+       if (output_queue_full(btrn))
+               return sched_request_timeout_ms(30, s);
+       if (ret > 0 || pfd->have_more)
+               return sched_min_delay(s);
 }
 
 static void flacdec_post_select(__a_unused struct sched *s, struct task *t)
 {
+       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct private_flacdec_data *pfd = fn->private_data;
+       struct btr_node *btrn = fn->btrn;
+       int ret;
 
+       if (output_queue_full(btrn))
+               return;
+       ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
+       if (ret < 0 && ret != -E_BTR_EOF) /* fatal error */
+               goto out;
+       if (ret <= 0 && !pfd->have_more) /* nothing to do */
+               goto out;
+       if (!pfd->decoder) {
+               ret = flacdec_init(fn);
+               goto out;
+       }
+       pfd->unconsumed = 0;
+       for (;;) {
+               if (output_queue_full(btrn)) {
+                       pfd->have_more = true;
+                       break;
+               }
+               pfd->have_more = false;
+               FLAC__StreamDecoderState state;
+               FLAC__stream_decoder_process_single(pfd->decoder);
+               state = FLAC__stream_decoder_get_state(pfd->decoder);
+               //PARA_CRIT_LOG("state: %s\n", FLAC__stream_decoder_get_resolved_state_string(pfd->decoder));
+               ret = -E_FLACDEC_EOF;
+               if (state == FLAC__STREAM_DECODER_END_OF_STREAM)
+                       goto out;
+               if (state == FLAC__STREAM_DECODER_ABORTED) {
+                       FLAC__stream_decoder_flush(pfd->decoder);
+                       fn->min_iqs = pfd->unconsumed + 1;
+                       break;
+               }
+               fn->min_iqs = 0;
+       }
+       ret = 1;
+out:
+       t->error = ret;
+       if (ret < 0)
+               btr_remove_node(btrn);
 }
 
 static void flacdec_close(struct filter_node *fn)
 {
+       struct private_flacdec_data *pfd = fn->private_data;
 
+       FLAC__stream_decoder_finish(pfd->decoder);
+       FLAC__stream_decoder_delete(pfd->decoder);
+       free(pfd);
+       fn->private_data = NULL;
 }
 
 static void flacdec_open(struct filter_node *fn)
 {
-
+       struct private_flacdec_data *pfd = para_calloc(sizeof(*pfd));
+       fn->private_data = pfd;
+       fn->min_iqs = 0;
 }
+
 /**
  * The init function of the flacdec filter.
  *
@@ -48,7 +291,7 @@ void flacdec_filter_init(struct filter *f)
 {
        f->open = flacdec_open;
        f->close = flacdec_close;
-       f->pre_select = generic_filter_pre_select;
+       f->pre_select = flacdec_pre_select;
        f->post_select = flacdec_post_select;
        f->execute = flacdec_execute;
 }