[FFmpeg-devel] [PATCH 1/2] avcodec: add Interplay ACM decoder

Paul B Mahol onemda at gmail.com
Sat Oct 31 22:08:15 CET 2015


Signed-off-by: Paul B Mahol <onemda at gmail.com>
---
 libavcodec/Makefile       |   1 +
 libavcodec/allcodecs.c    |   1 +
 libavcodec/avcodec.h      |   1 +
 libavcodec/codec_desc.c   |   7 +
 libavcodec/interplayacm.c | 590 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 600 insertions(+)
 create mode 100644 libavcodec/interplayacm.c

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 8162447..4a879d6 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -145,6 +145,7 @@ OBJS-$(CONFIG_AC3_FIXED_DECODER)       += ac3dec_fixed.o ac3dec_data.o ac3.o kbd
 OBJS-$(CONFIG_AC3_ENCODER)             += ac3enc_float.o ac3enc.o ac3tab.o \
                                           ac3.o kbdwin.o
 OBJS-$(CONFIG_AC3_FIXED_ENCODER)       += ac3enc_fixed.o ac3enc.o ac3tab.o ac3.o
+OBJS-$(CONFIG_ACM_DECODER)             += interplayacm.o
 OBJS-$(CONFIG_AIC_DECODER)             += aic.o
 OBJS-$(CONFIG_ALAC_DECODER)            += alac.o alac_data.o alacdsp.o
 OBJS-$(CONFIG_ALAC_ENCODER)            += alacenc.o alac_data.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 49478a1..cfb8912 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -371,6 +371,7 @@ void avcodec_register_all(void)
     REGISTER_DECODER(AAC_LATM,          aac_latm);
     REGISTER_ENCDEC (AC3,               ac3);
     REGISTER_ENCDEC (AC3_FIXED,         ac3_fixed);
+    REGISTER_DECODER(ACM,               acm);
     REGISTER_ENCDEC (ALAC,              alac);
     REGISTER_DECODER(ALS,               als);
     REGISTER_DECODER(AMRNB,             amrnb);
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index b4f113f..91cd6a8 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -496,6 +496,7 @@ enum AVCodecID {
     AV_CODEC_ID_DSD_LSBF_PLANAR,
     AV_CODEC_ID_DSD_MSBF_PLANAR,
     AV_CODEC_ID_4GV,
+    AV_CODEC_ID_INTERPLAY_ACM,
 
     /* subtitle codecs */
     AV_CODEC_ID_FIRST_SUBTITLE = 0x17000,          ///< A dummy ID pointing at the start of subtitle codecs.
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index bf7c425..58d9d93 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -2622,6 +2622,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("DSD (Direct Stream Digital), most significant bit first, planar"),
         .props     = AV_CODEC_PROP_LOSSY,
     },
+    {
+        .id        = AV_CODEC_ID_INTERPLAY_ACM,
+        .type      = AVMEDIA_TYPE_AUDIO,
+        .name      = "acm",
+        .long_name = NULL_IF_CONFIG_SMALL("Interplay ACM"),
+        .props     = AV_CODEC_PROP_LOSSY,
+    },
 
     /* subtitle codecs */
     {
diff --git a/libavcodec/interplayacm.c b/libavcodec/interplayacm.c
new file mode 100644
index 0000000..8a2e7c9
--- /dev/null
+++ b/libavcodec/interplayacm.c
@@ -0,0 +1,590 @@
+/*
+ * Interplay ACM decoder
+ *
+ * Copyright (c) 2004-2008, Marko Kreen
+ * Copyright (c) 2008, Adam Gashlin
+ * Copyright (c) 2015 Paul B Mahol
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "avcodec.h"
+#define BITSTREAM_READER_LE
+#include "get_bits.h"
+#include "internal.h"
+
+static const int8_t map_1bit[] = { -1, +1 };
+static const int8_t map_2bit_near[] = { -2, -1, +1, +2 };
+static const int8_t map_2bit_far[] = { -3, -2, +2, +3 };
+static const int8_t map_3bit[] = { -4, -3, -2, -1, +1, +2, +3, +4 };
+
+static int mul_3x3 [3 * 3 * 3];
+static int mul_3x5 [5 * 5 * 5];
+static int mul_2x11[11  *  11];
+
+typedef struct InterplayACMContext {
+    GetBitContext gb;
+    uint8_t *bitstream;
+    int max_framesize;
+    int bitstream_size;
+    int bitstream_index;
+
+    int level;
+    int rows;
+    int cols;
+    int wrapbuf_len;
+    int block_len;
+    int skip;
+
+    int *block;
+    int *wrapbuf;
+    int *ampbuf;
+    int *midbuf;
+} InterplayACMContext;
+
+static av_cold int decode_init(AVCodecContext *avctx)
+{
+    InterplayACMContext *s = avctx->priv_data;
+    int x1, x2, x3;
+
+    if (avctx->extradata_size < 14)
+        return AVERROR_INVALIDDATA;
+
+    s->level = AV_RL16(avctx->extradata + 12) & 0xf;
+    s->rows  = AV_RL16(avctx->extradata + 12) >>  4;
+    s->cols  = 1 << s->level;
+    s->wrapbuf_len = 2 * s->cols - 2;
+    s->block_len = s->rows * s->cols;
+    s->max_framesize = s->block_len;
+
+    s->block   = av_calloc(s->block_len, sizeof(int));
+    s->wrapbuf = av_calloc(s->wrapbuf_len, sizeof(int));
+    s->ampbuf  = av_calloc(0x10000, sizeof(int));
+    s->bitstream = av_calloc(s->max_framesize, sizeof(*s->bitstream));
+    if (!s->block || !s->wrapbuf || !s->ampbuf || !s->bitstream)
+        return AVERROR(ENOMEM);
+
+    s->midbuf  = s->ampbuf + 0x8000;
+    avctx->sample_fmt = AV_SAMPLE_FMT_S16;
+
+    for (x3 = 0; x3 < 3; x3++)
+        for (x2 = 0; x2 < 3; x2++)
+            for (x1 = 0; x1 < 3; x1++)
+                mul_3x3[x1 + x2 * 3 + x3* 3 * 3] = x1 + (x2 << 4) + (x3 << 8);
+    for (x3 = 0; x3 < 5; x3++)
+        for (x2 = 0; x2 < 5; x2++)
+            for (x1 = 0; x1 < 5; x1++)
+                mul_3x5[x1 + x2 * 5 + x3 * 5 * 5] = x1 + (x2 << 4) + (x3 << 8);
+    for (x2 = 0; x2 < 11; x2++)
+        for (x1 = 0; x1 < 11; x1++)
+            mul_2x11[x1 + x2 * 11] = x1 + (x2 << 4);
+
+    return 0;
+}
+
+#define set_pos(s, r, c, idx) do { \
+        unsigned pos = ((r) << s->level) + (c); \
+        s->block[pos] = s->midbuf[idx]; \
+    } while (0)
+
+static int zero(InterplayACMContext *s, unsigned ind, unsigned col)
+{
+    unsigned i;
+    for (i = 0; i < s->rows; i++)
+        set_pos(s, i, col, 0);
+    return 0;
+}
+
+static int bad(InterplayACMContext *s, unsigned ind, unsigned col)
+{
+    return AVERROR_INVALIDDATA;
+}
+
+static int linear(InterplayACMContext *s, unsigned ind, unsigned col)
+{
+    GetBitContext *gb = &s->gb;
+    unsigned int i;
+    int b, middle = 1 << (ind - 1);
+
+    for (i = 0; i < s->rows; i++) {
+        b = get_bits(gb, ind);
+        set_pos(s, i, col, b - middle);
+    }
+    return 0;
+}
+
+static int k13(InterplayACMContext *s, unsigned ind, unsigned col)
+{
+    GetBitContext *gb = &s->gb;
+    unsigned i, b;
+    for (i = 0; i < s->rows; i++) {
+        b = get_bits1(gb);
+        if (b == 0) {
+            set_pos(s, i++, col, 0);
+            if (i >= s->rows)
+                break;
+            set_pos(s, i, col, 0);
+            continue;
+        }
+        b = get_bits1(gb);
+        if (b == 0) {
+            set_pos(s, i, col, 0);
+            continue;
+        }
+        b = get_bits1(gb);
+        set_pos(s, i, col, map_1bit[b]);
+    }
+    return 0;
+}
+
+static int k12(InterplayACMContext *s, unsigned ind, unsigned col)
+{
+    GetBitContext *gb = &s->gb;
+    unsigned i, b;
+    for (i = 0; i < s->rows; i++) {
+        b = get_bits1(gb);
+        if (b == 0) {
+            set_pos(s, i, col, 0);
+            continue;
+        }
+
+        b = get_bits1(gb);
+        set_pos(s, i, col, map_1bit[b]);
+    }
+    return 0;
+}
+
+static int k24(InterplayACMContext *s, unsigned ind, unsigned col)
+{
+    GetBitContext *gb = &s->gb;
+    unsigned i, b;
+    for (i = 0; i < s->rows; i++) {
+        b = get_bits1(gb);
+        if (b == 0) {
+            set_pos(s, i++, col, 0);
+            if (i >= s->rows) break;
+            set_pos(s, i, col, 0);
+            continue;
+        }
+
+        b = get_bits1(gb);
+        if (b == 0) {
+            set_pos(s, i, col, 0);
+            continue;
+        }
+
+        b = get_bits(gb, 2);
+        set_pos(s, i, col, map_2bit_near[b]);
+    }
+    return 0;
+}
+
+static int k23(InterplayACMContext *s, unsigned ind, unsigned col)
+{
+    GetBitContext *gb = &s->gb;
+    unsigned i, b;
+    for (i = 0; i < s->rows; i++) {
+        b = get_bits1(gb);
+        if (b == 0) {
+            set_pos(s, i, col, 0);
+            continue;
+        }
+
+        b = get_bits(gb, 2);
+        set_pos(s, i, col, map_2bit_near[b]);
+    }
+    return 0;
+}
+
+static int k35(InterplayACMContext *s, unsigned ind, unsigned col)
+{
+    GetBitContext *gb = &s->gb;
+    unsigned i, b;
+    for (i = 0; i < s->rows; i++) {
+        b = get_bits1(gb);
+        if (b == 0) {
+            set_pos(s, i++, col, 0);
+            if (i >= s->rows)
+                break;
+            set_pos(s, i, col, 0);
+            continue;
+        }
+
+        b = get_bits1(gb);
+        if (b == 0) {
+            set_pos(s, i, col, 0);
+            continue;
+        }
+
+        b = get_bits1(gb);
+        if (b == 0) {
+            b = get_bits1(gb);
+            set_pos(s, i, col, map_1bit[b]);
+            continue;
+        }
+
+        b = get_bits(gb, 2);
+        set_pos(s, i, col, map_2bit_far[b]);
+    }
+    return 0;
+}
+
+static int k34(InterplayACMContext *s, unsigned ind, unsigned col)
+{
+    GetBitContext *gb = &s->gb;
+    unsigned i, b;
+    for (i = 0; i < s->rows; i++) {
+        b = get_bits1(gb);
+        if (b == 0) {
+            set_pos(s, i, col, 0);
+            continue;
+        }
+
+        b = get_bits1(gb);
+        if (b == 0) {
+            b = get_bits1(gb);
+            set_pos(s, i, col, map_1bit[b]);
+            continue;
+        }
+
+        b = get_bits(gb, 2);
+        set_pos(s, i, col, map_2bit_far[b]);
+    }
+    return 0;
+}
+
+static int k45(InterplayACMContext *s, unsigned ind, unsigned col)
+{
+    GetBitContext *gb = &s->gb;
+    unsigned i, b;
+    for (i = 0; i < s->rows; i++) {
+        b = get_bits1(gb);
+        if (b == 0) {
+            set_pos(s, i, col, 0); i++;
+            if (i >= s->rows)
+                break;
+            set_pos(s, i, col, 0);
+            continue;
+        }
+
+        b = get_bits1(gb);
+        if (b == 0) {
+            set_pos(s, i, col, 0);
+            continue;
+        }
+
+        b = get_bits(gb, 3);
+        set_pos(s, i, col, map_3bit[b]);
+    }
+    return 0;
+}
+
+static int k44(InterplayACMContext *s, unsigned ind, unsigned col)
+{
+    GetBitContext *gb = &s->gb;
+    unsigned i, b;
+    for (i = 0; i < s->rows; i++) {
+        b = get_bits1(gb);
+        if (b == 0) {
+            set_pos(s, i, col, 0);
+            continue;
+        }
+
+        b = get_bits(gb, 3);
+        set_pos(s, i, col, map_3bit[b]);
+    }
+    return 0;
+}
+
+static int t15(InterplayACMContext *s, unsigned ind, unsigned col)
+{
+    GetBitContext *gb = &s->gb;
+    unsigned i, b;
+    int n1, n2, n3;
+    for (i = 0; i < s->rows; i++) {
+        /* b = (x1) + (x2 * 3) + (x3 * 9) */
+        b = get_bits(gb, 5);
+
+        n1 =  (mul_3x3[b] & 0x0F) - 1;
+        n2 = ((mul_3x3[b] >> 4) & 0x0F) - 1;
+        n3 = ((mul_3x3[b] >> 8) & 0x0F) - 1;
+
+        set_pos(s, i++, col, n1);
+        if (i >= s->rows)
+            break;
+        set_pos(s, i++, col, n2);
+        if (i >= s->rows)
+            break;
+        set_pos(s, i, col, n3);
+    }
+    return 0;
+}
+
+static int t27(InterplayACMContext *s, unsigned ind, unsigned col)
+{
+    GetBitContext *gb = &s->gb;
+    unsigned i, b;
+    int n1, n2, n3;
+    for (i = 0; i < s->rows; i++) {
+        /* b = (x1) + (x2 * 5) + (x3 * 25) */
+        b = get_bits(gb, 7);
+
+        n1 =  (mul_3x5[b] & 0x0F) - 2;
+        n2 = ((mul_3x5[b] >> 4) & 0x0F) - 2;
+        n3 = ((mul_3x5[b] >> 8) & 0x0F) - 2;
+
+        set_pos(s, i++, col, n1);
+        if (i >= s->rows)
+            break;
+        set_pos(s, i++, col, n2);
+        if (i >= s->rows)
+            break;
+        set_pos(s, i, col, n3);
+    }
+    return 0;
+}
+
+static int t37(InterplayACMContext *s, unsigned ind, unsigned col)
+{
+    GetBitContext *gb = &s->gb;
+    unsigned i, b;
+    int n1, n2;
+    for (i = 0; i < s->rows; i++) {
+        /* b = (x1) + (x2 * 11) */
+        b = get_bits(gb, 7);
+
+        n1 =  (mul_2x11[b] & 0x0F) - 5;
+        n2 = ((mul_2x11[b] >> 4) & 0x0F) - 5;
+
+        set_pos(s, i++, col, n1);
+        if (i >= s->rows)
+            break;
+        set_pos(s, i, col, n2);
+    }
+    return 0;
+}
+
+typedef int (*filler)(InterplayACMContext *s, unsigned ind, unsigned col);
+
+static const filler filler_list[] = {
+    zero,   bad,    bad,    linear,
+    linear, linear, linear, linear,
+    linear, linear, linear, linear,
+    linear, linear, linear, linear,
+    linear, k13,    k12,    t15,
+    k24,    k23,    t27,    k35,
+    k34,    bad,    k45,    k44,
+    bad,    t37,    bad,    bad,
+};
+
+static int fill_block(InterplayACMContext *s)
+{
+    GetBitContext *gb = &s->gb;
+    unsigned i, ind;
+    int ret;
+
+    for (i = 0; i < s->cols; i++) {
+        ind = get_bits(gb, 5);
+        ret = filler_list[ind](s, ind, i);
+        if (ret < 0)
+            return ret;
+    }
+    return 0;
+}
+
+static void juggle(int *wrap_p, int *block_p, unsigned sub_len, unsigned sub_count)
+{
+    unsigned int i, j;
+    int *p, r0, r1, r2, r3;
+    for (i = 0; i < sub_len; i++) {
+        p = block_p;
+        r0 = wrap_p[0];
+        r1 = wrap_p[1];
+        for (j = 0; j < sub_count/2; j++) {
+            r2 = *p;  *p = r1*2 + (r0 + r2);  p += sub_len;
+            r3 = *p;  *p = r2*2 - (r1 + r3);  p += sub_len;
+            r0 = r2;  r1 = r3;
+        }
+        *wrap_p++ = r0;
+        *wrap_p++ = r1;
+        block_p++;
+    }
+}
+
+static void juggle_block(InterplayACMContext *s)
+{
+    unsigned sub_count, sub_len, todo_count, step_subcount, i;
+    int *wrap_p, *block_p, *p;
+
+    /* juggle only if subblock_len > 1 */
+    if (s->level == 0)
+        return;
+
+    /* 2048 / subblock_len */
+    if (s->level > 9)
+        step_subcount = 1;
+    else
+        step_subcount = (2048 >> s->level) - 2;
+
+    /* Apply juggle()  (rows)x(cols)
+     * from (step_subcount * 2)            x (subblock_len/2)
+     * to   (step_subcount * subblock_len) x (1)
+     */
+    todo_count = s->rows;
+    block_p = s->block;
+    while (1) {
+        wrap_p = s->wrapbuf;
+        sub_count = step_subcount;
+        if (sub_count > todo_count)
+            sub_count = todo_count;
+
+        sub_len = s->cols / 2;
+        sub_count *= 2;
+
+        juggle(wrap_p, block_p, sub_len, sub_count);
+        wrap_p += sub_len*2;
+
+        for (i = 0, p = block_p; i < sub_count; i++) {
+            p[0]++;
+            p += sub_len;
+        }
+
+        while (sub_len > 1) {
+            sub_len /= 2;
+            sub_count *= 2;
+            juggle(wrap_p, block_p, sub_len, sub_count);
+            wrap_p += sub_len*2;
+        }
+        if (todo_count <= step_subcount)
+            break;
+        todo_count -= step_subcount;
+        block_p += step_subcount << s->level;
+    }
+}
+
+static int decode_block(InterplayACMContext *s)
+{
+    GetBitContext *gb = &s->gb;
+    int pwr, count, val, i, x, ret;
+
+    pwr = get_bits(gb, 4);
+    val = get_bits(gb, 16);
+
+    count = 1 << pwr;
+
+    for (i = 0, x = 0; i < count; i++) {
+        s->midbuf[i] = x;
+        x += val;
+    }
+
+    for (i = 1, x = -val; i <= count; i++) {
+        s->midbuf[-i] = x;
+        x -= val;
+    }
+
+    ret = fill_block(s);
+    if (ret < 0)
+        return ret;
+
+    juggle_block(s);
+
+    return 0;
+}
+
+static int decode_frame(AVCodecContext *avctx, void *data,
+                        int *got_frame_ptr, AVPacket *pkt)
+{
+    InterplayACMContext *s = avctx->priv_data;
+    GetBitContext *gb = &s->gb;
+    AVFrame *frame = data;
+    const uint8_t *buf;
+    int16_t *samples;
+    int ret, n, buf_size, input_buf_size;
+
+    buf_size = FFMIN(pkt->size, s->max_framesize - s->bitstream_size);
+    input_buf_size = buf_size;
+    if (s->bitstream_index + s->bitstream_size + buf_size > s->max_framesize) {
+        memmove(s->bitstream, &s->bitstream[s->bitstream_index], s->bitstream_size);
+        s->bitstream_index = 0;
+    }
+    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;
+    if (buf_size < s->max_framesize && pkt->data) {
+        *got_frame_ptr = 0;
+        return input_buf_size;
+    }
+
+    if ((ret = init_get_bits8(gb, s->bitstream, s->bitstream_size)) < 0)
+        return ret;
+
+    frame->nb_samples = s->block_len / avctx->channels;
+    if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
+        return ret;
+
+    skip_bits(gb, s->skip);
+    ret = decode_block(s);
+    if (ret < 0)
+        return ret;
+
+    samples = (int16_t *)frame->data[0];
+    for (n = 0; n < frame->nb_samples * avctx->channels; n++) {
+        int val = s->block[n] >> s->level;
+        *samples++ = val;
+    }
+
+    *got_frame_ptr = 1;
+    s->skip = get_bits_count(gb) - 8 * (get_bits_count(gb) / 8);
+    n = get_bits_count(gb) / 8;
+
+    if (n > buf_size && pkt->data) {
+        s->bitstream_size = 0;
+        s->bitstream_index = 0;
+        return AVERROR_INVALIDDATA;
+    }
+
+    if (s->bitstream_size) {
+        s->bitstream_index += n;
+        s->bitstream_size  -= n;
+        return input_buf_size;
+    }
+    return n;
+}
+
+static av_cold int decode_close(AVCodecContext *avctx)
+{
+    InterplayACMContext *s = avctx->priv_data;
+
+    av_freep(&s->block);
+    av_freep(&s->wrapbuf);
+    av_freep(&s->ampbuf);
+    av_freep(&s->bitstream);
+    s->bitstream_size = 0;
+
+    return 0;
+}
+
+AVCodec ff_acm_decoder = {
+    .name           = "acm",
+    .long_name      = NULL_IF_CONFIG_SMALL("Interplay ACM"),
+    .type           = AVMEDIA_TYPE_AUDIO,
+    .id             = AV_CODEC_ID_INTERPLAY_ACM,
+    .init           = decode_init,
+    .close          = decode_close,
+    .decode         = decode_frame,
+    .capabilities   = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1,
+    .priv_data_size = sizeof(InterplayACMContext),
+};
-- 
1.9.1



More information about the ffmpeg-devel mailing list