From e280a4da2ae6fd44f0079358ecc5aa08e388a5ed Mon Sep 17 00:00:00 2001 From: Stefano Sabatini Date: Sun, 15 May 2011 13:24:46 +0200 Subject: [PATCH] iff/8svx: redesign 8SVX demuxing and decoding for handling stereo samples correctly 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 | 179 ++++++++++++++++++++++++++++------- libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/avcodec.h | 1 + libavcodec/version.h | 2 +- libavformat/iff.c | 40 +------- tests/ref/fate/iff-fibonacci | 2 +- 7 files changed, 152 insertions(+), 74 deletions(-) diff --git a/libavcodec/8svx.c b/libavcodec/8svx.c index 4f95d9034e..5d94e005a2 100644 --- a/libavcodec/8svx.c +++ b/libavcodec/8svx.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 Jaikrishnan Menon + * Copyright (C) 2011 Stefano Sabatini * * This file is part of FFmpeg. * @@ -38,62 +39,155 @@ /** 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"), +}; diff --git a/libavcodec/Makefile b/libavcodec/Makefile index e293438e45..ac16e06df8 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -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 diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index fc74eeaf8c..ff032dda85 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -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); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 2fbf9cfc2a..d1a5e6655e 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -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, diff --git a/libavcodec/version.h b/libavcodec/version.h index 067cf4af89..471e3aaa9a 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -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, \ diff --git a/libavformat/iff.c b/libavformat/iff.c index b092de0b08..f6edcdda2e 100644 --- a/libavformat/iff.c +++ b/libavformat/iff.c @@ -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; } diff --git a/tests/ref/fate/iff-fibonacci b/tests/ref/fate/iff-fibonacci index e452f31e6c..947f78e964 100644 --- a/tests/ref/fate/iff-fibonacci +++ b/tests/ref/fate/iff-fibonacci @@ -1 +1 @@ -e968a853779bb6438339e3b8d69d8d24 +e76b025238a6a27968f8644f4ccc3207