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 "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     if (s->vqa_version < 1 || s->vqa_version > 3) {
00138         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: unsupported version %d\n", s->vqa_version);
00139         return -1;
00140     }
00141     s->width = AV_RL16(&s->avctx->extradata[6]);
00142     s->height = AV_RL16(&s->avctx->extradata[8]);
00143     if(av_image_check_size(s->width, s->height, 0, avctx)){
00144         s->width= s->height= 0;
00145         return -1;
00146     }
00147     s->vector_width = s->avctx->extradata[10];
00148     s->vector_height = s->avctx->extradata[11];
00149     s->partial_count = s->partial_countdown = s->avctx->extradata[13];
00150 
00151     /* the vector dimensions have to meet very stringent requirements */
00152     if ((s->vector_width != 4) ||
00153         ((s->vector_height != 2) && (s->vector_height != 4))) {
00154         /* return without further initialization */
00155         return -1;
00156     }
00157 
00158     if (s->width % s->vector_width || s->height % s->vector_height) {
00159         av_log(avctx, AV_LOG_ERROR, "Image size not multiple of block size\n");
00160         return AVERROR_INVALIDDATA;
00161     }
00162 
00163     /* allocate codebooks */
00164     s->codebook_size = MAX_CODEBOOK_SIZE;
00165     s->codebook = av_malloc(s->codebook_size);
00166     s->next_codebook_buffer = av_malloc(s->codebook_size);
00167 
00168     /* initialize the solid-color vectors */
00169     if (s->vector_height == 4) {
00170         codebook_index = 0xFF00 * 16;
00171         for (i = 0; i < 256; i++)
00172             for (j = 0; j < 16; j++)
00173                 s->codebook[codebook_index++] = i;
00174     } else {
00175         codebook_index = 0xF00 * 8;
00176         for (i = 0; i < 256; i++)
00177             for (j = 0; j < 8; j++)
00178                 s->codebook[codebook_index++] = i;
00179     }
00180     s->next_codebook_buffer_index = 0;
00181 
00182     /* allocate decode buffer */
00183     s->decode_buffer_size = (s->width / s->vector_width) *
00184         (s->height / s->vector_height) * 2;
00185     s->decode_buffer = av_malloc(s->decode_buffer_size);
00186 
00187     avcodec_get_frame_defaults(&s->frame);
00188     s->frame.data[0] = NULL;
00189 
00190     return 0;
00191 }
00192 
00193 #define CHECK_COUNT() \
00194     if (dest_index + count > dest_size) { \
00195         av_log(NULL, AV_LOG_ERROR, "  VQA video: decode_format80 problem: next op would overflow dest_index\n"); \
00196         av_log(NULL, AV_LOG_ERROR, "  VQA video: current dest_index = %d, count = %d, dest_size = %d\n", \
00197             dest_index, count, dest_size); \
00198         return AVERROR_INVALIDDATA; \
00199     }
00200 
00201 #define CHECK_COPY(idx) \
00202     if (idx < 0 || idx + 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 src_pos = %d, count = %d, dest_size = %d\n", \
00205             src_pos, count, dest_size); \
00206         return AVERROR_INVALIDDATA; \
00207     }
00208 
00209 
00210 static int decode_format80(GetByteContext *gb, int src_size,
00211     unsigned char *dest, int dest_size, int check_size) {
00212 
00213     int dest_index = 0;
00214     int count, opcode, start;
00215     int src_pos;
00216     unsigned char color;
00217     int i;
00218 
00219     start = bytestream2_tell(gb);
00220     while (bytestream2_tell(gb) - start < src_size) {
00221         opcode = bytestream2_get_byte(gb);
00222         av_dlog(NULL, "      opcode %02X: ", opcode);
00223 
00224         /* 0x80 means that frame is finished */
00225         if (opcode == 0x80)
00226             return 0;
00227 
00228         if (dest_index >= dest_size) {
00229             av_log(NULL, AV_LOG_ERROR, "  VQA video: decode_format80 problem: dest_index (%d) exceeded dest_size (%d)\n",
00230                 dest_index, dest_size);
00231             return AVERROR_INVALIDDATA;
00232         }
00233 
00234         if (opcode == 0xFF) {
00235 
00236             count   = bytestream2_get_le16(gb);
00237             src_pos = bytestream2_get_le16(gb);
00238             av_dlog(NULL, "(1) copy %X bytes from absolute pos %X\n", count, src_pos);
00239             CHECK_COUNT();
00240             CHECK_COPY(src_pos);
00241             for (i = 0; i < count; i++)
00242                 dest[dest_index + i] = dest[src_pos + i];
00243             dest_index += count;
00244 
00245         } else if (opcode == 0xFE) {
00246 
00247             count = bytestream2_get_le16(gb);
00248             color = bytestream2_get_byte(gb);
00249             av_dlog(NULL, "(2) set %X bytes to %02X\n", count, color);
00250             CHECK_COUNT();
00251             memset(&dest[dest_index], color, count);
00252             dest_index += count;
00253 
00254         } else if ((opcode & 0xC0) == 0xC0) {
00255 
00256             count = (opcode & 0x3F) + 3;
00257             src_pos = bytestream2_get_le16(gb);
00258             av_dlog(NULL, "(3) copy %X bytes from absolute pos %X\n", count, src_pos);
00259             CHECK_COUNT();
00260             CHECK_COPY(src_pos);
00261             for (i = 0; i < count; i++)
00262                 dest[dest_index + i] = dest[src_pos + i];
00263             dest_index += count;
00264 
00265         } else if (opcode > 0x80) {
00266 
00267             count = opcode & 0x3F;
00268             av_dlog(NULL, "(4) copy %X bytes from source to dest\n", count);
00269             CHECK_COUNT();
00270             bytestream2_get_buffer(gb, &dest[dest_index], count);
00271             dest_index += count;
00272 
00273         } else {
00274 
00275             count = ((opcode & 0x70) >> 4) + 3;
00276             src_pos = bytestream2_get_byte(gb) | ((opcode & 0x0F) << 8);
00277             av_dlog(NULL, "(5) copy %X bytes from relpos %X\n", count, src_pos);
00278             CHECK_COUNT();
00279             CHECK_COPY(dest_index - src_pos);
00280             for (i = 0; i < count; i++)
00281                 dest[dest_index + i] = dest[dest_index - src_pos + i];
00282             dest_index += count;
00283         }
00284     }
00285 
00286     /* validate that the entire destination buffer was filled; this is
00287      * important for decoding frame maps since each vector needs to have a
00288      * codebook entry; it is not important for compressed codebooks because
00289      * not every entry needs to be filled */
00290     if (check_size)
00291         if (dest_index < dest_size)
00292             av_log(NULL, AV_LOG_ERROR, "  VQA video: decode_format80 problem: decode finished with dest_index (%d) < dest_size (%d)\n",
00293                 dest_index, dest_size);
00294 
00295     return 0; // let's display what we decoded anyway
00296 }
00297 
00298 static int vqa_decode_chunk(VqaContext *s)
00299 {
00300     unsigned int chunk_type;
00301     unsigned int chunk_size;
00302     int byte_skip;
00303     unsigned int index = 0;
00304     int i;
00305     unsigned char r, g, b;
00306     int index_shift;
00307     int res;
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 (bytestream2_get_bytes_left(&s->gb) >= 8) {
00328 
00329         chunk_type = bytestream2_get_be32u(&s->gb);
00330         index      = bytestream2_tell(&s->gb);
00331         chunk_size = bytestream2_get_be32u(&s->gb);
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         bytestream2_skip(&s->gb, 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 AVERROR_INVALIDDATA;
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         bytestream2_seek(&s->gb, cpl0_chunk, SEEK_SET);
00396         chunk_size = bytestream2_get_be32(&s->gb);
00397         /* sanity check the palette size */
00398         if (chunk_size / 3 > 256 || chunk_size > bytestream2_get_bytes_left(&s->gb)) {
00399             av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: found a palette chunk with %d colors\n",
00400                 chunk_size / 3);
00401             return AVERROR_INVALIDDATA;
00402         }
00403         for (i = 0; i < chunk_size / 3; i++) {
00404             /* scale by 4 to transform 6-bit palette -> 8-bit */
00405             r = bytestream2_get_byteu(&s->gb) * 4;
00406             g = bytestream2_get_byteu(&s->gb) * 4;
00407             b = bytestream2_get_byteu(&s->gb) * 4;
00408             s->palette[i] = 0xFF << 24 | r << 16 | g << 8 | b;
00409             s->palette[i] |= s->palette[i] >> 6 & 0x30303;
00410         }
00411     }
00412 
00413     /* next, look for a full codebook */
00414     if ((cbf0_chunk != -1) && (cbfz_chunk != -1)) {
00415 
00416         /* a chunk should not have both chunk types */
00417         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: found both CBF0 and CBFZ chunks\n");
00418         return AVERROR_INVALIDDATA;
00419     }
00420 
00421     /* decompress the full codebook chunk */
00422     if (cbfz_chunk != -1) {
00423 
00424         bytestream2_seek(&s->gb, cbfz_chunk, SEEK_SET);
00425         chunk_size = bytestream2_get_be32(&s->gb);
00426         if ((res = decode_format80(&s->gb, chunk_size, s->codebook,
00427                                    s->codebook_size, 0)) < 0)
00428             return res;
00429     }
00430 
00431     /* copy a full codebook */
00432     if (cbf0_chunk != -1) {
00433 
00434         bytestream2_seek(&s->gb, cbf0_chunk, SEEK_SET);
00435         chunk_size = bytestream2_get_be32(&s->gb);
00436         /* sanity check the full codebook size */
00437         if (chunk_size > MAX_CODEBOOK_SIZE) {
00438             av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: CBF0 chunk too large (0x%X bytes)\n",
00439                 chunk_size);
00440             return AVERROR_INVALIDDATA;
00441         }
00442 
00443         bytestream2_get_buffer(&s->gb, s->codebook, chunk_size);
00444     }
00445 
00446     /* decode the frame */
00447     if (vptz_chunk == -1) {
00448 
00449         /* something is wrong if there is no VPTZ chunk */
00450         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: no VPTZ chunk found\n");
00451         return AVERROR_INVALIDDATA;
00452     }
00453 
00454     bytestream2_seek(&s->gb, vptz_chunk, SEEK_SET);
00455     chunk_size = bytestream2_get_be32(&s->gb);
00456     if ((res = decode_format80(&s->gb, chunk_size,
00457                                s->decode_buffer, s->decode_buffer_size, 1)) < 0)
00458         return res;
00459 
00460     /* render the final PAL8 frame */
00461     if (s->vector_height == 4)
00462         index_shift = 4;
00463     else
00464         index_shift = 3;
00465     for (y = 0; y < s->frame.linesize[0] * s->height;
00466         y += s->frame.linesize[0] * s->vector_height) {
00467 
00468         for (x = y; x < y + s->width; x += 4, lobytes++, hibytes++) {
00469             pixel_ptr = x;
00470 
00471             /* get the vector index, the method for which varies according to
00472              * VQA file version */
00473             switch (s->vqa_version) {
00474 
00475             case 1:
00476                 lobyte = s->decode_buffer[lobytes * 2];
00477                 hibyte = s->decode_buffer[(lobytes * 2) + 1];
00478                 vector_index = ((hibyte << 8) | lobyte) >> 3;
00479                 vector_index <<= index_shift;
00480                 lines = s->vector_height;
00481                 /* uniform color fill - a quick hack */
00482                 if (hibyte == 0xFF) {
00483                     while (lines--) {
00484                         s->frame.data[0][pixel_ptr + 0] = 255 - lobyte;
00485                         s->frame.data[0][pixel_ptr + 1] = 255 - lobyte;
00486                         s->frame.data[0][pixel_ptr + 2] = 255 - lobyte;
00487                         s->frame.data[0][pixel_ptr + 3] = 255 - lobyte;
00488                         pixel_ptr += s->frame.linesize[0];
00489                     }
00490                     lines=0;
00491                 }
00492                 break;
00493 
00494             case 2:
00495                 lobyte = s->decode_buffer[lobytes];
00496                 hibyte = s->decode_buffer[hibytes];
00497                 vector_index = (hibyte << 8) | lobyte;
00498                 vector_index <<= index_shift;
00499                 lines = s->vector_height;
00500                 break;
00501 
00502             case 3:
00503 /* not implemented yet */
00504                 lines = 0;
00505                 break;
00506             }
00507 
00508             while (lines--) {
00509                 s->frame.data[0][pixel_ptr + 0] = s->codebook[vector_index++];
00510                 s->frame.data[0][pixel_ptr + 1] = s->codebook[vector_index++];
00511                 s->frame.data[0][pixel_ptr + 2] = s->codebook[vector_index++];
00512                 s->frame.data[0][pixel_ptr + 3] = s->codebook[vector_index++];
00513                 pixel_ptr += s->frame.linesize[0];
00514             }
00515         }
00516     }
00517 
00518     /* handle partial codebook */
00519     if ((cbp0_chunk != -1) && (cbpz_chunk != -1)) {
00520         /* a chunk should not have both chunk types */
00521         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: found both CBP0 and CBPZ chunks\n");
00522         return AVERROR_INVALIDDATA;
00523     }
00524 
00525     if (cbp0_chunk != -1) {
00526 
00527         bytestream2_seek(&s->gb, cbp0_chunk, SEEK_SET);
00528         chunk_size = bytestream2_get_be32(&s->gb);
00529 
00530         /* accumulate partial codebook */
00531         bytestream2_get_buffer(&s->gb, &s->next_codebook_buffer[s->next_codebook_buffer_index],
00532                                chunk_size);
00533         s->next_codebook_buffer_index += chunk_size;
00534 
00535         s->partial_countdown--;
00536         if (s->partial_countdown == 0) {
00537 
00538             /* time to replace codebook */
00539             memcpy(s->codebook, s->next_codebook_buffer,
00540                 s->next_codebook_buffer_index);
00541 
00542             /* reset accounting */
00543             s->next_codebook_buffer_index = 0;
00544             s->partial_countdown = s->partial_count;
00545         }
00546     }
00547 
00548     if (cbpz_chunk != -1) {
00549 
00550         bytestream2_seek(&s->gb, cbpz_chunk, SEEK_SET);
00551         chunk_size = bytestream2_get_be32(&s->gb);
00552 
00553         /* accumulate partial codebook */
00554         bytestream2_get_buffer(&s->gb, &s->next_codebook_buffer[s->next_codebook_buffer_index],
00555                                chunk_size);
00556         s->next_codebook_buffer_index += chunk_size;
00557 
00558         s->partial_countdown--;
00559         if (s->partial_countdown == 0) {
00560             GetByteContext gb;
00561 
00562             bytestream2_init(&gb, s->next_codebook_buffer, s->next_codebook_buffer_index);
00563             /* decompress codebook */
00564             if ((res = decode_format80(&gb, s->next_codebook_buffer_index,
00565                                        s->codebook, s->codebook_size, 0)) < 0)
00566                 return res;
00567 
00568             /* reset accounting */
00569             s->next_codebook_buffer_index = 0;
00570             s->partial_countdown = s->partial_count;
00571         }
00572     }
00573 
00574     return 0;
00575 }
00576 
00577 static int vqa_decode_frame(AVCodecContext *avctx,
00578                             void *data, int *data_size,
00579                             AVPacket *avpkt)
00580 {
00581     VqaContext *s = avctx->priv_data;
00582     int res;
00583 
00584     if (s->frame.data[0])
00585         avctx->release_buffer(avctx, &s->frame);
00586 
00587     if (avctx->get_buffer(avctx, &s->frame)) {
00588         av_log(s->avctx, AV_LOG_ERROR, "  VQA Video: get_buffer() failed\n");
00589         return -1;
00590     }
00591 
00592     bytestream2_init(&s->gb, avpkt->data, avpkt->size);
00593     if ((res = vqa_decode_chunk(s)) < 0)
00594         return res;
00595 
00596     /* make the palette available on the way out */
00597     memcpy(s->frame.data[1], s->palette, PALETTE_COUNT * 4);
00598     s->frame.palette_has_changed = 1;
00599 
00600     *data_size = sizeof(AVFrame);
00601     *(AVFrame*)data = s->frame;
00602 
00603     /* report that the buffer was completely consumed */
00604     return avpkt->size;
00605 }
00606 
00607 static av_cold int vqa_decode_end(AVCodecContext *avctx)
00608 {
00609     VqaContext *s = avctx->priv_data;
00610 
00611     av_free(s->codebook);
00612     av_free(s->next_codebook_buffer);
00613     av_free(s->decode_buffer);
00614 
00615     if (s->frame.data[0])
00616         avctx->release_buffer(avctx, &s->frame);
00617 
00618     return 0;
00619 }
00620 
00621 AVCodec ff_vqa_decoder = {
00622     .name           = "vqavideo",
00623     .type           = AVMEDIA_TYPE_VIDEO,
00624     .id             = CODEC_ID_WS_VQA,
00625     .priv_data_size = sizeof(VqaContext),
00626     .init           = vqa_decode_init,
00627     .close          = vqa_decode_end,
00628     .decode         = vqa_decode_frame,
00629     .capabilities   = CODEC_CAP_DR1,
00630     .long_name = NULL_IF_CONFIG_SMALL("Westwood Studios VQA (Vector Quantized Animation) video"),
00631 };