FFmpeg/libavcodec/apac.c
Andreas Rheinhardt 790f793844 avutil/common: Don't auto-include mem.h
There are lots of files that don't need it: The number of object
files that actually need it went down from 2011 to 884 here.

Keep it for external users in order to not cause breakages.

Also improve the other headers a bit while just at it.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2024-03-31 00:08:43 +01:00

280 lines
8.1 KiB
C

/*
* APAC audio 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
*/
#include "libavutil/audio_fifo.h"
#include "libavutil/mem.h"
#include "avcodec.h"
#include "codec_internal.h"
#include "decode.h"
#include "get_bits.h"
typedef struct ChContext {
int have_code;
int last_sample;
int last_delta;
int bit_length;
int block_length;
uint8_t block[32 * 2];
AVAudioFifo *samples;
} ChContext;
typedef struct APACContext {
GetBitContext gb;
int skip;
int cur_ch;
ChContext ch[2];
uint8_t *bitstream;
int64_t max_framesize;
int bitstream_size;
int bitstream_index;
} APACContext;
static av_cold int apac_close(AVCodecContext *avctx)
{
APACContext *s = avctx->priv_data;
av_freep(&s->bitstream);
s->bitstream_size = 0;
for (int ch = 0; ch < 2; ch++) {
ChContext *c = &s->ch[ch];
av_audio_fifo_free(c->samples);
}
return 0;
}
static av_cold int apac_init(AVCodecContext *avctx)
{
APACContext *s = avctx->priv_data;
if (avctx->bits_per_coded_sample > 8)
avctx->sample_fmt = AV_SAMPLE_FMT_S16P;
else
avctx->sample_fmt = AV_SAMPLE_FMT_U8P;
if (avctx->ch_layout.nb_channels < 1 ||
avctx->ch_layout.nb_channels > 2 ||
avctx->bits_per_coded_sample < 8 ||
avctx->bits_per_coded_sample > 16
)
return AVERROR_INVALIDDATA;
for (int ch = 0; ch < avctx->ch_layout.nb_channels; ch++) {
ChContext *c = &s->ch[ch];
c->bit_length = avctx->bits_per_coded_sample;
c->block_length = 8;
c->have_code = 0;
c->samples = av_audio_fifo_alloc(avctx->sample_fmt, 1, 1024);
if (!c->samples)
return AVERROR(ENOMEM);
}
s->max_framesize = 1024;
s->bitstream = av_realloc_f(s->bitstream, s->max_framesize + AV_INPUT_BUFFER_PADDING_SIZE, sizeof(*s->bitstream));
if (!s->bitstream)
return AVERROR(ENOMEM);
return 0;
}
static int get_code(ChContext *c, GetBitContext *gb)
{
if (get_bits1(gb)) {
int code = get_bits(gb, 2);
switch (code) {
case 0:
c->bit_length--;
break;
case 1:
c->bit_length++;
break;
case 2:
c->bit_length = get_bits(gb, 5);
break;
case 3:
c->block_length = get_bits(gb, 4);
return 1;
}
}
return 0;
}
static int apac_decode(AVCodecContext *avctx, AVFrame *frame,
int *got_frame_ptr, AVPacket *pkt)
{
APACContext *s = avctx->priv_data;
GetBitContext *gb = &s->gb;
int ret, n, buf_size, input_buf_size;
const uint8_t *buf;
int nb_samples;
if (!pkt->size && s->bitstream_size <= 0) {
*got_frame_ptr = 0;
return 0;
}
buf_size = pkt->size;
input_buf_size = buf_size;
if (s->bitstream_index > 0 && s->bitstream_size > 0) {
memmove(s->bitstream, &s->bitstream[s->bitstream_index], s->bitstream_size);
s->bitstream_index = 0;
}
if (s->bitstream_index + s->bitstream_size + buf_size > s->max_framesize) {
s->bitstream = av_realloc_f(s->bitstream, s->bitstream_index +
s->bitstream_size +
buf_size + AV_INPUT_BUFFER_PADDING_SIZE,
sizeof(*s->bitstream));
if (!s->bitstream)
return AVERROR(ENOMEM);
s->max_framesize = s->bitstream_index + s->bitstream_size + buf_size;
}
if (pkt->data)
memcpy(&s->bitstream[s->bitstream_index + s->bitstream_size], pkt->data, buf_size);
buf = &s->bitstream[s->bitstream_index];
buf_size += s->bitstream_size;
s->bitstream_size = buf_size;
frame->nb_samples = s->bitstream_size * 16 * 8;
if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
return ret;
if ((ret = init_get_bits8(gb, buf, buf_size)) < 0)
return ret;
skip_bits(gb, s->skip);
s->skip = 0;
while (get_bits_left(gb) > 0) {
for (int ch = s->cur_ch; ch < avctx->ch_layout.nb_channels; ch++) {
ChContext *c = &s->ch[ch];
int16_t *dst16 = (int16_t *)c->block;
uint8_t *dst8 = (uint8_t *)c->block;
void *samples[4];
samples[0] = &c->block[0];
if (get_bits_left(gb) < 16 && pkt->size) {
s->cur_ch = ch;
goto end;
}
if (!c->have_code && get_code(c, gb))
get_code(c, gb);
c->have_code = 0;
if (c->block_length <= 0)
continue;
if (c->bit_length < 0 ||
c->bit_length > 17) {
c->bit_length = avctx->bits_per_coded_sample;
s->bitstream_index = 0;
s->bitstream_size = 0;
return AVERROR_INVALIDDATA;
}
if (get_bits_left(gb) < c->block_length * c->bit_length) {
if (pkt->size) {
c->have_code = 1;
s->cur_ch = ch;
goto end;
} else {
break;
}
}
for (int i = 0; i < c->block_length; i++) {
int val = get_bits_long(gb, c->bit_length);
unsigned delta = (val & 1) ? ~(val >> 1) : (val >> 1);
int sample;
delta += c->last_delta;
sample = c->last_sample + delta;
c->last_delta = delta;
c->last_sample = sample;
switch (avctx->sample_fmt) {
case AV_SAMPLE_FMT_S16P:
dst16[i] = sample;
break;
case AV_SAMPLE_FMT_U8P:
dst8[i] = sample;
break;
}
}
av_audio_fifo_write(c->samples, samples, c->block_length);
}
s->cur_ch = 0;
}
end:
nb_samples = frame->nb_samples;
for (int ch = 0; ch < avctx->ch_layout.nb_channels; ch++)
nb_samples = FFMIN(av_audio_fifo_size(s->ch[ch].samples), nb_samples);
frame->nb_samples = nb_samples;
for (int ch = 0; ch < avctx->ch_layout.nb_channels; ch++) {
void *samples[1] = { frame->extended_data[ch] };
av_audio_fifo_read(s->ch[ch].samples, samples, nb_samples);
}
s->skip = get_bits_count(gb) - 8 * (get_bits_count(gb) / 8);
n = get_bits_count(gb) / 8;
if (nb_samples > 0 || pkt->size)
*got_frame_ptr = 1;
if (s->bitstream_size > 0) {
s->bitstream_index += n;
s->bitstream_size -= n;
return input_buf_size;
}
return n;
}
const FFCodec ff_apac_decoder = {
.p.name = "apac",
CODEC_LONG_NAME("Marian's A-pac audio"),
.p.type = AVMEDIA_TYPE_AUDIO,
.p.id = AV_CODEC_ID_APAC,
.priv_data_size = sizeof(APACContext),
.init = apac_init,
FF_CODEC_DECODE_CB(apac_decode),
.close = apac_close,
.p.capabilities = AV_CODEC_CAP_DELAY |
#if FF_API_SUBFRAMES
AV_CODEC_CAP_SUBFRAMES |
#endif
AV_CODEC_CAP_DR1,
.caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
.p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_U8P,
AV_SAMPLE_FMT_S16P,
AV_SAMPLE_FMT_NONE },
};