#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.
*
{
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;
}