Commit e280a4da authored by Stefano Sabatini's avatar Stefano Sabatini

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.
parent d8353256
/*
* 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"),
};
......@@ -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
......
......@@ -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);
......
......@@ -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,
......
......@@ -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, \
......
......@@ -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;
}
......
e968a853779bb6438339e3b8d69d8d24
e76b025238a6a27968f8644f4ccc3207
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment