Mercurial > libavcodec.hg
annotate eatgv.c @ 9317:2c06c31c8664 libavcodec
One more simplification for ipvideo_decode_block_opcode_0xA
author | reimar |
---|---|
date | Tue, 31 Mar 2009 19:18:13 +0000 |
parents | bf274494b66e |
children | 54bc8a2727b0 |
rev | line source |
---|---|
7510 | 1 /* |
2 * Electronic Arts TGV Video Decoder | |
3 * Copyright (c) 2007-2008 Peter Ross | |
4 * | |
5 * This file is part of FFmpeg. | |
6 * | |
7 * FFmpeg is free software; you can redistribute it and/or | |
8 * modify it under the terms of the GNU Lesser General Public | |
9 * License as published by the Free Software Foundation; either | |
10 * version 2.1 of the License, or (at your option) any later version. | |
11 * | |
12 * FFmpeg is distributed in the hope that it will be useful, | |
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Lesser General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Lesser General Public | |
18 * License along with FFmpeg; if not, write to the Free Software | |
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
20 */ | |
21 | |
22 /** | |
8718
e9d9d946f213
Use full internal pathname in doxygen @file directives.
diego
parents:
8597
diff
changeset
|
23 * @file libavcodec/eatgv.c |
7510 | 24 * Electronic Arts TGV Video Decoder |
25 * by Peter Ross (suxen_drol at hotmail dot com) | |
26 * | |
27 * Technical details here: | |
28 * http://wiki.multimedia.cx/index.php?title=Electronic_Arts_TGV | |
29 */ | |
30 | |
31 #include "avcodec.h" | |
32 #define ALT_BITSTREAM_READER_LE | |
33 #include "bitstream.h" | |
8597 | 34 #include "libavutil/lzo.h" |
7510 | 35 |
36 #define EA_PREAMBLE_SIZE 8 | |
37 #define kVGT_TAG MKTAG('k', 'V', 'G', 'T') | |
38 | |
39 typedef struct TgvContext { | |
40 AVCodecContext *avctx; | |
41 AVFrame frame; | |
42 AVFrame last_frame; | |
43 int width,height; | |
44 unsigned int palette[AVPALETTE_COUNT]; | |
45 | |
46 int (*mv_codebook)[2]; | |
47 unsigned char (*block_codebook)[16]; | |
48 int num_mvs; ///< current length of mv_codebook | |
49 int num_blocks_packed; ///< current length of block_codebook | |
50 } TgvContext; | |
51 | |
52 static av_cold int tgv_decode_init(AVCodecContext *avctx){ | |
53 TgvContext *s = avctx->priv_data; | |
54 s->avctx = avctx; | |
55 avctx->time_base = (AVRational){1, 15}; | |
56 avctx->pix_fmt = PIX_FMT_PAL8; | |
57 return 0; | |
58 } | |
59 | |
60 /** | |
61 * Unpack buffer | |
62 * @return 0 on success, -1 on critical buffer underflow | |
63 */ | |
64 static int unpack(const uint8_t *src, const uint8_t *src_end, unsigned char *dst, int width, int height) { | |
65 unsigned char *dst_end = dst + width*height; | |
8801
154d81241ac0
Add av_uninit macro to variable to avoid false positive warning:
diego
parents:
8718
diff
changeset
|
66 int size, size1, size2, av_uninit(offset), run; |
7510 | 67 unsigned char *dst_start = dst; |
68 | |
69 if (src[0] & 0x01) | |
70 src += 5; | |
71 else | |
72 src += 2; | |
73 | |
74 if (src+3>src_end) | |
75 return -1; | |
76 size = AV_RB24(src); | |
77 src += 3; | |
78 | |
79 while(size>0 && src<src_end) { | |
80 | |
81 /* determine size1 and size2 */ | |
82 size1 = (src[0] & 3); | |
83 if ( src[0] & 0x80 ) { // 1 | |
84 if (src[0] & 0x40 ) { // 11 | |
85 if ( src[0] & 0x20 ) { // 111 | |
86 if ( src[0] < 0xFC ) // !(111111) | |
87 size1 = (((src[0] & 31) + 1) << 2); | |
88 src++; | |
89 size2 = 0; | |
90 } else { // 110 | |
91 offset = ((src[0] & 0x10) << 12) + AV_RB16(&src[1]) + 1; | |
92 size2 = ((src[0] & 0xC) << 6) + src[3] + 5; | |
93 src += 4; | |
94 } | |
95 } else { // 10 | |
96 size1 = ( ( src[1] & 0xC0) >> 6 ); | |
97 offset = (AV_RB16(&src[1]) & 0x3FFF) + 1; | |
98 size2 = (src[0] & 0x3F) + 4; | |
99 src += 3; | |
100 } | |
101 } else { // 0 | |
102 offset = ((src[0] & 0x60) << 3) + src[1] + 1; | |
103 size2 = ((src[0] & 0x1C) >> 2) + 3; | |
104 src += 2; | |
105 } | |
106 | |
107 | |
108 /* fetch strip from src */ | |
109 if (size1>src_end-src) | |
110 break; | |
111 | |
112 if (size1>0) { | |
113 size -= size1; | |
114 run = FFMIN(size1, dst_end-dst); | |
115 memcpy(dst, src, run); | |
116 dst += run; | |
117 src += run; | |
118 } | |
119 | |
120 if (size2>0) { | |
121 if (dst-dst_start<offset) | |
122 return 0; | |
123 size -= size2; | |
124 run = FFMIN(size2, dst_end-dst); | |
125 av_memcpy_backptr(dst, offset, run); | |
126 dst += run; | |
127 } | |
128 } | |
129 | |
130 return 0; | |
131 } | |
132 | |
133 /** | |
134 * Decode inter-frame | |
135 * @return 0 on success, -1 on critical buffer underflow | |
136 */ | |
137 static int tgv_decode_inter(TgvContext * s, const uint8_t *buf, const uint8_t *buf_end){ | |
138 unsigned char *frame0_end = s->last_frame.data[0] + s->avctx->width*s->last_frame.linesize[0]; | |
139 int num_mvs; | |
140 int num_blocks_raw; | |
141 int num_blocks_packed; | |
142 int vector_bits; | |
143 int i,j,x,y; | |
144 GetBitContext gb; | |
145 int mvbits; | |
146 const unsigned char *blocks_raw; | |
147 | |
148 if(buf+12>buf_end) | |
149 return -1; | |
150 | |
151 num_mvs = AV_RL16(&buf[0]); | |
152 num_blocks_raw = AV_RL16(&buf[2]); | |
153 num_blocks_packed = AV_RL16(&buf[4]); | |
154 vector_bits = AV_RL16(&buf[6]); | |
155 buf += 12; | |
156 | |
157 /* allocate codebook buffers as neccessary */ | |
158 if (num_mvs > s->num_mvs) { | |
159 s->mv_codebook = av_realloc(s->mv_codebook, num_mvs*2*sizeof(int)); | |
160 s->num_mvs = num_mvs; | |
161 } | |
162 | |
163 if (num_blocks_packed > s->num_blocks_packed) { | |
164 s->block_codebook = av_realloc(s->block_codebook, num_blocks_packed*16*sizeof(unsigned char)); | |
165 s->num_blocks_packed = num_blocks_packed; | |
166 } | |
167 | |
168 /* read motion vectors */ | |
169 mvbits = (num_mvs*2*10+31) & ~31; | |
170 | |
171 if (buf+(mvbits>>3)+16*num_blocks_raw+8*num_blocks_packed>buf_end) | |
172 return -1; | |
173 | |
174 init_get_bits(&gb, buf, mvbits); | |
175 for (i=0; i<num_mvs; i++) { | |
176 s->mv_codebook[i][0] = get_sbits(&gb, 10); | |
177 s->mv_codebook[i][1] = get_sbits(&gb, 10); | |
178 } | |
179 buf += mvbits>>3; | |
180 | |
181 /* note ptr to uncompressed blocks */ | |
182 blocks_raw = buf; | |
183 buf += num_blocks_raw*16; | |
184 | |
185 /* read compressed blocks */ | |
186 init_get_bits(&gb, buf, (buf_end-buf)<<3); | |
187 for (i=0; i<num_blocks_packed; i++) { | |
188 int tmp[4]; | |
189 for(j=0; j<4; j++) | |
190 tmp[j] = get_bits(&gb, 8); | |
191 for(j=0; j<16; j++) | |
192 s->block_codebook[i][15-j] = tmp[get_bits(&gb, 2)]; | |
193 } | |
194 | |
195 /* read vectors and build frame */ | |
196 for(y=0; y<s->avctx->height/4; y++) | |
197 for(x=0; x<s->avctx->width/4; x++) { | |
198 unsigned int vector = get_bits(&gb, vector_bits); | |
199 const unsigned char *src; | |
200 int src_stride; | |
201 | |
202 if (vector < num_mvs) { | |
203 src = s->last_frame.data[0] + | |
204 (y*4 + s->mv_codebook[vector][1])*s->last_frame.linesize[0] + | |
205 x*4 + s->mv_codebook[vector][0]; | |
206 src_stride = s->last_frame.linesize[0]; | |
207 if (src+3*src_stride+3>=frame0_end) | |
208 continue; | |
209 }else{ | |
210 int offset = vector - num_mvs; | |
211 if (offset<num_blocks_raw) | |
212 src = blocks_raw + 16*offset; | |
213 else if (offset-num_blocks_raw<num_blocks_packed) | |
214 src = s->block_codebook[offset-num_blocks_raw]; | |
215 else | |
216 continue; | |
217 src_stride = 4; | |
218 } | |
219 | |
220 for(j=0; j<4; j++) | |
221 for(i=0; i<4; i++) | |
222 s->frame.data[0][ (y*4+j)*s->frame.linesize[0] + (x*4+i) ] = | |
223 src[j*src_stride + i]; | |
224 } | |
225 | |
226 return 0; | |
227 } | |
228 | |
229 /** release AVFrame buffers if allocated */ | |
230 static void cond_release_buffer(AVFrame *pic) | |
231 { | |
232 if (pic->data[0]) { | |
233 av_freep(&pic->data[0]); | |
234 av_free(pic->data[1]); | |
235 } | |
236 } | |
237 | |
238 static int tgv_decode_frame(AVCodecContext *avctx, | |
239 void *data, int *data_size, | |
240 const uint8_t *buf, int buf_size) | |
241 { | |
242 TgvContext *s = avctx->priv_data; | |
243 const uint8_t *buf_end = buf + buf_size; | |
244 int chunk_type; | |
245 | |
246 chunk_type = AV_RL32(&buf[0]); | |
247 buf += EA_PREAMBLE_SIZE; | |
248 | |
249 if (chunk_type==kVGT_TAG) { | |
250 int pal_count, i; | |
251 if(buf+12>buf_end) { | |
252 av_log(avctx, AV_LOG_WARNING, "truncated header\n"); | |
253 return -1; | |
254 } | |
255 | |
256 s->width = AV_RL16(&buf[0]); | |
257 s->height = AV_RL16(&buf[2]); | |
258 if (s->avctx->width!=s->width || s->avctx->height!=s->height) { | |
259 avcodec_set_dimensions(s->avctx, s->width, s->height); | |
260 cond_release_buffer(&s->frame); | |
261 cond_release_buffer(&s->last_frame); | |
262 } | |
263 | |
264 pal_count = AV_RL16(&buf[6]); | |
265 buf += 12; | |
266 for(i=0; i<pal_count && i<AVPALETTE_COUNT && buf+2<buf_end; i++) { | |
267 s->palette[i] = AV_RB24(buf); | |
268 buf += 3; | |
269 } | |
270 } | |
271 | |
272 if (avcodec_check_dimensions(avctx, s->width, s->height)) | |
273 return -1; | |
274 | |
275 /* shuffle */ | |
276 FFSWAP(AVFrame, s->frame, s->last_frame); | |
277 if (!s->frame.data[0]) { | |
278 s->frame.reference = 1; | |
279 s->frame.buffer_hints = FF_BUFFER_HINTS_VALID; | |
280 s->frame.linesize[0] = s->width; | |
281 | |
282 /* allocate additional 12 bytes to accomodate av_memcpy_backptr() OUTBUF_PADDED optimisation */ | |
283 s->frame.data[0] = av_malloc(s->width*s->height + 12); | |
284 if (!s->frame.data[0]) | |
285 return AVERROR_NOMEM; | |
286 s->frame.data[1] = av_malloc(AVPALETTE_SIZE); | |
287 if (!s->frame.data[1]) { | |
288 av_freep(&s->frame.data[0]); | |
289 return AVERROR_NOMEM; | |
290 } | |
291 } | |
292 memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE); | |
293 | |
294 if(chunk_type==kVGT_TAG) { | |
295 s->frame.key_frame = 1; | |
296 s->frame.pict_type = FF_I_TYPE; | |
297 if (unpack(buf, buf_end, s->frame.data[0], s->avctx->width, s->avctx->height)<0) { | |
298 av_log(avctx, AV_LOG_WARNING, "truncated intra frame\n"); | |
299 return -1; | |
300 } | |
301 }else{ | |
302 if (!s->last_frame.data[0]) { | |
303 av_log(avctx, AV_LOG_WARNING, "inter frame without corresponding intra frame\n"); | |
304 return buf_size; | |
305 } | |
306 s->frame.key_frame = 0; | |
307 s->frame.pict_type = FF_P_TYPE; | |
308 if (tgv_decode_inter(s, buf, buf_end)<0) { | |
309 av_log(avctx, AV_LOG_WARNING, "truncated inter frame\n"); | |
310 return -1; | |
311 } | |
312 } | |
313 | |
314 *data_size = sizeof(AVFrame); | |
315 *(AVFrame*)data = s->frame; | |
316 | |
317 return buf_size; | |
318 } | |
319 | |
320 static av_cold int tgv_decode_end(AVCodecContext *avctx) | |
321 { | |
322 TgvContext *s = avctx->priv_data; | |
323 cond_release_buffer(&s->frame); | |
324 cond_release_buffer(&s->last_frame); | |
325 av_free(s->mv_codebook); | |
326 av_free(s->block_codebook); | |
327 return 0; | |
328 } | |
329 | |
330 AVCodec eatgv_decoder = { | |
331 "eatgv", | |
332 CODEC_TYPE_VIDEO, | |
333 CODEC_ID_TGV, | |
334 sizeof(TgvContext), | |
335 tgv_decode_init, | |
336 NULL, | |
337 tgv_decode_end, | |
338 tgv_decode_frame, | |
9083
bf274494b66e
Change a bunch of codec long_names to be more consistent and descriptive.
diego
parents:
8801
diff
changeset
|
339 .long_name = NULL_IF_CONFIG_SMALL("Electronic Arts TGV video"), |
7510 | 340 }; |