aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustin Ruggles <justin.ruggles@gmail.com>2011-11-21 01:49:37 -0500
committerJustin Ruggles <justin.ruggles@gmail.com>2011-11-26 16:25:07 -0500
commit27360ccc5edcaa1a37dec39127e87c461ce668b6 (patch)
treed0dd010e4a00fc34ec3b2aa8cb8c83f85958b246
parentd8cec2d7fc1c2c5b6f82b8743b3cfd04cd4bb8cc (diff)
downloadffmpeg-27360ccc5edcaa1a37dec39127e87c461ce668b6.tar.gz
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.
-rw-r--r--libavcodec/Makefile1
-rw-r--r--libavcodec/adx.h3
-rw-r--r--libavcodec/adx_parser.c104
-rw-r--r--libavcodec/adxdec.c100
-rw-r--r--libavcodec/allcodecs.c1
-rw-r--r--libavcodec/version.h4
-rw-r--r--libavformat/segafilm.c1
7 files changed, 154 insertions, 60 deletions
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 *