Mercurial > libavcodec.hg
comparison cinepak.c @ 1491:222643544cf1 libavcodec
New demuxers: Sega FILM/CPK, Westwood VQA & AUD; new decoders: MS RLE &
Video-1, Apple RPZA, Cinepak, Westwood IMA ADPCM
author | tmmm |
---|---|
date | Wed, 01 Oct 2003 04:39:38 +0000 |
parents | |
children | 860e44e2c20c |
comparison
equal
deleted
inserted
replaced
1490:0355f2b3519a | 1491:222643544cf1 |
---|---|
1 /* | |
2 * Cinepak Video Decoder | |
3 * Copyright (C) 2003 the ffmpeg project | |
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 * | |
19 */ | |
20 | |
21 /** | |
22 * @file cinepak.c | |
23 * Cinepak video decoder | |
24 * by Ewald Snel <ewald@rambo.its.tudelft.nl> | |
25 * For more information on the Cinepak algorithm, visit: | |
26 * http://www.csse.monash.edu.au/~timf/ | |
27 */ | |
28 | |
29 #include <stdio.h> | |
30 #include <stdlib.h> | |
31 #include <string.h> | |
32 #include <unistd.h> | |
33 | |
34 #include "common.h" | |
35 #include "avcodec.h" | |
36 #include "dsputil.h" | |
37 | |
38 #define PALETTE_COUNT 256 | |
39 | |
40 #define BE_16(x) ((((uint8_t*)(x))[0] << 8) | ((uint8_t*)(x))[1]) | |
41 #define BE_32(x) ((((uint8_t*)(x))[0] << 24) | \ | |
42 (((uint8_t*)(x))[1] << 16) | \ | |
43 (((uint8_t*)(x))[2] << 8) | \ | |
44 ((uint8_t*)(x))[3]) | |
45 | |
46 typedef struct { | |
47 uint8_t y0, y1, y2, y3; | |
48 uint8_t u, v; | |
49 } cvid_codebook_t; | |
50 | |
51 #define MAX_STRIPS 32 | |
52 | |
53 typedef struct { | |
54 uint16_t id; | |
55 uint16_t x1, y1; | |
56 uint16_t x2, y2; | |
57 cvid_codebook_t v4_codebook[256]; | |
58 cvid_codebook_t v1_codebook[256]; | |
59 } cvid_strip_t; | |
60 | |
61 typedef struct CinepakContext { | |
62 | |
63 AVCodecContext *avctx; | |
64 DSPContext dsp; | |
65 AVFrame frame; | |
66 AVFrame prev_frame; | |
67 | |
68 unsigned char *data; | |
69 int size; | |
70 | |
71 unsigned char palette[PALETTE_COUNT * 4]; | |
72 int palette_video; | |
73 cvid_strip_t strips[MAX_STRIPS]; | |
74 | |
75 } CinepakContext; | |
76 | |
77 static void cinepak_decode_codebook (cvid_codebook_t *codebook, | |
78 int chunk_id, int size, uint8_t *data) | |
79 { | |
80 uint8_t *eod = (data + size); | |
81 uint32_t flag, mask; | |
82 int i, n; | |
83 | |
84 /* check if this chunk contains 4- or 6-element vectors */ | |
85 n = (chunk_id & 0x0400) ? 4 : 6; | |
86 flag = 0; | |
87 mask = 0; | |
88 | |
89 for (i=0; i < 256; i++) { | |
90 if ((chunk_id & 0x0100) && !(mask >>= 1)) { | |
91 if ((data + 4) > eod) | |
92 break; | |
93 | |
94 flag = BE_32 (data); | |
95 data += 4; | |
96 mask = 0x80000000; | |
97 } | |
98 | |
99 if (!(chunk_id & 0x0100) || (flag & mask)) { | |
100 if ((data + n) > eod) | |
101 break; | |
102 | |
103 if (n == 6) { | |
104 codebook[i].y0 = *data++; | |
105 codebook[i].y1 = *data++; | |
106 codebook[i].y2 = *data++; | |
107 codebook[i].y3 = *data++; | |
108 codebook[i].u = 128 + *data++; | |
109 codebook[i].v = 128 + *data++; | |
110 } else { | |
111 /* this codebook type indicates either greyscale or | |
112 * palettized video; if palettized, U & V components will | |
113 * not be used so it is safe to set them to 128 for the | |
114 * benefit of greyscale rendering in YUV420P */ | |
115 codebook[i].y0 = *data++; | |
116 codebook[i].y1 = *data++; | |
117 codebook[i].y2 = *data++; | |
118 codebook[i].y3 = *data++; | |
119 codebook[i].u = 128; | |
120 codebook[i].v = 128; | |
121 } | |
122 } | |
123 } | |
124 } | |
125 | |
126 static int cinepak_decode_vectors (CinepakContext *s, cvid_strip_t *strip, | |
127 int chunk_id, int size, uint8_t *data) | |
128 { | |
129 uint8_t *eod = (data + size); | |
130 uint32_t flag, mask; | |
131 cvid_codebook_t *codebook; | |
132 unsigned int i, j, x, y; | |
133 uint32_t iy[4]; | |
134 uint32_t iu[2]; | |
135 uint32_t iv[2]; | |
136 | |
137 flag = 0; | |
138 mask = 0; | |
139 | |
140 for (y=strip->y1; y < strip->y2; y+=4) { | |
141 | |
142 iy[0] = strip->x1 + (y * s->frame.linesize[0]); | |
143 iy[1] = iy[0] + s->frame.linesize[0]; | |
144 iy[2] = iy[1] + s->frame.linesize[0]; | |
145 iy[3] = iy[2] + s->frame.linesize[0]; | |
146 iu[0] = (strip->x1/2) + ((y/2) * s->frame.linesize[1]); | |
147 iu[1] = iu[0] + s->frame.linesize[1]; | |
148 iv[0] = (strip->x1/2) + ((y/2) * s->frame.linesize[2]); | |
149 iv[1] = iv[0] + s->frame.linesize[2]; | |
150 | |
151 for (x=strip->x1; x < strip->x2; x+=4) { | |
152 if ((chunk_id & 0x0100) && !(mask >>= 1)) { | |
153 if ((data + 4) > eod) | |
154 return -1; | |
155 | |
156 flag = BE_32 (data); | |
157 data += 4; | |
158 mask = 0x80000000; | |
159 } | |
160 | |
161 if (!(chunk_id & 0x0100) || (flag & mask)) { | |
162 if (!(chunk_id & 0x0200) && !(mask >>= 1)) { | |
163 if ((data + 4) > eod) | |
164 return -1; | |
165 | |
166 flag = BE_32 (data); | |
167 data += 4; | |
168 mask = 0x80000000; | |
169 } | |
170 | |
171 if ((chunk_id & 0x0200) || (~flag & mask)) { | |
172 if (data >= eod) | |
173 return -1; | |
174 | |
175 codebook = &strip->v1_codebook[*data++]; | |
176 s->frame.data[0][iy[0] + 0] = codebook->y0; | |
177 s->frame.data[0][iy[0] + 1] = codebook->y0; | |
178 s->frame.data[0][iy[1] + 0] = codebook->y0; | |
179 s->frame.data[0][iy[1] + 1] = codebook->y0; | |
180 if (!s->palette_video) { | |
181 s->frame.data[1][iu[0]] = codebook->u; | |
182 s->frame.data[2][iv[0]] = codebook->v; | |
183 } | |
184 | |
185 s->frame.data[0][iy[0] + 2] = codebook->y0; | |
186 s->frame.data[0][iy[0] + 3] = codebook->y0; | |
187 s->frame.data[0][iy[1] + 2] = codebook->y0; | |
188 s->frame.data[0][iy[1] + 3] = codebook->y0; | |
189 if (!s->palette_video) { | |
190 s->frame.data[1][iu[0] + 1] = codebook->u; | |
191 s->frame.data[2][iv[0] + 1] = codebook->v; | |
192 } | |
193 | |
194 s->frame.data[0][iy[2] + 0] = codebook->y0; | |
195 s->frame.data[0][iy[2] + 1] = codebook->y0; | |
196 s->frame.data[0][iy[3] + 0] = codebook->y0; | |
197 s->frame.data[0][iy[3] + 1] = codebook->y0; | |
198 if (!s->palette_video) { | |
199 s->frame.data[1][iu[1]] = codebook->u; | |
200 s->frame.data[2][iv[1]] = codebook->v; | |
201 } | |
202 | |
203 s->frame.data[0][iy[2] + 2] = codebook->y0; | |
204 s->frame.data[0][iy[2] + 3] = codebook->y0; | |
205 s->frame.data[0][iy[3] + 2] = codebook->y0; | |
206 s->frame.data[0][iy[3] + 3] = codebook->y0; | |
207 if (!s->palette_video) { | |
208 s->frame.data[1][iu[1] + 1] = codebook->u; | |
209 s->frame.data[2][iv[1] + 1] = codebook->v; | |
210 } | |
211 | |
212 } else if (flag & mask) { | |
213 if ((data + 4) > eod) | |
214 return -1; | |
215 | |
216 codebook = &strip->v4_codebook[*data++]; | |
217 s->frame.data[0][iy[0] + 0] = codebook->y0; | |
218 s->frame.data[0][iy[0] + 1] = codebook->y1; | |
219 s->frame.data[0][iy[1] + 0] = codebook->y2; | |
220 s->frame.data[0][iy[1] + 1] = codebook->y3; | |
221 if (!s->palette_video) { | |
222 s->frame.data[1][iu[0]] = codebook->u; | |
223 s->frame.data[2][iv[0]] = codebook->v; | |
224 } | |
225 | |
226 codebook = &strip->v4_codebook[*data++]; | |
227 s->frame.data[0][iy[0] + 2] = codebook->y0; | |
228 s->frame.data[0][iy[0] + 3] = codebook->y1; | |
229 s->frame.data[0][iy[1] + 2] = codebook->y2; | |
230 s->frame.data[0][iy[1] + 3] = codebook->y3; | |
231 if (!s->palette_video) { | |
232 s->frame.data[1][iu[0] + 1] = codebook->u; | |
233 s->frame.data[2][iv[0] + 1] = codebook->v; | |
234 } | |
235 | |
236 codebook = &strip->v4_codebook[*data++]; | |
237 s->frame.data[0][iy[2] + 0] = codebook->y0; | |
238 s->frame.data[0][iy[2] + 1] = codebook->y1; | |
239 s->frame.data[0][iy[3] + 0] = codebook->y2; | |
240 s->frame.data[0][iy[3] + 1] = codebook->y3; | |
241 if (!s->palette_video) { | |
242 s->frame.data[1][iu[1]] = codebook->u; | |
243 s->frame.data[2][iv[1]] = codebook->v; | |
244 } | |
245 | |
246 codebook = &strip->v4_codebook[*data++]; | |
247 s->frame.data[0][iy[2] + 2] = codebook->y0; | |
248 s->frame.data[0][iy[2] + 3] = codebook->y1; | |
249 s->frame.data[0][iy[3] + 2] = codebook->y2; | |
250 s->frame.data[0][iy[3] + 3] = codebook->y3; | |
251 if (!s->palette_video) { | |
252 s->frame.data[1][iu[1] + 1] = codebook->u; | |
253 s->frame.data[2][iv[1] + 1] = codebook->v; | |
254 } | |
255 | |
256 } | |
257 } else { | |
258 /* copy from the previous frame */ | |
259 for (i = 0; i < 4; i++) { | |
260 for (j = 0; j < 4; j++) { | |
261 s->frame.data[0][iy[i] + j] = | |
262 s->prev_frame.data[0][iy[i] + j]; | |
263 } | |
264 } | |
265 for (i = 0; i < 2; i++) { | |
266 for (j = 0; j < 2; j++) { | |
267 s->frame.data[1][iu[i] + j] = | |
268 s->prev_frame.data[1][iu[i] + j]; | |
269 s->frame.data[2][iv[i] + j] = | |
270 s->prev_frame.data[2][iv[i] + j]; | |
271 } | |
272 } | |
273 } | |
274 | |
275 iy[0] += 4; iy[1] += 4; | |
276 iy[2] += 4; iy[3] += 4; | |
277 iu[0] += 2; iu[1] += 2; | |
278 iv[0] += 2; iv[1] += 2; | |
279 } | |
280 } | |
281 | |
282 return 0; | |
283 } | |
284 | |
285 static int cinepak_decode_strip (CinepakContext *s, | |
286 cvid_strip_t *strip, uint8_t *data, int size) | |
287 { | |
288 uint8_t *eod = (data + size); | |
289 int chunk_id, chunk_size; | |
290 | |
291 /* coordinate sanity checks */ | |
292 if (strip->x1 >= s->avctx->width || strip->x2 > s->avctx->width || | |
293 strip->y1 >= s->avctx->height || strip->y2 > s->avctx->height || | |
294 strip->x1 >= strip->x2 || strip->y1 >= strip->y2) | |
295 return -1; | |
296 | |
297 while ((data + 4) <= eod) { | |
298 chunk_id = BE_16 (&data[0]); | |
299 chunk_size = BE_16 (&data[2]) - 4; | |
300 data += 4; | |
301 chunk_size = ((data + chunk_size) > eod) ? (eod - data) : chunk_size; | |
302 | |
303 switch (chunk_id) { | |
304 | |
305 case 0x2000: | |
306 case 0x2100: | |
307 case 0x2400: | |
308 case 0x2500: | |
309 cinepak_decode_codebook (strip->v4_codebook, chunk_id, | |
310 chunk_size, data); | |
311 break; | |
312 | |
313 case 0x2200: | |
314 case 0x2300: | |
315 case 0x2600: | |
316 case 0x2700: | |
317 cinepak_decode_codebook (strip->v1_codebook, chunk_id, | |
318 chunk_size, data); | |
319 break; | |
320 | |
321 case 0x3000: | |
322 case 0x3100: | |
323 case 0x3200: | |
324 return cinepak_decode_vectors (s, strip, chunk_id, | |
325 chunk_size, data); | |
326 } | |
327 | |
328 data += chunk_size; | |
329 } | |
330 | |
331 return -1; | |
332 } | |
333 | |
334 static int cinepak_decode (CinepakContext *s) | |
335 { | |
336 uint8_t *eod = (s->data + s->size); | |
337 int i, result, strip_size, frame_flags, num_strips; | |
338 int y0 = 0; | |
339 | |
340 if (s->size < 10) | |
341 return -1; | |
342 | |
343 frame_flags = s->data[0]; | |
344 num_strips = BE_16 (&s->data[8]); | |
345 s->data += 10; | |
346 | |
347 if (num_strips > MAX_STRIPS) | |
348 num_strips = MAX_STRIPS; | |
349 | |
350 for (i=0; i < num_strips; i++) { | |
351 if ((s->data + 12) > eod) | |
352 return -1; | |
353 | |
354 s->strips[i].id = BE_16 (s->data); | |
355 s->strips[i].y1 = y0; | |
356 s->strips[i].x1 = 0; | |
357 s->strips[i].y2 = y0 + BE_16 (&s->data[8]); | |
358 s->strips[i].x2 = s->avctx->width; | |
359 | |
360 strip_size = BE_16 (&s->data[2]) - 12; | |
361 s->data += 12; | |
362 strip_size = ((s->data + strip_size) > eod) ? (eod - s->data) : strip_size; | |
363 | |
364 if ((i > 0) && !(frame_flags & 0x01)) { | |
365 memcpy (s->strips[i].v4_codebook, s->strips[i-1].v4_codebook, | |
366 sizeof(s->strips[i].v4_codebook)); | |
367 memcpy (s->strips[i].v1_codebook, s->strips[i-1].v1_codebook, | |
368 sizeof(s->strips[i].v1_codebook)); | |
369 } | |
370 | |
371 result = cinepak_decode_strip (s, &s->strips[i], s->data, strip_size); | |
372 | |
373 if (result != 0) | |
374 return result; | |
375 | |
376 s->data += strip_size; | |
377 y0 = s->strips[i].y2; | |
378 } | |
379 return 0; | |
380 } | |
381 | |
382 static int cinepak_decode_init(AVCodecContext *avctx) | |
383 { | |
384 CinepakContext *s = (CinepakContext *)avctx->priv_data; | |
385 /* | |
386 int i; | |
387 unsigned char r, g, b; | |
388 unsigned char *raw_palette; | |
389 unsigned int *palette32; | |
390 */ | |
391 | |
392 s->avctx = avctx; | |
393 | |
394 // check for paletted data | |
395 s->palette_video = 0; | |
396 | |
397 | |
398 avctx->pix_fmt = PIX_FMT_YUV420P; | |
399 avctx->has_b_frames = 0; | |
400 dsputil_init(&s->dsp, avctx); | |
401 | |
402 s->frame.data[0] = s->prev_frame.data[0] = NULL; | |
403 | |
404 return 0; | |
405 } | |
406 | |
407 static int cinepak_decode_frame(AVCodecContext *avctx, | |
408 void *data, int *data_size, | |
409 uint8_t *buf, int buf_size) | |
410 { | |
411 CinepakContext *s = (CinepakContext *)avctx->priv_data; | |
412 | |
413 s->data = buf; | |
414 s->size = buf_size; | |
415 | |
416 if (avctx->get_buffer(avctx, &s->frame)) { | |
417 printf (" Cinepak: get_buffer() failed\n"); | |
418 return -1; | |
419 } | |
420 | |
421 cinepak_decode(s); | |
422 | |
423 if (s->prev_frame.data[0]) | |
424 avctx->release_buffer(avctx, &s->prev_frame); | |
425 | |
426 /* shuffle frames */ | |
427 s->prev_frame = s->frame; | |
428 | |
429 *data_size = sizeof(AVFrame); | |
430 *(AVFrame*)data = s->frame; | |
431 | |
432 /* report that the buffer was completely consumed */ | |
433 return buf_size; | |
434 } | |
435 | |
436 static int cinepak_decode_end(AVCodecContext *avctx) | |
437 { | |
438 CinepakContext *s = (CinepakContext *)avctx->priv_data; | |
439 | |
440 if (s->prev_frame.data[0]) | |
441 avctx->release_buffer(avctx, &s->prev_frame); | |
442 | |
443 return 0; | |
444 } | |
445 | |
446 AVCodec cinepak_decoder = { | |
447 "cinepak", | |
448 CODEC_TYPE_VIDEO, | |
449 CODEC_ID_CINEPAK, | |
450 sizeof(CinepakContext), | |
451 cinepak_decode_init, | |
452 NULL, | |
453 cinepak_decode_end, | |
454 cinepak_decode_frame, | |
455 CODEC_CAP_DR1, | |
456 }; |