From c89e075d5a66e095c4c21d6bb7ecd80bb1137f73 Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Fri, 30 Oct 2015 18:15:22 +0100 Subject: [PATCH] avcodec: add Interplay ACM decoder Signed-off-by: Paul B Mahol --- doc/general.texi | 1 + libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/avcodec.h | 1 + libavcodec/codec_desc.c | 7 + libavcodec/interplayacm.c | 615 ++++++++++++++++++++++++++++++++++++++ libavcodec/version.h | 4 +- 7 files changed, 628 insertions(+), 2 deletions(-) create mode 100644 libavcodec/interplayacm.c diff --git a/doc/general.texi b/doc/general.texi index e51771a182..f647b81bf9 100644 --- a/doc/general.texi +++ b/doc/general.texi @@ -989,6 +989,7 @@ following image formats are supported: @item iLBC (Internet Low Bitrate Codec) @tab E @tab E @tab encoding and decoding supported through external library libilbc @item IMC (Intel Music Coder) @tab @tab X +@item Interplay ACM @tab @tab X @item MACE (Macintosh Audio Compression/Expansion) 3:1 @tab @tab X @item MACE (Macintosh Audio Compression/Expansion) 6:1 @tab @tab X @item MLP (Meridian Lossless Packing) @tab @tab X diff --git a/libavcodec/Makefile b/libavcodec/Makefile index b61424f639..67fb72ad9d 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -320,6 +320,7 @@ OBJS-$(CONFIG_INDEO2_DECODER) += indeo2.o OBJS-$(CONFIG_INDEO3_DECODER) += indeo3.o OBJS-$(CONFIG_INDEO4_DECODER) += indeo4.o ivi.o OBJS-$(CONFIG_INDEO5_DECODER) += indeo5.o ivi.o +OBJS-$(CONFIG_INTERPLAY_ACM_DECODER) += interplayacm.o OBJS-$(CONFIG_INTERPLAY_DPCM_DECODER) += dpcm.o OBJS-$(CONFIG_INTERPLAY_VIDEO_DECODER) += interplayvideo.o OBJS-$(CONFIG_JACOSUB_DECODER) += jacosubdec.o ass.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 29225bf8c9..095f8128e2 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -399,6 +399,7 @@ void avcodec_register_all(void) REGISTER_DECODER(GSM_MS, gsm_ms); REGISTER_DECODER(IAC, iac); REGISTER_DECODER(IMC, imc); + REGISTER_DECODER(INTERPLAY_ACM, interplay_acm); REGISTER_DECODER(MACE3, mace3); REGISTER_DECODER(MACE6, mace6); REGISTER_DECODER(METASOUND, metasound); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index b4f113f66b..91cd6a829f 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 bf7c425818..c6117400a8 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 = "interplayacm", + .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 0000000000..a676bcb931 --- /dev/null +++ b/libavcodec/interplayacm.c @@ -0,0 +1,615 @@ +/* + * 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 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; + + if (!pkt->size && !s->bitstream_size) { + *got_frame_ptr = 0; + return 0; + } + + 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, buf, buf_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_interplay_acm_decoder = { + .name = "interplayacm", + .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), +}; diff --git a/libavcodec/version.h b/libavcodec/version.h index 02c2a089e6..cc4e2367b6 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -29,8 +29,8 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 57 -#define LIBAVCODEC_VERSION_MINOR 13 -#define LIBAVCODEC_VERSION_MICRO 102 +#define LIBAVCODEC_VERSION_MINOR 14 +#define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ LIBAVCODEC_VERSION_MINOR, \