Commit 27360ccc authored by Justin Ruggles's avatar Justin Ruggles

adx: add an ADX parser.

This simplifies the decoder so it doesn't have to process an in-packet header
or handle arbitrary-sized packets. It also fixes decoding of files with large
headers.
parent d8cec2d7
......@@ -595,6 +595,7 @@ OBJS-$(CONFIG_AAC_PARSER) += aac_parser.o aac_ac3_parser.o \
aacadtsdec.o mpeg4audio.o
OBJS-$(CONFIG_AC3_PARSER) += ac3_parser.o ac3tab.o \
aac_ac3_parser.o
OBJS-$(CONFIG_ADX_PARSER) += adx_parser.o adx.o
OBJS-$(CONFIG_CAVSVIDEO_PARSER) += cavs_parser.o
OBJS-$(CONFIG_DCA_PARSER) += dca_parser.o
OBJS-$(CONFIG_DIRAC_PARSER) += dirac_parser.o
......
......@@ -43,8 +43,7 @@ typedef struct {
int channels;
ADXChannelState prev[2];
int header_parsed;
unsigned char dec_temp[18*2];
int in_temp;
int eof;
int cutoff;
int coeff[2];
} ADXContext;
......
/*
* Copyright (c) 2011 Justin Ruggles
*
* This file is part of Libav.
*
* Libav 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.
*
* Libav 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 Libav; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* ADX audio parser
*
* Reads header to extradata and splits packets into individual blocks.
*/
#include "libavutil/intreadwrite.h"
#include "parser.h"
#include "adx.h"
typedef struct ADXParseContext {
ParseContext pc;
int header_size;
int block_size;
int buf_pos;
} ADXParseContext;
#define MIN_HEADER_SIZE 24
static int adx_parse(AVCodecParserContext *s1,
AVCodecContext *avctx,
const uint8_t **poutbuf, int *poutbuf_size,
const uint8_t *buf, int buf_size)
{
ADXParseContext *s = s1->priv_data;
ParseContext *pc = &s->pc;
int next = END_NOT_FOUND;
if (!avctx->extradata_size) {
int ret;
ff_combine_frame(pc, END_NOT_FOUND, &buf, &buf_size);
if (!s->header_size && pc->index >= MIN_HEADER_SIZE) {
if (ret = ff_adx_decode_header(avctx, pc->buffer, pc->index,
&s->header_size, NULL))
return AVERROR_INVALIDDATA;
s->block_size = BLOCK_SIZE * avctx->channels;
}
if (s->header_size && s->header_size <= pc->index) {
avctx->extradata = av_mallocz(s->header_size + FF_INPUT_BUFFER_PADDING_SIZE);
if (!avctx->extradata)
return AVERROR(ENOMEM);
avctx->extradata_size = s->header_size;
memcpy(avctx->extradata, pc->buffer, s->header_size);
memmove(pc->buffer, pc->buffer + s->header_size, s->header_size);
pc->index -= s->header_size;
}
*poutbuf = NULL;
*poutbuf_size = 0;
return buf_size;
}
if (pc->index - s->buf_pos >= s->block_size) {
*poutbuf = &pc->buffer[s->buf_pos];
*poutbuf_size = s->block_size;
s->buf_pos += s->block_size;
return 0;
}
if (pc->index && s->buf_pos) {
memmove(pc->buffer, &pc->buffer[s->buf_pos], pc->index - s->buf_pos);
pc->index -= s->buf_pos;
s->buf_pos = 0;
}
if (buf_size + pc->index >= s->block_size)
next = s->block_size - pc->index;
if (ff_combine_frame(pc, next, &buf, &buf_size) < 0 || !buf_size) {
*poutbuf = NULL;
*poutbuf_size = 0;
return buf_size;
}
*poutbuf = buf;
*poutbuf_size = buf_size;
return next;
}
AVCodecParser ff_adx_parser = {
.codec_ids = { CODEC_ID_ADPCM_ADX },
.priv_data_size = sizeof(ADXParseContext),
.parser_parse = adx_parse,
.parser_close = ff_parse_close,
};
......@@ -35,6 +35,19 @@
static av_cold int adx_decode_init(AVCodecContext *avctx)
{
ADXContext *c = avctx->priv_data;
int ret, header_size;
if (avctx->extradata_size < 24)
return AVERROR_INVALIDDATA;
if ((ret = ff_adx_decode_header(avctx, avctx->extradata, avctx->extradata_size,
&header_size, c->coeff)) < 0) {
av_log(avctx, AV_LOG_ERROR, "error parsing ADX header\n");
return AVERROR_INVALIDDATA;
}
c->channels = avctx->channels;
avctx->sample_fmt = AV_SAMPLE_FMT_S16;
return 0;
}
......@@ -46,7 +59,7 @@ static av_cold int adx_decode_init(AVCodecContext *avctx)
* 2nd-order LPC filter applied to it to form the output signal for a single
* channel.
*/
static void adx_decode(ADXContext *c, int16_t *out, const uint8_t *in, int ch)
static int adx_decode(ADXContext *c, int16_t *out, const uint8_t *in, int ch)
{
ADXChannelState *prev = &c->prev[ch];
GetBitContext gb;
......@@ -54,6 +67,10 @@ static void adx_decode(ADXContext *c, int16_t *out, const uint8_t *in, int ch)
int i;
int s0, s1, s2, d;
/* check if this is an EOF packet */
if (scale & 0x8000)
return -1;
init_get_bits(&gb, in + 2, (BLOCK_SIZE - 2) * 8);
s1 = prev->s1;
s2 = prev->s2;
......@@ -67,84 +84,55 @@ static void adx_decode(ADXContext *c, int16_t *out, const uint8_t *in, int ch)
}
prev->s1 = s1;
prev->s2 = s2;
}
static int adx_decode_header(AVCodecContext *avctx, const uint8_t *buf,
int bufsize)
{
ADXContext *c = avctx->priv_data;
int ret, header_size;
if ((ret = ff_adx_decode_header(avctx, buf, bufsize, &header_size,
c->coeff)) < 0)
return ret;
c->channels = avctx->channels;
return header_size;
return 0;
}
static int adx_decode_frame(AVCodecContext *avctx, void *data, int *data_size,
AVPacket *avpkt)
{
const uint8_t *buf0 = avpkt->data;
int buf_size = avpkt->size;
ADXContext *c = avctx->priv_data;
int16_t *samples = data;
const uint8_t *buf = buf0;
int rest = buf_size;
int num_blocks;
if (!c->header_parsed) {
int hdrsize = adx_decode_header(avctx, buf, rest);
if (hdrsize < 0) {
av_log(avctx, AV_LOG_ERROR, "invalid stream header\n");
return hdrsize;
}
c->header_parsed = 1;
buf += hdrsize;
rest -= hdrsize;
const uint8_t *buf = avpkt->data;
int num_blocks, ch;
if (c->eof) {
*data_size = 0;
return buf_size;
}
/* 18 bytes of data are expanded into 32*2 bytes of audio,
so guard against buffer overflows */
num_blocks = (rest + c->in_temp) / (BLOCK_SIZE * c->channels);
num_blocks = buf_size / (BLOCK_SIZE * c->channels);
if (num_blocks > *data_size / (BLOCK_SAMPLES * c->channels)) {
rest = (*data_size / (BLOCK_SAMPLES * c->channels)) * BLOCK_SIZE;
num_blocks = (rest + c->in_temp) / (BLOCK_SIZE * c->channels);
buf_size = (*data_size / (BLOCK_SAMPLES * c->channels)) * BLOCK_SIZE;
num_blocks = buf_size / (BLOCK_SIZE * c->channels);
}
if (!num_blocks) {
av_log(avctx, AV_LOG_ERROR, "packet is too small\n");
if (!buf_size || buf_size % (BLOCK_SIZE * avctx->channels)) {
if (buf_size >= 4 && (AV_RB16(buf) & 0x8000)) {
c->eof = 1;
*data_size = 0;
return avpkt->size;
}
return AVERROR_INVALIDDATA;
}
if (c->in_temp) {
int copysize = BLOCK_SIZE * avctx->channels - c->in_temp;
memcpy(c->dec_temp + c->in_temp, buf, copysize);
rest -= copysize;
buf += copysize;
adx_decode(c, samples, c->dec_temp, 0);
if (avctx->channels == 2)
adx_decode(c, samples + 1, c->dec_temp + BLOCK_SIZE, 1);
samples += BLOCK_SAMPLES * c->channels;
num_blocks--;
}
while (num_blocks--) {
adx_decode(c, samples, buf, 0);
if (c->channels == 2)
adx_decode(c, samples + 1, buf + BLOCK_SIZE, 1);
rest -= BLOCK_SIZE * c->channels;
buf += BLOCK_SIZE * c->channels;
for (ch = 0; ch < c->channels; ch++) {
if (adx_decode(c, samples + ch, buf, ch)) {
c->eof = 1;
buf = avpkt->data + avpkt->size;
break;
}
buf_size -= BLOCK_SIZE;
buf += BLOCK_SIZE;
}
samples += BLOCK_SAMPLES * c->channels;
}
c->in_temp = rest;
if (rest) {
memcpy(c->dec_temp, buf, rest);
buf += rest;
}
*data_size = (uint8_t*)samples - (uint8_t*)data;
return buf - buf0;
return buf - avpkt->data;
}
AVCodec ff_adpcm_adx_decoder = {
......
......@@ -388,6 +388,7 @@ void avcodec_register_all(void)
REGISTER_PARSER (AAC, aac);
REGISTER_PARSER (AAC_LATM, aac_latm);
REGISTER_PARSER (AC3, ac3);
REGISTER_PARSER (ADX, adx);
REGISTER_PARSER (CAVSVIDEO, cavsvideo);
REGISTER_PARSER (DCA, dca);
REGISTER_PARSER (DIRAC, dirac);
......
......@@ -21,8 +21,8 @@
#define AVCODEC_VERSION_H
#define LIBAVCODEC_VERSION_MAJOR 53
#define LIBAVCODEC_VERSION_MINOR 22
#define LIBAVCODEC_VERSION_MICRO 1
#define LIBAVCODEC_VERSION_MINOR 23
#define LIBAVCODEC_VERSION_MICRO 0
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
LIBAVCODEC_VERSION_MINOR, \
......
......@@ -172,6 +172,7 @@ static int film_read_header(AVFormatContext *s,
if (film->audio_type == CODEC_ID_ADPCM_ADX) {
st->codec->bits_per_coded_sample = 18 * 8 / 32;
st->codec->block_align = st->codec->channels * 18;
st->need_parsing = AVSTREAM_PARSE_FULL;
} else {
st->codec->bits_per_coded_sample = film->audio_bits;
st->codec->block_align = st->codec->channels *
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment