mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
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.
This commit is contained in:
parent
d8353256a3
commit
e280a4da2a
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2008 Jaikrishnan Menon
|
* Copyright (C) 2008 Jaikrishnan Menon
|
||||||
|
* Copyright (C) 2011 Stefano Sabatini
|
||||||
*
|
*
|
||||||
* This file is part of FFmpeg.
|
* This file is part of FFmpeg.
|
||||||
*
|
*
|
||||||
@ -38,62 +39,155 @@
|
|||||||
|
|
||||||
/** decoder context */
|
/** decoder context */
|
||||||
typedef struct EightSvxContext {
|
typedef struct EightSvxContext {
|
||||||
int16_t fib_acc;
|
const int8_t *table;
|
||||||
const int16_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;
|
} EightSvxContext;
|
||||||
|
|
||||||
static const int16_t fibonacci[16] = { -34<<8, -21<<8, -13<<8, -8<<8, -5<<8, -3<<8, -2<<8, -1<<8,
|
static const int8_t fibonacci[16] = { -34, -21, -13, -8, -5, -3, -2, -1, 0, 1, 2, 3, 5, 8, 13, 21 };
|
||||||
0, 1<<8, 2<<8, 3<<8, 5<<8, 8<<8, 13<<8, 21<<8 };
|
static const int8_t exponential[16] = { -128, -64, -32, -16, -8, -4, -2, -1, 0, 1, 2, 4, 8, 16, 32, 64 };
|
||||||
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 };
|
#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,
|
static int eightsvx_decode_frame(AVCodecContext *avctx, void *data, int *data_size,
|
||||||
AVPacket *avpkt)
|
AVPacket *avpkt)
|
||||||
{
|
{
|
||||||
const uint8_t *buf = avpkt->data;
|
|
||||||
int buf_size = avpkt->size;
|
|
||||||
EightSvxContext *esc = avctx->priv_data;
|
EightSvxContext *esc = avctx->priv_data;
|
||||||
int16_t *out_data = data;
|
int out_data_size, n;
|
||||||
int consumed = buf_size;
|
uint8_t *src, *dst;
|
||||||
const uint8_t *buf_end = buf + buf_size;
|
|
||||||
|
|
||||||
if((*data_size >> 2) < buf_size)
|
/* decode and interleave the first packet */
|
||||||
return -1;
|
if (!esc->samples && avpkt) {
|
||||||
|
uint8_t *deinterleaved_samples;
|
||||||
|
|
||||||
if(avctx->frame_number == 0) {
|
esc->samples_size = avctx->codec->id == CODEC_ID_8SVX_RAW ?
|
||||||
esc->fib_acc = buf[1] << 8;
|
avpkt->size : avctx->channels + (avpkt->size-avctx->channels) * 2;
|
||||||
buf_size -= 2;
|
if (!(esc->samples = av_malloc(esc->samples_size)))
|
||||||
buf += 2;
|
return AVERROR(ENOMEM);
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
*data_size = buf_size << 2;
|
/* return single packed with fixed size */
|
||||||
|
out_data_size = FFMIN(MAX_FRAME_SIZE, esc->samples_size - esc->samples_idx);
|
||||||
while(buf < buf_end) {
|
if (*data_size < out_data_size) {
|
||||||
uint8_t d = *buf++;
|
av_log(avctx, AV_LOG_ERROR, "Provided buffer with size %d is too small.\n", *data_size);
|
||||||
esc->fib_acc += esc->table[d & 0x0f];
|
return AVERROR(EINVAL);
|
||||||
*out_data++ = esc->fib_acc;
|
|
||||||
esc->fib_acc += esc->table[d >> 4];
|
|
||||||
*out_data++ = esc->fib_acc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return consumed;
|
*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)
|
static av_cold int eightsvx_decode_init(AVCodecContext *avctx)
|
||||||
{
|
{
|
||||||
EightSvxContext *esc = avctx->priv_data;
|
EightSvxContext *esc = avctx->priv_data;
|
||||||
|
|
||||||
switch(avctx->codec->id) {
|
if (avctx->channels > 2) {
|
||||||
case CODEC_ID_8SVX_FIB:
|
av_log(avctx, AV_LOG_ERROR, "8SVX does not support more than 2 channels\n");
|
||||||
esc->table = fibonacci;
|
return AVERROR_INVALIDDATA;
|
||||||
break;
|
|
||||||
case CODEC_ID_8SVX_EXP:
|
|
||||||
esc->table = exponential;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +198,7 @@ AVCodec ff_eightsvx_fib_decoder = {
|
|||||||
.priv_data_size = sizeof (EightSvxContext),
|
.priv_data_size = sizeof (EightSvxContext),
|
||||||
.init = eightsvx_decode_init,
|
.init = eightsvx_decode_init,
|
||||||
.decode = eightsvx_decode_frame,
|
.decode = eightsvx_decode_frame,
|
||||||
|
.close = eightsvx_decode_close,
|
||||||
.long_name = NULL_IF_CONFIG_SMALL("8SVX fibonacci"),
|
.long_name = NULL_IF_CONFIG_SMALL("8SVX fibonacci"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -114,5 +209,17 @@ AVCodec ff_eightsvx_exp_decoder = {
|
|||||||
.priv_data_size = sizeof (EightSvxContext),
|
.priv_data_size = sizeof (EightSvxContext),
|
||||||
.init = eightsvx_decode_init,
|
.init = eightsvx_decode_init,
|
||||||
.decode = eightsvx_decode_frame,
|
.decode = eightsvx_decode_frame,
|
||||||
|
.close = eightsvx_decode_close,
|
||||||
.long_name = NULL_IF_CONFIG_SMALL("8SVX exponential"),
|
.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_EIGHTBPS_DECODER) += 8bps.o
|
||||||
OBJS-$(CONFIG_EIGHTSVX_EXP_DECODER) += 8svx.o
|
OBJS-$(CONFIG_EIGHTSVX_EXP_DECODER) += 8svx.o
|
||||||
OBJS-$(CONFIG_EIGHTSVX_FIB_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_ESCAPE124_DECODER) += escape124.o
|
||||||
OBJS-$(CONFIG_FFV1_DECODER) += ffv1.o rangecoder.o
|
OBJS-$(CONFIG_FFV1_DECODER) += ffv1.o rangecoder.o
|
||||||
OBJS-$(CONFIG_FFV1_ENCODER) += 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 (EIGHTBPS, eightbps);
|
||||||
REGISTER_DECODER (EIGHTSVX_EXP, eightsvx_exp);
|
REGISTER_DECODER (EIGHTSVX_EXP, eightsvx_exp);
|
||||||
REGISTER_DECODER (EIGHTSVX_FIB, eightsvx_fib);
|
REGISTER_DECODER (EIGHTSVX_FIB, eightsvx_fib);
|
||||||
|
REGISTER_DECODER (EIGHTSVX_RAW, eightsvx_raw);
|
||||||
REGISTER_DECODER (ESCAPE124, escape124);
|
REGISTER_DECODER (ESCAPE124, escape124);
|
||||||
REGISTER_ENCDEC (FFV1, ffv1);
|
REGISTER_ENCDEC (FFV1, ffv1);
|
||||||
REGISTER_ENCDEC (FFVHUFF, ffvhuff);
|
REGISTER_ENCDEC (FFVHUFF, ffvhuff);
|
||||||
|
@ -204,6 +204,7 @@ enum CodecID {
|
|||||||
CODEC_ID_PRORES,
|
CODEC_ID_PRORES,
|
||||||
CODEC_ID_JV,
|
CODEC_ID_JV,
|
||||||
CODEC_ID_DFA,
|
CODEC_ID_DFA,
|
||||||
|
CODEC_ID_8SVX_RAW,
|
||||||
|
|
||||||
/* various PCM "codecs" */
|
/* various PCM "codecs" */
|
||||||
CODEC_ID_PCM_S16LE= 0x10000,
|
CODEC_ID_PCM_S16LE= 0x10000,
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
#define AVCODEC_VERSION_H
|
#define AVCODEC_VERSION_H
|
||||||
|
|
||||||
#define LIBAVCODEC_VERSION_MAJOR 53
|
#define LIBAVCODEC_VERSION_MAJOR 53
|
||||||
#define LIBAVCODEC_VERSION_MINOR 5
|
#define LIBAVCODEC_VERSION_MINOR 6
|
||||||
#define LIBAVCODEC_VERSION_MICRO 0
|
#define LIBAVCODEC_VERSION_MICRO 0
|
||||||
|
|
||||||
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
|
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
|
||||||
|
@ -60,8 +60,6 @@
|
|||||||
#define RIGHT 4
|
#define RIGHT 4
|
||||||
#define STEREO 6
|
#define STEREO 6
|
||||||
|
|
||||||
#define PACKET_SIZE 1024
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This number of bytes if added at the beginning of each AVPacket
|
* This number of bytes if added at the beginning of each AVPacket
|
||||||
* which contain additional information about video properties
|
* which contain additional information about video properties
|
||||||
@ -97,19 +95,6 @@ typedef struct {
|
|||||||
unsigned masking; ///< masking method used
|
unsigned masking; ///< masking method used
|
||||||
} IffDemuxContext;
|
} 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 */
|
/* Metadata string read */
|
||||||
static int get_metadata(AVFormatContext *s,
|
static int get_metadata(AVFormatContext *s,
|
||||||
const char *const tag,
|
const char *const tag,
|
||||||
@ -255,7 +240,7 @@ static int iff_read_header(AVFormatContext *s,
|
|||||||
|
|
||||||
switch (iff->svx8_compression) {
|
switch (iff->svx8_compression) {
|
||||||
case COMP_NONE:
|
case COMP_NONE:
|
||||||
st->codec->codec_id = CODEC_ID_PCM_S8;
|
st->codec->codec_id = CODEC_ID_8SVX_RAW;
|
||||||
break;
|
break;
|
||||||
case COMP_FIB:
|
case COMP_FIB:
|
||||||
st->codec->codec_id = CODEC_ID_8SVX_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)
|
if(iff->sent_bytes >= iff->body_size)
|
||||||
return AVERROR(EIO);
|
return AVERROR(EIO);
|
||||||
|
|
||||||
if(st->codec->channels == 2) {
|
if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||||
uint8_t sample_buffer[PACKET_SIZE];
|
ret = av_get_packet(pb, pkt, iff->body_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);
|
|
||||||
} else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
|
} else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
|
|
||||||
@ -349,23 +327,13 @@ static int iff_read_packet(AVFormatContext *s,
|
|||||||
buf = pkt->data;
|
buf = pkt->data;
|
||||||
bytestream_put_be16(&buf, 2);
|
bytestream_put_be16(&buf, 2);
|
||||||
ret = avio_read(pb, buf, iff->body_size);
|
ret = avio_read(pb, buf, iff->body_size);
|
||||||
} else {
|
|
||||||
ret = av_get_packet(pb, pkt, PACKET_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(iff->sent_bytes == 0)
|
if(iff->sent_bytes == 0)
|
||||||
pkt->flags |= AV_PKT_FLAG_KEY;
|
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;
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
e968a853779bb6438339e3b8d69d8d24
|
e76b025238a6a27968f8644f4ccc3207
|
||||||
|
Loading…
Reference in New Issue
Block a user