vqavideo.c
Go to the documentation of this file.
1 /*
2  * Westwood Studios VQA Video Decoder
3  * Copyright (C) 2003 the ffmpeg project
4  *
5  * This file is part of Libav.
6  *
7  * Libav 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  * Libav 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 Libav; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 
70 #include "libavutil/intreadwrite.h"
71 #include "libavutil/imgutils.h"
72 #include "avcodec.h"
73 #include "bytestream.h"
74 #include "internal.h"
75 
76 #define PALETTE_COUNT 256
77 #define VQA_HEADER_SIZE 0x2A
78 
79 /* allocate the maximum vector space, regardless of the file version:
80  * (0xFF00 codebook vectors + 0x100 solid pixel vectors) * (4x4 pixels/block) */
81 #define MAX_CODEBOOK_VECTORS 0xFF00
82 #define SOLID_PIXEL_VECTORS 0x100
83 #define MAX_VECTORS (MAX_CODEBOOK_VECTORS + SOLID_PIXEL_VECTORS)
84 #define MAX_CODEBOOK_SIZE (MAX_VECTORS * 4 * 4)
85 
86 #define CBF0_TAG MKBETAG('C', 'B', 'F', '0')
87 #define CBFZ_TAG MKBETAG('C', 'B', 'F', 'Z')
88 #define CBP0_TAG MKBETAG('C', 'B', 'P', '0')
89 #define CBPZ_TAG MKBETAG('C', 'B', 'P', 'Z')
90 #define CPL0_TAG MKBETAG('C', 'P', 'L', '0')
91 #define CPLZ_TAG MKBETAG('C', 'P', 'L', 'Z')
92 #define VPTZ_TAG MKBETAG('V', 'P', 'T', 'Z')
93 
94 typedef struct VqaContext {
95 
99 
101 
102  int width; /* width of a frame */
103  int height; /* height of a frame */
104  int vector_width; /* width of individual vector */
105  int vector_height; /* height of individual vector */
106  int vqa_version; /* this should be either 1, 2 or 3 */
107 
108  unsigned char *codebook; /* the current codebook */
110  unsigned char *next_codebook_buffer; /* accumulator for next codebook */
112 
113  unsigned char *decode_buffer;
115 
116  /* number of frames to go before replacing codebook */
119 
120 } VqaContext;
121 
123 {
124  VqaContext *s = avctx->priv_data;
125  int i, j, codebook_index;
126 
127  s->avctx = avctx;
128  avctx->pix_fmt = AV_PIX_FMT_PAL8;
129 
130  /* make sure the extradata made it */
131  if (s->avctx->extradata_size != VQA_HEADER_SIZE) {
132  av_log(s->avctx, AV_LOG_ERROR, " VQA video: expected extradata size of %d\n", VQA_HEADER_SIZE);
133  return -1;
134  }
135 
136  /* load up the VQA parameters from the header */
137  s->vqa_version = s->avctx->extradata[0];
138  s->width = AV_RL16(&s->avctx->extradata[6]);
139  s->height = AV_RL16(&s->avctx->extradata[8]);
140  if(av_image_check_size(s->width, s->height, 0, avctx)){
141  s->width= s->height= 0;
142  return -1;
143  }
144  s->vector_width = s->avctx->extradata[10];
145  s->vector_height = s->avctx->extradata[11];
147 
148  /* the vector dimensions have to meet very stringent requirements */
149  if ((s->vector_width != 4) ||
150  ((s->vector_height != 2) && (s->vector_height != 4))) {
151  /* return without further initialization */
152  return -1;
153  }
154 
155  if (s->width & (s->vector_width - 1) ||
156  s->height & (s->vector_height - 1)) {
157  av_log(avctx, AV_LOG_ERROR, "Image size not multiple of block size\n");
158  return AVERROR_INVALIDDATA;
159  }
160 
161  /* allocate codebooks */
164  if (!s->codebook)
165  goto fail;
167  if (!s->next_codebook_buffer)
168  goto fail;
169 
170  /* allocate decode buffer */
171  s->decode_buffer_size = (s->width / s->vector_width) *
172  (s->height / s->vector_height) * 2;
174  if (!s->decode_buffer)
175  goto fail;
176 
177  /* initialize the solid-color vectors */
178  if (s->vector_height == 4) {
179  codebook_index = 0xFF00 * 16;
180  for (i = 0; i < 256; i++)
181  for (j = 0; j < 16; j++)
182  s->codebook[codebook_index++] = i;
183  } else {
184  codebook_index = 0xF00 * 8;
185  for (i = 0; i < 256; i++)
186  for (j = 0; j < 8; j++)
187  s->codebook[codebook_index++] = i;
188  }
190 
191  s->frame.data[0] = NULL;
192 
193  return 0;
194 fail:
195  av_freep(&s->codebook);
197  av_freep(&s->decode_buffer);
198  return AVERROR(ENOMEM);
199 }
200 
201 #define CHECK_COUNT() \
202  if (dest_index + count > dest_size) { \
203  av_log(NULL, AV_LOG_ERROR, " VQA video: decode_format80 problem: next op would overflow dest_index\n"); \
204  av_log(NULL, AV_LOG_ERROR, " VQA video: current dest_index = %d, count = %d, dest_size = %d\n", \
205  dest_index, count, dest_size); \
206  return AVERROR_INVALIDDATA; \
207  }
208 
209 #define CHECK_COPY(idx) \
210  if (idx < 0 || idx + count > dest_size) { \
211  av_log(NULL, AV_LOG_ERROR, " VQA video: decode_format80 problem: next op would overflow dest_index\n"); \
212  av_log(NULL, AV_LOG_ERROR, " VQA video: current src_pos = %d, count = %d, dest_size = %d\n", \
213  src_pos, count, dest_size); \
214  return AVERROR_INVALIDDATA; \
215  }
216 
217 
218 static int decode_format80(GetByteContext *gb, int src_size,
219  unsigned char *dest, int dest_size, int check_size) {
220 
221  int dest_index = 0;
222  int count, opcode, start;
223  int src_pos;
224  unsigned char color;
225  int i;
226 
227  start = bytestream2_tell(gb);
228  while (bytestream2_tell(gb) - start < src_size) {
229  opcode = bytestream2_get_byte(gb);
230  av_dlog(NULL, " opcode %02X: ", opcode);
231 
232  /* 0x80 means that frame is finished */
233  if (opcode == 0x80)
234  return 0;
235 
236  if (dest_index >= dest_size) {
237  av_log(NULL, AV_LOG_ERROR, " VQA video: decode_format80 problem: dest_index (%d) exceeded dest_size (%d)\n",
238  dest_index, dest_size);
239  return AVERROR_INVALIDDATA;
240  }
241 
242  if (opcode == 0xFF) {
243 
244  count = bytestream2_get_le16(gb);
245  src_pos = bytestream2_get_le16(gb);
246  av_dlog(NULL, "(1) copy %X bytes from absolute pos %X\n", count, src_pos);
247  CHECK_COUNT();
248  CHECK_COPY(src_pos);
249  for (i = 0; i < count; i++)
250  dest[dest_index + i] = dest[src_pos + i];
251  dest_index += count;
252 
253  } else if (opcode == 0xFE) {
254 
255  count = bytestream2_get_le16(gb);
256  color = bytestream2_get_byte(gb);
257  av_dlog(NULL, "(2) set %X bytes to %02X\n", count, color);
258  CHECK_COUNT();
259  memset(&dest[dest_index], color, count);
260  dest_index += count;
261 
262  } else if ((opcode & 0xC0) == 0xC0) {
263 
264  count = (opcode & 0x3F) + 3;
265  src_pos = bytestream2_get_le16(gb);
266  av_dlog(NULL, "(3) copy %X bytes from absolute pos %X\n", count, src_pos);
267  CHECK_COUNT();
268  CHECK_COPY(src_pos);
269  for (i = 0; i < count; i++)
270  dest[dest_index + i] = dest[src_pos + i];
271  dest_index += count;
272 
273  } else if (opcode > 0x80) {
274 
275  count = opcode & 0x3F;
276  av_dlog(NULL, "(4) copy %X bytes from source to dest\n", count);
277  CHECK_COUNT();
278  bytestream2_get_buffer(gb, &dest[dest_index], count);
279  dest_index += count;
280 
281  } else {
282 
283  count = ((opcode & 0x70) >> 4) + 3;
284  src_pos = bytestream2_get_byte(gb) | ((opcode & 0x0F) << 8);
285  av_dlog(NULL, "(5) copy %X bytes from relpos %X\n", count, src_pos);
286  CHECK_COUNT();
287  CHECK_COPY(dest_index - src_pos);
288  for (i = 0; i < count; i++)
289  dest[dest_index + i] = dest[dest_index - src_pos + i];
290  dest_index += count;
291  }
292  }
293 
294  /* validate that the entire destination buffer was filled; this is
295  * important for decoding frame maps since each vector needs to have a
296  * codebook entry; it is not important for compressed codebooks because
297  * not every entry needs to be filled */
298  if (check_size)
299  if (dest_index < dest_size)
300  av_log(NULL, AV_LOG_ERROR, " VQA video: decode_format80 problem: decode finished with dest_index (%d) < dest_size (%d)\n",
301  dest_index, dest_size);
302 
303  return 0; // let's display what we decoded anyway
304 }
305 
307 {
308  unsigned int chunk_type;
309  unsigned int chunk_size;
310  int byte_skip;
311  unsigned int index = 0;
312  int i;
313  unsigned char r, g, b;
314  int index_shift;
315  int res;
316 
317  int cbf0_chunk = -1;
318  int cbfz_chunk = -1;
319  int cbp0_chunk = -1;
320  int cbpz_chunk = -1;
321  int cpl0_chunk = -1;
322  int cplz_chunk = -1;
323  int vptz_chunk = -1;
324 
325  int x, y;
326  int lines = 0;
327  int pixel_ptr;
328  int vector_index = 0;
329  int lobyte = 0;
330  int hibyte = 0;
331  int lobytes = 0;
332  int hibytes = s->decode_buffer_size / 2;
333 
334  /* first, traverse through the frame and find the subchunks */
335  while (bytestream2_get_bytes_left(&s->gb) >= 8) {
336 
337  chunk_type = bytestream2_get_be32u(&s->gb);
338  index = bytestream2_tell(&s->gb);
339  chunk_size = bytestream2_get_be32u(&s->gb);
340 
341  switch (chunk_type) {
342 
343  case CBF0_TAG:
344  cbf0_chunk = index;
345  break;
346 
347  case CBFZ_TAG:
348  cbfz_chunk = index;
349  break;
350 
351  case CBP0_TAG:
352  cbp0_chunk = index;
353  break;
354 
355  case CBPZ_TAG:
356  cbpz_chunk = index;
357  break;
358 
359  case CPL0_TAG:
360  cpl0_chunk = index;
361  break;
362 
363  case CPLZ_TAG:
364  cplz_chunk = index;
365  break;
366 
367  case VPTZ_TAG:
368  vptz_chunk = index;
369  break;
370 
371  default:
372  av_log(s->avctx, AV_LOG_ERROR, " VQA video: Found unknown chunk type: %c%c%c%c (%08X)\n",
373  (chunk_type >> 24) & 0xFF,
374  (chunk_type >> 16) & 0xFF,
375  (chunk_type >> 8) & 0xFF,
376  (chunk_type >> 0) & 0xFF,
377  chunk_type);
378  break;
379  }
380 
381  byte_skip = chunk_size & 0x01;
382  bytestream2_skip(&s->gb, chunk_size + byte_skip);
383  }
384 
385  /* next, deal with the palette */
386  if ((cpl0_chunk != -1) && (cplz_chunk != -1)) {
387 
388  /* a chunk should not have both chunk types */
389  av_log(s->avctx, AV_LOG_ERROR, " VQA video: problem: found both CPL0 and CPLZ chunks\n");
390  return AVERROR_INVALIDDATA;
391  }
392 
393  /* decompress the palette chunk */
394  if (cplz_chunk != -1) {
395 
396 /* yet to be handled */
397 
398  }
399 
400  /* convert the RGB palette into the machine's endian format */
401  if (cpl0_chunk != -1) {
402 
403  bytestream2_seek(&s->gb, cpl0_chunk, SEEK_SET);
404  chunk_size = bytestream2_get_be32(&s->gb);
405  /* sanity check the palette size */
406  if (chunk_size / 3 > 256 || chunk_size > bytestream2_get_bytes_left(&s->gb)) {
407  av_log(s->avctx, AV_LOG_ERROR, " VQA video: problem: found a palette chunk with %d colors\n",
408  chunk_size / 3);
409  return AVERROR_INVALIDDATA;
410  }
411  for (i = 0; i < chunk_size / 3; i++) {
412  /* scale by 4 to transform 6-bit palette -> 8-bit */
413  r = bytestream2_get_byteu(&s->gb) * 4;
414  g = bytestream2_get_byteu(&s->gb) * 4;
415  b = bytestream2_get_byteu(&s->gb) * 4;
416  s->palette[i] = (r << 16) | (g << 8) | (b);
417  }
418  }
419 
420  /* next, look for a full codebook */
421  if ((cbf0_chunk != -1) && (cbfz_chunk != -1)) {
422 
423  /* a chunk should not have both chunk types */
424  av_log(s->avctx, AV_LOG_ERROR, " VQA video: problem: found both CBF0 and CBFZ chunks\n");
425  return AVERROR_INVALIDDATA;
426  }
427 
428  /* decompress the full codebook chunk */
429  if (cbfz_chunk != -1) {
430 
431  bytestream2_seek(&s->gb, cbfz_chunk, SEEK_SET);
432  chunk_size = bytestream2_get_be32(&s->gb);
433  if ((res = decode_format80(&s->gb, chunk_size, s->codebook,
434  s->codebook_size, 0)) < 0)
435  return res;
436  }
437 
438  /* copy a full codebook */
439  if (cbf0_chunk != -1) {
440 
441  bytestream2_seek(&s->gb, cbf0_chunk, SEEK_SET);
442  chunk_size = bytestream2_get_be32(&s->gb);
443  /* sanity check the full codebook size */
444  if (chunk_size > MAX_CODEBOOK_SIZE) {
445  av_log(s->avctx, AV_LOG_ERROR, " VQA video: problem: CBF0 chunk too large (0x%X bytes)\n",
446  chunk_size);
447  return AVERROR_INVALIDDATA;
448  }
449 
450  bytestream2_get_buffer(&s->gb, s->codebook, chunk_size);
451  }
452 
453  /* decode the frame */
454  if (vptz_chunk == -1) {
455 
456  /* something is wrong if there is no VPTZ chunk */
457  av_log(s->avctx, AV_LOG_ERROR, " VQA video: problem: no VPTZ chunk found\n");
458  return AVERROR_INVALIDDATA;
459  }
460 
461  bytestream2_seek(&s->gb, vptz_chunk, SEEK_SET);
462  chunk_size = bytestream2_get_be32(&s->gb);
463  if ((res = decode_format80(&s->gb, chunk_size,
464  s->decode_buffer, s->decode_buffer_size, 1)) < 0)
465  return res;
466 
467  /* render the final PAL8 frame */
468  if (s->vector_height == 4)
469  index_shift = 4;
470  else
471  index_shift = 3;
472  for (y = 0; y < s->height; y += s->vector_height) {
473  for (x = 0; x < s->width; x += 4, lobytes++, hibytes++) {
474  pixel_ptr = y * s->frame.linesize[0] + x;
475 
476  /* get the vector index, the method for which varies according to
477  * VQA file version */
478  switch (s->vqa_version) {
479 
480  case 1:
481  lobyte = s->decode_buffer[lobytes * 2];
482  hibyte = s->decode_buffer[(lobytes * 2) + 1];
483  vector_index = ((hibyte << 8) | lobyte) >> 3;
484  vector_index <<= index_shift;
485  lines = s->vector_height;
486  /* uniform color fill - a quick hack */
487  if (hibyte == 0xFF) {
488  while (lines--) {
489  s->frame.data[0][pixel_ptr + 0] = 255 - lobyte;
490  s->frame.data[0][pixel_ptr + 1] = 255 - lobyte;
491  s->frame.data[0][pixel_ptr + 2] = 255 - lobyte;
492  s->frame.data[0][pixel_ptr + 3] = 255 - lobyte;
493  pixel_ptr += s->frame.linesize[0];
494  }
495  lines=0;
496  }
497  break;
498 
499  case 2:
500  lobyte = s->decode_buffer[lobytes];
501  hibyte = s->decode_buffer[hibytes];
502  vector_index = (hibyte << 8) | lobyte;
503  vector_index <<= index_shift;
504  lines = s->vector_height;
505  break;
506 
507  case 3:
508 /* not implemented yet */
509  lines = 0;
510  break;
511  }
512 
513  while (lines--) {
514  s->frame.data[0][pixel_ptr + 0] = s->codebook[vector_index++];
515  s->frame.data[0][pixel_ptr + 1] = s->codebook[vector_index++];
516  s->frame.data[0][pixel_ptr + 2] = s->codebook[vector_index++];
517  s->frame.data[0][pixel_ptr + 3] = s->codebook[vector_index++];
518  pixel_ptr += s->frame.linesize[0];
519  }
520  }
521  }
522 
523  /* handle partial codebook */
524  if ((cbp0_chunk != -1) && (cbpz_chunk != -1)) {
525  /* a chunk should not have both chunk types */
526  av_log(s->avctx, AV_LOG_ERROR, " VQA video: problem: found both CBP0 and CBPZ chunks\n");
527  return AVERROR_INVALIDDATA;
528  }
529 
530  if (cbp0_chunk != -1) {
531 
532  bytestream2_seek(&s->gb, cbp0_chunk, SEEK_SET);
533  chunk_size = bytestream2_get_be32(&s->gb);
534 
535  if (chunk_size > MAX_CODEBOOK_SIZE - s->next_codebook_buffer_index) {
536  av_log(s->avctx, AV_LOG_ERROR, "cbp0 chunk too large (%u bytes)\n",
537  chunk_size);
538  return AVERROR_INVALIDDATA;
539  }
540 
541  /* accumulate partial codebook */
543  chunk_size);
544  s->next_codebook_buffer_index += chunk_size;
545 
546  s->partial_countdown--;
547  if (s->partial_countdown == 0) {
548 
549  /* time to replace codebook */
550  memcpy(s->codebook, s->next_codebook_buffer,
552 
553  /* reset accounting */
556  }
557  }
558 
559  if (cbpz_chunk != -1) {
560 
561  bytestream2_seek(&s->gb, cbpz_chunk, SEEK_SET);
562  chunk_size = bytestream2_get_be32(&s->gb);
563 
564  if (chunk_size > MAX_CODEBOOK_SIZE - s->next_codebook_buffer_index) {
565  av_log(s->avctx, AV_LOG_ERROR, "cbpz chunk too large (%u bytes)\n",
566  chunk_size);
567  return AVERROR_INVALIDDATA;
568  }
569 
570  /* accumulate partial codebook */
572  chunk_size);
573  s->next_codebook_buffer_index += chunk_size;
574 
575  s->partial_countdown--;
576  if (s->partial_countdown == 0) {
577  GetByteContext gb;
578 
580  /* decompress codebook */
581  if ((res = decode_format80(&gb, s->next_codebook_buffer_index,
582  s->codebook, s->codebook_size, 0)) < 0)
583  return res;
584 
585  /* reset accounting */
588  }
589  }
590 
591  return 0;
592 }
593 
595  void *data, int *got_frame,
596  AVPacket *avpkt)
597 {
598  VqaContext *s = avctx->priv_data;
599  int res;
600 
601  if (s->frame.data[0])
602  avctx->release_buffer(avctx, &s->frame);
603 
604  if (ff_get_buffer(avctx, &s->frame)) {
605  av_log(s->avctx, AV_LOG_ERROR, " VQA Video: get_buffer() failed\n");
606  return -1;
607  }
608 
609  bytestream2_init(&s->gb, avpkt->data, avpkt->size);
610  if ((res = vqa_decode_chunk(s)) < 0)
611  return res;
612 
613  /* make the palette available on the way out */
614  memcpy(s->frame.data[1], s->palette, PALETTE_COUNT * 4);
615  s->frame.palette_has_changed = 1;
616 
617  *got_frame = 1;
618  *(AVFrame*)data = s->frame;
619 
620  /* report that the buffer was completely consumed */
621  return avpkt->size;
622 }
623 
625 {
626  VqaContext *s = avctx->priv_data;
627 
628  av_freep(&s->codebook);
630  av_freep(&s->decode_buffer);
631 
632  if (s->frame.data[0])
633  avctx->release_buffer(avctx, &s->frame);
634 
635  return 0;
636 }
637 
639  .name = "vqavideo",
640  .type = AVMEDIA_TYPE_VIDEO,
641  .id = AV_CODEC_ID_WS_VQA,
642  .priv_data_size = sizeof(VqaContext),
646  .capabilities = CODEC_CAP_DR1,
647  .long_name = NULL_IF_CONFIG_SMALL("Westwood Studios VQA (Vector Quantized Animation) video"),
648 };