adx: add an ADX parser.
authorJustin Ruggles <justin.ruggles@gmail.com>
Mon, 21 Nov 2011 06:49:37 +0000 (01:49 -0500)
committerJustin Ruggles <justin.ruggles@gmail.com>
Sat, 26 Nov 2011 21:25:07 +0000 (16:25 -0500)
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
libavcodec/adx.h
libavcodec/adx_parser.c [new file with mode: 0644]
libavcodec/adxdec.c
libavcodec/allcodecs.c
libavcodec/version.h
libavformat/segafilm.c

index 2cdcca2..926056f 100644 (file)
@@ -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
index f68a3cb..93d547d 100644 (file)
@@ -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 (file)
index 0000000..6de5ad4
--- /dev/null
@@ -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,
+};
index 0b0eac2..ca96a90 100644 (file)
 
 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 = {
index db213a1..82023ff 100644 (file)
@@ -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);
index 2313fae..0bd1781 100644 (file)
@@ -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, \
index b75c877..9bf81d3 100644 (file)
@@ -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 *