Mercurial > libavcodec.hg
annotate eatgv.c @ 11811:2501df1cabc5 libavcodec
bink: correctly align local arrays
author | mru |
---|---|
date | Wed, 02 Jun 2010 15:22:21 +0000 |
parents | 7dd2a45249a9 |
children | 914f484bb476 |
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 /** | |
11644
7dd2a45249a9
Remove explicit filename from Doxygen @file commands.
diego
parents:
11581
diff
changeset
|
23 * @file |
7510 | 24 * Electronic Arts TGV Video Decoder |
10825 | 25 * by Peter Ross (pross@xvid.org) |
7510 | 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 | |
9428 | 33 #include "get_bits.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 | |
11303
b0cb1f7efcbc
Do not read beyond end of input in EA-TGV. This should avoid FATE test #362
vitor
parents:
10825
diff
changeset
|
195 if (get_bits_left(&gb) < vector_bits * |
b0cb1f7efcbc
Do not read beyond end of input in EA-TGV. This should avoid FATE test #362
vitor
parents:
10825
diff
changeset
|
196 (s->avctx->height/4) * (s->avctx->width/4)) |
b0cb1f7efcbc
Do not read beyond end of input in EA-TGV. This should avoid FATE test #362
vitor
parents:
10825
diff
changeset
|
197 return -1; |
b0cb1f7efcbc
Do not read beyond end of input in EA-TGV. This should avoid FATE test #362
vitor
parents:
10825
diff
changeset
|
198 |
7510 | 199 /* read vectors and build frame */ |
200 for(y=0; y<s->avctx->height/4; y++) | |
201 for(x=0; x<s->avctx->width/4; x++) { | |
202 unsigned int vector = get_bits(&gb, vector_bits); | |
203 const unsigned char *src; | |
204 int src_stride; | |
205 | |
206 if (vector < num_mvs) { | |
207 src = s->last_frame.data[0] + | |
208 (y*4 + s->mv_codebook[vector][1])*s->last_frame.linesize[0] + | |
209 x*4 + s->mv_codebook[vector][0]; | |
210 src_stride = s->last_frame.linesize[0]; | |
211 if (src+3*src_stride+3>=frame0_end) | |
212 continue; | |
213 }else{ | |
214 int offset = vector - num_mvs; | |
215 if (offset<num_blocks_raw) | |
216 src = blocks_raw + 16*offset; | |
217 else if (offset-num_blocks_raw<num_blocks_packed) | |
218 src = s->block_codebook[offset-num_blocks_raw]; | |
219 else | |
220 continue; | |
221 src_stride = 4; | |
222 } | |
223 | |
224 for(j=0; j<4; j++) | |
225 for(i=0; i<4; i++) | |
226 s->frame.data[0][ (y*4+j)*s->frame.linesize[0] + (x*4+i) ] = | |
227 src[j*src_stride + i]; | |
228 } | |
229 | |
230 return 0; | |
231 } | |
232 | |
233 /** release AVFrame buffers if allocated */ | |
234 static void cond_release_buffer(AVFrame *pic) | |
235 { | |
236 if (pic->data[0]) { | |
237 av_freep(&pic->data[0]); | |
238 av_free(pic->data[1]); | |
239 } | |
240 } | |
241 | |
242 static int tgv_decode_frame(AVCodecContext *avctx, | |
243 void *data, int *data_size, | |
9355
54bc8a2727b0
Implement avcodec_decode_video2(), _audio3() and _subtitle2() which takes an
rbultje
parents:
9083
diff
changeset
|
244 AVPacket *avpkt) |
7510 | 245 { |
9355
54bc8a2727b0
Implement avcodec_decode_video2(), _audio3() and _subtitle2() which takes an
rbultje
parents:
9083
diff
changeset
|
246 const uint8_t *buf = avpkt->data; |
54bc8a2727b0
Implement avcodec_decode_video2(), _audio3() and _subtitle2() which takes an
rbultje
parents:
9083
diff
changeset
|
247 int buf_size = avpkt->size; |
7510 | 248 TgvContext *s = avctx->priv_data; |
249 const uint8_t *buf_end = buf + buf_size; | |
250 int chunk_type; | |
251 | |
252 chunk_type = AV_RL32(&buf[0]); | |
253 buf += EA_PREAMBLE_SIZE; | |
254 | |
255 if (chunk_type==kVGT_TAG) { | |
256 int pal_count, i; | |
257 if(buf+12>buf_end) { | |
258 av_log(avctx, AV_LOG_WARNING, "truncated header\n"); | |
259 return -1; | |
260 } | |
261 | |
262 s->width = AV_RL16(&buf[0]); | |
263 s->height = AV_RL16(&buf[2]); | |
264 if (s->avctx->width!=s->width || s->avctx->height!=s->height) { | |
265 avcodec_set_dimensions(s->avctx, s->width, s->height); | |
266 cond_release_buffer(&s->frame); | |
267 cond_release_buffer(&s->last_frame); | |
268 } | |
269 | |
270 pal_count = AV_RL16(&buf[6]); | |
271 buf += 12; | |
272 for(i=0; i<pal_count && i<AVPALETTE_COUNT && buf+2<buf_end; i++) { | |
273 s->palette[i] = AV_RB24(buf); | |
274 buf += 3; | |
275 } | |
276 } | |
277 | |
278 if (avcodec_check_dimensions(avctx, s->width, s->height)) | |
279 return -1; | |
280 | |
281 /* shuffle */ | |
282 FFSWAP(AVFrame, s->frame, s->last_frame); | |
283 if (!s->frame.data[0]) { | |
284 s->frame.reference = 1; | |
285 s->frame.buffer_hints = FF_BUFFER_HINTS_VALID; | |
286 s->frame.linesize[0] = s->width; | |
287 | |
288 /* allocate additional 12 bytes to accomodate av_memcpy_backptr() OUTBUF_PADDED optimisation */ | |
289 s->frame.data[0] = av_malloc(s->width*s->height + 12); | |
290 if (!s->frame.data[0]) | |
11581
998691d7f8f5
Replace all remaining occurrences of AVERROR_NOMEM with
stefano
parents:
11560
diff
changeset
|
291 return AVERROR(ENOMEM); |
7510 | 292 s->frame.data[1] = av_malloc(AVPALETTE_SIZE); |
293 if (!s->frame.data[1]) { | |
294 av_freep(&s->frame.data[0]); | |
11581
998691d7f8f5
Replace all remaining occurrences of AVERROR_NOMEM with
stefano
parents:
11560
diff
changeset
|
295 return AVERROR(ENOMEM); |
7510 | 296 } |
297 } | |
298 memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE); | |
299 | |
300 if(chunk_type==kVGT_TAG) { | |
301 s->frame.key_frame = 1; | |
302 s->frame.pict_type = FF_I_TYPE; | |
303 if (unpack(buf, buf_end, s->frame.data[0], s->avctx->width, s->avctx->height)<0) { | |
304 av_log(avctx, AV_LOG_WARNING, "truncated intra frame\n"); | |
305 return -1; | |
306 } | |
307 }else{ | |
308 if (!s->last_frame.data[0]) { | |
309 av_log(avctx, AV_LOG_WARNING, "inter frame without corresponding intra frame\n"); | |
310 return buf_size; | |
311 } | |
312 s->frame.key_frame = 0; | |
313 s->frame.pict_type = FF_P_TYPE; | |
314 if (tgv_decode_inter(s, buf, buf_end)<0) { | |
315 av_log(avctx, AV_LOG_WARNING, "truncated inter frame\n"); | |
316 return -1; | |
317 } | |
318 } | |
319 | |
320 *data_size = sizeof(AVFrame); | |
321 *(AVFrame*)data = s->frame; | |
322 | |
323 return buf_size; | |
324 } | |
325 | |
326 static av_cold int tgv_decode_end(AVCodecContext *avctx) | |
327 { | |
328 TgvContext *s = avctx->priv_data; | |
329 cond_release_buffer(&s->frame); | |
330 cond_release_buffer(&s->last_frame); | |
331 av_free(s->mv_codebook); | |
332 av_free(s->block_codebook); | |
333 return 0; | |
334 } | |
335 | |
336 AVCodec eatgv_decoder = { | |
337 "eatgv", | |
11560
8a4984c5cacc
Define AVMediaType enum, and use it instead of enum CodecType, which
stefano
parents:
11303
diff
changeset
|
338 AVMEDIA_TYPE_VIDEO, |
7510 | 339 CODEC_ID_TGV, |
340 sizeof(TgvContext), | |
341 tgv_decode_init, | |
342 NULL, | |
343 tgv_decode_end, | |
344 tgv_decode_frame, | |
9083
bf274494b66e
Change a bunch of codec long_names to be more consistent and descriptive.
diego
parents:
8801
diff
changeset
|
345 .long_name = NULL_IF_CONFIG_SMALL("Electronic Arts TGV video"), |
7510 | 346 }; |