171 lines
5.2 KiB
C
171 lines
5.2 KiB
C
/*
|
|
* Libaac-next decoder (libxaac based)
|
|
* Copyright (c) 2025 Wrapper
|
|
*
|
|
* This file is part of FFmpeg.
|
|
*
|
|
* FFmpeg is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* FFmpeg is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with FFmpeg; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* Interface to libaac-next decoder.
|
|
*/
|
|
|
|
#include "avcodec.h"
|
|
#include <libaac.h>
|
|
#include "libavcodec/codec_id.h"
|
|
#include "libavutil/channel_layout.h"
|
|
#include "libavutil/error.h"
|
|
#include "libavutil/log.h"
|
|
#include "libavutil/opt.h"
|
|
#include "avcodec.h"
|
|
#include "codec_internal.h"
|
|
#include "decode.h"
|
|
#include "libavutil/samplefmt.h"
|
|
#include "libavutil/mem.h"
|
|
|
|
typedef struct {
|
|
AVClass *class;
|
|
AACDecode *decoder;
|
|
int error_conceal;
|
|
int esbr;
|
|
unsigned char *decode_buffer;
|
|
} libaacDecodeCTX;
|
|
|
|
static const AVChannelLayout aac_ch_layouts[7] = {
|
|
AV_CHANNEL_LAYOUT_MONO,
|
|
AV_CHANNEL_LAYOUT_STEREO,
|
|
AV_CHANNEL_LAYOUT_SURROUND,
|
|
AV_CHANNEL_LAYOUT_4POINT0,
|
|
AV_CHANNEL_LAYOUT_5POINT0_BACK,
|
|
AV_CHANNEL_LAYOUT_5POINT1_BACK,
|
|
{ 0 },
|
|
};
|
|
|
|
static const AVOption aac_dec_options[] = {
|
|
{ "conceal", "Error concealment", offsetof(libaacDecodeCTX, error_conceal), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_DECODING_PARAM },
|
|
{ "esbr", "Enable the use of Enhanced SBR", offsetof(libaacDecodeCTX, esbr), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_DECODING_PARAM },
|
|
{ NULL }
|
|
};
|
|
|
|
static const AVClass aac_dec_class = {
|
|
.class_name = "libaac",
|
|
.item_name = av_default_item_name,
|
|
.option = aac_dec_options,
|
|
.version = LIBAVUTIL_VERSION_INT,
|
|
};
|
|
|
|
static void aac_dec_error_handler(uint32_t errorCode, const char *section, const char *errorMsg, bool isFatal, void *handle) {
|
|
AVCodecContext *ctx = (AVCodecContext *)handle;
|
|
av_log(ctx, AV_LOG_ERROR, "%s: %s (0x%08X)\n", section, errorMsg, errorCode);
|
|
}
|
|
|
|
static av_cold int libaac_decode_init(AVCodecContext *avctx)
|
|
{
|
|
libaacDecodeCTX *s = avctx->priv_data;
|
|
AACDecodeSettings cfg = {0};
|
|
|
|
cfg.bitsPerSamples = 16;
|
|
cfg.errorConceal = s->error_conceal;
|
|
cfg.eSBR = s->esbr;
|
|
cfg.frameSize = 0;
|
|
cfg.asc = avctx->extradata;
|
|
cfg.ascSize = avctx->extradata_size;
|
|
cfg.errorHandleCtx = avctx;
|
|
cfg.errorHandler = aac_dec_error_handler;
|
|
|
|
s->decoder = aac_decode_open(cfg);
|
|
if (!s->decoder)
|
|
{
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
s->decode_buffer = av_mallocz(4096 * 8);
|
|
if (!s->decode_buffer)
|
|
{
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
|
|
avctx->sample_fmt = AV_SAMPLE_FMT_S16;
|
|
return 0;
|
|
}
|
|
|
|
static av_cold int libaac_decode_close(AVCodecContext *avctx)
|
|
{
|
|
libaacDecodeCTX *s = avctx->priv_data;
|
|
|
|
if (s->decoder)
|
|
aac_decode_close(s->decoder);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int libaac_decode_frame(AVCodecContext *avctx, AVFrame *frame,
|
|
int *got_frame_ptr, AVPacket *avpkt)
|
|
{
|
|
libaacDecodeCTX *s = avctx->priv_data;
|
|
uint32_t decode_size, read_size;
|
|
int ret;
|
|
|
|
ret = aac_decode(s->decoder, avpkt->data, avpkt->size, s->decode_buffer, &decode_size, &read_size);
|
|
if (ret < 0)
|
|
return AVERROR_EXTERNAL;
|
|
|
|
avctx->sample_rate = s->decoder->sampleRate;
|
|
avctx->profile = s->decoder->aot - 1;
|
|
avctx->ch_layout = aac_ch_layouts[s->decoder->noChannels - 1];
|
|
avctx->frame_size = 1024;
|
|
|
|
if (decode_size <= 0)
|
|
return AVERROR_EOF;
|
|
|
|
frame->nb_samples = decode_size / av_get_bytes_per_sample(avctx->sample_fmt) / avctx->ch_layout.nb_channels;
|
|
|
|
if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
|
|
return ret;
|
|
|
|
av_log(avctx, AV_LOG_TRACE, "aac decode size: %d, decode buf: %d\n", decode_size, avctx->ch_layout.nb_channels * frame->nb_samples * av_get_bytes_per_sample(avctx->sample_fmt));
|
|
memcpy(frame->data[0], s->decode_buffer, decode_size);
|
|
|
|
*got_frame_ptr = 1;
|
|
return read_size;
|
|
}
|
|
|
|
static av_cold void libaac_decode_flush(AVCodecContext *avctx)
|
|
{
|
|
libaacDecodeCTX *s = avctx->priv_data;
|
|
|
|
if (!s->decoder)
|
|
return;
|
|
|
|
aac_decode_flush_buffer(s->decoder);
|
|
}
|
|
|
|
const FFCodec ff_libaac_next_decoder = {
|
|
.p.name = "libaac",
|
|
CODEC_LONG_NAME("custom libxaac-based AAC decoder"),
|
|
.p.type = AVMEDIA_TYPE_AUDIO,
|
|
.p.id = AV_CODEC_ID_AAC,
|
|
.priv_data_size = sizeof(libaacDecodeCTX),
|
|
.init = libaac_decode_init,
|
|
FF_CODEC_DECODE_CB(libaac_decode_frame),
|
|
.close = libaac_decode_close,
|
|
.flush = libaac_decode_flush,
|
|
.p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF,
|
|
.caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE,
|
|
.p.priv_class = &aac_dec_class,
|
|
.p.wrapper_name = "libaac",
|
|
};
|