From 27360ccc5edcaa1a37dec39127e87c461ce668b6 Mon Sep 17 00:00:00 2001 From: Justin Ruggles Date: Mon, 21 Nov 2011 01:49:37 -0500 Subject: [PATCH] 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. --- libavcodec/Makefile | 1 + libavcodec/adx.h | 3 +- libavcodec/adx_parser.c | 104 ++++++++++++++++++++++++++++++++++++++++ libavcodec/adxdec.c | 100 +++++++++++++++++--------------------- libavcodec/allcodecs.c | 1 + libavcodec/version.h | 4 +- libavformat/segafilm.c | 1 + 7 files changed, 154 insertions(+), 60 deletions(-) create mode 100644 libavcodec/adx_parser.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 2cdcca2cfc..926056f42a 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -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 diff --git a/libavcodec/adx.h b/libavcodec/adx.h index f68a3cb793..93d547dcb2 100644 --- a/libavcodec/adx.h +++ b/libavcodec/adx.h @@ -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; diff --git a/libavcodec/adx_parser.c b/libavcodec/adx_parser.c new file mode 100644 index 0000000000..6de5ad48d1 --- /dev/null +++ b/libavcodec/adx_parser.c @@ -0,0 +1,104 @@ +/* + * 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, +}; diff --git a/libavcodec/adxdec.c b/libavcodec/adxdec.c index 0b0eac262c..ca96a904d6 100644 --- a/libavcodec/adxdec.c +++ b/libavcodec/adxdec.c @@ -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 = { diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index db213a1ae0..82023ff230 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -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); diff --git a/libavcodec/version.h b/libavcodec/version.h index 2313fae021..0bd17817ec 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -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, \ diff --git a/libavformat/segafilm.c b/libavformat/segafilm.c index b75c8774ce..9bf81d30d4 100644 --- a/libavformat/segafilm.c +++ b/libavformat/segafilm.c @@ -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 *