Libav

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