cb21e0a7 |
/*
* Copyright (C) 2008 Jaikrishnan Menon |
e280a4da |
* Copyright (C) 2011 Stefano Sabatini |
cb21e0a7 |
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/** |
ba87f080 |
* @file |
cb21e0a7 |
* 8svx audio decoder
* @author Jaikrishnan Menon |
ec679eb4 |
* |
cb21e0a7 |
* supports: fibonacci delta encoding
* : exponential encoding |
48dce860 |
*
* For more information about the 8SVX format:
* http://netghost.narod.ru/gff/vendspec/iff/iff.txt
* http://sox.sourceforge.net/AudioFormats-11.html
* http://aminet.net/package/mus/misc/wavepak
* http://amigan.1emu.net/reg/8SVX.txt
*
* Samples can be found here:
* http://aminet.net/mods/smpl/ |
cb21e0a7 |
*/
|
5caea648 |
#include "libavutil/avassert.h" |
cb21e0a7 |
#include "avcodec.h" |
594d4d5d |
#include "internal.h" |
1d9c2dc8 |
#include "libavutil/common.h" |
cb21e0a7 |
|
1b41f260 |
/** decoder context */ |
cb21e0a7 |
typedef struct EightSvxContext { |
df824548 |
uint8_t fib_acc[2]; |
e280a4da |
const int8_t *table;
|
df824548 |
/* buffer used to store the whole first packet.
data is only sent as one large packet */
uint8_t *data[2];
int data_size;
int data_idx; |
cb21e0a7 |
} EightSvxContext;
|
e280a4da |
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
/**
* Delta decode the compressed values in src, and put the resulting |
df824548 |
* decoded samples in dst. |
e280a4da |
* |
df824548 |
* @param[in,out] state starting value. it is saved for use in the next call. |
e280a4da |
* @param table delta sequence table
*/ |
df824548 |
static void delta_decode(uint8_t *dst, const uint8_t *src, int src_size,
uint8_t *state, const int8_t *table) |
e280a4da |
{ |
df824548 |
uint8_t val = *state; |
e280a4da |
|
26d1c7d9 |
while (src_size--) { |
e280a4da |
uint8_t d = *src++; |
26d1c7d9 |
val = av_clip_uint8(val + table[d & 0xF]); |
e280a4da |
*dst++ = val; |
26d1c7d9 |
val = av_clip_uint8(val + table[d >> 4]); |
e280a4da |
*dst++ = val;
}
|
df824548 |
*state = val; |
e280a4da |
} |
cb21e0a7 |
|
1b41f260 |
/** decode a frame */ |
0eea2129 |
static int eightsvx_decode_frame(AVCodecContext *avctx, void *data,
int *got_frame_ptr, AVPacket *avpkt) |
cb21e0a7 |
{
EightSvxContext *esc = avctx->priv_data; |
a3de4010 |
AVFrame *frame = data; |
df824548 |
int buf_size; |
cf8c93ad |
int ch, ret; |
da8242e2 |
int hdr_size = 2; |
cb21e0a7 |
|
e280a4da |
/* decode and interleave the first packet */ |
df824548 |
if (!esc->data[0] && avpkt) {
int chan_size = avpkt->size / avctx->channels - hdr_size; |
6eee9f55 |
|
df824548 |
if (avpkt->size % avctx->channels) { |
6eee9f55 |
av_log(avctx, AV_LOG_WARNING, "Packet with odd size, ignoring last byte\n");
} |
df824548 |
if (avpkt->size < (hdr_size + 1) * avctx->channels) {
av_log(avctx, AV_LOG_ERROR, "packet size is too small\n"); |
74512f7e |
return AVERROR_INVALIDDATA; |
df824548 |
} |
cb21e0a7 |
|
da8242e2 |
esc->fib_acc[0] = avpkt->data[1] + 128;
if (avctx->channels == 2)
esc->fib_acc[1] = avpkt->data[2+chan_size+1] + 128; |
e280a4da |
|
df824548 |
esc->data_idx = 0;
esc->data_size = chan_size;
if (!(esc->data[0] = av_malloc(chan_size)))
return AVERROR(ENOMEM);
if (avctx->channels == 2) {
if (!(esc->data[1] = av_malloc(chan_size))) {
av_freep(&esc->data[0]);
return AVERROR(ENOMEM); |
7a539e67 |
} |
e280a4da |
} |
df824548 |
memcpy(esc->data[0], &avpkt->data[hdr_size], chan_size);
if (avctx->channels == 2)
memcpy(esc->data[1], &avpkt->data[2*hdr_size+chan_size], chan_size);
}
if (!esc->data[0]) {
av_log(avctx, AV_LOG_ERROR, "unexpected empty packet\n"); |
74512f7e |
return AVERROR_INVALIDDATA; |
df824548 |
}
/* decode next piece of data from the buffer */
buf_size = FFMIN(MAX_FRAME_SIZE, esc->data_size - esc->data_idx);
if (buf_size <= 0) {
*got_frame_ptr = 0;
return avpkt->size; |
cb21e0a7 |
}
|
0eea2129 |
/* get output buffer */ |
4789955e |
frame->nb_samples = buf_size * 2; |
1ec94b0f |
if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) |
0eea2129 |
return ret; |
e280a4da |
|
df824548 |
for (ch = 0; ch < avctx->channels; ch++) { |
4789955e |
delta_decode(frame->data[ch], &esc->data[ch][esc->data_idx], |
da8242e2 |
buf_size, &esc->fib_acc[ch], esc->table); |
df824548 |
}
esc->data_idx += buf_size;
|
a3de4010 |
*got_frame_ptr = 1; |
cb21e0a7 |
|
df824548 |
return ((avctx->frame_number == 0)*hdr_size + buf_size)*avctx->channels; |
cb21e0a7 |
}
static av_cold int eightsvx_decode_init(AVCodecContext *avctx)
{
EightSvxContext *esc = avctx->priv_data;
|
1993c684 |
if (avctx->channels < 1 || avctx->channels > 2) { |
e280a4da |
av_log(avctx, AV_LOG_ERROR, "8SVX does not support more than 2 channels\n");
return AVERROR_INVALIDDATA; |
cb21e0a7 |
} |
e280a4da |
switch (avctx->codec->id) { |
7a72695c |
case AV_CODEC_ID_8SVX_FIB: esc->table = fibonacci; break;
case AV_CODEC_ID_8SVX_EXP: esc->table = exponential; break; |
e280a4da |
default:
av_log(avctx, AV_LOG_ERROR, "Invalid codec id %d.\n", avctx->codec->id);
return AVERROR_INVALIDDATA;
} |
cf8c93ad |
avctx->sample_fmt = AV_SAMPLE_FMT_U8P; |
e280a4da |
return 0;
}
static av_cold int eightsvx_decode_close(AVCodecContext *avctx)
{
EightSvxContext *esc = avctx->priv_data;
|
df824548 |
av_freep(&esc->data[0]);
av_freep(&esc->data[1]);
esc->data_size = 0;
esc->data_idx = 0; |
e280a4da |
|
cb21e0a7 |
return 0;
}
|
137e8081 |
#if CONFIG_EIGHTSVX_FIB_DECODER |
e7e2df27 |
AVCodec ff_eightsvx_fib_decoder = { |
99e3913d |
.name = "8svx_fib", |
b2bed932 |
.long_name = NULL_IF_CONFIG_SMALL("8SVX fibonacci"), |
72415b2a |
.type = AVMEDIA_TYPE_AUDIO, |
36ef5369 |
.id = AV_CODEC_ID_8SVX_FIB, |
cb21e0a7 |
.priv_data_size = sizeof (EightSvxContext),
.init = eightsvx_decode_init,
.decode = eightsvx_decode_frame, |
e280a4da |
.close = eightsvx_decode_close, |
e4de7167 |
.capabilities = CODEC_CAP_DR1, |
cf8c93ad |
.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_U8P,
AV_SAMPLE_FMT_NONE }, |
cb21e0a7 |
}; |
137e8081 |
#endif
#if CONFIG_EIGHTSVX_EXP_DECODER |
e7e2df27 |
AVCodec ff_eightsvx_exp_decoder = { |
99e3913d |
.name = "8svx_exp", |
b2bed932 |
.long_name = NULL_IF_CONFIG_SMALL("8SVX exponential"), |
72415b2a |
.type = AVMEDIA_TYPE_AUDIO, |
36ef5369 |
.id = AV_CODEC_ID_8SVX_EXP, |
cb21e0a7 |
.priv_data_size = sizeof (EightSvxContext),
.init = eightsvx_decode_init,
.decode = eightsvx_decode_frame, |
e280a4da |
.close = eightsvx_decode_close, |
e4de7167 |
.capabilities = CODEC_CAP_DR1, |
cf8c93ad |
.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_U8P,
AV_SAMPLE_FMT_NONE }, |
cb21e0a7 |
}; |
137e8081 |
#endif |