/* SPDX-License-Identifier: GPL-2.0 */ /* * based in parts on libfaad, Copyright (C) 2003-2005 M. Bakker, * Ahead Software AG */ /** \file aacdec_filter.c paraslash's aac (m4a) decoder. */ #include #include "para.h" #include "portable_io.h" #include "list.h" #include "sched.h" #include "buffer_tree.h" #include "filter.h" #include "error.h" #include "string.h" /** Give up decoding after that many errors. */ #define MAX_ERRORS 20 /** * data specific to the aacdec filter * * \sa \ref filter, \ref filter_node. */ struct private_aacdec_data { /** the return value of aac_open */ NeAACDecHandle handle; /** whether this instance of the aac decoder is already initialized */ bool initialized; /** number of times the decoder returned an error */ unsigned error_count; /** number of bytes already consumed from the imput stream */ size_t consumed_total; /** The number of channels of the current stream. */ unsigned int channels; /** Current sample rate in Hz. */ unsigned int sample_rate; }; static int aacdec_execute(const struct btr_node *btrn, const char *cmd, char **result) { struct filter_node *fn = btr_context(btrn); struct private_aacdec_data *padd = fn->private_data; return decoder_execute(cmd, padd->sample_rate, padd->channels, result); } static void aacdec_open(struct filter_node *fn) { NeAACDecConfigurationPtr c; struct private_aacdec_data *padd = zalloc(sizeof(*padd)); padd->handle = NeAACDecOpen(); c = NeAACDecGetCurrentConfiguration(padd->handle); c->defObjectType = LC; c->outputFormat = FAAD_FMT_16BIT; c->downMatrix = 0; NeAACDecSetConfiguration(padd->handle, c); fn->private_data = padd; fn->min_iqs = 2048; } static void aacdec_close(struct filter_node *fn) { struct private_aacdec_data *padd = fn->private_data; NeAACDecClose(padd->handle); free(padd); fn->private_data = NULL; } static int aacdec_post_monitor(__a_unused struct sched *s, void *context) { struct filter_node *fn = context; struct btr_node *btrn = fn->btrn; struct private_aacdec_data *padd = fn->private_data; int i, ret; char *inbuf, *outbuf, *btrbuf; size_t len, consumed, loaded = 0; NeAACDecFrameInfo frame_info; next_buffer: ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL); if (ret < 0) goto err; if (ret == 0) return 0; btr_merge(btrn, fn->min_iqs); len = btr_next_buffer(btrn, &inbuf); len = PARA_MIN(len, (size_t)8192); consumed = 0; if (!padd->initialized) { unsigned long rate = 0; unsigned char channels = 0; ret = NeAACDecInit(padd->handle, (unsigned char *)inbuf, len, &rate, &channels); PARA_INFO_LOG("decoder init: %d\n", ret); if (ret < 0) { ret = -E_AACDEC_INIT; goto err; } consumed = ret; padd->sample_rate = rate; padd->channels = channels; PARA_INFO_LOG("rate: %u, channels: %u\n", padd->sample_rate, padd->channels); padd->initialized = true; } if (consumed >= len) goto success; //PARA_CRIT_LOG("consumed: %zu (%zu + %zu), have: %zu\n", padd->consumed_total + consumed, // padd->consumed_total, consumed, len - consumed); outbuf = NeAACDecDecode(padd->handle, &frame_info, (unsigned char *)inbuf + consumed, len - consumed); if (frame_info.error) { int err = frame_info.error; ret = -E_AAC_DECODE; if (padd->error_count++ > MAX_ERRORS) goto err; PARA_NOTICE_LOG("error #%u: (%s)\n", padd->error_count, NeAACDecGetErrorMessage(err)); PARA_NOTICE_LOG("consumed (total, buffer, frame): " "%zu, %zu, %lu\n", padd->consumed_total, consumed, frame_info.bytesconsumed); consumed++; /* just eat one byte and hope for the best */ goto success; } padd->error_count = 0; //PARA_CRIT_LOG("decoder ate %lu\n", frame_info.bytesconsumed); consumed += frame_info.bytesconsumed; if (!frame_info.samples) goto success; btrbuf = arr_alloc(2, frame_info.samples); for (i = 0; i < frame_info.samples; i++) { short sh = ((short *)outbuf)[i]; write_int16_host_endian(btrbuf + loaded, sh); loaded += 2; } btr_add_output(btrbuf, loaded, btrn); success: btr_consume(btrn, consumed); padd->consumed_total += consumed; if (loaded == 0) goto next_buffer; return 1; err: assert(ret < 0); btr_remove_node(&fn->btrn); return ret; } /** \cond doxygen_ignore */ const struct filter lsg_filter_cmd_com_aacdec_user_data = { .open = aacdec_open, .close = aacdec_close, .pre_monitor = generic_filter_pre_monitor, .post_monitor = aacdec_post_monitor, .execute = aacdec_execute }; /** \endcond */