external_ffmpeg/libavcodec/libhelix-aac.c

238 lines
7.2 KiB
C

/*
* libhelix aac decoder
*
* 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
* libhelix AAC decoder implementation
*/
#include "avcodec.h"
#include "internal.h"
#include "codec_internal.h"
#include "decode.h"
#include "get_bits.h"
#include "mpeg4audio.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mem.h"
#include <aacdec.h>
#define SYNCWORDH 0xff
#define SYNCWORDL 0xf0
#define LIBHELIX_AAC_MAX_CHANNELS 2
#define LIBHELIX_AAC_MAX_NSAMPS 2048
#define LATM_SYNCWORDH 0xe0
#define LATM_SYNCWORDL 0x56
typedef struct HAACDecContext {
AVClass *class;
HAACDecoder context;
uint8_t *pcm;
int pcm_size;
MPEG4AudioConfig m4ac;
} HAACDecContext;
static int aac_decode_init(AVCodecContext *avctx)
{
HAACDecContext *aac = avctx->priv_data;
GetBitContext gb;
int ret;
aac->context = AACInitDecoder();
if (!aac->context)
return AVERROR(ENOMEM);
avctx->sample_fmt = AV_SAMPLE_FMT_S16;
aac->pcm_size = LIBHELIX_AAC_MAX_NSAMPS * LIBHELIX_AAC_MAX_CHANNELS *
av_get_bytes_per_sample(avctx->sample_fmt);
aac->pcm = av_malloc(aac->pcm_size);
if (!aac->pcm)
return AVERROR(ENOMEM);
if (avctx->extradata_size > 0) {
if ((ret = init_get_bits(&gb, avctx->extradata, avctx->extradata_size)) < 0)
return ret;
if ((ret = ff_mpeg4audio_get_config_gb(&aac->m4ac, &gb, 1, NULL)) < 0)
return ret;
}
return 0;
}
static int aac_decode_frame(AVCodecContext *avctx, AVFrame *frame,
int *got_frame_ptr, AVPacket *avpkt)
{
HAACDecContext *aac = avctx->priv_data;
AACFrameInfo info;
uint8_t *in_data;
int ret, in_size;
if (!aac)
return AVERROR(EIO);
in_data = avpkt->data;
in_size = avpkt->size;
if ((in_data[0] & SYNCWORDH) == SYNCWORDH && (in_data[1] & SYNCWORDL) == SYNCWORDL) {
ret = AACSetFormat(aac->context, AAC_FF_ADTS);
if (ret < 0)
return 0;
} else {
info.sampRateCore = aac->m4ac.sample_rate ? aac->m4ac.sample_rate : avctx->sample_rate;
info.nChans = aac->m4ac.channels ? aac->m4ac.channels : avctx->ch_layout.nb_channels;
info.profile = AAC_PROFILE_LC;
ret = AACSetRawBlockParams(aac->context, 0, &info);
if (ret < 0)
return ret;
}
ret = AACDecode(aac->context, &in_data, &in_size, (int16_t *)aac->pcm);
if (ret < 0) {
av_log(avctx, AV_LOG_ERROR, "%s error ret %d.\n", __func__, ret);
AACFlushCodec(aac->context);
return ret;
}
AACGetLastFrameInfo(aac->context, &info);
if (!avctx->sample_rate)
avctx->sample_rate = info.sampRateOut;
if (!avctx->ch_layout.nb_channels)
av_channel_layout_default(&avctx->ch_layout, info.nChans);
avctx->frame_size = info.outputSamps / info.nChans;
frame->nb_samples = avctx->frame_size;
if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
return ret;
memcpy(frame->extended_data[0], aac->pcm,
avctx->ch_layout.nb_channels * avctx->frame_size *
av_get_bytes_per_sample(avctx->sample_fmt));
*got_frame_ptr = 1;
return avpkt->size - in_size;
}
static int aac_latm_decode_frame(AVCodecContext *avctx, AVFrame *frame,
int *got_frame_ptr, AVPacket *avpkt)
{
HAACDecContext *aac = avctx->priv_data;
AACFrameInfo info;
uint8_t *in_data;
int ret, in_size;
if (!aac)
return AVERROR(EIO);
in_data = avpkt->data;
in_size = avpkt->size;
if(in_data[0] != LATM_SYNCWORDL || (in_data[1] & 0xf0) != LATM_SYNCWORDH) {
av_log(avctx, AV_LOG_ERROR, "%s Invalid sync word! %0x %0x\n",
__func__, in_data[0], (in_data[1] & 0xf0));
return AVERROR_INVALIDDATA;
}
AACSetFormat(aac->context, AAC_FF_LATM_MCP1);
in_data += 3;
in_size -= 3;
ret = AACDecode(aac->context, &in_data, &in_size, (int16_t *)aac->pcm);
if (ret < 0) {
av_log(avctx, AV_LOG_ERROR, "%s error ret %d.\n", __func__, ret);
AACFlushCodec(aac->context);
return ret;
}
AACGetLastFrameInfo(aac->context, &info);
if (!avctx->sample_rate)
avctx->sample_rate = info.sampRateOut;
if (!avctx->ch_layout.nb_channels)
av_channel_layout_default(&avctx->ch_layout, info.nChans);
avctx->frame_size = info.outputSamps / info.nChans;
frame->nb_samples = avctx->frame_size;
if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
return ret;
memcpy(frame->extended_data[0], aac->pcm,
avctx->ch_layout.nb_channels * avctx->frame_size *
av_get_bytes_per_sample(avctx->sample_fmt));
*got_frame_ptr = 1;
return avpkt->size;
}
static av_cold int aac_decode_close(AVCodecContext *avctx)
{
HAACDecContext *aac = avctx->priv_data;
if (aac->context)
AACFreeDecoder(aac->context);
av_freep(&aac->pcm);
return 0;
}
const FFCodec ff_libhelix_aac_decoder = {
.p.name = "libhelix_aac",
.p.long_name = NULL_IF_CONFIG_SMALL("libHelix AAC Decoder"),
.p.type = AVMEDIA_TYPE_AUDIO,
.p.id = AV_CODEC_ID_AAC,
.priv_data_size = sizeof(HAACDecContext),
.init = aac_decode_init,
FF_CODEC_DECODE_CB(aac_decode_frame),
.close = aac_decode_close,
.p.capabilities = AV_CODEC_CAP_CHANNEL_CONF | AV_CODEC_CAP_DR1,
.p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16,
AV_SAMPLE_FMT_NONE },
.p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO,
AV_CHANNEL_LAYOUT_STEREO, { 0 } },
};
const FFCodec ff_libhelix_aac_latm_a2dp_decoder = {
.p.name = "libhelix_aac_latm_a2dp",
.p.long_name = NULL_IF_CONFIG_SMALL("libHelix AAC Decoder"),
.p.type = AVMEDIA_TYPE_AUDIO,
.p.id = AV_CODEC_ID_AAC_LATM_A2DP,
.priv_data_size = sizeof(HAACDecContext),
.init = aac_decode_init,
FF_CODEC_DECODE_CB(aac_latm_decode_frame),
.close = aac_decode_close,
.p.capabilities = AV_CODEC_CAP_CHANNEL_CONF | AV_CODEC_CAP_DR1,
.p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16,
AV_SAMPLE_FMT_NONE },
.p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO,
AV_CHANNEL_LAYOUT_STEREO, { 0 } },
.bsfs = "a2dp_rechunk",
};