fraps.c 11.7 KB
Newer Older
1 2 3
/*
 * Fraps FPS1 decoder
 * Copyright (c) 2005 Roine Gustafsson
4
 * Copyright (c) 2006 Konstantin Shishkov
5
 *
6
 * This file is part of Libav.
7
 *
8
 * Libav is free software; you can redistribute it and/or
9 10
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * Libav is distributed in the hope that it will be useful,
14 15 16 17 18
 * 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
19
 * License along with Libav; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
 */
22

23
/**
24
 * @file
25
 * Lossless Fraps 'FPS1' decoder
26
 * @author Roine Gustafsson (roine at users sf net)
27
 * @author Konstantin Shishkov
28
 *
29 30
 * Codec algorithm for version 0 is taken from Transcode <www.transcoding.org>
 *
31
 * Version 2 files support by Konstantin Shishkov
32
 */
33

34
#include "avcodec.h"
35
#include "get_bits.h"
36 37
#include "huffman.h"
#include "bytestream.h"
38
#include "dsputil.h"
39

Mike Melanson's avatar
Mike Melanson committed
40
#define FPS_TAG MKTAG('F', 'P', 'S', 'x')
41 42 43 44 45 46 47

/**
 * local variable storage
 */
typedef struct FrapsContext{
    AVCodecContext *avctx;
    AVFrame frame;
48 49
    uint8_t *tmpbuf;
    DSPContext dsp;
50 51 52 53 54 55 56 57
} FrapsContext;


/**
 * initializes decoder
 * @param avctx codec context
 * @return 0 on success or negative if fails
 */
58
static av_cold int decode_init(AVCodecContext *avctx)
59 60 61 62 63 64 65
{
    FrapsContext * const s = avctx->priv_data;

    avctx->coded_frame = (AVFrame*)&s->frame;
    avctx->pix_fmt= PIX_FMT_NONE; /* set in decode_frame */

    s->avctx = avctx;
66 67 68
    s->tmpbuf = NULL;

    dsputil_init(&s->dsp, avctx);
69 70 71 72

    return 0;
}

73 74 75 76
/**
 * Comparator - our nodes should ascend by count
 * but with preserved symbol order
 */
77 78
static int huff_cmp(const void *va, const void *vb){
    const Node *a = va, *b = vb;
79 80 81 82 83 84 85
    return (a->count - b->count)*256 + a->sym - b->sym;
}

/**
 * decode Fraps v2 packed plane
 */
static int fraps2_decode_plane(FrapsContext *s, uint8_t *dst, int stride, int w,
86 87
                               int h, const uint8_t *src, int size, int Uoff,
                               const int step)
88 89 90 91
{
    int i, j;
    GetBitContext gb;
    VLC vlc;
92
    Node nodes[512];
93

94 95 96
    for(i = 0; i < 256; i++)
        nodes[i].count = bytestream_get_le32(&src);
    size -= 1024;
97 98
    if (ff_huff_build_tree(s->avctx, &vlc, 256, nodes, huff_cmp,
                           FF_HUFFMAN_FLAG_ZERO_COUNT) < 0)
99 100 101 102
        return -1;
    /* we have built Huffman table and are ready to decode plane */

    /* convert bits so they may be used by standard bitreader */
103
    s->dsp.bswap_buf((uint32_t *)s->tmpbuf, (const uint32_t *)src, size >> 2);
104 105 106

    init_get_bits(&gb, s->tmpbuf, size * 8);
    for(j = 0; j < h; j++){
107
        for(i = 0; i < w*step; i += step){
108
            dst[i] = get_vlc2(&gb, vlc.table, 9, 3);
109 110 111 112 113 114 115 116 117 118 119
            /* 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;
}
120

121
static int decode_frame(AVCodecContext *avctx,
122
                        void *data, int *data_size,
123
                        AVPacket *avpkt)
124
{
125 126
    const uint8_t *buf = avpkt->data;
    int buf_size = avpkt->size;
127 128 129 130 131 132
    FrapsContext * const s = avctx->priv_data;
    AVFrame *frame = data;
    AVFrame * const f = (AVFrame*)&s->frame;
    uint32_t header;
    unsigned int version,header_size;
    unsigned int x, y;
Michael Niedermayer's avatar
Michael Niedermayer committed
133
    const uint32_t *buf32;
134
    uint32_t *luma1,*luma2,*cb,*cr;
135
    uint32_t offs[4];
136
    int i, j, is_chroma, planes;
137 138


139
    header = AV_RL32(buf);
140 141 142
    version = header & 0xff;
    header_size = (header & (1<<30))? 8 : 4; /* bit 30 means pad to 8 bytes */

143
    if (version > 5) {
144
        av_log(avctx, AV_LOG_ERROR,
145
               "This file is encoded with Fraps version %d. " \
146
               "This codec can only decode versions <= 5.\n", version);
147 148 149 150 151 152
        return -1;
    }

    buf+=4;
    if (header_size == 8)
        buf+=4;
153

154 155 156 157
    switch(version) {
    case 0:
    default:
        /* Fraps v0 is a reordered YUV420 */
158
        avctx->pix_fmt = PIX_FMT_YUVJ420P;
159

160
        if ( (buf_size != avctx->width*avctx->height*3/2+header_size) &&
161 162
             (buf_size != header_size) ) {
            av_log(avctx, AV_LOG_ERROR,
163
                   "Invalid frame length %d (should be %d)\n",
164 165 166
                   buf_size, avctx->width*avctx->height*3/2+header_size);
            return -1;
        }
167

168
        if (( (avctx->width % 8) != 0) || ( (avctx->height % 2) != 0 )) {
169
            av_log(avctx, AV_LOG_ERROR, "Invalid frame size %dx%d\n",
170 171 172 173
                   avctx->width, avctx->height);
            return -1;
        }

174 175 176
        f->reference = 1;
        f->buffer_hints = FF_BUFFER_HINTS_VALID |
                          FF_BUFFER_HINTS_PRESERVE |
177 178 179 180
                          FF_BUFFER_HINTS_REUSABLE;
        if (avctx->reget_buffer(avctx, f)) {
            av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
            return -1;
181
        }
182
        /* bit 31 means same as previous pic */
183
        f->pict_type = (header & (1<<31))? FF_P_TYPE : FF_I_TYPE;
184 185
        f->key_frame = f->pict_type == FF_I_TYPE;

186
        if (f->pict_type == FF_I_TYPE) {
Michael Niedermayer's avatar
Michael Niedermayer committed
187
            buf32=(const uint32_t*)buf;
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
            for(y=0; y<avctx->height/2; y++){
                luma1=(uint32_t*)&f->data[0][ y*2*f->linesize[0] ];
                luma2=(uint32_t*)&f->data[0][ (y*2+1)*f->linesize[0] ];
                cr=(uint32_t*)&f->data[1][ y*f->linesize[1] ];
                cb=(uint32_t*)&f->data[2][ y*f->linesize[2] ];
                for(x=0; x<avctx->width; x+=8){
                    *(luma1++) = *(buf32++);
                    *(luma1++) = *(buf32++);
                    *(luma2++) = *(buf32++);
                    *(luma2++) = *(buf32++);
                    *(cr++) = *(buf32++);
                    *(cb++) = *(buf32++);
                }
            }
        }
        break;

    case 1:
        /* Fraps v1 is an upside-down BGR24 */
        avctx->pix_fmt = PIX_FMT_BGR24;

209
        if ( (buf_size != avctx->width*avctx->height*3+header_size) &&
210
             (buf_size != header_size) ) {
211
            av_log(avctx, AV_LOG_ERROR,
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
                   "Invalid frame length %d (should be %d)\n",
                   buf_size, avctx->width*avctx->height*3+header_size);
            return -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;
        }
        /* bit 31 means same as previous pic */
        f->pict_type = (header & (1<<31))? FF_P_TYPE : FF_I_TYPE;
        f->key_frame = f->pict_type == FF_I_TYPE;

        if (f->pict_type == FF_I_TYPE) {
            for(y=0; y<avctx->height; y++)
                memcpy(&f->data[0][ (avctx->height-y)*f->linesize[0] ],
                       &buf[y*avctx->width*3],
233
                       3*avctx->width);
234 235 236 237
        }
        break;

    case 2:
238
    case 4:
239
        /**
240
         * Fraps v2 is Huffman-coded YUV420 planes
241
         * Fraps v4 is virtually the same
242
         */
243
        avctx->pix_fmt = PIX_FMT_YUVJ420P;
244
        planes = 3;
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
        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;
261
        if ((AV_RL32(buf) != FPS_TAG)||(buf_size < (planes*1024 + 24))) {
262 263 264
            av_log(avctx, AV_LOG_ERROR, "Fraps: error in data stream\n");
            return -1;
        }
265
        for(i = 0; i < planes; i++) {
266
            offs[i] = AV_RL32(buf + 4 + i * 4);
267 268 269 270 271 272 273 274
            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;
275
            s->tmpbuf = av_realloc(s->tmpbuf, offs[i + 1] - offs[i] - 1024 + FF_INPUT_BUFFER_PADDING_SIZE);
276
            if(fraps2_decode_plane(s, f->data[i], f->linesize[i], avctx->width >> is_chroma,
277
                    avctx->height >> is_chroma, buf + offs[i], offs[i + 1] - offs[i], is_chroma, 1) < 0) {
278 279 280 281
                av_log(avctx, AV_LOG_ERROR, "Error decoding plane %i\n", i);
                return -1;
            }
        }
282
        break;
283
    case 3:
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
    case 5:
        /* Virtually the same as version 4, but is for RGB24 */
        avctx->pix_fmt = PIX_FMT_BGR24;
        planes = 3;
        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 ((AV_RL32(buf) != FPS_TAG)||(buf_size < (planes*1024 + 24))) {
            av_log(avctx, AV_LOG_ERROR, "Fraps: error in data stream\n");
            return -1;
        }
        for(i = 0; i < planes; i++) {
            offs[i] = AV_RL32(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++){
            s->tmpbuf = av_realloc(s->tmpbuf, offs[i + 1] - offs[i] - 1024 + FF_INPUT_BUFFER_PADDING_SIZE);
            if(fraps2_decode_plane(s, f->data[0] + i + (f->linesize[0] * (avctx->height - 1)), -f->linesize[0],
319
                    avctx->width, avctx->height, buf + offs[i], offs[i + 1] - offs[i], 0, 3) < 0) {
320 321 322 323
                av_log(avctx, AV_LOG_ERROR, "Error decoding plane %i\n", i);
                return -1;
            }
        }
324 325 326
        // convert pseudo-YUV into real RGB
        for(j = 0; j < avctx->height; j++){
            for(i = 0; i < avctx->width; i++){
327 328
                f->data[0][0 + i*3 + j*f->linesize[0]] += f->data[0][1 + i*3 + j*f->linesize[0]];
                f->data[0][2 + i*3 + j*f->linesize[0]] += f->data[0][1 + i*3 + j*f->linesize[0]];
329 330
            }
        }
331
        break;
332 333 334 335 336 337 338 339 340 341 342 343 344 345
    }

    *frame = *f;
    *data_size = sizeof(AVFrame);

    return buf_size;
}


/**
 * closes decoder
 * @param avctx codec context
 * @return 0 on success or negative if fails
 */
346
static av_cold int decode_end(AVCodecContext *avctx)
347 348 349 350 351 352
{
    FrapsContext *s = (FrapsContext*)avctx->priv_data;

    if (s->frame.data[0])
        avctx->release_buffer(avctx, &s->frame);

353
    av_freep(&s->tmpbuf);
354 355 356 357
    return 0;
}


358
AVCodec ff_fraps_decoder = {
359
    "fraps",
360
    AVMEDIA_TYPE_VIDEO,
361 362 363 364 365 366 367
    CODEC_ID_FRAPS,
    sizeof(FrapsContext),
    decode_init,
    NULL,
    decode_end,
    decode_frame,
    CODEC_CAP_DR1,
368
    .long_name = NULL_IF_CONFIG_SMALL("Fraps"),
369
};