1 | /*
|
---|
2 | * Fraps FPS1 decoder
|
---|
3 | * Copyright (c) 2005 Roine Gustafsson
|
---|
4 | *
|
---|
5 | * This library is free software; you can redistribute it and/or
|
---|
6 | * modify it under the terms of the GNU Lesser General Public
|
---|
7 | * License as published by the Free Software Foundation; either
|
---|
8 | * version 2 of the License, or (at your option) any later version.
|
---|
9 | *
|
---|
10 | * This library is distributed in the hope that it will be useful,
|
---|
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
13 | * Lesser General Public License for more details.
|
---|
14 | *
|
---|
15 | * You should have received a copy of the GNU Lesser General Public
|
---|
16 | * License along with this library; if not, write to the Free Software
|
---|
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
---|
18 | *
|
---|
19 | */
|
---|
20 |
|
---|
21 | /**
|
---|
22 | * @file fraps.c
|
---|
23 | * Lossless Fraps 'FPS1' decoder
|
---|
24 | * @author Roine Gustafsson <roine at users sf net>
|
---|
25 | *
|
---|
26 | * Only decodes version 0 and 1 files.
|
---|
27 | * Codec algorithm for version 0 is taken from Transcode <www.transcoding.org>
|
---|
28 | *
|
---|
29 | * Version 2 files, which are the most commonly found Fraps files, cannot be
|
---|
30 | * decoded yet.
|
---|
31 | */
|
---|
32 |
|
---|
33 | #include "avcodec.h"
|
---|
34 |
|
---|
35 | #define FPS_TAG MKTAG('F', 'P', 'S', 'x')
|
---|
36 |
|
---|
37 | /**
|
---|
38 | * local variable storage
|
---|
39 | */
|
---|
40 | typedef struct FrapsContext{
|
---|
41 | AVCodecContext *avctx;
|
---|
42 | AVFrame frame;
|
---|
43 | } FrapsContext;
|
---|
44 |
|
---|
45 |
|
---|
46 | /**
|
---|
47 | * initializes decoder
|
---|
48 | * @param avctx codec context
|
---|
49 | * @return 0 on success or negative if fails
|
---|
50 | */
|
---|
51 | static int decode_init(AVCodecContext *avctx)
|
---|
52 | {
|
---|
53 | FrapsContext * const s = avctx->priv_data;
|
---|
54 |
|
---|
55 | avctx->coded_frame = (AVFrame*)&s->frame;
|
---|
56 | avctx->has_b_frames = 0;
|
---|
57 | avctx->pix_fmt= PIX_FMT_NONE; /* set in decode_frame */
|
---|
58 |
|
---|
59 | s->avctx = avctx;
|
---|
60 | s->frame.data[0] = NULL;
|
---|
61 |
|
---|
62 | return 0;
|
---|
63 | }
|
---|
64 |
|
---|
65 |
|
---|
66 | /**
|
---|
67 | * decode a frame
|
---|
68 | * @param avctx codec context
|
---|
69 | * @param data output AVFrame
|
---|
70 | * @param data_size size of output data or 0 if no picture is returned
|
---|
71 | * @param buf input data frame
|
---|
72 | * @param buf_size size of input data frame
|
---|
73 | * @return number of consumed bytes on success or negative if decode fails
|
---|
74 | */
|
---|
75 | static int decode_frame(AVCodecContext *avctx,
|
---|
76 | void *data, int *data_size,
|
---|
77 | uint8_t *buf, int buf_size)
|
---|
78 | {
|
---|
79 | FrapsContext * const s = avctx->priv_data;
|
---|
80 | AVFrame *frame = data;
|
---|
81 | AVFrame * const f = (AVFrame*)&s->frame;
|
---|
82 | uint32_t header;
|
---|
83 | unsigned int version,header_size;
|
---|
84 | unsigned int x, y;
|
---|
85 | uint32_t *buf32;
|
---|
86 | uint32_t *luma1,*luma2,*cb,*cr;
|
---|
87 |
|
---|
88 |
|
---|
89 | header = LE_32(buf);
|
---|
90 | version = header & 0xff;
|
---|
91 | header_size = (header & (1<<30))? 8 : 4; /* bit 30 means pad to 8 bytes */
|
---|
92 |
|
---|
93 | if (version > 1) {
|
---|
94 | av_log(avctx, AV_LOG_ERROR,
|
---|
95 | "This file is encoded with Fraps version %d. " \
|
---|
96 | "This codec can only decode version 0 and 1.\n", version);
|
---|
97 | return -1;
|
---|
98 | }
|
---|
99 |
|
---|
100 | buf+=4;
|
---|
101 | if (header_size == 8)
|
---|
102 | buf+=4;
|
---|
103 |
|
---|
104 | switch(version) {
|
---|
105 | case 0:
|
---|
106 | default:
|
---|
107 | /* Fraps v0 is a reordered YUV420 */
|
---|
108 | avctx->pix_fmt = PIX_FMT_YUV420P;
|
---|
109 |
|
---|
110 | if ( (buf_size != avctx->width*avctx->height*3/2+header_size) &&
|
---|
111 | (buf_size != header_size) ) {
|
---|
112 | av_log(avctx, AV_LOG_ERROR,
|
---|
113 | "Invalid frame length %d (should be %d)\n",
|
---|
114 | buf_size, avctx->width*avctx->height*3/2+header_size);
|
---|
115 | return -1;
|
---|
116 | }
|
---|
117 |
|
---|
118 | if (( (avctx->width % 8) != 0) || ( (avctx->height % 2) != 0 )) {
|
---|
119 | av_log(avctx, AV_LOG_ERROR, "Invalid frame size %dx%d\n",
|
---|
120 | avctx->width, avctx->height);
|
---|
121 | return -1;
|
---|
122 | }
|
---|
123 |
|
---|
124 | f->reference = 1;
|
---|
125 | f->buffer_hints = FF_BUFFER_HINTS_VALID |
|
---|
126 | FF_BUFFER_HINTS_PRESERVE |
|
---|
127 | FF_BUFFER_HINTS_REUSABLE;
|
---|
128 | if (avctx->reget_buffer(avctx, f)) {
|
---|
129 | av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
|
---|
130 | return -1;
|
---|
131 | }
|
---|
132 | /* bit 31 means same as previous pic */
|
---|
133 | f->pict_type = (header & (1<<31))? FF_P_TYPE : FF_I_TYPE;
|
---|
134 | f->key_frame = f->pict_type == FF_I_TYPE;
|
---|
135 |
|
---|
136 | if (f->pict_type == FF_I_TYPE) {
|
---|
137 | buf32=(uint32_t*)buf;
|
---|
138 | for(y=0; y<avctx->height/2; y++){
|
---|
139 | luma1=(uint32_t*)&f->data[0][ y*2*f->linesize[0] ];
|
---|
140 | luma2=(uint32_t*)&f->data[0][ (y*2+1)*f->linesize[0] ];
|
---|
141 | cr=(uint32_t*)&f->data[1][ y*f->linesize[1] ];
|
---|
142 | cb=(uint32_t*)&f->data[2][ y*f->linesize[2] ];
|
---|
143 | for(x=0; x<avctx->width; x+=8){
|
---|
144 | *(luma1++) = *(buf32++);
|
---|
145 | *(luma1++) = *(buf32++);
|
---|
146 | *(luma2++) = *(buf32++);
|
---|
147 | *(luma2++) = *(buf32++);
|
---|
148 | *(cr++) = *(buf32++);
|
---|
149 | *(cb++) = *(buf32++);
|
---|
150 | }
|
---|
151 | }
|
---|
152 | }
|
---|
153 | break;
|
---|
154 |
|
---|
155 | case 1:
|
---|
156 | /* Fraps v1 is an upside-down BGR24 */
|
---|
157 | avctx->pix_fmt = PIX_FMT_BGR24;
|
---|
158 |
|
---|
159 | if ( (buf_size != avctx->width*avctx->height*3+header_size) &&
|
---|
160 | (buf_size != header_size) ) {
|
---|
161 | av_log(avctx, AV_LOG_ERROR,
|
---|
162 | "Invalid frame length %d (should be %d)\n",
|
---|
163 | buf_size, avctx->width*avctx->height*3+header_size);
|
---|
164 | return -1;
|
---|
165 | }
|
---|
166 |
|
---|
167 | f->reference = 1;
|
---|
168 | f->buffer_hints = FF_BUFFER_HINTS_VALID |
|
---|
169 | FF_BUFFER_HINTS_PRESERVE |
|
---|
170 | FF_BUFFER_HINTS_REUSABLE;
|
---|
171 | if (avctx->reget_buffer(avctx, f)) {
|
---|
172 | av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
|
---|
173 | return -1;
|
---|
174 | }
|
---|
175 | /* bit 31 means same as previous pic */
|
---|
176 | f->pict_type = (header & (1<<31))? FF_P_TYPE : FF_I_TYPE;
|
---|
177 | f->key_frame = f->pict_type == FF_I_TYPE;
|
---|
178 |
|
---|
179 | if (f->pict_type == FF_I_TYPE) {
|
---|
180 | for(y=0; y<avctx->height; y++)
|
---|
181 | memcpy(&f->data[0][ (avctx->height-y)*f->linesize[0] ],
|
---|
182 | &buf[y*avctx->width*3],
|
---|
183 | f->linesize[0]);
|
---|
184 | }
|
---|
185 | break;
|
---|
186 |
|
---|
187 | case 2:
|
---|
188 | /**
|
---|
189 | * Fraps v2 sub-header description. All numbers are little-endian:
|
---|
190 | * (this is all guesswork)
|
---|
191 | *
|
---|
192 | * 0: DWORD 'FPSx'
|
---|
193 | * 4: DWORD 0x00000010 unknown, perhaps flags
|
---|
194 | * 8: DWORD off_2 offset to plane 2
|
---|
195 | * 12: DWORD off_3 offset to plane 3
|
---|
196 | * 16: 256xDWORD freqtbl_1 frequency table for plane 1
|
---|
197 | * 1040: plane_1
|
---|
198 | * ...
|
---|
199 | * off_2: 256xDWORD freqtbl_2 frequency table for plane 2
|
---|
200 | * plane_2
|
---|
201 | * ...
|
---|
202 | * off_3: 256xDWORD freqtbl_3 frequency table for plane 3
|
---|
203 | * plane_3
|
---|
204 | */
|
---|
205 | if ((BE_32(buf) != FPS_TAG)||(buf_size < (3*1024 + 8))) {
|
---|
206 | av_log(avctx, AV_LOG_ERROR, "Fraps: error in data stream\n");
|
---|
207 | return -1;
|
---|
208 | }
|
---|
209 |
|
---|
210 | /* NOT FINISHED */
|
---|
211 |
|
---|
212 | break;
|
---|
213 | }
|
---|
214 |
|
---|
215 | *frame = *f;
|
---|
216 | *data_size = sizeof(AVFrame);
|
---|
217 |
|
---|
218 | return buf_size;
|
---|
219 | }
|
---|
220 |
|
---|
221 |
|
---|
222 | /**
|
---|
223 | * closes decoder
|
---|
224 | * @param avctx codec context
|
---|
225 | * @return 0 on success or negative if fails
|
---|
226 | */
|
---|
227 | static int decode_end(AVCodecContext *avctx)
|
---|
228 | {
|
---|
229 | FrapsContext *s = (FrapsContext*)avctx->priv_data;
|
---|
230 |
|
---|
231 | if (s->frame.data[0])
|
---|
232 | avctx->release_buffer(avctx, &s->frame);
|
---|
233 |
|
---|
234 | return 0;
|
---|
235 | }
|
---|
236 |
|
---|
237 |
|
---|
238 | AVCodec fraps_decoder = {
|
---|
239 | "fraps",
|
---|
240 | CODEC_TYPE_VIDEO,
|
---|
241 | CODEC_ID_FRAPS,
|
---|
242 | sizeof(FrapsContext),
|
---|
243 | decode_init,
|
---|
244 | NULL,
|
---|
245 | decode_end,
|
---|
246 | decode_frame,
|
---|
247 | CODEC_CAP_DR1,
|
---|
248 | };
|
---|