From 89b3918d545796c71dec33e73b4c5173410e3a38 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Thu, 4 Aug 2011 00:52:48 +0200 Subject: [PATCH] Implement the flac decoding filter. This replaces the dummy functions in flacdec_filter.c by a working implementation. Although it contains an ugly workaround for a shortcoming in the flac library, see the comment in read_cb() for details, it seems to work fine. --- error.h | 7 +- flacdec_filter.c | 249 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 252 insertions(+), 4 deletions(-) diff --git a/error.h b/error.h index 7a2b1617..311b6ebd 100644 --- a/error.h +++ b/error.h @@ -35,10 +35,15 @@ DEFINE_ERRLIST_OBJECT_ENUM; #define FILE_WRITE_ERRORS #define STDIN_ERRORS #define WRITE_ERRORS -#define FLACDEC_FILTER_ERRORS extern const char **para_errlist[]; +#define FLACDEC_FILTER_ERRORS \ + PARA_ERROR(FLACDEC_DECODER_ALLOC, "could not allocate stream decoder"), \ + PARA_ERROR(FLACDEC_DECODER_INIT, "could not init stream decoder"), \ + PARA_ERROR(FLACDEC_EOF, "flacdec encountered end of file condition"), \ + + #define FLAC_AFH_ERRORS \ PARA_ERROR(FLAC_CHAIN_ALLOC, "could not create metadata chain"), \ PARA_ERROR(FLAC_CHAIN_READ, "could not read meta chain"), \ diff --git a/flacdec_filter.c b/flacdec_filter.c index 40795023..02759643 100644 --- a/flacdec_filter.c +++ b/flacdec_filter.c @@ -8,6 +8,7 @@ #include #include +#include #include "para.h" #include "list.h" @@ -17,26 +18,268 @@ #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; } -- 2.30.2