|
|
|
@ -1,6 +1,7 @@ |
|
|
|
|
/*
|
|
|
|
|
* Fraps FPS1 decoder |
|
|
|
|
* Copyright (c) 2005 Roine Gustafsson |
|
|
|
|
* Copyright (c) 2006 Konstantin Shishkov |
|
|
|
|
* |
|
|
|
|
* This file is part of FFmpeg. |
|
|
|
|
* |
|
|
|
@ -24,24 +25,42 @@ |
|
|
|
|
* @file fraps.c |
|
|
|
|
* Lossless Fraps 'FPS1' decoder |
|
|
|
|
* @author Roine Gustafsson <roine at users sf net> |
|
|
|
|
* @author Konstantin Shishkov |
|
|
|
|
* |
|
|
|
|
* Only decodes version 0 and 1 files. |
|
|
|
|
* Codec algorithm for version 0 is taken from Transcode <www.transcoding.org> |
|
|
|
|
* |
|
|
|
|
* Version 2 files, which are the most commonly found Fraps files, cannot be |
|
|
|
|
* decoded yet. |
|
|
|
|
* Version 2 files support by Konstantin Shishkov |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
#include "avcodec.h" |
|
|
|
|
#include "bitstream.h" |
|
|
|
|
#include "dsputil.h" |
|
|
|
|
|
|
|
|
|
#define FPS_TAG MKTAG('F', 'P', 'S', 'x') |
|
|
|
|
|
|
|
|
|
/* symbol for Huffman tree node */ |
|
|
|
|
#define HNODE -1 |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Huffman node |
|
|
|
|
* FIXME one day this should belong to one general framework |
|
|
|
|
*/ |
|
|
|
|
typedef struct Node{ |
|
|
|
|
int16_t sym; |
|
|
|
|
int16_t n0; |
|
|
|
|
int count; |
|
|
|
|
}Node; |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* local variable storage |
|
|
|
|
*/ |
|
|
|
|
typedef struct FrapsContext{ |
|
|
|
|
AVCodecContext *avctx; |
|
|
|
|
AVFrame frame; |
|
|
|
|
Node nodes[512]; |
|
|
|
|
uint8_t *tmpbuf; |
|
|
|
|
DSPContext dsp; |
|
|
|
|
} FrapsContext; |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -60,10 +79,110 @@ static int decode_init(AVCodecContext *avctx) |
|
|
|
|
|
|
|
|
|
s->avctx = avctx; |
|
|
|
|
s->frame.data[0] = NULL; |
|
|
|
|
s->tmpbuf = NULL; |
|
|
|
|
|
|
|
|
|
dsputil_init(&s->dsp, avctx); |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Comparator - our nodes should ascend by count |
|
|
|
|
* but with preserved symbol order |
|
|
|
|
*/ |
|
|
|
|
static int huff_cmp(const Node *a, const Node *b){ |
|
|
|
|
return (a->count - b->count)*256 + a->sym - b->sym; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void get_tree_codes(uint32_t *bits, int16_t *lens, Node *nodes, int node, uint32_t pfx, int pl) |
|
|
|
|
{ |
|
|
|
|
int s; |
|
|
|
|
|
|
|
|
|
s = nodes[node].sym; |
|
|
|
|
if(s != HNODE){ |
|
|
|
|
bits[s] = pfx; |
|
|
|
|
lens[s] = pl; |
|
|
|
|
}else{ |
|
|
|
|
pfx <<= 1; |
|
|
|
|
pl++; |
|
|
|
|
get_tree_codes(bits, lens, nodes, nodes[node].n0, pfx, pl); |
|
|
|
|
pfx |= 1; |
|
|
|
|
get_tree_codes(bits, lens, nodes, nodes[node].n0+1, pfx, pl); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int build_huff_tree(VLC *vlc, Node *nodes) |
|
|
|
|
{ |
|
|
|
|
uint32_t bits[256]; |
|
|
|
|
int16_t lens[256]; |
|
|
|
|
|
|
|
|
|
get_tree_codes(bits, lens, nodes, 510, 0, 0); |
|
|
|
|
return init_vlc(vlc, 9, 256, lens, 2, 2, bits, 4, 4, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* decode Fraps v2 packed plane |
|
|
|
|
*/ |
|
|
|
|
static int fraps2_decode_plane(FrapsContext *s, uint8_t *dst, int stride, int w, |
|
|
|
|
int h, uint8_t *src, int size, int Uoff) |
|
|
|
|
{ |
|
|
|
|
int i, j; |
|
|
|
|
int cur_node; |
|
|
|
|
GetBitContext gb; |
|
|
|
|
VLC vlc; |
|
|
|
|
int64_t sum = 0; |
|
|
|
|
|
|
|
|
|
for(i = 0; i < 256; i++){ |
|
|
|
|
s->nodes[i].sym = i; |
|
|
|
|
s->nodes[i].count = LE_32(src); |
|
|
|
|
s->nodes[i].n0 = -2; |
|
|
|
|
src += 4; |
|
|
|
|
sum += s->nodes[i].count; |
|
|
|
|
} |
|
|
|
|
size -= 1024; |
|
|
|
|
|
|
|
|
|
if(sum >> 31) { |
|
|
|
|
av_log(s->avctx, AV_LOG_ERROR, "Too high symbol frequencies. Tree construction is not possible\n"); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
qsort(s->nodes, 256, sizeof(Node), huff_cmp); |
|
|
|
|
cur_node = 256; |
|
|
|
|
// FIXME how it will handle nodes with zero count?
|
|
|
|
|
for(i = 0; i < 511; i += 2){ |
|
|
|
|
s->nodes[cur_node].sym = HNODE; |
|
|
|
|
s->nodes[cur_node].count = s->nodes[i].count + s->nodes[i+1].count; |
|
|
|
|
s->nodes[cur_node].n0 = i; |
|
|
|
|
for(j = cur_node; j > 0; j--){ |
|
|
|
|
if(s->nodes[j].count >= s->nodes[j - 1].count) break; |
|
|
|
|
FFSWAP(Node, s->nodes[j], s->nodes[j - 1]); |
|
|
|
|
} |
|
|
|
|
cur_node++; |
|
|
|
|
} |
|
|
|
|
if(build_huff_tree(&vlc, s->nodes) < 0){ |
|
|
|
|
av_log(s->avctx, AV_LOG_ERROR, "Error building tree\n"); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
/* we have built Huffman table and are ready to decode plane */ |
|
|
|
|
|
|
|
|
|
/* convert bits so they may be used by standard bitreader */ |
|
|
|
|
s->dsp.bswap_buf(s->tmpbuf, src, size >> 2); |
|
|
|
|
|
|
|
|
|
init_get_bits(&gb, s->tmpbuf, size * 8); |
|
|
|
|
for(j = 0; j < h; j++){ |
|
|
|
|
for(i = 0; i < w; i++){ |
|
|
|
|
dst[i] = get_vlc2(&gb, vlc.table, 9, 3); |
|
|
|
|
/* lines are stored as deltas between previous lines
|
|
|
|
|
* and we need to add 0x80 to the first lines of chroma planes |
|
|
|
|
*/ |
|
|
|
|
if(j) dst[i] += dst[i - stride]; |
|
|
|
|
else if(Uoff) dst[i] += 0x80; |
|
|
|
|
} |
|
|
|
|
dst += stride; |
|
|
|
|
} |
|
|
|
|
free_vlc(&vlc); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* decode a frame |
|
|
|
@ -86,16 +205,18 @@ static int decode_frame(AVCodecContext *avctx, |
|
|
|
|
unsigned int x, y; |
|
|
|
|
uint32_t *buf32; |
|
|
|
|
uint32_t *luma1,*luma2,*cb,*cr; |
|
|
|
|
uint32_t offs[4]; |
|
|
|
|
int i, is_chroma, planes; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
header = LE_32(buf); |
|
|
|
|
version = header & 0xff; |
|
|
|
|
header_size = (header & (1<<30))? 8 : 4; /* bit 30 means pad to 8 bytes */ |
|
|
|
|
|
|
|
|
|
if (version > 1) { |
|
|
|
|
if (version > 2 && version != 4) { |
|
|
|
|
av_log(avctx, AV_LOG_ERROR, |
|
|
|
|
"This file is encoded with Fraps version %d. " \
|
|
|
|
|
"This codec can only decode version 0 and 1.\n", version); |
|
|
|
|
"This codec can only decode version 0, 1, 2 and 4.\n", version); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -187,30 +308,50 @@ static int decode_frame(AVCodecContext *avctx, |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 2: |
|
|
|
|
case 4: |
|
|
|
|
/**
|
|
|
|
|
* Fraps v2 sub-header description. All numbers are little-endian: |
|
|
|
|
* (this is all guesswork) |
|
|
|
|
* |
|
|
|
|
* 0: DWORD 'FPSx' |
|
|
|
|
* 4: DWORD 0x00000010 unknown, perhaps flags |
|
|
|
|
* 8: DWORD off_2 offset to plane 2 |
|
|
|
|
* 12: DWORD off_3 offset to plane 3 |
|
|
|
|
* 16: 256xDWORD freqtbl_1 frequency table for plane 1 |
|
|
|
|
* 1040: plane_1 |
|
|
|
|
* ... |
|
|
|
|
* off_2: 256xDWORD freqtbl_2 frequency table for plane 2 |
|
|
|
|
* plane_2 |
|
|
|
|
* ... |
|
|
|
|
* off_3: 256xDWORD freqtbl_3 frequency table for plane 3 |
|
|
|
|
* plane_3 |
|
|
|
|
* Fraps v2 is Huffman-coded YUV420 planes |
|
|
|
|
* Fraps v4 is the same except it works in grayscale |
|
|
|
|
*/ |
|
|
|
|
if ((BE_32(buf) != FPS_TAG)||(buf_size < (3*1024 + 8))) { |
|
|
|
|
avctx->pix_fmt = (version == 2) ? PIX_FMT_YUV420P : PIX_FMT_GRAY8; |
|
|
|
|
planes = (version == 2) ? 3 : 1; |
|
|
|
|
f->reference = 1; |
|
|
|
|
f->buffer_hints = FF_BUFFER_HINTS_VALID | |
|
|
|
|
FF_BUFFER_HINTS_PRESERVE | |
|
|
|
|
FF_BUFFER_HINTS_REUSABLE; |
|
|
|
|
if (avctx->reget_buffer(avctx, f)) { |
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n"); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
/* skip frame */ |
|
|
|
|
if(buf_size == 8) { |
|
|
|
|
f->pict_type = FF_P_TYPE; |
|
|
|
|
f->key_frame = 0; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
f->pict_type = FF_I_TYPE; |
|
|
|
|
f->key_frame = 1; |
|
|
|
|
if ((LE_32(buf) != FPS_TAG)||(buf_size < (planes*1024 + 24))) { |
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Fraps: error in data stream\n"); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* NOT FINISHED */ |
|
|
|
|
|
|
|
|
|
for(i = 0; i < planes; i++) { |
|
|
|
|
offs[i] = LE_32(buf + 4 + i * 4); |
|
|
|
|
if(offs[i] >= buf_size || (i && offs[i] <= offs[i - 1] + 1024)) { |
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Fraps: plane %i offset is out of bounds\n", i); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
offs[planes] = buf_size; |
|
|
|
|
for(i = 0; i < planes; i++){ |
|
|
|
|
is_chroma = !!i; |
|
|
|
|
s->tmpbuf = av_realloc(s->tmpbuf, offs[i + 1] - offs[i] - 1024); |
|
|
|
|
if(fraps2_decode_plane(s, f->data[i], f->linesize[i], avctx->width >> is_chroma, |
|
|
|
|
avctx->height >> is_chroma, buf + offs[i], offs[i + 1] - offs[i], is_chroma) < 0) { |
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "Error decoding plane %i\n", i); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -233,6 +374,7 @@ static int decode_end(AVCodecContext *avctx) |
|
|
|
|
if (s->frame.data[0]) |
|
|
|
|
avctx->release_buffer(avctx, &s->frame); |
|
|
|
|
|
|
|
|
|
av_freep(&s->tmpbuf); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|