Mercurial > libavcodec.hg
annotate eatgv.c @ 12494:94eaea836bf4 libavcodec
Check avctx width/height more thoroughly (e.g. all values 0 except width would
have been accepted before).
Also do not fail if they are invalid but instead override them to 0.
This allows decoding e.g. MPEG video when only the container values are corrupted.
For encoding a value of 0,0 of course makes no sense, but was allowed
through before and will be caught by an extra check in the encode function.
author | reimar |
---|---|
date | Wed, 15 Sep 2010 04:46:55 +0000 |
parents | ffb3668ff7af |
children |
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" |
12372
914f484bb476
Remove use of the deprecated function avcodec_check_dimensions(), use
stefano
parents:
11644
diff
changeset
|
35 #include "libavcore/imgutils.h" |
7510 | 36 |
37 #define EA_PREAMBLE_SIZE 8 | |
38 #define kVGT_TAG MKTAG('k', 'V', 'G', 'T') | |
39 | |
40 typedef struct TgvContext { | |
41 AVCodecContext *avctx; | |
42 AVFrame frame; | |
43 AVFrame last_frame; | |
44 int width,height; | |
45 unsigned int palette[AVPALETTE_COUNT]; | |
46 | |
47 int (*mv_codebook)[2]; | |
48 unsigned char (*block_codebook)[16]; | |
49 int num_mvs; ///< current length of mv_codebook | |
50 int num_blocks_packed; ///< current length of block_codebook | |
51 } TgvContext; | |
52 | |
53 static av_cold int tgv_decode_init(AVCodecContext *avctx){ | |
54 TgvContext *s = avctx->priv_data; | |
55 s->avctx = avctx; | |
56 avctx->time_base = (AVRational){1, 15}; | |
57 avctx->pix_fmt = PIX_FMT_PAL8; | |
58 return 0; | |
59 } | |
60 | |
61 /** | |
62 * Unpack buffer | |
63 * @return 0 on success, -1 on critical buffer underflow | |
64 */ | |
65 static int unpack(const uint8_t *src, const uint8_t *src_end, unsigned char *dst, int width, int height) { | |
66 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
|
67 int size, size1, size2, av_uninit(offset), run; |
7510 | 68 unsigned char *dst_start = dst; |
69 | |
70 if (src[0] & 0x01) | |
71 src += 5; | |
72 else | |
73 src += 2; | |
74 | |
75 if (src+3>src_end) | |
76 return -1; | |
77 size = AV_RB24(src); | |
78 src += 3; | |
79 | |
80 while(size>0 && src<src_end) { | |
81 | |
82 /* determine size1 and size2 */ | |
83 size1 = (src[0] & 3); | |
84 if ( src[0] & 0x80 ) { // 1 | |
85 if (src[0] & 0x40 ) { // 11 | |
86 if ( src[0] & 0x20 ) { // 111 | |
87 if ( src[0] < 0xFC ) // !(111111) | |
88 size1 = (((src[0] & 31) + 1) << 2); | |
89 src++; | |
90 size2 = 0; | |
91 } else { // 110 | |
92 offset = ((src[0] & 0x10) << 12) + AV_RB16(&src[1]) + 1; | |
93 size2 = ((src[0] & 0xC) << 6) + src[3] + 5; | |
94 src += 4; | |
95 } | |
96 } else { // 10 | |
97 size1 = ( ( src[1] & 0xC0) >> 6 ); | |
98 offset = (AV_RB16(&src[1]) & 0x3FFF) + 1; | |
99 size2 = (src[0] & 0x3F) + 4; | |
100 src += 3; | |
101 } | |
102 } else { // 0 | |
103 offset = ((src[0] & 0x60) << 3) + src[1] + 1; | |
104 size2 = ((src[0] & 0x1C) >> 2) + 3; | |
105 src += 2; | |
106 } | |
107 | |
108 | |
109 /* fetch strip from src */ | |
110 if (size1>src_end-src) | |
111 break; | |
112 | |
113 if (size1>0) { | |
114 size -= size1; | |
115 run = FFMIN(size1, dst_end-dst); | |
116 memcpy(dst, src, run); | |
117 dst += run; | |
118 src += run; | |
119 } | |
120 | |
121 if (size2>0) { | |
122 if (dst-dst_start<offset) | |
123 return 0; | |
124 size -= size2; | |
125 run = FFMIN(size2, dst_end-dst); | |
126 av_memcpy_backptr(dst, offset, run); | |
127 dst += run; | |
128 } | |
129 } | |
130 | |
131 return 0; | |
132 } | |
133 | |
134 /** | |
135 * Decode inter-frame | |
136 * @return 0 on success, -1 on critical buffer underflow | |
137 */ | |
138 static int tgv_decode_inter(TgvContext * s, const uint8_t *buf, const uint8_t *buf_end){ | |
139 unsigned char *frame0_end = s->last_frame.data[0] + s->avctx->width*s->last_frame.linesize[0]; | |
140 int num_mvs; | |
141 int num_blocks_raw; | |
142 int num_blocks_packed; | |
143 int vector_bits; | |
144 int i,j,x,y; | |
145 GetBitContext gb; | |
146 int mvbits; | |
147 const unsigned char *blocks_raw; | |
148 | |
149 if(buf+12>buf_end) | |
150 return -1; | |
151 | |
152 num_mvs = AV_RL16(&buf[0]); | |
153 num_blocks_raw = AV_RL16(&buf[2]); | |
154 num_blocks_packed = AV_RL16(&buf[4]); | |
155 vector_bits = AV_RL16(&buf[6]); | |
156 buf += 12; | |
157 | |
158 /* allocate codebook buffers as neccessary */ | |
159 if (num_mvs > s->num_mvs) { | |
160 s->mv_codebook = av_realloc(s->mv_codebook, num_mvs*2*sizeof(int)); | |
161 s->num_mvs = num_mvs; | |
162 } | |
163 | |
164 if (num_blocks_packed > s->num_blocks_packed) { | |
165 s->block_codebook = av_realloc(s->block_codebook, num_blocks_packed*16*sizeof(unsigned char)); | |
166 s->num_blocks_packed = num_blocks_packed; | |
167 } | |
168 | |
169 /* read motion vectors */ | |
170 mvbits = (num_mvs*2*10+31) & ~31; | |
171 | |
172 if (buf+(mvbits>>3)+16*num_blocks_raw+8*num_blocks_packed>buf_end) | |
173 return -1; | |
174 | |
175 init_get_bits(&gb, buf, mvbits); | |
176 for (i=0; i<num_mvs; i++) { | |
177 s->mv_codebook[i][0] = get_sbits(&gb, 10); | |
178 s->mv_codebook[i][1] = get_sbits(&gb, 10); | |
179 } | |
180 buf += mvbits>>3; | |
181 | |
182 /* note ptr to uncompressed blocks */ | |
183 blocks_raw = buf; | |
184 buf += num_blocks_raw*16; | |
185 | |
186 /* read compressed blocks */ | |
187 init_get_bits(&gb, buf, (buf_end-buf)<<3); | |
188 for (i=0; i<num_blocks_packed; i++) { | |
189 int tmp[4]; | |
190 for(j=0; j<4; j++) | |
191 tmp[j] = get_bits(&gb, 8); | |
192 for(j=0; j<16; j++) | |
193 s->block_codebook[i][15-j] = tmp[get_bits(&gb, 2)]; | |
194 } | |
195 | |
11303
b0cb1f7efcbc
Do not read beyond end of input in EA-TGV. This should avoid FATE test #362
vitor
parents:
10825
diff
changeset
|
196 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
|
197 (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
|
198 return -1; |
b0cb1f7efcbc
Do not read beyond end of input in EA-TGV. This should avoid FATE test #362
vitor
parents:
10825
diff
changeset
|
199 |
7510 | 200 /* read vectors and build frame */ |
201 for(y=0; y<s->avctx->height/4; y++) | |
202 for(x=0; x<s->avctx->width/4; x++) { | |
203 unsigned int vector = get_bits(&gb, vector_bits); | |
204 const unsigned char *src; | |
205 int src_stride; | |
206 | |
207 if (vector < num_mvs) { | |
208 src = s->last_frame.data[0] + | |
209 (y*4 + s->mv_codebook[vector][1])*s->last_frame.linesize[0] + | |
210 x*4 + s->mv_codebook[vector][0]; | |
211 src_stride = s->last_frame.linesize[0]; | |
212 if (src+3*src_stride+3>=frame0_end) | |
213 continue; | |
214 }else{ | |
215 int offset = vector - num_mvs; | |
216 if (offset<num_blocks_raw) | |
217 src = blocks_raw + 16*offset; | |
218 else if (offset-num_blocks_raw<num_blocks_packed) | |
219 src = s->block_codebook[offset-num_blocks_raw]; | |
220 else | |
221 continue; | |
222 src_stride = 4; | |
223 } | |
224 | |
225 for(j=0; j<4; j++) | |
226 for(i=0; i<4; i++) | |
227 s->frame.data[0][ (y*4+j)*s->frame.linesize[0] + (x*4+i) ] = | |
228 src[j*src_stride + i]; | |
229 } | |
230 | |
231 return 0; | |
232 } | |
233 | |
234 /** release AVFrame buffers if allocated */ | |
235 static void cond_release_buffer(AVFrame *pic) | |
236 { | |
237 if (pic->data[0]) { | |
238 av_freep(&pic->data[0]); | |
239 av_free(pic->data[1]); | |
240 } | |
241 } | |
242 | |
243 static int tgv_decode_frame(AVCodecContext *avctx, | |
244 void *data, int *data_size, | |
9355
54bc8a2727b0
Implement avcodec_decode_video2(), _audio3() and _subtitle2() which takes an
rbultje
parents:
9083
diff
changeset
|
245 AVPacket *avpkt) |
7510 | 246 { |
9355
54bc8a2727b0
Implement avcodec_decode_video2(), _audio3() and _subtitle2() which takes an
rbultje
parents:
9083
diff
changeset
|
247 const uint8_t *buf = avpkt->data; |
54bc8a2727b0
Implement avcodec_decode_video2(), _audio3() and _subtitle2() which takes an
rbultje
parents:
9083
diff
changeset
|
248 int buf_size = avpkt->size; |
7510 | 249 TgvContext *s = avctx->priv_data; |
250 const uint8_t *buf_end = buf + buf_size; | |
251 int chunk_type; | |
252 | |
253 chunk_type = AV_RL32(&buf[0]); | |
254 buf += EA_PREAMBLE_SIZE; | |
255 | |
256 if (chunk_type==kVGT_TAG) { | |
257 int pal_count, i; | |
258 if(buf+12>buf_end) { | |
259 av_log(avctx, AV_LOG_WARNING, "truncated header\n"); | |
260 return -1; | |
261 } | |
262 | |
263 s->width = AV_RL16(&buf[0]); | |
264 s->height = AV_RL16(&buf[2]); | |
265 if (s->avctx->width!=s->width || s->avctx->height!=s->height) { | |
266 avcodec_set_dimensions(s->avctx, s->width, s->height); | |
267 cond_release_buffer(&s->frame); | |
268 cond_release_buffer(&s->last_frame); | |
269 } | |
270 | |
271 pal_count = AV_RL16(&buf[6]); | |
272 buf += 12; | |
273 for(i=0; i<pal_count && i<AVPALETTE_COUNT && buf+2<buf_end; i++) { | |
274 s->palette[i] = AV_RB24(buf); | |
275 buf += 3; | |
276 } | |
277 } | |
278 | |
12462
ffb3668ff7af
Use new imgutils.h API names, fix deprecation warnings.
stefano
parents:
12372
diff
changeset
|
279 if (av_image_check_size(s->width, s->height, 0, avctx)) |
7510 | 280 return -1; |
281 | |
282 /* shuffle */ | |
283 FFSWAP(AVFrame, s->frame, s->last_frame); | |
284 if (!s->frame.data[0]) { | |
285 s->frame.reference = 1; | |
286 s->frame.buffer_hints = FF_BUFFER_HINTS_VALID; | |
287 s->frame.linesize[0] = s->width; | |
288 | |
289 /* allocate additional 12 bytes to accomodate av_memcpy_backptr() OUTBUF_PADDED optimisation */ | |
290 s->frame.data[0] = av_malloc(s->width*s->height + 12); | |
291 if (!s->frame.data[0]) | |
11581
998691d7f8f5
Replace all remaining occurrences of AVERROR_NOMEM with
stefano
parents:
11560
diff
changeset
|
292 return AVERROR(ENOMEM); |
7510 | 293 s->frame.data[1] = av_malloc(AVPALETTE_SIZE); |
294 if (!s->frame.data[1]) { | |
295 av_freep(&s->frame.data[0]); | |
11581
998691d7f8f5
Replace all remaining occurrences of AVERROR_NOMEM with
stefano
parents:
11560
diff
changeset
|
296 return AVERROR(ENOMEM); |
7510 | 297 } |
298 } | |
299 memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE); | |
300 | |
301 if(chunk_type==kVGT_TAG) { | |
302 s->frame.key_frame = 1; | |
303 s->frame.pict_type = FF_I_TYPE; | |
304 if (unpack(buf, buf_end, s->frame.data[0], s->avctx->width, s->avctx->height)<0) { | |
305 av_log(avctx, AV_LOG_WARNING, "truncated intra frame\n"); | |
306 return -1; | |
307 } | |
308 }else{ | |
309 if (!s->last_frame.data[0]) { | |
310 av_log(avctx, AV_LOG_WARNING, "inter frame without corresponding intra frame\n"); | |
311 return buf_size; | |
312 } | |
313 s->frame.key_frame = 0; | |
314 s->frame.pict_type = FF_P_TYPE; | |
315 if (tgv_decode_inter(s, buf, buf_end)<0) { | |
316 av_log(avctx, AV_LOG_WARNING, "truncated inter frame\n"); | |
317 return -1; | |
318 } | |
319 } | |
320 | |
321 *data_size = sizeof(AVFrame); | |
322 *(AVFrame*)data = s->frame; | |
323 | |
324 return buf_size; | |
325 } | |
326 | |
327 static av_cold int tgv_decode_end(AVCodecContext *avctx) | |
328 { | |
329 TgvContext *s = avctx->priv_data; | |
330 cond_release_buffer(&s->frame); | |
331 cond_release_buffer(&s->last_frame); | |
332 av_free(s->mv_codebook); | |
333 av_free(s->block_codebook); | |
334 return 0; | |
335 } | |
336 | |
337 AVCodec eatgv_decoder = { | |
338 "eatgv", | |
11560
8a4984c5cacc
Define AVMediaType enum, and use it instead of enum CodecType, which
stefano
parents:
11303
diff
changeset
|
339 AVMEDIA_TYPE_VIDEO, |
7510 | 340 CODEC_ID_TGV, |
341 sizeof(TgvContext), | |
342 tgv_decode_init, | |
343 NULL, | |
344 tgv_decode_end, | |
345 tgv_decode_frame, | |
9083
bf274494b66e
Change a bunch of codec long_names to be more consistent and descriptive.
diego
parents:
8801
diff
changeset
|
346 .long_name = NULL_IF_CONFIG_SMALL("Electronic Arts TGV video"), |
7510 | 347 }; |