Smacker demuxer and decoder.

Originally committed as revision 5189 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
Kostya Shishkov 2006-03-21 17:27:47 +00:00
parent 305ee50fbb
commit 348efc1836
10 changed files with 1059 additions and 5 deletions

View File

@ -41,6 +41,7 @@ version <next>
- Improved Theora/VP3 decoder
- True Audio (TTA) decoder
- AVS demuxer and video decoder
- Smacker demuxer and decoder
version 0.4.9-pre1:

View File

@ -702,6 +702,8 @@ library:
@tab Multimedia format used in games like Mad Dog McCree
@item AVS @tab @tab X
@tab Multimedia format used by the Creature Shock game.
@item Smacker @tab @tab X
@tab Multimedia format used by many games.
@end multitable
@code{X} means that encoding (resp. decoding) is supported.
@ -796,6 +798,7 @@ following image formats are supported:
@item American Laser Games Video @tab @tab X @tab Used in games like Mad Dog McCree
@item ZMBV @tab @tab X @tab
@item AVS Video @tab @tab X @tab Video encoding used by the Creature Shock game.
@item Smacker Video @tab @tab X @tab Video encoding used in Smacker.
@end multitable
@code{X} means that encoding (resp. decoding) is supported.
@ -871,6 +874,7 @@ other implementations.
@tab All versions except 5.1 are supported
@item DSP Group TrueSpeech @tab @tab X
@item True Audio (TTA) @tab @tab X
@item Smacker Audio @tab @tab X
@end multitable
@code{X} means that encoding (resp. decoding) is supported.

View File

@ -146,6 +146,9 @@ endif
ifeq ($(CONFIG_SHORTEN_DECODER),yes)
OBJS+= shorten.o
endif
ifneq ($(CONFIG_SMACKER_DECODER)$(CONFIG_SMACKAUD_DECODER),)
OBJS+= smacker.o
endif
ifeq ($(CONFIG_SMC_DECODER),yes)
OBJS+= smc.o
endif

View File

@ -447,6 +447,12 @@ void avcodec_register_all(void)
#ifdef CONFIG_ZMBV_DECODER
register_avcodec(&zmbv_decoder);
#endif //CONFIG_ZMBV_DECODER
#ifdef CONFIG_SMACKER_DECODER
register_avcodec(&smacker_decoder);
#endif //CONFIG_SMACKER_DECODER
#ifdef CONFIG_SMACKAUD_DECODER
register_avcodec(&smackaud_decoder);
#endif //CONFIG_SMACKAUD_DECODER
#ifdef CONFIG_SONIC_DECODER
register_avcodec(&sonic_decoder);
#endif //CONFIG_SONIC_DECODER

View File

@ -21,8 +21,8 @@ extern "C" {
#define AV_STRINGIFY(s) AV_TOSTRING(s)
#define AV_TOSTRING(s) #s
#define LIBAVCODEC_VERSION_INT ((51<<16)+(7<<8)+0)
#define LIBAVCODEC_VERSION 51.7.0
#define LIBAVCODEC_VERSION_INT ((51<<16)+(8<<8)+0)
#define LIBAVCODEC_VERSION 51.8.0
#define LIBAVCODEC_BUILD LIBAVCODEC_VERSION_INT
#define LIBAVCODEC_IDENT "Lavc" AV_STRINGIFY(LIBAVCODEC_VERSION)
@ -118,6 +118,7 @@ enum CodecID {
CODEC_ID_MMVIDEO,
CODEC_ID_ZMBV,
CODEC_ID_AVS,
CODEC_ID_SMACKVIDEO,
/* various pcm "codecs" */
CODEC_ID_PCM_S16LE= 0x10000,
@ -198,6 +199,7 @@ enum CodecID {
CODEC_ID_COOK,
CODEC_ID_TRUESPEECH,
CODEC_ID_TTA,
CODEC_ID_SMACKAUDIO,
CODEC_ID_OGGTHEORA= 0x16000,
@ -2238,6 +2240,8 @@ extern AVCodec bmp_decoder;
extern AVCodec mmvideo_decoder;
extern AVCodec zmbv_decoder;
extern AVCodec avs_decoder;
extern AVCodec smacker_decoder;
extern AVCodec smackaud_decoder;
/* pcm codecs */
#define PCM_CODEC(id, name) \

689
libavcodec/smacker.c Normal file
View File

@ -0,0 +1,689 @@
/*
* Smacker decoder
* Copyright (c) 2006 Konstantin Shishkov
*
* This library 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 of the License, or (at your option) any later version.
*
* This library 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 this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/**
* @file smacker.c
* Smacker decoder
*/
/*
* Based on http://wiki.multimedia.cx/index.php?title=Smacker
*/
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include "avcodec.h"
#define ALT_BITSTREAM_READER_LE
#include "bitstream.h"
#define SMKTREE_BITS 9
#define SMK_NODE 0x80000000
/*
* Decoder context
*/
typedef struct SmackVContext {
AVCodecContext *avctx;
AVFrame pic;
int *mmap_tbl, *mclr_tbl, *full_tbl, *type_tbl;
int mmap_last[3], mclr_last[3], full_last[3], type_last[3];
} SmackVContext;
/**
* Context used for code reconstructing
*/
typedef struct HuffContext {
int length;
int maxlength;
int current;
uint32_t *bits;
int *lengths;
int *values;
} HuffContext;
/* common parameters used for decode_bigtree */
typedef struct DBCtx {
VLC *v1, *v2;
int *recode1, *recode2;
int escapes[3];
int *last;
int lcur;
} DBCtx;
/* possible runs of blocks */
static const int block_runs[64] = {
1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56,
57, 58, 59, 128, 256, 512, 1024, 2048 };
enum SmkBlockTypes {
SMK_BLK_MONO = 0,
SMK_BLK_FULL = 1,
SMK_BLK_SKIP = 2,
SMK_BLK_FILL = 3 };
/**
* Decode local frame tree
*/
static int smacker_decode_tree(GetBitContext *gb, HuffContext *hc, uint32_t prefix, int length)
{
if(!get_bits1(gb)){ //Leaf
if(hc->current >= 256){
av_log(NULL, AV_LOG_ERROR, "Tree size exceeded!\n");
return -1;
}
if(length){
hc->bits[hc->current] = prefix;
hc->lengths[hc->current] = length;
} else {
hc->bits[hc->current] = 0;
hc->lengths[hc->current] = 0;
}
hc->values[hc->current] = get_bits(gb, 8);
hc->current++;
if(hc->maxlength < length)
hc->maxlength = length;
return 0;
} else { //Node
int r;
length++;
r = smacker_decode_tree(gb, hc, prefix, length);
if(r)
return r;
return smacker_decode_tree(gb, hc, prefix | (1 << (length - 1)), length);
}
}
/**
* Decode header tree
*/
static int smacker_decode_bigtree(GetBitContext *gb, HuffContext *hc, DBCtx *ctx)
{
if(!get_bits1(gb)){ //Leaf
int val, i1, i2, b1, b2;
if(hc->current >= hc->length){
av_log(NULL, AV_LOG_ERROR, "Tree size exceeded!\n");
return -1;
}
b1 = get_bits_count(gb);
i1 = get_vlc2(gb, ctx->v1->table, SMKTREE_BITS, 3);
b1 = get_bits_count(gb) - b1;
b2 = get_bits_count(gb);
i2 = get_vlc2(gb, ctx->v2->table, SMKTREE_BITS, 3);
b2 = get_bits_count(gb) - b2;
val = ctx->recode1[i1] | (ctx->recode2[i2] << 8);
if(val == ctx->escapes[0]) {
ctx->last[0] = hc->current;
val = 0;
} else if(val == ctx->escapes[1]) {
ctx->last[1] = hc->current;
val = 0;
} else if(val == ctx->escapes[2]) {
ctx->last[2] = hc->current;
val = 0;
}
hc->values[hc->current++] = val;
return 1;
} else { //Node
int r = 0, t;
t = hc->current++;
r = smacker_decode_bigtree(gb, hc, ctx);
if(r < 0)
return r;
hc->values[t] = SMK_NODE | r;
r++;
r += smacker_decode_bigtree(gb, hc, ctx);
return r;
}
}
/**
* Store large tree as FFmpeg's vlc codes
*/
static int smacker_decode_header_tree(SmackVContext *smk, GetBitContext *gb, int **recodes, int *last, int size)
{
int res;
HuffContext huff;
HuffContext tmp1, tmp2;
VLC vlc[2];
int escapes[3];
DBCtx ctx;
tmp1.length = 256;
tmp1.maxlength = 0;
tmp1.current = 0;
tmp1.bits = av_mallocz(256 * 4);
tmp1.lengths = av_mallocz(256 * sizeof(int));
tmp1.values = av_mallocz(256 * sizeof(int));
tmp2.length = 256;
tmp2.maxlength = 0;
tmp2.current = 0;
tmp2.bits = av_mallocz(256 * 4);
tmp2.lengths = av_mallocz(256 * sizeof(int));
tmp2.values = av_mallocz(256 * sizeof(int));
memset(&vlc[0], 0, sizeof(VLC));
memset(&vlc[1], 0, sizeof(VLC));
if(get_bits1(gb)) {
smacker_decode_tree(gb, &tmp1, 0, 0);
get_bits1(gb);
res = init_vlc(&vlc[0], SMKTREE_BITS, tmp1.length,
tmp1.lengths, sizeof(int), sizeof(int),
tmp1.bits, sizeof(uint32_t), sizeof(uint32_t), INIT_VLC_LE);
if(res < 0) {
av_log(smk->avctx, AV_LOG_ERROR, "Cannot build VLC table\n");
return -1;
}
} else {
av_log(smk->avctx, AV_LOG_ERROR, "Skipping low bytes tree\n");
}
if(get_bits1(gb)){
smacker_decode_tree(gb, &tmp2, 0, 0);
get_bits1(gb);
res = init_vlc(&vlc[1], SMKTREE_BITS, tmp2.length,
tmp2.lengths, sizeof(int), sizeof(int),
tmp2.bits, sizeof(uint32_t), sizeof(uint32_t), INIT_VLC_LE);
if(res < 0) {
av_log(smk->avctx, AV_LOG_ERROR, "Cannot build VLC table\n");
return -1;
}
} else {
av_log(smk->avctx, AV_LOG_ERROR, "Skipping high bytes tree\n");
}
escapes[0] = get_bits(gb, 8);
escapes[0] |= get_bits(gb, 8) << 8;
escapes[1] = get_bits(gb, 8);
escapes[1] |= get_bits(gb, 8) << 8;
escapes[2] = get_bits(gb, 8);
escapes[2] |= get_bits(gb, 8) << 8;
last[0] = last[1] = last[2] = -1;
ctx.escapes[0] = escapes[0];
ctx.escapes[1] = escapes[1];
ctx.escapes[2] = escapes[2];
ctx.v1 = &vlc[0];
ctx.v2 = &vlc[1];
ctx.recode1 = tmp1.values;
ctx.recode2 = tmp2.values;
ctx.last = last;
huff.length = ((size + 3) >> 2) + 3;
huff.maxlength = 0;
huff.current = 0;
huff.values = av_mallocz(huff.length * sizeof(int));
smacker_decode_bigtree(gb, &huff, &ctx);
get_bits1(gb);
if(ctx.last[0] == -1) ctx.last[0] = huff.current++;
if(ctx.last[1] == -1) ctx.last[1] = huff.current++;
if(ctx.last[2] == -1) ctx.last[2] = huff.current++;
*recodes = huff.values;
if(vlc[0].table)
free_vlc(&vlc[0]);
if(vlc[1].table)
free_vlc(&vlc[1]);
av_free(tmp1.bits);
av_free(tmp1.lengths);
av_free(tmp1.values);
av_free(tmp2.bits);
av_free(tmp2.lengths);
av_free(tmp2.values);
return 0;
}
static int decode_header_trees(SmackVContext *smk) {
GetBitContext gb;
int mmap_size, mclr_size, full_size, type_size;
mmap_size = LE_32(smk->avctx->extradata);
mclr_size = LE_32(smk->avctx->extradata + 4);
full_size = LE_32(smk->avctx->extradata + 8);
type_size = LE_32(smk->avctx->extradata + 12);
init_get_bits(&gb, smk->avctx->extradata + 16, (smk->avctx->extradata_size - 16) * 8);
if(!get_bits1(&gb)) {
av_log(smk->avctx, AV_LOG_INFO, "Skipping MMAP tree\n");
smk->mmap_tbl = av_malloc(sizeof(int) * 2);
smk->mmap_tbl[0] = 0;
smk->mmap_last[0] = smk->mmap_last[1] = smk->mmap_last[2] = 1;
} else {
smacker_decode_header_tree(smk, &gb, &smk->mmap_tbl, smk->mmap_last, mmap_size);
}
if(!get_bits(&gb, 1)) {
av_log(smk->avctx, AV_LOG_INFO, "Skipping MCLR tree\n");
smk->mclr_tbl = av_malloc(sizeof(int) * 2);
smk->mclr_tbl[0] = 0;
smk->mclr_last[0] = smk->mclr_last[1] = smk->mclr_last[2] = 1;
} else {
smacker_decode_header_tree(smk, &gb, &smk->mclr_tbl, smk->mclr_last, mclr_size);
}
if(!get_bits(&gb, 1)) {
av_log(smk->avctx, AV_LOG_INFO, "Skipping FULL tree\n");
smk->full_tbl = av_malloc(sizeof(int) * 2);
smk->full_tbl[0] = 0;
smk->full_last[0] = smk->full_last[1] = smk->full_last[2] = 1;
} else {
smacker_decode_header_tree(smk, &gb, &smk->full_tbl, smk->full_last, full_size);
}
if(!get_bits(&gb, 1)) {
av_log(smk->avctx, AV_LOG_INFO, "Skipping TYPE tree\n");
smk->type_tbl = av_malloc(sizeof(int) * 2);
smk->type_tbl[0] = 0;
smk->type_last[0] = smk->type_last[1] = smk->type_last[2] = 1;
} else {
smacker_decode_header_tree(smk, &gb, &smk->type_tbl, smk->type_last, type_size);
}
return 0;
}
static always_inline void last_reset(int *recode, int *last) {
recode[last[0]] = recode[last[1]] = recode[last[2]] = 0;
}
/* get code and update history */
static always_inline int smk_get_code(GetBitContext *gb, int *recode, int *last) {
register int *table = recode;
int v, b;
b = get_bits_count(gb);
while(*table & SMK_NODE) {
if(get_bits1(gb))
table += (*table) & (~SMK_NODE);
table++;
}
v = *table;
b = get_bits_count(gb) - b;
if(v != recode[last[0]]) {
recode[last[2]] = recode[last[1]];
recode[last[1]] = recode[last[0]];
recode[last[0]] = v;
}
return v;
}
static int decode_frame(AVCodecContext *avctx, void *data, int *data_size, uint8_t *buf, int buf_size)
{
SmackVContext * const smk = (SmackVContext *)avctx->priv_data;
uint8_t *out;
uint32_t *pal;
GetBitContext gb;
int blocks, blk, bw, bh;
int i;
int stride;
if(buf_size == 769)
return 0;
if(smk->pic.data[0])
avctx->release_buffer(avctx, &smk->pic);
smk->pic.reference = 1;
smk->pic.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE | FF_BUFFER_HINTS_REUSABLE;
if(avctx->reget_buffer(avctx, &smk->pic) < 0){
av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
return -1;
}
/* make the palette available on the way out */
out = buf + 1;
pal = (uint32_t*)smk->pic.data[1];
smk->pic.palette_has_changed = buf[0] & 1;
smk->pic.key_frame = !!(buf[0] & 2);
if(smk->pic.key_frame)
smk->pic.pict_type = FF_I_TYPE;
else
smk->pic.pict_type = FF_P_TYPE;
for(i = 0; i < 256; i++) {
int r, g, b;
r = *out++;
g = *out++;
b = *out++;
*pal++ = (r << 16) | (g << 8) | b;
}
last_reset(smk->mmap_tbl, smk->mmap_last);
last_reset(smk->mclr_tbl, smk->mclr_last);
last_reset(smk->full_tbl, smk->full_last);
last_reset(smk->type_tbl, smk->type_last);
init_get_bits(&gb, buf + 769, (buf_size - 769) * 8);
blk = 0;
bw = avctx->width >> 2;
bh = avctx->height >> 2;
blocks = bw * bh;
out = smk->pic.data[0];
stride = smk->pic.linesize[0];
while(blk < blocks) {
int type, run, mode;
uint16_t pix;
type = smk_get_code(&gb, smk->type_tbl, smk->type_last);
run = block_runs[(type >> 2) & 0x3F];
switch(type & 3){
case SMK_BLK_MONO:
while(run-- && blk < blocks){
int clr, map;
int hi, lo;
clr = smk_get_code(&gb, smk->mclr_tbl, smk->mclr_last);
map = smk_get_code(&gb, smk->mmap_tbl, smk->mmap_last);
out = smk->pic.data[0] + (blk / bw) * (stride * 4) + (blk % bw) * 4;
hi = clr >> 8;
lo = clr & 0xFF;
for(i = 0; i < 4; i++) {
if(map & 1) out[0] = hi; else out[0] = lo;
if(map & 2) out[1] = hi; else out[1] = lo;
if(map & 4) out[2] = hi; else out[2] = lo;
if(map & 8) out[3] = hi; else out[3] = lo;
map >>= 4;
out += stride;
}
blk++;
}
break;
case SMK_BLK_FULL:
mode = 0;
if(avctx->codec_tag != 0) { // In case of Smacker v4 we have three modes
if(get_bits1(&gb)) mode = 1;
else if(get_bits1(&gb)) mode = 2;
}
while(run-- && blk < blocks){
out = smk->pic.data[0] + (blk / bw) * (stride * 4) + (blk % bw) * 4;
switch(mode){
case 0:
for(i = 0; i < 4; i++) {
pix = smk_get_code(&gb, smk->full_tbl, smk->full_last);
out[2] = pix & 0xFF;
out[3] = pix >> 8;
pix = smk_get_code(&gb, smk->full_tbl, smk->full_last);
out[0] = pix & 0xFF;
out[1] = pix >> 8;
out += stride;
}
break;
case 1:
pix = smk_get_code(&gb, smk->full_tbl, smk->full_last);
out[0] = out[1] = pix & 0xFF;
out[2] = out[3] = pix >> 8;
out += stride;
out[0] = out[1] = pix & 0xFF;
out[2] = out[3] = pix >> 8;
out += stride;
pix = smk_get_code(&gb, smk->full_tbl, smk->full_last);
out[0] = out[1] = pix & 0xFF;
out[2] = out[3] = pix >> 8;
out += stride;
out[0] = out[1] = pix & 0xFF;
out[2] = out[3] = pix >> 8;
out += stride;
break;
case 2:
for(i = 0; i < 2; i++) {
uint16_t pix1, pix2;
pix1 = smk_get_code(&gb, smk->full_tbl, smk->full_last);
pix2 = smk_get_code(&gb, smk->full_tbl, smk->full_last);
out[0] = pix1 & 0xFF; out[1] = pix1 >> 8;
out[2] = pix2 & 0xFF; out[3] = pix2 >> 8;
out += stride;
out[0] = pix1 & 0xFF; out[1] = pix1 >> 8;
out[2] = pix2 & 0xFF; out[3] = pix2 >> 8;
out += stride;
}
break;
}
blk++;
}
break;
case SMK_BLK_SKIP:
while(run-- && blk < blocks)
blk++;
break;
case SMK_BLK_FILL:
mode = type >> 8;
while(run-- && blk < blocks){
uint32_t col;
out = smk->pic.data[0] + (blk / bw) * (stride * 4) + (blk % bw) * 4;
col = mode * 0x01010101;
for(i = 0; i < 4; i++) {
*((uint32_t*)out) = col;
out += stride;
}
blk++;
}
break;
}
}
*data_size = sizeof(AVFrame);
*(AVFrame*)data = smk->pic;
/* always report that the buffer was completely consumed */
return buf_size;
}
/*
*
* Init smacker decoder
*
*/
static int decode_init(AVCodecContext *avctx)
{
SmackVContext * const c = (SmackVContext *)avctx->priv_data;
c->avctx = avctx;
avctx->has_b_frames = 0;
c->pic.data[0] = NULL;
if (avcodec_check_dimensions(avctx, avctx->height, avctx->width) < 0) {
return 1;
}
avctx->pix_fmt = PIX_FMT_PAL8;
/* decode huffman trees from extradata */
if(avctx->extradata_size < 16){
av_log(avctx, AV_LOG_ERROR, "Extradata missing!\n");
return -1;
}
decode_header_trees(c);
return 0;
}
/*
*
* Uninit smacker decoder
*
*/
static int decode_end(AVCodecContext *avctx)
{
SmackVContext * const smk = (SmackVContext *)avctx->priv_data;
if(smk->mmap_tbl)
av_free(smk->mmap_tbl);
if(smk->mclr_tbl)
av_free(smk->mclr_tbl);
if(smk->full_tbl)
av_free(smk->full_tbl);
if(smk->type_tbl)
av_free(smk->type_tbl);
if (smk->pic.data[0])
avctx->release_buffer(avctx, &smk->pic);
return 0;
}
static int smka_decode_init(AVCodecContext *avctx)
{
return 0;
}
/**
* Decode Smacker audio data
*/
static int smka_decode_frame(AVCodecContext *avctx, void *data, int *data_size, uint8_t *buf, int buf_size)
{
GetBitContext gb;
HuffContext h[4];
VLC vlc[4];
int16_t *samples = data;
int val;
int i, res;
int unp_size;
int bits, stereo;
int pred[2] = {0, 0};
unp_size = LE_32(buf);
init_get_bits(&gb, buf + 4, (buf_size - 4) * 8);
if(!get_bits1(&gb)){
av_log(avctx, AV_LOG_INFO, "Sound: no data\n");
*data_size = 0;
return 1;
}
stereo = get_bits1(&gb);
bits = get_bits1(&gb);
memset(vlc, 0, sizeof(VLC) * 4);
memset(h, 0, sizeof(HuffContext) * 4);
// Initialize
for(i = 0; i < (1 << (bits + stereo)); i++) {
h[i].length = 256;
h[i].maxlength = 0;
h[i].current = 0;
h[i].bits = av_mallocz(256 * 4);
h[i].lengths = av_mallocz(256 * sizeof(int));
h[i].values = av_mallocz(256 * sizeof(int));
get_bits1(&gb);
smacker_decode_tree(&gb, &h[i], 0, 0);
get_bits1(&gb);
res = init_vlc(&vlc[i], SMKTREE_BITS, h[i].length,
h[i].lengths, sizeof(int), sizeof(int),
h[i].bits, sizeof(uint32_t), sizeof(uint32_t), INIT_VLC_LE);
if(res < 0) {
av_log(avctx, AV_LOG_ERROR, "Cannot build VLC table\n");
return -1;
}
}
if(bits) { //decode 16-bit data
pred[0] = get_bits(&gb, 8);
pred[0] |= get_bits(&gb, 8);
if(stereo) {
pred[1] = get_bits(&gb, 8);
pred[1] |= get_bits(&gb, 8);
}
for(i = 0; i < unp_size / 2; i++) {
if(i & stereo) {
val = h[2].values[get_vlc2(&gb, vlc[2].table, SMKTREE_BITS, 3)];
val |= (int8_t)h[3].values[get_vlc2(&gb, vlc[3].table, SMKTREE_BITS, 3)] << 8;
pred[1] += val;
*samples++ = pred[1];
} else {
val = h[0].values[get_vlc2(&gb, vlc[0].table, SMKTREE_BITS, 3)];
val |= (int8_t)h[1].values[get_vlc2(&gb, vlc[1].table, SMKTREE_BITS, 3)] << 8;
pred[0] += val;
*samples++ = pred[0];
}
}
} else { //8-bit data
pred[0] = get_bits(&gb, 8);
if(stereo)
pred[1] = get_bits(&gb, 8);
for(i = 0; i < unp_size; i++) {
if(i & stereo){
pred[1] += (int8_t)h[1].values[get_vlc2(&gb, vlc[1].table, SMKTREE_BITS, 3)];
*samples++ = (pred[1] - 0x80) << 8;
} else {
pred[0] += (int8_t)h[0].values[get_vlc2(&gb, vlc[0].table, SMKTREE_BITS, 3)];
*samples++ = (pred[0] - 0x80) << 8;
}
}
unp_size *= 2;
}
for(i = 0; i < 4; i++) {
if(vlc[i].table)
free_vlc(&vlc[i]);
if(h[i].bits)
av_free(h[i].bits);
if(h[i].lengths)
av_free(h[i].lengths);
if(h[i].values)
av_free(h[i].values);
}
*data_size = unp_size;
return buf_size;
}
AVCodec smacker_decoder = {
"smackvid",
CODEC_TYPE_VIDEO,
CODEC_ID_SMACKVIDEO,
sizeof(SmackVContext),
decode_init,
NULL,
decode_end,
decode_frame
};
AVCodec smackaud_decoder = {
"smackaud",
CODEC_TYPE_AUDIO,
CODEC_ID_SMACKAUDIO,
0,
smka_decode_init,
NULL,
NULL,
smka_decode_frame
};

View File

@ -18,7 +18,7 @@ OBJS+=mpeg.o mpegts.o mpegtsenc.o ffm.o crc.o img.o img2.o raw.o rm.o \
nut.o wc3movie.o mp3.o westwood.o segafilm.o idcin.o flic.o \
sierravmd.o matroska.o sol.o electronicarts.o nsvdec.o asf.o \
ogg2.o oggparsevorbis.o oggparsetheora.o oggparseflac.o daud.o aiff.o \
voc.o tta.o mm.o avs.o
voc.o tta.o mm.o avs.o smacker.o
# muxers
ifeq ($(CONFIG_MUXERS),yes)

View File

@ -80,6 +80,7 @@ void av_register_all(void)
flic_init();
vmd_init();
mm_init();
smacker_init();
#if defined(AMR_NB) || defined(AMR_NB_FIXED) || defined(AMR_WB)
amr_init();

View File

@ -5,8 +5,8 @@
extern "C" {
#endif
#define LIBAVFORMAT_VERSION_INT ((50<<16)+(3<<8)+0)
#define LIBAVFORMAT_VERSION 50.3.0
#define LIBAVFORMAT_VERSION_INT ((50<<16)+(4<<8)+0)
#define LIBAVFORMAT_VERSION 50.4.0
#define LIBAVFORMAT_BUILD LIBAVFORMAT_VERSION_INT
#define LIBAVFORMAT_IDENT "Lavf" AV_STRINGIFY(LIBAVFORMAT_VERSION)
@ -567,6 +567,9 @@ int mm_init(void);
/* avs.c */
int avs_init(void);
/* smacker.c */
int smacker_init(void);
#include "rtp.h"
#include "rtsp.h"

343
libavformat/smacker.c Normal file
View File

@ -0,0 +1,343 @@
/*
* Smacker decoder
* Copyright (c) 2006 Konstantin Shishkov.
*
* This library 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 of the License, or (at your option) any later version.
*
* This library 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 this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Based on http://wiki.multimedia.cx/index.php?title=Smacker
*/
#include "avformat.h"
#include "avi.h"
#include "bswap.h"
#define SMACKER_PAL 0x01
enum SAudFlags {
SMK_AUD_PACKED = 0x80000000,
SMK_AUD_16BITS = 0x20000000,
SMK_AUD_STEREO = 0x10000000,
SMK_AUD_BINKAUD = 0x08000000,
SMK_AUD_USEDCT = 0x04000000
};
typedef struct SmackerContext {
/* Smacker file header */
uint32_t magic;
uint32_t width, height;
uint32_t frames;
int pts_inc;
uint32_t flags;
uint32_t audio[7];
uint32_t treesize;
uint32_t mmap_size, mclr_size, full_size, type_size;
uint32_t rates[7];
uint32_t pad;
/* frame info */
uint32_t *frm_size;
uint8_t *frm_flags;
/* internal variables */
int cur_frame;
int is_ver4;
int64_t cur_pts;
/* current frame for demuxing */
uint8_t pal[768];
int indexes[7];
int videoindex;
uint8_t *bufs[7];
int buf_sizes[7];
int stream_id[7];
int curstream;
offset_t nextpos;
} SmackerContext;
typedef struct SmackerFrame {
int64_t pts;
int stream;
} SmackerFrame;
/* palette used in Smacker */
static const uint8_t smk_pal[64] = {
0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C,
0x20, 0x24, 0x28, 0x2C, 0x30, 0x34, 0x38, 0x3C,
0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, 0x59, 0x5D,
0x61, 0x65, 0x69, 0x6D, 0x71, 0x75, 0x79, 0x7D,
0x82, 0x86, 0x8A, 0x8E, 0x92, 0x96, 0x9A, 0x9E,
0xA2, 0xA6, 0xAA, 0xAE, 0xB2, 0xB6, 0xBA, 0xBE,
0xC3, 0xC7, 0xCB, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF,
0xE3, 0xE7, 0xEB, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF
};
static int smacker_probe(AVProbeData *p)
{
if (p->buf_size < 4)
return 0;
if(p->buf[0] == 'S' && p->buf[1] == 'M' && p->buf[2] == 'K'
&& (p->buf[3] == '2' || p->buf[3] == '4'))
return AVPROBE_SCORE_MAX;
else
return 0;
}
static int smacker_read_header(AVFormatContext *s, AVFormatParameters *ap)
{
ByteIOContext *pb = &s->pb;
SmackerContext *smk = (SmackerContext *)s->priv_data;
AVStream *st, *ast[7];
int i, ret;
int tbase;
/* read and check header */
smk->magic = get_le32(pb);
if (smk->magic != MKTAG('S', 'M', 'K', '2') && smk->magic != MKTAG('S', 'M', 'K', '4'))
return -1;
smk->width = get_le32(pb);
smk->height = get_le32(pb);
smk->frames = get_le32(pb);
smk->pts_inc = (int32_t)get_le32(pb);
smk->flags = get_le32(pb);
for(i = 0; i < 7; i++)
smk->audio[i] = get_le32(pb);
smk->treesize = get_le32(pb);
smk->mmap_size = get_le32(pb);
smk->mclr_size = get_le32(pb);
smk->full_size = get_le32(pb);
smk->type_size = get_le32(pb);
for(i = 0; i < 7; i++)
smk->rates[i] = get_le32(pb);
smk->pad = get_le32(pb);
/* setup data */
if(smk->frames > 0xFFFFFF) {
av_log(s, AV_LOG_ERROR, "Too many frames: %i\n", smk->frames);
return -1;
}
smk->frm_size = av_malloc(smk->frames * 4);
smk->frm_flags = av_malloc(smk->frames);
smk->is_ver4 = (smk->magic != MKTAG('S', 'M', 'K', '2'));
/* read frame info */
for(i = 0; i < smk->frames; i++) {
smk->frm_size[i] = get_le32(pb);
}
for(i = 0; i < smk->frames; i++) {
smk->frm_flags[i] = get_byte(pb);
}
/* init video codec */
st = av_new_stream(s, 0);
if (!st)
return -1;
smk->videoindex = st->index;
st->codec->width = smk->width;
st->codec->height = smk->height;
st->codec->pix_fmt = PIX_FMT_PAL8;
st->codec->codec_type = CODEC_TYPE_VIDEO;
st->codec->codec_id = CODEC_ID_SMACKVIDEO;
st->codec->codec_tag = smk->is_ver4;
/* Smacker uses 100000 as internal timebase */
if(smk->pts_inc < 0)
smk->pts_inc = -smk->pts_inc;
else
smk->pts_inc *= 100;
tbase = 100000;
av_reduce(&tbase, &smk->pts_inc, tbase, smk->pts_inc, (1UL<<31)-1);
av_set_pts_info(st, 33, smk->pts_inc, tbase);
/* handle possible audio streams */
for(i = 0; i < 7; i++) {
smk->indexes[i] = -1;
if((smk->rates[i] & 0xFFFFFF) && !(smk->rates[i] & SMK_AUD_BINKAUD)){
ast[i] = av_new_stream(s, 0);
smk->indexes[i] = ast[i]->index;
av_set_pts_info(ast[i], 33, smk->pts_inc, tbase);
ast[i]->codec->codec_type = CODEC_TYPE_AUDIO;
ast[i]->codec->codec_id = (smk->rates[i] & SMK_AUD_PACKED) ? CODEC_ID_SMACKAUDIO : CODEC_ID_PCM_U8;
ast[i]->codec->codec_tag = 0;
ast[i]->codec->channels = (smk->rates[i] & SMK_AUD_STEREO) ? 2 : 1;
ast[i]->codec->sample_rate = smk->rates[i] & 0xFFFFFF;
ast[i]->codec->bits_per_sample = (smk->rates[i] & SMK_AUD_16BITS) ? 16 : 8;
if(ast[i]->codec->bits_per_sample == 16 && ast[i]->codec->codec_id == CODEC_ID_PCM_U8)
ast[i]->codec->codec_id = CODEC_ID_PCM_S16LE;
}
}
/* load trees to extradata, they will be unpacked by decoder */
st->codec->extradata = av_malloc(smk->treesize + 16);
st->codec->extradata_size = smk->treesize + 16;
if(!st->codec->extradata){
av_log(s, AV_LOG_ERROR, "Cannot allocate %i bytes of extradata\n", smk->treesize + 16);
av_free(smk->frm_size);
av_free(smk->frm_flags);
return -1;
}
ret = get_buffer(pb, st->codec->extradata + 16, st->codec->extradata_size - 16);
if(ret != st->codec->extradata_size - 16){
av_free(smk->frm_size);
av_free(smk->frm_flags);
return AVERROR_IO;
}
((int32_t*)st->codec->extradata)[0] = le2me_32(smk->mmap_size);
((int32_t*)st->codec->extradata)[1] = le2me_32(smk->mclr_size);
((int32_t*)st->codec->extradata)[2] = le2me_32(smk->full_size);
((int32_t*)st->codec->extradata)[3] = le2me_32(smk->type_size);
smk->curstream = -1;
smk->nextpos = url_ftell(pb);
return 0;
}
static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt)
{
SmackerContext *smk = (SmackerContext *)s->priv_data;
int flags;
int ret;
int i;
int frame_size = 0;
int palchange = 0;
int pos;
if (url_feof(&s->pb) || smk->cur_frame >= smk->frames)
return -EIO;
/* if we demuxed all streams, pass another frame */
if(smk->curstream < 0) {
url_fseek(&s->pb, smk->nextpos, 0);
frame_size = smk->frm_size[smk->cur_frame] & (~3);
flags = smk->frm_flags[smk->cur_frame];
/* handle palette change event */
pos = url_ftell(&s->pb);
if(flags & SMACKER_PAL){
int size, sz, t, off, j, pos;
uint8_t *pal = smk->pal;
uint8_t oldpal[768];
memcpy(oldpal, pal, 768);
size = get_byte(&s->pb);
size = size * 4 - 1;
frame_size -= size;
frame_size--;
sz = 0;
pos = url_ftell(&s->pb) + size;
while(sz < 256){
t = get_byte(&s->pb);
if(t & 0x80){ /* skip palette entries */
sz += (t & 0x7F) + 1;
pal += ((t & 0x7F) + 1) * 3;
} else if(t & 0x40){ /* copy with offset */
off = get_byte(&s->pb) * 3;
j = (t & 0x3F) + 1;
while(j-- && sz < 256) {
*pal++ = oldpal[off + 0];
*pal++ = oldpal[off + 1];
*pal++ = oldpal[off + 2];
sz++;
off += 3;
}
} else { /* new entries */
*pal++ = smk_pal[t];
*pal++ = smk_pal[get_byte(&s->pb) & 0x3F];
*pal++ = smk_pal[get_byte(&s->pb) & 0x3F];
sz++;
}
}
url_fseek(&s->pb, pos, 0);
palchange |= 1;
}
flags >>= 1;
smk->curstream = -1;
/* if audio chunks are present, put them to stack and retrieve later */
for(i = 0; i < 7; i++) {
if(flags & 1) {
int size;
size = get_le32(&s->pb) - 4;
frame_size -= size;
frame_size -= 4;
smk->curstream++;
smk->bufs[smk->curstream] = av_realloc(smk->bufs[smk->curstream], size);
smk->buf_sizes[smk->curstream] = size;
ret = get_buffer(&s->pb, smk->bufs[smk->curstream], size);
if(ret != size)
return AVERROR_IO;
smk->stream_id[smk->curstream] = smk->indexes[i];
}
flags >>= 1;
}
if (av_new_packet(pkt, frame_size + 768))
return AVERROR_NOMEM;
if(smk->frm_size[smk->cur_frame] & 1)
palchange |= 2;
pkt->data[0] = palchange;
memcpy(pkt->data + 1, smk->pal, 768);
ret = get_buffer(&s->pb, pkt->data + 769, frame_size);
if(ret != frame_size)
return AVERROR_IO;
pkt->stream_index = smk->videoindex;
pkt->size = ret + 769;
smk->cur_frame++;
smk->nextpos = url_ftell(&s->pb);
} else {
if (av_new_packet(pkt, smk->buf_sizes[smk->curstream]))
return AVERROR_NOMEM;
memcpy(pkt->data, smk->bufs[smk->curstream], smk->buf_sizes[smk->curstream]);
pkt->size = smk->buf_sizes[smk->curstream];
pkt->stream_index = smk->stream_id[smk->curstream];
smk->curstream--;
}
return 0;
}
static int smacker_read_close(AVFormatContext *s)
{
SmackerContext *smk = (SmackerContext *)s->priv_data;
int i;
for(i = 0; i < 7; i++)
if(smk->bufs[i])
av_free(smk->bufs[i]);
if(smk->frm_size)
av_free(smk->frm_size);
if(smk->frm_flags)
av_free(smk->frm_flags);
for(i=0;i<s->nb_streams;i++) {
AVStream *st = s->streams[i];
if(st->codec->extradata)
av_free(st->codec->extradata);
}
return 0;
}
static AVInputFormat smacker_iformat = {
"smk",
"Smacker Video",
sizeof(SmackerContext),
smacker_probe,
smacker_read_header,
smacker_read_packet,
smacker_read_close,
};
int smacker_init(void)
{
av_register_input_format(&smacker_iformat);
return 0;
}