/* SPDX-License-Identifier: GPL-2.0 */ /** \file flacdec_filter.c The flac decoder. */ #include #include "para.h" #include "list.h" #include "sched.h" #include "buffer_tree.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 have been fed to libflac but 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; /** * Nothing was copied. If the input queue of the btrn is smaller than * the minimal input queue size, our parent must have been gone, so * we're not going to get more input. Since our remaining data is not * sufficient do decode a single frame, we have an EOF condition. */ if (btr_get_input_queue_size(btrn) < fn->min_iqs) { assert(btr_no_parent(btrn)); return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; } /* * 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 = arr_alloc(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 * channels * 2, 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(const struct btr_node *btrn, const char *cmd, char **result) { 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_monitor(struct sched *s, void *context) { struct filter_node *fn = context; 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 int flacdec_post_monitor(__a_unused struct sched *s, void *context) { struct filter_node *fn = context; struct private_flacdec_data *pfd = fn->private_data; struct btr_node *btrn = fn->btrn; int ret; FLAC__StreamDecoderState state; if (output_queue_full(btrn)) return 0; ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL); if (ret < 0 && ret != -E_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; } if (output_queue_full(btrn)) { pfd->have_more = true; ret = 1; goto out; } pfd->have_more = false; FLAC__stream_decoder_process_single(pfd->decoder); state = FLAC__stream_decoder_get_state(pfd->decoder); ret = -E_EOF; if (state == FLAC__STREAM_DECODER_END_OF_STREAM) goto out; if (state == FLAC__STREAM_DECODER_ABORTED) { FLAC__stream_decoder_flush(pfd->decoder); pfd->unconsumed = 0; /* feed unconsumed bytes again */ fn->min_iqs = btr_get_input_queue_size(btrn) + 1; ret = 1; goto out; } pfd->have_more = true; fn->min_iqs = 0; ret = 1; out: if (ret < 0) btr_remove_node(&fn->btrn); return ret; } static void flacdec_close(struct filter_node *fn) { struct private_flacdec_data *pfd; if (!fn) return; pfd = fn->private_data; if (!pfd) return; if (pfd->decoder) { 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 = zalloc(sizeof(*pfd)); fn->private_data = pfd; fn->min_iqs = 0; } /** \cond doxygen_ignore */ const struct filter lsg_filter_cmd_com_flacdec_user_data = { .open = flacdec_open, .close = flacdec_close, .pre_monitor = flacdec_pre_monitor, .post_monitor = flacdec_post_monitor, .execute = flacdec_execute, }; /** \endcond */