mirror of https://github.com/FFmpeg/FFmpeg.git
Originally committed as revision 5189 to svn://svn.ffmpeg.org/ffmpeg/trunkpull/126/head
parent
305ee50fbb
commit
348efc1836
10 changed files with 1059 additions and 5 deletions
@ -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 |
||||
}; |
||||
|
@ -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; |
||||
} |
Loading…
Reference in new issue