iff/8svx: redesign 8SVX demuxing and decoding for handling stereo samples correctly
authorStefano Sabatini <stefano.sabatini-lala@poste.it>
Sun, 15 May 2011 11:24:46 +0000 (13:24 +0200)
committerStefano Sabatini <stefano.sabatini-lala@poste.it>
Mon, 16 May 2011 22:58:33 +0000 (00:58 +0200)
Make the iff demuxer send the whole audio chunk to the decoder as a
single packet, move stereo interleaving from the iff demuxer to the
decoder, and introduce an 8svx_raw decoder which performs
stereo interleaving.

This is required for handling stereo data correctly, indeed samples
are stored like:
LLLLLL....RRRRRR

that is all left samples are at the beginning of the chunk, all right
samples at the end, so it is necessary to store and process the whole
buffer in order to decode each frame. Thus the decoder needs all the
audio chunk before it can return interleaved data.

Fix decoding of files 8svx_exp.iff and 8svx_fib.iff, fix trac issue #169.

libavcodec/8svx.c
libavcodec/Makefile
libavcodec/allcodecs.c
libavcodec/avcodec.h
libavcodec/version.h
libavformat/iff.c
tests/ref/fate/iff-fibonacci

index 4f95d90..5d94e00 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2008 Jaikrishnan Menon
+ * Copyright (C) 2011 Stefano Sabatini
  *
  * This file is part of FFmpeg.
  *
 
 /** decoder context */
 typedef struct EightSvxContext {
-    int16_t fib_acc;
-    const int16_t *table;
+    const int8_t *table;
+
+    /* buffer used to store the whole audio decoded/interleaved chunk,
+     * which is sent with the first packet */
+    uint8_t *samples;
+    size_t samples_size;
+    int samples_idx;
 } EightSvxContext;
 
-static const int16_t fibonacci[16]   = { -34<<8, -21<<8, -13<<8,  -8<<8, -5<<8, -3<<8, -2<<8, -1<<8,
-                                          0, 1<<8, 2<<8, 3<<8, 5<<8, 8<<8, 13<<8, 21<<8 };
-static const int16_t exponential[16] = { -128<<8, -64<<8, -32<<8, -16<<8, -8<<8, -4<<8, -2<<8, -1<<8,
-                                          0, 1<<8, 2<<8, 4<<8, 8<<8, 16<<8, 32<<8, 64<<8 };
+static const int8_t fibonacci[16]   = { -34,  -21, -13,  -8, -5, -3, -2, -1, 0, 1, 2, 3, 5, 8,  13, 21 };
+static const int8_t exponential[16] = { -128, -64, -32, -16, -8, -4, -2, -1, 0, 1, 2, 4, 8, 16, 32, 64 };
+
+#define MAX_FRAME_SIZE 2048
+
+/**
+ * Interleave samples in buffer containing all left channel samples
+ * at the beginning, and right channel samples at the end.
+ * Each sample is assumed to be in signed 8-bit format.
+ *
+ * @param size the size in bytes of the dst and src buffer
+ */
+static void interleave_stereo(uint8_t *dst, const uint8_t *src, int size)
+{
+    uint8_t *dst_end = dst + size;
+    size = size>>1;
+
+    while (dst < dst_end) {
+        *dst++ = *src;
+        *dst++ = *(src+size);
+        src++;
+    }
+}
+
+/**
+ * Delta decode the compressed values in src, and put the resulting
+ * decoded n samples in dst.
+ *
+ * @param val starting value assumed by the delta sequence
+ * @param table delta sequence table
+ * @return size in bytes of the decoded data, must be src_size*2
+ */
+static int delta_decode(int8_t *dst, const uint8_t *src, int src_size,
+                        int8_t val, const int8_t *table)
+{
+    int n = src_size;
+    int8_t *dst0 = dst;
+
+    while (n--) {
+        uint8_t d = *src++;
+        val = av_clip(val + table[d & 0x0f], -127, 128);
+        *dst++ = val;
+        val = av_clip(val + table[d >> 4]  , -127, 128);
+        *dst++ = val;
+    }
+
+    return dst-dst0;
+}
 
 static int eightsvx_decode_frame(AVCodecContext *avctx, void *data, int *data_size,
                                  AVPacket *avpkt)
 {
-    const uint8_t *buf = avpkt->data;
-    int buf_size = avpkt->size;
     EightSvxContext *esc = avctx->priv_data;
-    int16_t *out_data = data;
-    int consumed = buf_size;
-    const uint8_t *buf_end = buf + buf_size;
+    int out_data_size, n;
+    uint8_t *src, *dst;
 
-    if((*data_size >> 2) < buf_size)
-        return -1;
+    /* decode and interleave the first packet */
+    if (!esc->samples && avpkt) {
+        uint8_t *deinterleaved_samples;
 
-    if(avctx->frame_number == 0) {
-        esc->fib_acc = buf[1] << 8;
-        buf_size -= 2;
-        buf += 2;
-    }
+        esc->samples_size = avctx->codec->id == CODEC_ID_8SVX_RAW ?
+            avpkt->size : avctx->channels + (avpkt->size-avctx->channels) * 2;
+        if (!(esc->samples = av_malloc(esc->samples_size)))
+            return AVERROR(ENOMEM);
 
-    *data_size = buf_size << 2;
+        /* decompress */
+        if (avctx->codec->id == CODEC_ID_8SVX_FIB || avctx->codec->id == CODEC_ID_8SVX_EXP) {
+            const uint8_t *buf = avpkt->data;
+            int buf_size = avpkt->size;
+            int n = esc->samples_size;
 
-    while(buf < buf_end) {
-        uint8_t d = *buf++;
-        esc->fib_acc += esc->table[d & 0x0f];
-        *out_data++ = esc->fib_acc;
-        esc->fib_acc += esc->table[d >> 4];
-        *out_data++ = esc->fib_acc;
+            if (!(deinterleaved_samples = av_mallocz(n)))
+                return AVERROR(ENOMEM);
+
+            /* the uncompressed starting value is contained in the first byte */
+            if (avctx->channels == 2) {
+                delta_decode(deinterleaved_samples      , buf+1, buf_size/2-1, buf[0], esc->table);
+                buf += buf_size/2;
+                delta_decode(deinterleaved_samples+n/2-1, buf+1, buf_size/2-1, buf[0], esc->table);
+            } else
+                delta_decode(deinterleaved_samples      , buf+1, buf_size-1  , buf[0], esc->table);
+        } else {
+            deinterleaved_samples = avpkt->data;
+        }
+
+        if (avctx->channels == 2)
+            interleave_stereo(esc->samples, deinterleaved_samples, esc->samples_size);
+        else
+            memcpy(esc->samples, deinterleaved_samples, esc->samples_size);
     }
 
-    return consumed;
+    /* return single packed with fixed size */
+    out_data_size = FFMIN(MAX_FRAME_SIZE, esc->samples_size - esc->samples_idx);
+    if (*data_size < out_data_size) {
+        av_log(avctx, AV_LOG_ERROR, "Provided buffer with size %d is too small.\n", *data_size);
+        return AVERROR(EINVAL);
+    }
+
+    *data_size = out_data_size;
+    dst = data;
+    src = esc->samples + esc->samples_idx;
+    for (n = out_data_size; n > 0; n--)
+        *dst++ = *src++ + 128;
+    esc->samples_idx += *data_size;
+
+    return avctx->codec->id == CODEC_ID_8SVX_FIB || avctx->codec->id == CODEC_ID_8SVX_EXP ?
+        (avctx->frame_number == 0)*2 + out_data_size / 2 :
+        out_data_size;
 }
 
 static av_cold int eightsvx_decode_init(AVCodecContext *avctx)
 {
     EightSvxContext *esc = avctx->priv_data;
 
-    switch(avctx->codec->id) {
-        case CODEC_ID_8SVX_FIB:
-          esc->table = fibonacci;
-          break;
-        case CODEC_ID_8SVX_EXP:
-          esc->table = exponential;
-          break;
-        default:
-          return -1;
+    if (avctx->channels > 2) {
+        av_log(avctx, AV_LOG_ERROR, "8SVX does not support more than 2 channels\n");
+        return AVERROR_INVALIDDATA;
     }
-    avctx->sample_fmt = AV_SAMPLE_FMT_S16;
+
+    switch (avctx->codec->id) {
+    case CODEC_ID_8SVX_FIB: esc->table = fibonacci;    break;
+    case CODEC_ID_8SVX_EXP: esc->table = exponential;  break;
+    case CODEC_ID_8SVX_RAW: esc->table = NULL;         break;
+    default:
+        av_log(avctx, AV_LOG_ERROR, "Invalid codec id %d.\n", avctx->codec->id);
+        return AVERROR_INVALIDDATA;
+    }
+    avctx->sample_fmt = AV_SAMPLE_FMT_U8;
+
+    return 0;
+}
+
+static av_cold int eightsvx_decode_close(AVCodecContext *avctx)
+{
+    EightSvxContext *esc = avctx->priv_data;
+
+    av_freep(&esc->samples);
+    esc->samples_size = 0;
+    esc->samples_idx = 0;
+
     return 0;
 }
 
@@ -104,6 +198,7 @@ AVCodec ff_eightsvx_fib_decoder = {
   .priv_data_size = sizeof (EightSvxContext),
   .init           = eightsvx_decode_init,
   .decode         = eightsvx_decode_frame,
+  .close          = eightsvx_decode_close,
   .long_name      = NULL_IF_CONFIG_SMALL("8SVX fibonacci"),
 };
 
@@ -114,5 +209,17 @@ AVCodec ff_eightsvx_exp_decoder = {
   .priv_data_size = sizeof (EightSvxContext),
   .init           = eightsvx_decode_init,
   .decode         = eightsvx_decode_frame,
+  .close          = eightsvx_decode_close,
   .long_name      = NULL_IF_CONFIG_SMALL("8SVX exponential"),
 };
+
+AVCodec ff_eightsvx_raw_decoder = {
+  .name           = "8svx_raw",
+  .type           = AVMEDIA_TYPE_AUDIO,
+  .id             = CODEC_ID_8SVX_RAW,
+  .priv_data_size = sizeof(EightSvxContext),
+  .init           = eightsvx_decode_init,
+  .decode         = eightsvx_decode_frame,
+  .close          = eightsvx_decode_close,
+  .long_name      = NULL_IF_CONFIG_SMALL("8SVX rawaudio"),
+};
index e293438..ac16e06 100644 (file)
@@ -136,6 +136,7 @@ OBJS-$(CONFIG_EATQI_DECODER)           += eatqi.o eaidct.o mpeg12.o \
 OBJS-$(CONFIG_EIGHTBPS_DECODER)        += 8bps.o
 OBJS-$(CONFIG_EIGHTSVX_EXP_DECODER)    += 8svx.o
 OBJS-$(CONFIG_EIGHTSVX_FIB_DECODER)    += 8svx.o
+OBJS-$(CONFIG_EIGHTSVX_RAW_DECODER)    += 8svx.o
 OBJS-$(CONFIG_ESCAPE124_DECODER)       += escape124.o
 OBJS-$(CONFIG_FFV1_DECODER)            += ffv1.o rangecoder.o
 OBJS-$(CONFIG_FFV1_ENCODER)            += ffv1.o rangecoder.o
index fc74eea..ff032dd 100644 (file)
@@ -104,6 +104,7 @@ void avcodec_register_all(void)
     REGISTER_DECODER (EIGHTBPS, eightbps);
     REGISTER_DECODER (EIGHTSVX_EXP, eightsvx_exp);
     REGISTER_DECODER (EIGHTSVX_FIB, eightsvx_fib);
+    REGISTER_DECODER (EIGHTSVX_RAW, eightsvx_raw);
     REGISTER_DECODER (ESCAPE124, escape124);
     REGISTER_ENCDEC  (FFV1, ffv1);
     REGISTER_ENCDEC  (FFVHUFF, ffvhuff);
index 2fbf9cf..d1a5e66 100644 (file)
@@ -204,6 +204,7 @@ enum CodecID {
     CODEC_ID_PRORES,
     CODEC_ID_JV,
     CODEC_ID_DFA,
+    CODEC_ID_8SVX_RAW,
 
     /* various PCM "codecs" */
     CODEC_ID_PCM_S16LE= 0x10000,
index 067cf4a..471e3aa 100644 (file)
@@ -21,7 +21,7 @@
 #define AVCODEC_VERSION_H
 
 #define LIBAVCODEC_VERSION_MAJOR 53
-#define LIBAVCODEC_VERSION_MINOR  5
+#define LIBAVCODEC_VERSION_MINOR  6
 #define LIBAVCODEC_VERSION_MICRO  0
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
index b092de0..f6edcdd 100644 (file)
@@ -60,8 +60,6 @@
 #define RIGHT   4
 #define STEREO  6
 
-#define PACKET_SIZE 1024
-
 /**
  * This number of bytes if added at the beginning of each AVPacket
  * which contain additional information about video properties
@@ -97,19 +95,6 @@ typedef struct {
     unsigned  masking;      ///< masking method used
 } IffDemuxContext;
 
-
-static void interleave_stereo(const uint8_t *src, uint8_t *dest, int size)
-{
-    uint8_t *end = dest + size;
-    size = size>>1;
-
-    while(dest < end) {
-        *dest++ = *src;
-        *dest++ = *(src+size);
-        src++;
-    }
-}
-
 /* Metadata string read */
 static int get_metadata(AVFormatContext *s,
                         const char *const tag,
@@ -255,7 +240,7 @@ static int iff_read_header(AVFormatContext *s,
 
         switch (iff->svx8_compression) {
         case COMP_NONE:
-            st->codec->codec_id = CODEC_ID_PCM_S8;
+            st->codec->codec_id = CODEC_ID_8SVX_RAW;
             break;
         case COMP_FIB:
             st->codec->codec_id = CODEC_ID_8SVX_FIB;
@@ -330,15 +315,8 @@ static int iff_read_packet(AVFormatContext *s,
     if(iff->sent_bytes >= iff->body_size)
         return AVERROR(EIO);
 
-    if(st->codec->channels == 2) {
-        uint8_t sample_buffer[PACKET_SIZE];
-
-        ret = avio_read(pb, sample_buffer, PACKET_SIZE);
-        if(av_new_packet(pkt, PACKET_SIZE) < 0) {
-            av_log(s, AV_LOG_ERROR, "cannot allocate packet\n");
-            return AVERROR(ENOMEM);
-        }
-        interleave_stereo(sample_buffer, pkt->data, PACKET_SIZE);
+    if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
+        ret = av_get_packet(pb, pkt, iff->body_size);
     } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
         uint8_t *buf;
 
@@ -349,23 +327,13 @@ static int iff_read_packet(AVFormatContext *s,
         buf = pkt->data;
         bytestream_put_be16(&buf, 2);
         ret = avio_read(pb, buf, iff->body_size);
-    } else {
-        ret = av_get_packet(pb, pkt, PACKET_SIZE);
     }
 
     if(iff->sent_bytes == 0)
         pkt->flags |= AV_PKT_FLAG_KEY;
+    iff->sent_bytes = iff->body_size;
 
-    if(st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
-        iff->sent_bytes += PACKET_SIZE;
-    } else {
-        iff->sent_bytes = iff->body_size;
-    }
     pkt->stream_index = 0;
-    if(st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
-        pkt->pts = iff->audio_frame_count;
-        iff->audio_frame_count += ret / st->codec->channels;
-    }
     return ret;
 }
 
index e452f31..947f78e 100644 (file)
@@ -1 +1 @@
-e968a853779bb6438339e3b8d69d8d24
+e76b025238a6a27968f8644f4ccc3207