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

Generated on Fri Sep 16 2011 17:17:46 for FFmpeg by  doxygen 1.7.1