blob: b97032ff7e88e8152ab16a5f31072a00cf24d4a6
1 | /* |
2 | * Sierra VMD video decoder |
3 | * Copyright (c) 2004 The FFmpeg Project |
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 Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ |
21 | |
22 | /** |
23 | * @file |
24 | * Sierra VMD video decoder |
25 | * by Vladimir "VAG" Gneushev (vagsoft at mail.ru) |
26 | * for more information on the Sierra VMD format, visit: |
27 | * http://www.pcisys.net/~melanson/codecs/ |
28 | * |
29 | * The video decoder outputs PAL8 colorspace data. The decoder expects |
30 | * a 0x330-byte VMD file header to be transmitted via extradata during |
31 | * codec initialization. Each encoded frame that is sent to this decoder |
32 | * is expected to be prepended with the appropriate 16-byte frame |
33 | * information record from the VMD file. |
34 | */ |
35 | |
36 | #include <string.h> |
37 | |
38 | #include "libavutil/common.h" |
39 | #include "libavutil/intreadwrite.h" |
40 | |
41 | #include "avcodec.h" |
42 | #include "internal.h" |
43 | #include "bytestream.h" |
44 | |
45 | #define VMD_HEADER_SIZE 0x330 |
46 | #define PALETTE_COUNT 256 |
47 | |
48 | typedef struct VmdVideoContext { |
49 | |
50 | AVCodecContext *avctx; |
51 | AVFrame *prev_frame; |
52 | |
53 | const unsigned char *buf; |
54 | int size; |
55 | |
56 | unsigned char palette[PALETTE_COUNT * 4]; |
57 | unsigned char *unpack_buffer; |
58 | int unpack_buffer_size; |
59 | |
60 | int x_off, y_off; |
61 | } VmdVideoContext; |
62 | |
63 | #define QUEUE_SIZE 0x1000 |
64 | #define QUEUE_MASK 0x0FFF |
65 | |
66 | static int lz_unpack(const unsigned char *src, int src_len, |
67 | unsigned char *dest, int dest_len) |
68 | { |
69 | unsigned char *d; |
70 | unsigned char *d_end; |
71 | unsigned char queue[QUEUE_SIZE]; |
72 | unsigned int qpos; |
73 | unsigned int dataleft; |
74 | unsigned int chainofs; |
75 | unsigned int chainlen; |
76 | unsigned int speclen; |
77 | unsigned char tag; |
78 | unsigned int i, j; |
79 | GetByteContext gb; |
80 | |
81 | bytestream2_init(&gb, src, src_len); |
82 | d = dest; |
83 | d_end = d + dest_len; |
84 | dataleft = bytestream2_get_le32(&gb); |
85 | memset(queue, 0x20, QUEUE_SIZE); |
86 | if (bytestream2_get_bytes_left(&gb) < 4) |
87 | return AVERROR_INVALIDDATA; |
88 | if (bytestream2_peek_le32(&gb) == 0x56781234) { |
89 | bytestream2_skipu(&gb, 4); |
90 | qpos = 0x111; |
91 | speclen = 0xF + 3; |
92 | } else { |
93 | qpos = 0xFEE; |
94 | speclen = 100; /* no speclen */ |
95 | } |
96 | |
97 | while (dataleft > 0 && bytestream2_get_bytes_left(&gb) > 0) { |
98 | tag = bytestream2_get_byteu(&gb); |
99 | if ((tag == 0xFF) && (dataleft > 8)) { |
100 | if (d_end - d < 8 || bytestream2_get_bytes_left(&gb) < 8) |
101 | return AVERROR_INVALIDDATA; |
102 | for (i = 0; i < 8; i++) { |
103 | queue[qpos++] = *d++ = bytestream2_get_byteu(&gb); |
104 | qpos &= QUEUE_MASK; |
105 | } |
106 | dataleft -= 8; |
107 | } else { |
108 | for (i = 0; i < 8; i++) { |
109 | if (dataleft == 0) |
110 | break; |
111 | if (tag & 0x01) { |
112 | if (d_end - d < 1 || bytestream2_get_bytes_left(&gb) < 1) |
113 | return AVERROR_INVALIDDATA; |
114 | queue[qpos++] = *d++ = bytestream2_get_byteu(&gb); |
115 | qpos &= QUEUE_MASK; |
116 | dataleft--; |
117 | } else { |
118 | chainofs = bytestream2_get_byte(&gb); |
119 | chainofs |= ((bytestream2_peek_byte(&gb) & 0xF0) << 4); |
120 | chainlen = (bytestream2_get_byte(&gb) & 0x0F) + 3; |
121 | if (chainlen == speclen) { |
122 | chainlen = bytestream2_get_byte(&gb) + 0xF + 3; |
123 | } |
124 | if (d_end - d < chainlen) |
125 | return AVERROR_INVALIDDATA; |
126 | for (j = 0; j < chainlen; j++) { |
127 | *d = queue[chainofs++ & QUEUE_MASK]; |
128 | queue[qpos++] = *d++; |
129 | qpos &= QUEUE_MASK; |
130 | } |
131 | dataleft -= chainlen; |
132 | } |
133 | tag >>= 1; |
134 | } |
135 | } |
136 | } |
137 | return d - dest; |
138 | } |
139 | static int rle_unpack(const unsigned char *src, unsigned char *dest, |
140 | int src_count, int src_size, int dest_len) |
141 | { |
142 | unsigned char *pd; |
143 | int i, l, used = 0; |
144 | unsigned char *dest_end = dest + dest_len; |
145 | GetByteContext gb; |
146 | uint16_t run_val; |
147 | |
148 | bytestream2_init(&gb, src, src_size); |
149 | pd = dest; |
150 | if (src_count & 1) { |
151 | if (bytestream2_get_bytes_left(&gb) < 1) |
152 | return 0; |
153 | *pd++ = bytestream2_get_byteu(&gb); |
154 | used++; |
155 | } |
156 | |
157 | do { |
158 | if (bytestream2_get_bytes_left(&gb) < 1) |
159 | break; |
160 | l = bytestream2_get_byteu(&gb); |
161 | if (l & 0x80) { |
162 | l = (l & 0x7F) * 2; |
163 | if (dest_end - pd < l || bytestream2_get_bytes_left(&gb) < l) |
164 | return bytestream2_tell(&gb); |
165 | bytestream2_get_bufferu(&gb, pd, l); |
166 | pd += l; |
167 | } else { |
168 | if (dest_end - pd < 2*l || bytestream2_get_bytes_left(&gb) < 2) |
169 | return bytestream2_tell(&gb); |
170 | run_val = bytestream2_get_ne16(&gb); |
171 | for (i = 0; i < l; i++) { |
172 | AV_WN16(pd, run_val); |
173 | pd += 2; |
174 | } |
175 | l *= 2; |
176 | } |
177 | used += l; |
178 | } while (used < src_count); |
179 | |
180 | return bytestream2_tell(&gb); |
181 | } |
182 | |
183 | static int vmd_decode(VmdVideoContext *s, AVFrame *frame) |
184 | { |
185 | int i; |
186 | unsigned int *palette32; |
187 | unsigned char r, g, b; |
188 | |
189 | GetByteContext gb; |
190 | |
191 | unsigned char meth; |
192 | unsigned char *dp; /* pointer to current frame */ |
193 | unsigned char *pp; /* pointer to previous frame */ |
194 | unsigned char len; |
195 | int ofs; |
196 | |
197 | int frame_x, frame_y; |
198 | int frame_width, frame_height; |
199 | |
200 | frame_x = AV_RL16(&s->buf[6]); |
201 | frame_y = AV_RL16(&s->buf[8]); |
202 | frame_width = AV_RL16(&s->buf[10]) - frame_x + 1; |
203 | frame_height = AV_RL16(&s->buf[12]) - frame_y + 1; |
204 | |
205 | if ((frame_width == s->avctx->width && frame_height == s->avctx->height) && |
206 | (frame_x || frame_y)) { |
207 | |
208 | s->x_off = frame_x; |
209 | s->y_off = frame_y; |
210 | } |
211 | frame_x -= s->x_off; |
212 | frame_y -= s->y_off; |
213 | |
214 | if (frame_x < 0 || frame_width < 0 || |
215 | frame_x >= s->avctx->width || |
216 | frame_width > s->avctx->width || |
217 | frame_x + frame_width > s->avctx->width) { |
218 | av_log(s->avctx, AV_LOG_ERROR, |
219 | "Invalid horizontal range %d-%d\n", |
220 | frame_x, frame_width); |
221 | return AVERROR_INVALIDDATA; |
222 | } |
223 | if (frame_y < 0 || frame_height < 0 || |
224 | frame_y >= s->avctx->height || |
225 | frame_height > s->avctx->height || |
226 | frame_y + frame_height > s->avctx->height) { |
227 | av_log(s->avctx, AV_LOG_ERROR, |
228 | "Invalid vertical range %d-%d\n", |
229 | frame_x, frame_width); |
230 | return AVERROR_INVALIDDATA; |
231 | } |
232 | |
233 | /* if only a certain region will be updated, copy the entire previous |
234 | * frame before the decode */ |
235 | if (s->prev_frame->data[0] && |
236 | (frame_x || frame_y || (frame_width != s->avctx->width) || |
237 | (frame_height != s->avctx->height))) { |
238 | |
239 | memcpy(frame->data[0], s->prev_frame->data[0], |
240 | s->avctx->height * frame->linesize[0]); |
241 | } |
242 | |
243 | /* check if there is a new palette */ |
244 | bytestream2_init(&gb, s->buf + 16, s->size - 16); |
245 | if (s->buf[15] & 0x02) { |
246 | bytestream2_skip(&gb, 2); |
247 | palette32 = (unsigned int *)s->palette; |
248 | if (bytestream2_get_bytes_left(&gb) >= PALETTE_COUNT * 3) { |
249 | for (i = 0; i < PALETTE_COUNT; i++) { |
250 | r = bytestream2_get_byteu(&gb) * 4; |
251 | g = bytestream2_get_byteu(&gb) * 4; |
252 | b = bytestream2_get_byteu(&gb) * 4; |
253 | palette32[i] = 0xFFU << 24 | (r << 16) | (g << 8) | (b); |
254 | palette32[i] |= palette32[i] >> 6 & 0x30303; |
255 | } |
256 | } else { |
257 | av_log(s->avctx, AV_LOG_ERROR, "Incomplete palette\n"); |
258 | return AVERROR_INVALIDDATA; |
259 | } |
260 | } |
261 | |
262 | if (!s->size) |
263 | return 0; |
264 | |
265 | /* originally UnpackFrame in VAG's code */ |
266 | if (bytestream2_get_bytes_left(&gb) < 1) |
267 | return AVERROR_INVALIDDATA; |
268 | meth = bytestream2_get_byteu(&gb); |
269 | if (meth & 0x80) { |
270 | int size; |
271 | if (!s->unpack_buffer_size) { |
272 | av_log(s->avctx, AV_LOG_ERROR, |
273 | "Trying to unpack LZ-compressed frame with no LZ buffer\n"); |
274 | return AVERROR_INVALIDDATA; |
275 | } |
276 | size = lz_unpack(gb.buffer, bytestream2_get_bytes_left(&gb), |
277 | s->unpack_buffer, s->unpack_buffer_size); |
278 | if (size < 0) |
279 | return size; |
280 | meth &= 0x7F; |
281 | bytestream2_init(&gb, s->unpack_buffer, size); |
282 | } |
283 | |
284 | dp = &frame->data[0][frame_y * frame->linesize[0] + frame_x]; |
285 | pp = &s->prev_frame->data[0][frame_y * s->prev_frame->linesize[0] + frame_x]; |
286 | switch (meth) { |
287 | case 1: |
288 | for (i = 0; i < frame_height; i++) { |
289 | ofs = 0; |
290 | do { |
291 | len = bytestream2_get_byte(&gb); |
292 | if (len & 0x80) { |
293 | len = (len & 0x7F) + 1; |
294 | if (ofs + len > frame_width || |
295 | bytestream2_get_bytes_left(&gb) < len) |
296 | return AVERROR_INVALIDDATA; |
297 | bytestream2_get_bufferu(&gb, &dp[ofs], len); |
298 | ofs += len; |
299 | } else { |
300 | /* interframe pixel copy */ |
301 | if (ofs + len + 1 > frame_width || !s->prev_frame->data[0]) |
302 | return AVERROR_INVALIDDATA; |
303 | memcpy(&dp[ofs], &pp[ofs], len + 1); |
304 | ofs += len + 1; |
305 | } |
306 | } while (ofs < frame_width); |
307 | if (ofs > frame_width) { |
308 | av_log(s->avctx, AV_LOG_ERROR, |
309 | "offset > width (%d > %d)\n", |
310 | ofs, frame_width); |
311 | return AVERROR_INVALIDDATA; |
312 | } |
313 | dp += frame->linesize[0]; |
314 | pp += s->prev_frame->linesize[0]; |
315 | } |
316 | break; |
317 | |
318 | case 2: |
319 | for (i = 0; i < frame_height; i++) { |
320 | bytestream2_get_buffer(&gb, dp, frame_width); |
321 | dp += frame->linesize[0]; |
322 | pp += s->prev_frame->linesize[0]; |
323 | } |
324 | break; |
325 | |
326 | case 3: |
327 | for (i = 0; i < frame_height; i++) { |
328 | ofs = 0; |
329 | do { |
330 | len = bytestream2_get_byte(&gb); |
331 | if (len & 0x80) { |
332 | len = (len & 0x7F) + 1; |
333 | if (bytestream2_peek_byte(&gb) == 0xFF) { |
334 | int slen = len; |
335 | bytestream2_get_byte(&gb); |
336 | len = rle_unpack(gb.buffer, &dp[ofs], |
337 | len, bytestream2_get_bytes_left(&gb), |
338 | frame_width - ofs); |
339 | ofs += slen; |
340 | bytestream2_skip(&gb, len); |
341 | } else { |
342 | if (ofs + len > frame_width || |
343 | bytestream2_get_bytes_left(&gb) < len) |
344 | return AVERROR_INVALIDDATA; |
345 | bytestream2_get_buffer(&gb, &dp[ofs], len); |
346 | ofs += len; |
347 | } |
348 | } else { |
349 | /* interframe pixel copy */ |
350 | if (ofs + len + 1 > frame_width || !s->prev_frame->data[0]) |
351 | return AVERROR_INVALIDDATA; |
352 | memcpy(&dp[ofs], &pp[ofs], len + 1); |
353 | ofs += len + 1; |
354 | } |
355 | } while (ofs < frame_width); |
356 | if (ofs > frame_width) { |
357 | av_log(s->avctx, AV_LOG_ERROR, |
358 | "offset > width (%d > %d)\n", |
359 | ofs, frame_width); |
360 | return AVERROR_INVALIDDATA; |
361 | } |
362 | dp += frame->linesize[0]; |
363 | pp += s->prev_frame->linesize[0]; |
364 | } |
365 | break; |
366 | } |
367 | return 0; |
368 | } |
369 | |
370 | static av_cold int vmdvideo_decode_end(AVCodecContext *avctx) |
371 | { |
372 | VmdVideoContext *s = avctx->priv_data; |
373 | |
374 | av_frame_free(&s->prev_frame); |
375 | av_freep(&s->unpack_buffer); |
376 | s->unpack_buffer_size = 0; |
377 | |
378 | return 0; |
379 | } |
380 | |
381 | static av_cold int vmdvideo_decode_init(AVCodecContext *avctx) |
382 | { |
383 | VmdVideoContext *s = avctx->priv_data; |
384 | int i; |
385 | unsigned int *palette32; |
386 | int palette_index = 0; |
387 | unsigned char r, g, b; |
388 | unsigned char *vmd_header; |
389 | unsigned char *raw_palette; |
390 | |
391 | s->avctx = avctx; |
392 | avctx->pix_fmt = AV_PIX_FMT_PAL8; |
393 | |
394 | /* make sure the VMD header made it */ |
395 | if (s->avctx->extradata_size != VMD_HEADER_SIZE) { |
396 | av_log(s->avctx, AV_LOG_ERROR, "expected extradata size of %d\n", |
397 | VMD_HEADER_SIZE); |
398 | return AVERROR_INVALIDDATA; |
399 | } |
400 | vmd_header = (unsigned char *)avctx->extradata; |
401 | |
402 | s->unpack_buffer_size = AV_RL32(&vmd_header[800]); |
403 | if (s->unpack_buffer_size) { |
404 | s->unpack_buffer = av_malloc(s->unpack_buffer_size); |
405 | if (!s->unpack_buffer) |
406 | return AVERROR(ENOMEM); |
407 | } |
408 | |
409 | /* load up the initial palette */ |
410 | raw_palette = &vmd_header[28]; |
411 | palette32 = (unsigned int *)s->palette; |
412 | for (i = 0; i < PALETTE_COUNT; i++) { |
413 | r = raw_palette[palette_index++] * 4; |
414 | g = raw_palette[palette_index++] * 4; |
415 | b = raw_palette[palette_index++] * 4; |
416 | palette32[i] = 0xFFU << 24 | (r << 16) | (g << 8) | (b); |
417 | palette32[i] |= palette32[i] >> 6 & 0x30303; |
418 | } |
419 | |
420 | s->prev_frame = av_frame_alloc(); |
421 | if (!s->prev_frame) { |
422 | vmdvideo_decode_end(avctx); |
423 | return AVERROR(ENOMEM); |
424 | } |
425 | |
426 | return 0; |
427 | } |
428 | |
429 | static int vmdvideo_decode_frame(AVCodecContext *avctx, |
430 | void *data, int *got_frame, |
431 | AVPacket *avpkt) |
432 | { |
433 | const uint8_t *buf = avpkt->data; |
434 | int buf_size = avpkt->size; |
435 | VmdVideoContext *s = avctx->priv_data; |
436 | AVFrame *frame = data; |
437 | int ret; |
438 | |
439 | s->buf = buf; |
440 | s->size = buf_size; |
441 | |
442 | if (buf_size < 16) |
443 | return AVERROR_INVALIDDATA; |
444 | |
445 | if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0) |
446 | return ret; |
447 | |
448 | if ((ret = vmd_decode(s, frame)) < 0) |
449 | return ret; |
450 | |
451 | /* make the palette available on the way out */ |
452 | memcpy(frame->data[1], s->palette, PALETTE_COUNT * 4); |
453 | |
454 | /* shuffle frames */ |
455 | av_frame_unref(s->prev_frame); |
456 | if ((ret = av_frame_ref(s->prev_frame, frame)) < 0) |
457 | return ret; |
458 | |
459 | *got_frame = 1; |
460 | |
461 | /* report that the buffer was completely consumed */ |
462 | return buf_size; |
463 | } |
464 | |
465 | AVCodec ff_vmdvideo_decoder = { |
466 | .name = "vmdvideo", |
467 | .long_name = NULL_IF_CONFIG_SMALL("Sierra VMD video"), |
468 | .type = AVMEDIA_TYPE_VIDEO, |
469 | .id = AV_CODEC_ID_VMDVIDEO, |
470 | .priv_data_size = sizeof(VmdVideoContext), |
471 | .init = vmdvideo_decode_init, |
472 | .close = vmdvideo_decode_end, |
473 | .decode = vmdvideo_decode_frame, |
474 | .capabilities = AV_CODEC_CAP_DR1, |
475 | }; |
476 |