• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • Examples
  • File List
  • Globals

libavcodec/vqavideo.c

Go to the documentation of this file.
00001 /*
00002  * Westwood Studios VQA Video Decoder
00003  * Copyright (C) 2003 the ffmpeg project
00004  *
00005  * This file is part of Libav.
00006  *
00007  * Libav is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * Libav is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with Libav; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  */
00021 
00066 #include <stdio.h>
00067 #include <stdlib.h>
00068 #include <string.h>
00069 
00070 #include "libavutil/intreadwrite.h"
00071 #include "libavutil/imgutils.h"
00072 #include "avcodec.h"
00073 #include "bytestream.h"
00074 
00075 #define PALETTE_COUNT 256
00076 #define VQA_HEADER_SIZE 0x2A
00077 
00078 /* allocate the maximum vector space, regardless of the file version:
00079  * (0xFF00 codebook vectors + 0x100 solid pixel vectors) * (4x4 pixels/block) */
00080 #define MAX_CODEBOOK_VECTORS 0xFF00
00081 #define SOLID_PIXEL_VECTORS 0x100
00082 #define MAX_VECTORS (MAX_CODEBOOK_VECTORS + SOLID_PIXEL_VECTORS)
00083 #define MAX_CODEBOOK_SIZE (MAX_VECTORS * 4 * 4)
00084 
00085 #define CBF0_TAG MKBETAG('C', 'B', 'F', '0')
00086 #define CBFZ_TAG MKBETAG('C', 'B', 'F', 'Z')
00087 #define CBP0_TAG MKBETAG('C', 'B', 'P', '0')
00088 #define CBPZ_TAG MKBETAG('C', 'B', 'P', 'Z')
00089 #define CPL0_TAG MKBETAG('C', 'P', 'L', '0')
00090 #define CPLZ_TAG MKBETAG('C', 'P', 'L', 'Z')
00091 #define VPTZ_TAG MKBETAG('V', 'P', 'T', 'Z')
00092 
00093 typedef struct VqaContext {
00094 
00095     AVCodecContext *avctx;
00096     AVFrame frame;
00097     GetByteContext gb;
00098 
00099     uint32_t palette[PALETTE_COUNT];
00100 
00101     int width;   /* width of a frame */
00102     int height;   /* height of a frame */
00103     int vector_width;  /* width of individual vector */
00104     int vector_height;  /* height of individual vector */
00105     int vqa_version;  /* this should be either 1, 2 or 3 */
00106 
00107     unsigned char *codebook;         /* the current codebook */
00108     int codebook_size;
00109     unsigned char *next_codebook_buffer;  /* accumulator for next codebook */
00110     int next_codebook_buffer_index;
00111 
00112     unsigned char *decode_buffer;
00113     int decode_buffer_size;
00114 
00115     /* number of frames to go before replacing codebook */
00116     int partial_countdown;
00117     int partial_count;
00118 
00119 } VqaContext;
00120 
00121 static av_cold int vqa_decode_init(AVCodecContext *avctx)
00122 {
00123     VqaContext *s = avctx->priv_data;
00124     int i, j, codebook_index;
00125 
00126     s->avctx = avctx;
00127     avctx->pix_fmt = PIX_FMT_PAL8;
00128 
00129     /* make sure the extradata made it */
00130     if (s->avctx->extradata_size != VQA_HEADER_SIZE) {
00131         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: expected extradata size of %d\n", VQA_HEADER_SIZE);
00132         return -1;
00133     }
00134 
00135     /* load up the VQA parameters from the header */
00136     s->vqa_version = s->avctx->extradata[0];
00137     s->width = AV_RL16(&s->avctx->extradata[6]);
00138     s->height = AV_RL16(&s->avctx->extradata[8]);
00139     if(av_image_check_size(s->width, s->height, 0, avctx)){
00140         s->width= s->height= 0;
00141         return -1;
00142     }
00143     s->vector_width = s->avctx->extradata[10];
00144     s->vector_height = s->avctx->extradata[11];
00145     s->partial_count = s->partial_countdown = s->avctx->extradata[13];
00146 
00147     /* the vector dimensions have to meet very stringent requirements */
00148     if ((s->vector_width != 4) ||
00149         ((s->vector_height != 2) && (s->vector_height != 4))) {
00150         /* return without further initialization */
00151         return -1;
00152     }
00153 
00154     if (s->width  & (s->vector_width  - 1) ||
00155         s->height & (s->vector_height - 1)) {
00156         av_log(avctx, AV_LOG_ERROR, "Image size not multiple of block size\n");
00157         return AVERROR_INVALIDDATA;
00158     }
00159 
00160     /* allocate codebooks */
00161     s->codebook_size = MAX_CODEBOOK_SIZE;
00162     s->codebook = av_malloc(s->codebook_size);
00163     s->next_codebook_buffer = av_malloc(s->codebook_size);
00164 
00165     /* initialize the solid-color vectors */
00166     if (s->vector_height == 4) {
00167         codebook_index = 0xFF00 * 16;
00168         for (i = 0; i < 256; i++)
00169             for (j = 0; j < 16; j++)
00170                 s->codebook[codebook_index++] = i;
00171     } else {
00172         codebook_index = 0xF00 * 8;
00173         for (i = 0; i < 256; i++)
00174             for (j = 0; j < 8; j++)
00175                 s->codebook[codebook_index++] = i;
00176     }
00177     s->next_codebook_buffer_index = 0;
00178 
00179     /* allocate decode buffer */
00180     s->decode_buffer_size = (s->width / s->vector_width) *
00181         (s->height / s->vector_height) * 2;
00182     s->decode_buffer = av_malloc(s->decode_buffer_size);
00183 
00184     s->frame.data[0] = NULL;
00185 
00186     return 0;
00187 }
00188 
00189 #define CHECK_COUNT() \
00190     if (dest_index + count > dest_size) { \
00191         av_log(NULL, AV_LOG_ERROR, "  VQA video: decode_format80 problem: next op would overflow dest_index\n"); \
00192         av_log(NULL, AV_LOG_ERROR, "  VQA video: current dest_index = %d, count = %d, dest_size = %d\n", \
00193             dest_index, count, dest_size); \
00194         return AVERROR_INVALIDDATA; \
00195     }
00196 
00197 #define CHECK_COPY(idx) \
00198     if (idx < 0 || idx + count > dest_size) { \
00199         av_log(NULL, AV_LOG_ERROR, "  VQA video: decode_format80 problem: next op would overflow dest_index\n"); \
00200         av_log(NULL, AV_LOG_ERROR, "  VQA video: current src_pos = %d, count = %d, dest_size = %d\n", \
00201             src_pos, count, dest_size); \
00202         return AVERROR_INVALIDDATA; \
00203     }
00204 
00205 
00206 static int decode_format80(GetByteContext *gb, int src_size,
00207     unsigned char *dest, int dest_size, int check_size) {
00208 
00209     int dest_index = 0;
00210     int count, opcode, start;
00211     int src_pos;
00212     unsigned char color;
00213     int i;
00214 
00215     start = bytestream2_tell(gb);
00216     while (bytestream2_tell(gb) - start < src_size) {
00217         opcode = bytestream2_get_byte(gb);
00218         av_dlog(NULL, "      opcode %02X: ", opcode);
00219 
00220         /* 0x80 means that frame is finished */
00221         if (opcode == 0x80)
00222             return 0;
00223 
00224         if (dest_index >= dest_size) {
00225             av_log(NULL, AV_LOG_ERROR, "  VQA video: decode_format80 problem: dest_index (%d) exceeded dest_size (%d)\n",
00226                 dest_index, dest_size);
00227             return AVERROR_INVALIDDATA;
00228         }
00229 
00230         if (opcode == 0xFF) {
00231 
00232             count   = bytestream2_get_le16(gb);
00233             src_pos = bytestream2_get_le16(gb);
00234             av_dlog(NULL, "(1) copy %X bytes from absolute pos %X\n", count, src_pos);
00235             CHECK_COUNT();
00236             CHECK_COPY(src_pos);
00237             for (i = 0; i < count; i++)
00238                 dest[dest_index + i] = dest[src_pos + i];
00239             dest_index += count;
00240 
00241         } else if (opcode == 0xFE) {
00242 
00243             count = bytestream2_get_le16(gb);
00244             color = bytestream2_get_byte(gb);
00245             av_dlog(NULL, "(2) set %X bytes to %02X\n", count, color);
00246             CHECK_COUNT();
00247             memset(&dest[dest_index], color, count);
00248             dest_index += count;
00249 
00250         } else if ((opcode & 0xC0) == 0xC0) {
00251 
00252             count = (opcode & 0x3F) + 3;
00253             src_pos = bytestream2_get_le16(gb);
00254             av_dlog(NULL, "(3) copy %X bytes from absolute pos %X\n", count, src_pos);
00255             CHECK_COUNT();
00256             CHECK_COPY(src_pos);
00257             for (i = 0; i < count; i++)
00258                 dest[dest_index + i] = dest[src_pos + i];
00259             dest_index += count;
00260 
00261         } else if (opcode > 0x80) {
00262 
00263             count = opcode & 0x3F;
00264             av_dlog(NULL, "(4) copy %X bytes from source to dest\n", count);
00265             CHECK_COUNT();
00266             bytestream2_get_buffer(gb, &dest[dest_index], count);
00267             dest_index += count;
00268 
00269         } else {
00270 
00271             count = ((opcode & 0x70) >> 4) + 3;
00272             src_pos = bytestream2_get_byte(gb) | ((opcode & 0x0F) << 8);
00273             av_dlog(NULL, "(5) copy %X bytes from relpos %X\n", count, src_pos);
00274             CHECK_COUNT();
00275             CHECK_COPY(dest_index - src_pos);
00276             for (i = 0; i < count; i++)
00277                 dest[dest_index + i] = dest[dest_index - src_pos + i];
00278             dest_index += count;
00279         }
00280     }
00281 
00282     /* validate that the entire destination buffer was filled; this is
00283      * important for decoding frame maps since each vector needs to have a
00284      * codebook entry; it is not important for compressed codebooks because
00285      * not every entry needs to be filled */
00286     if (check_size)
00287         if (dest_index < dest_size)
00288             av_log(NULL, AV_LOG_ERROR, "  VQA video: decode_format80 problem: decode finished with dest_index (%d) < dest_size (%d)\n",
00289                 dest_index, dest_size);
00290 
00291     return 0; // let's display what we decoded anyway
00292 }
00293 
00294 static int vqa_decode_chunk(VqaContext *s)
00295 {
00296     unsigned int chunk_type;
00297     unsigned int chunk_size;
00298     int byte_skip;
00299     unsigned int index = 0;
00300     int i;
00301     unsigned char r, g, b;
00302     int index_shift;
00303     int res;
00304 
00305     int cbf0_chunk = -1;
00306     int cbfz_chunk = -1;
00307     int cbp0_chunk = -1;
00308     int cbpz_chunk = -1;
00309     int cpl0_chunk = -1;
00310     int cplz_chunk = -1;
00311     int vptz_chunk = -1;
00312 
00313     int x, y;
00314     int lines = 0;
00315     int pixel_ptr;
00316     int vector_index = 0;
00317     int lobyte = 0;
00318     int hibyte = 0;
00319     int lobytes = 0;
00320     int hibytes = s->decode_buffer_size / 2;
00321 
00322     /* first, traverse through the frame and find the subchunks */
00323     while (bytestream2_get_bytes_left(&s->gb) >= 8) {
00324 
00325         chunk_type = bytestream2_get_be32u(&s->gb);
00326         index      = bytestream2_tell(&s->gb);
00327         chunk_size = bytestream2_get_be32u(&s->gb);
00328 
00329         switch (chunk_type) {
00330 
00331         case CBF0_TAG:
00332             cbf0_chunk = index;
00333             break;
00334 
00335         case CBFZ_TAG:
00336             cbfz_chunk = index;
00337             break;
00338 
00339         case CBP0_TAG:
00340             cbp0_chunk = index;
00341             break;
00342 
00343         case CBPZ_TAG:
00344             cbpz_chunk = index;
00345             break;
00346 
00347         case CPL0_TAG:
00348             cpl0_chunk = index;
00349             break;
00350 
00351         case CPLZ_TAG:
00352             cplz_chunk = index;
00353             break;
00354 
00355         case VPTZ_TAG:
00356             vptz_chunk = index;
00357             break;
00358 
00359         default:
00360             av_log(s->avctx, AV_LOG_ERROR, "  VQA video: Found unknown chunk type: %c%c%c%c (%08X)\n",
00361             (chunk_type >> 24) & 0xFF,
00362             (chunk_type >> 16) & 0xFF,
00363             (chunk_type >>  8) & 0xFF,
00364             (chunk_type >>  0) & 0xFF,
00365             chunk_type);
00366             break;
00367         }
00368 
00369         byte_skip = chunk_size & 0x01;
00370         bytestream2_skip(&s->gb, chunk_size + byte_skip);
00371     }
00372 
00373     /* next, deal with the palette */
00374     if ((cpl0_chunk != -1) && (cplz_chunk != -1)) {
00375 
00376         /* a chunk should not have both chunk types */
00377         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: found both CPL0 and CPLZ chunks\n");
00378         return AVERROR_INVALIDDATA;
00379     }
00380 
00381     /* decompress the palette chunk */
00382     if (cplz_chunk != -1) {
00383 
00384 /* yet to be handled */
00385 
00386     }
00387 
00388     /* convert the RGB palette into the machine's endian format */
00389     if (cpl0_chunk != -1) {
00390 
00391         bytestream2_seek(&s->gb, cpl0_chunk, SEEK_SET);
00392         chunk_size = bytestream2_get_be32(&s->gb);
00393         /* sanity check the palette size */
00394         if (chunk_size / 3 > 256 || chunk_size > bytestream2_get_bytes_left(&s->gb)) {
00395             av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: found a palette chunk with %d colors\n",
00396                 chunk_size / 3);
00397             return AVERROR_INVALIDDATA;
00398         }
00399         for (i = 0; i < chunk_size / 3; i++) {
00400             /* scale by 4 to transform 6-bit palette -> 8-bit */
00401             r = bytestream2_get_byteu(&s->gb) * 4;
00402             g = bytestream2_get_byteu(&s->gb) * 4;
00403             b = bytestream2_get_byteu(&s->gb) * 4;
00404             s->palette[i] = (r << 16) | (g << 8) | (b);
00405         }
00406     }
00407 
00408     /* next, look for a full codebook */
00409     if ((cbf0_chunk != -1) && (cbfz_chunk != -1)) {
00410 
00411         /* a chunk should not have both chunk types */
00412         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: found both CBF0 and CBFZ chunks\n");
00413         return AVERROR_INVALIDDATA;
00414     }
00415 
00416     /* decompress the full codebook chunk */
00417     if (cbfz_chunk != -1) {
00418 
00419         bytestream2_seek(&s->gb, cbfz_chunk, SEEK_SET);
00420         chunk_size = bytestream2_get_be32(&s->gb);
00421         if ((res = decode_format80(&s->gb, chunk_size, s->codebook,
00422                                    s->codebook_size, 0)) < 0)
00423             return res;
00424     }
00425 
00426     /* copy a full codebook */
00427     if (cbf0_chunk != -1) {
00428 
00429         bytestream2_seek(&s->gb, cbf0_chunk, SEEK_SET);
00430         chunk_size = bytestream2_get_be32(&s->gb);
00431         /* sanity check the full codebook size */
00432         if (chunk_size > MAX_CODEBOOK_SIZE) {
00433             av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: CBF0 chunk too large (0x%X bytes)\n",
00434                 chunk_size);
00435             return AVERROR_INVALIDDATA;
00436         }
00437 
00438         bytestream2_get_buffer(&s->gb, s->codebook, chunk_size);
00439     }
00440 
00441     /* decode the frame */
00442     if (vptz_chunk == -1) {
00443 
00444         /* something is wrong if there is no VPTZ chunk */
00445         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: no VPTZ chunk found\n");
00446         return AVERROR_INVALIDDATA;
00447     }
00448 
00449     bytestream2_seek(&s->gb, vptz_chunk, SEEK_SET);
00450     chunk_size = bytestream2_get_be32(&s->gb);
00451     if ((res = decode_format80(&s->gb, chunk_size,
00452                                s->decode_buffer, s->decode_buffer_size, 1)) < 0)
00453         return res;
00454 
00455     /* render the final PAL8 frame */
00456     if (s->vector_height == 4)
00457         index_shift = 4;
00458     else
00459         index_shift = 3;
00460     for (y = 0; y < s->frame.linesize[0] * s->height;
00461         y += s->frame.linesize[0] * s->vector_height) {
00462 
00463         for (x = y; x < y + s->width; x += 4, lobytes++, hibytes++) {
00464             pixel_ptr = x;
00465 
00466             /* get the vector index, the method for which varies according to
00467              * VQA file version */
00468             switch (s->vqa_version) {
00469 
00470             case 1:
00471                 lobyte = s->decode_buffer[lobytes * 2];
00472                 hibyte = s->decode_buffer[(lobytes * 2) + 1];
00473                 vector_index = ((hibyte << 8) | lobyte) >> 3;
00474                 vector_index <<= index_shift;
00475                 lines = s->vector_height;
00476                 /* uniform color fill - a quick hack */
00477                 if (hibyte == 0xFF) {
00478                     while (lines--) {
00479                         s->frame.data[0][pixel_ptr + 0] = 255 - lobyte;
00480                         s->frame.data[0][pixel_ptr + 1] = 255 - lobyte;
00481                         s->frame.data[0][pixel_ptr + 2] = 255 - lobyte;
00482                         s->frame.data[0][pixel_ptr + 3] = 255 - lobyte;
00483                         pixel_ptr += s->frame.linesize[0];
00484                     }
00485                     lines=0;
00486                 }
00487                 break;
00488 
00489             case 2:
00490                 lobyte = s->decode_buffer[lobytes];
00491                 hibyte = s->decode_buffer[hibytes];
00492                 vector_index = (hibyte << 8) | lobyte;
00493                 vector_index <<= index_shift;
00494                 lines = s->vector_height;
00495                 break;
00496 
00497             case 3:
00498 /* not implemented yet */
00499                 lines = 0;
00500                 break;
00501             }
00502 
00503             while (lines--) {
00504                 s->frame.data[0][pixel_ptr + 0] = s->codebook[vector_index++];
00505                 s->frame.data[0][pixel_ptr + 1] = s->codebook[vector_index++];
00506                 s->frame.data[0][pixel_ptr + 2] = s->codebook[vector_index++];
00507                 s->frame.data[0][pixel_ptr + 3] = s->codebook[vector_index++];
00508                 pixel_ptr += s->frame.linesize[0];
00509             }
00510         }
00511     }
00512 
00513     /* handle partial codebook */
00514     if ((cbp0_chunk != -1) && (cbpz_chunk != -1)) {
00515         /* a chunk should not have both chunk types */
00516         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: found both CBP0 and CBPZ chunks\n");
00517         return AVERROR_INVALIDDATA;
00518     }
00519 
00520     if (cbp0_chunk != -1) {
00521 
00522         bytestream2_seek(&s->gb, cbp0_chunk, SEEK_SET);
00523         chunk_size = bytestream2_get_be32(&s->gb);
00524 
00525         /* accumulate partial codebook */
00526         bytestream2_get_buffer(&s->gb, &s->next_codebook_buffer[s->next_codebook_buffer_index],
00527                                chunk_size);
00528         s->next_codebook_buffer_index += chunk_size;
00529 
00530         s->partial_countdown--;
00531         if (s->partial_countdown == 0) {
00532 
00533             /* time to replace codebook */
00534             memcpy(s->codebook, s->next_codebook_buffer,
00535                 s->next_codebook_buffer_index);
00536 
00537             /* reset accounting */
00538             s->next_codebook_buffer_index = 0;
00539             s->partial_countdown = s->partial_count;
00540         }
00541     }
00542 
00543     if (cbpz_chunk != -1) {
00544 
00545         bytestream2_seek(&s->gb, cbpz_chunk, SEEK_SET);
00546         chunk_size = bytestream2_get_be32(&s->gb);
00547 
00548         /* accumulate partial codebook */
00549         bytestream2_get_buffer(&s->gb, &s->next_codebook_buffer[s->next_codebook_buffer_index],
00550                                chunk_size);
00551         s->next_codebook_buffer_index += chunk_size;
00552 
00553         s->partial_countdown--;
00554         if (s->partial_countdown == 0) {
00555             GetByteContext gb;
00556 
00557             bytestream2_init(&gb, s->next_codebook_buffer, s->next_codebook_buffer_index);
00558             /* decompress codebook */
00559             if ((res = decode_format80(&gb, s->next_codebook_buffer_index,
00560                                        s->codebook, s->codebook_size, 0)) < 0)
00561                 return res;
00562 
00563             /* reset accounting */
00564             s->next_codebook_buffer_index = 0;
00565             s->partial_countdown = s->partial_count;
00566         }
00567     }
00568 
00569     return 0;
00570 }
00571 
00572 static int vqa_decode_frame(AVCodecContext *avctx,
00573                             void *data, int *data_size,
00574                             AVPacket *avpkt)
00575 {
00576     VqaContext *s = avctx->priv_data;
00577     int res;
00578 
00579     if (s->frame.data[0])
00580         avctx->release_buffer(avctx, &s->frame);
00581 
00582     if (avctx->get_buffer(avctx, &s->frame)) {
00583         av_log(s->avctx, AV_LOG_ERROR, "  VQA Video: get_buffer() failed\n");
00584         return -1;
00585     }
00586 
00587     bytestream2_init(&s->gb, avpkt->data, avpkt->size);
00588     if ((res = vqa_decode_chunk(s)) < 0)
00589         return res;
00590 
00591     /* make the palette available on the way out */
00592     memcpy(s->frame.data[1], s->palette, PALETTE_COUNT * 4);
00593     s->frame.palette_has_changed = 1;
00594 
00595     *data_size = sizeof(AVFrame);
00596     *(AVFrame*)data = s->frame;
00597 
00598     /* report that the buffer was completely consumed */
00599     return avpkt->size;
00600 }
00601 
00602 static av_cold int vqa_decode_end(AVCodecContext *avctx)
00603 {
00604     VqaContext *s = avctx->priv_data;
00605 
00606     av_free(s->codebook);
00607     av_free(s->next_codebook_buffer);
00608     av_free(s->decode_buffer);
00609 
00610     if (s->frame.data[0])
00611         avctx->release_buffer(avctx, &s->frame);
00612 
00613     return 0;
00614 }
00615 
00616 AVCodec ff_vqa_decoder = {
00617     .name           = "vqavideo",
00618     .type           = AVMEDIA_TYPE_VIDEO,
00619     .id             = CODEC_ID_WS_VQA,
00620     .priv_data_size = sizeof(VqaContext),
00621     .init           = vqa_decode_init,
00622     .close          = vqa_decode_end,
00623     .decode         = vqa_decode_frame,
00624     .capabilities   = CODEC_CAP_DR1,
00625     .long_name = NULL_IF_CONFIG_SMALL("Westwood Studios VQA (Vector Quantized Animation) video"),
00626 };
Generated on Thu Jan 24 2013 17:08:54 for Libav by doxygen 1.7.1