From 348efc18364a9daca569ed77e184b843d9bc5f11 Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Tue, 21 Mar 2006 17:27:47 +0000 Subject: [PATCH] Smacker demuxer and decoder. Originally committed as revision 5189 to svn://svn.ffmpeg.org/ffmpeg/trunk --- Changelog | 1 + doc/ffmpeg-doc.texi | 4 + libavcodec/Makefile | 3 + libavcodec/allcodecs.c | 6 + libavcodec/avcodec.h | 8 +- libavcodec/smacker.c | 689 +++++++++++++++++++++++++++++++++++++++++++++++ libavformat/Makefile | 2 +- libavformat/allformats.c | 1 + libavformat/avformat.h | 7 +- libavformat/smacker.c | 343 +++++++++++++++++++++++ 10 files changed, 1059 insertions(+), 5 deletions(-) create mode 100644 libavcodec/smacker.c create mode 100644 libavformat/smacker.c diff --git a/Changelog b/Changelog index e5a5fd9..e682099 100644 --- a/Changelog +++ b/Changelog @@ -41,6 +41,7 @@ version - Improved Theora/VP3 decoder - True Audio (TTA) decoder - AVS demuxer and video decoder +- Smacker demuxer and decoder version 0.4.9-pre1: diff --git a/doc/ffmpeg-doc.texi b/doc/ffmpeg-doc.texi index c50e919..14d4f1b 100644 --- a/doc/ffmpeg-doc.texi +++ b/doc/ffmpeg-doc.texi @@ -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. diff --git a/libavcodec/Makefile b/libavcodec/Makefile index d63a4d8..72fedd0 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -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 diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index d578306..19a67f3 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -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 diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 6c33373..a4df507 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -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) \ diff --git a/libavcodec/smacker.c b/libavcodec/smacker.c new file mode 100644 index 0000000..8842b5e --- /dev/null +++ b/libavcodec/smacker.c @@ -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 +#include + +#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 +}; + diff --git a/libavformat/Makefile b/libavformat/Makefile index 1a8f2b7..86cb0cc 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -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) diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 7d22414..978e089 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -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(); diff --git a/libavformat/avformat.h b/libavformat/avformat.h index f95462e..0d36ebd 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -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" diff --git a/libavformat/smacker.c b/libavformat/smacker.c new file mode 100644 index 0000000..916dd84 --- /dev/null +++ b/libavformat/smacker.c @@ -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;inb_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; +} -- 2.7.4