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

Generated on Tue Nov 4 2014 12:59:23 for ffmpeg by  doxygen 1.7.1