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 
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 typedef struct VqaContext {
00094 
00095     AVCodecContext *avctx;
00096     AVFrame frame;
00097 
00098     const unsigned char *buf;
00099     int size;
00100 
00101     uint32_t palette[PALETTE_COUNT];
00102 
00103     int width;   /* width of a frame */
00104     int height;   /* height of a frame */
00105     int vector_width;  /* width of individual vector */
00106     int vector_height;  /* height of individual vector */
00107     int vqa_version;  /* this should be either 1, 2 or 3 */
00108 
00109     unsigned char *codebook;         /* the current codebook */
00110     int codebook_size;
00111     unsigned char *next_codebook_buffer;  /* accumulator for next codebook */
00112     int next_codebook_buffer_index;
00113 
00114     unsigned char *decode_buffer;
00115     int decode_buffer_size;
00116 
00117     /* number of frames to go before replacing codebook */
00118     int partial_countdown;
00119     int partial_count;
00120 
00121 } VqaContext;
00122 
00123 static av_cold int vqa_decode_init(AVCodecContext *avctx)
00124 {
00125     VqaContext *s = avctx->priv_data;
00126     unsigned char *vqa_header;
00127     int i, j, codebook_index;
00128 
00129     s->avctx = avctx;
00130     avctx->pix_fmt = PIX_FMT_PAL8;
00131 
00132     /* make sure the extradata made it */
00133     if (s->avctx->extradata_size != VQA_HEADER_SIZE) {
00134         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: expected extradata size of %d\n", VQA_HEADER_SIZE);
00135         return -1;
00136     }
00137 
00138     /* load up the VQA parameters from the header */
00139     vqa_header = (unsigned char *)s->avctx->extradata;
00140     s->vqa_version = vqa_header[0];
00141     if (s->vqa_version < 1 || s->vqa_version > 3) {
00142         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: unsupported version %d\n", s->vqa_version);
00143         return -1;
00144     }
00145     s->width = AV_RL16(&vqa_header[6]);
00146     s->height = AV_RL16(&vqa_header[8]);
00147     if(av_image_check_size(s->width, s->height, 0, avctx)){
00148         s->width= s->height= 0;
00149         return -1;
00150     }
00151     s->vector_width = vqa_header[10];
00152     s->vector_height = vqa_header[11];
00153     s->partial_count = s->partial_countdown = vqa_header[13];
00154 
00155     /* the vector dimensions have to meet very stringent requirements */
00156     if ((s->vector_width != 4) ||
00157         ((s->vector_height != 2) && (s->vector_height != 4))) {
00158         /* return without further initialization */
00159         return -1;
00160     }
00161 
00162     /* allocate codebooks */
00163     s->codebook_size = MAX_CODEBOOK_SIZE;
00164     s->codebook = av_malloc(s->codebook_size);
00165     s->next_codebook_buffer = av_malloc(s->codebook_size);
00166 
00167     /* initialize the solid-color vectors */
00168     if (s->vector_height == 4) {
00169         codebook_index = 0xFF00 * 16;
00170         for (i = 0; i < 256; i++)
00171             for (j = 0; j < 16; j++)
00172                 s->codebook[codebook_index++] = i;
00173     } else {
00174         codebook_index = 0xF00 * 8;
00175         for (i = 0; i < 256; i++)
00176             for (j = 0; j < 8; j++)
00177                 s->codebook[codebook_index++] = i;
00178     }
00179     s->next_codebook_buffer_index = 0;
00180 
00181     /* allocate decode buffer */
00182     s->decode_buffer_size = (s->width / s->vector_width) *
00183         (s->height / s->vector_height) * 2;
00184     s->decode_buffer = av_malloc(s->decode_buffer_size);
00185 
00186     avcodec_get_frame_defaults(&s->frame);
00187     s->frame.data[0] = NULL;
00188 
00189     return 0;
00190 }
00191 
00192 #define CHECK_COUNT() \
00193     if (dest_index + count > dest_size) { \
00194         av_log(NULL, AV_LOG_ERROR, "  VQA video: decode_format80 problem: next op would overflow dest_index\n"); \
00195         av_log(NULL, AV_LOG_ERROR, "  VQA video: current dest_index = %d, count = %d, dest_size = %d\n", \
00196             dest_index, count, dest_size); \
00197         return; \
00198     }
00199 
00200 static void decode_format80(const unsigned char *src, int src_size,
00201     unsigned char *dest, int dest_size, int check_size) {
00202 
00203     int src_index = 0;
00204     int dest_index = 0;
00205     int count;
00206     int src_pos;
00207     unsigned char color;
00208     int i;
00209 
00210     while (src_index < src_size) {
00211 
00212         av_dlog(NULL, "      opcode %02X: ", src[src_index]);
00213 
00214         /* 0x80 means that frame is finished */
00215         if (src[src_index] == 0x80)
00216             return;
00217 
00218         if (dest_index >= dest_size) {
00219             av_log(NULL, AV_LOG_ERROR, "  VQA video: decode_format80 problem: dest_index (%d) exceeded dest_size (%d)\n",
00220                 dest_index, dest_size);
00221             return;
00222         }
00223 
00224         if (src[src_index] == 0xFF) {
00225 
00226             src_index++;
00227             count = AV_RL16(&src[src_index]);
00228             src_index += 2;
00229             src_pos = AV_RL16(&src[src_index]);
00230             src_index += 2;
00231             av_dlog(NULL, "(1) copy %X bytes from absolute pos %X\n", count, src_pos);
00232             CHECK_COUNT();
00233             if (src_pos + count > dest_size)
00234                 return;
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             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 ((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             av_dlog(NULL, "(3) copy %X bytes from absolute pos %X\n", count, src_pos);
00256             CHECK_COUNT();
00257             if (src_pos + count > dest_size)
00258                 return;
00259             for (i = 0; i < count; i++)
00260                 dest[dest_index + i] = dest[src_pos + i];
00261             dest_index += count;
00262 
00263         } else if (src[src_index] > 0x80) {
00264 
00265             count = src[src_index++] & 0x3F;
00266             av_dlog(NULL, "(4) copy %X bytes from source to dest\n", count);
00267             CHECK_COUNT();
00268             memcpy(&dest[dest_index], &src[src_index], count);
00269             src_index += count;
00270             dest_index += count;
00271 
00272         } else {
00273 
00274             count = ((src[src_index] & 0x70) >> 4) + 3;
00275             src_pos = AV_RB16(&src[src_index]) & 0x0FFF;
00276             src_index += 2;
00277             av_dlog(NULL, "(5) copy %X bytes from relpos %X\n", count, src_pos);
00278             CHECK_COUNT();
00279             if (dest_index < src_pos)
00280                 return;
00281             for (i = 0; i < count; i++)
00282                 dest[dest_index + i] = dest[dest_index - src_pos + i];
00283             dest_index += count;
00284         }
00285     }
00286 
00287     /* validate that the entire destination buffer was filled; this is
00288      * important for decoding frame maps since each vector needs to have a
00289      * codebook entry; it is not important for compressed codebooks because
00290      * not every entry needs to be filled */
00291     if (check_size)
00292         if (dest_index < dest_size)
00293             av_log(NULL, AV_LOG_ERROR, "  VQA video: decode_format80 problem: decode finished with dest_index (%d) < dest_size (%d)\n",
00294                 dest_index, dest_size);
00295 }
00296 
00297 static void vqa_decode_chunk(VqaContext *s)
00298 {
00299     unsigned int chunk_type;
00300     unsigned int chunk_size;
00301     int byte_skip;
00302     unsigned int index = 0;
00303     int i;
00304     unsigned char r, g, b;
00305     int index_shift;
00306 
00307     int cbf0_chunk = -1;
00308     int cbfz_chunk = -1;
00309     int cbp0_chunk = -1;
00310     int cbpz_chunk = -1;
00311     int cpl0_chunk = -1;
00312     int cplz_chunk = -1;
00313     int vptz_chunk = -1;
00314 
00315     int x, y;
00316     int lines = 0;
00317     int pixel_ptr;
00318     int vector_index = 0;
00319     int lobyte = 0;
00320     int hibyte = 0;
00321     int lobytes = 0;
00322     int hibytes = s->decode_buffer_size / 2;
00323 
00324     /* first, traverse through the frame and find the subchunks */
00325     while (index + CHUNK_PREAMBLE_SIZE <= s->size) {
00326         unsigned next_index;
00327 
00328         chunk_type = AV_RB32(&s->buf[index]);
00329         chunk_size = AV_RB32(&s->buf[index + 4]);
00330         byte_skip = chunk_size & 0x01;
00331         next_index = index + CHUNK_PREAMBLE_SIZE + chunk_size + byte_skip;
00332         if (next_index > s->size) {
00333             av_log(s->avctx, AV_LOG_ERROR, "Dropping incomplete chunk\n");
00334             break;
00335         }
00336 
00337         switch (chunk_type) {
00338 
00339         case CBF0_TAG:
00340             cbf0_chunk = index;
00341             break;
00342 
00343         case CBFZ_TAG:
00344             cbfz_chunk = index;
00345             break;
00346 
00347         case CBP0_TAG:
00348             cbp0_chunk = index;
00349             break;
00350 
00351         case CBPZ_TAG:
00352             cbpz_chunk = index;
00353             break;
00354 
00355         case CPL0_TAG:
00356             cpl0_chunk = index;
00357             break;
00358 
00359         case CPLZ_TAG:
00360             cplz_chunk = index;
00361             break;
00362 
00363         case VPTZ_TAG:
00364             vptz_chunk = index;
00365             break;
00366 
00367         default:
00368             av_log(s->avctx, AV_LOG_ERROR, "  VQA video: Found unknown chunk type: %c%c%c%c (%08X)\n",
00369             (chunk_type >> 24) & 0xFF,
00370             (chunk_type >> 16) & 0xFF,
00371             (chunk_type >>  8) & 0xFF,
00372             (chunk_type >>  0) & 0xFF,
00373             chunk_type);
00374             break;
00375         }
00376         index = next_index;
00377     }
00378 
00379     /* next, deal with the palette */
00380     if ((cpl0_chunk != -1) && (cplz_chunk != -1)) {
00381 
00382         /* a chunk should not have both chunk types */
00383         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: found both CPL0 and CPLZ chunks\n");
00384         return;
00385     }
00386 
00387     /* decompress the palette chunk */
00388     if (cplz_chunk != -1) {
00389 
00390 /* yet to be handled */
00391 
00392     }
00393 
00394     /* convert the RGB palette into the machine's endian format */
00395     if (cpl0_chunk != -1) {
00396 
00397         chunk_size = AV_RB32(&s->buf[cpl0_chunk + 4]);
00398         /* sanity check the palette size */
00399         if (chunk_size / 3 > 256) {
00400             av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: found a palette chunk with %d colors\n",
00401                 chunk_size / 3);
00402             return;
00403         }
00404         cpl0_chunk += CHUNK_PREAMBLE_SIZE;
00405         for (i = 0; i < chunk_size / 3; i++) {
00406             /* scale by 4 to transform 6-bit palette -> 8-bit */
00407             r = s->buf[cpl0_chunk++] * 4;
00408             g = s->buf[cpl0_chunk++] * 4;
00409             b = s->buf[cpl0_chunk++] * 4;
00410             s->palette[i] = 0xFF << 24 | r << 16 | g << 8 | b;
00411             s->palette[i] |= s->palette[i] >> 6 & 0x30303;
00412         }
00413     }
00414 
00415     /* next, look for a full codebook */
00416     if ((cbf0_chunk != -1) && (cbfz_chunk != -1)) {
00417 
00418         /* a chunk should not have both chunk types */
00419         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: found both CBF0 and CBFZ chunks\n");
00420         return;
00421     }
00422 
00423     /* decompress the full codebook chunk */
00424     if (cbfz_chunk != -1) {
00425 
00426         chunk_size = AV_RB32(&s->buf[cbfz_chunk + 4]);
00427         cbfz_chunk += CHUNK_PREAMBLE_SIZE;
00428         decode_format80(&s->buf[cbfz_chunk], chunk_size,
00429             s->codebook, s->codebook_size, 0);
00430     }
00431 
00432     /* copy a full codebook */
00433     if (cbf0_chunk != -1) {
00434 
00435         chunk_size = AV_RB32(&s->buf[cbf0_chunk + 4]);
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;
00441         }
00442         cbf0_chunk += CHUNK_PREAMBLE_SIZE;
00443 
00444         memcpy(s->codebook, &s->buf[cbf0_chunk], chunk_size);
00445     }
00446 
00447     /* decode the frame */
00448     if (vptz_chunk == -1) {
00449 
00450         /* something is wrong if there is no VPTZ chunk */
00451         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: no VPTZ chunk found\n");
00452         return;
00453     }
00454 
00455     chunk_size = AV_RB32(&s->buf[vptz_chunk + 4]);
00456     vptz_chunk += CHUNK_PREAMBLE_SIZE;
00457     decode_format80(&s->buf[vptz_chunk], chunk_size,
00458         s->decode_buffer, s->decode_buffer_size, 1);
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;
00523     }
00524 
00525     if (cbp0_chunk != -1) {
00526 
00527         chunk_size = AV_RB32(&s->buf[cbp0_chunk + 4]);
00528         cbp0_chunk += CHUNK_PREAMBLE_SIZE;
00529 
00530         /* accumulate partial codebook */
00531         memcpy(&s->next_codebook_buffer[s->next_codebook_buffer_index],
00532             &s->buf[cbp0_chunk], 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         chunk_size = AV_RB32(&s->buf[cbpz_chunk + 4]);
00551         cbpz_chunk += CHUNK_PREAMBLE_SIZE;
00552 
00553         /* accumulate partial codebook */
00554         memcpy(&s->next_codebook_buffer[s->next_codebook_buffer_index],
00555             &s->buf[cbpz_chunk], chunk_size);
00556         s->next_codebook_buffer_index += chunk_size;
00557 
00558         s->partial_countdown--;
00559         if (s->partial_countdown == 0) {
00560 
00561             /* decompress codebook */
00562             decode_format80(s->next_codebook_buffer,
00563                 s->next_codebook_buffer_index,
00564                 s->codebook, s->codebook_size, 0);
00565 
00566             /* reset accounting */
00567             s->next_codebook_buffer_index = 0;
00568             s->partial_countdown = s->partial_count;
00569         }
00570     }
00571 }
00572 
00573 static int vqa_decode_frame(AVCodecContext *avctx,
00574                             void *data, int *data_size,
00575                             AVPacket *avpkt)
00576 {
00577     const uint8_t *buf = avpkt->data;
00578     int buf_size = avpkt->size;
00579     VqaContext *s = avctx->priv_data;
00580 
00581     s->buf = buf;
00582     s->size = buf_size;
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     vqa_decode_chunk(s);
00593 
00594     /* make the palette available on the way out */
00595     memcpy(s->frame.data[1], s->palette, PALETTE_COUNT * 4);
00596     s->frame.palette_has_changed = 1;
00597 
00598     *data_size = sizeof(AVFrame);
00599     *(AVFrame*)data = s->frame;
00600 
00601     /* report that the buffer was completely consumed */
00602     return buf_size;
00603 }
00604 
00605 static av_cold int vqa_decode_end(AVCodecContext *avctx)
00606 {
00607     VqaContext *s = avctx->priv_data;
00608 
00609     av_free(s->codebook);
00610     av_free(s->next_codebook_buffer);
00611     av_free(s->decode_buffer);
00612 
00613     if (s->frame.data[0])
00614         avctx->release_buffer(avctx, &s->frame);
00615 
00616     return 0;
00617 }
00618 
00619 AVCodec ff_vqa_decoder = {
00620     .name           = "vqavideo",
00621     .type           = AVMEDIA_TYPE_VIDEO,
00622     .id             = CODEC_ID_WS_VQA,
00623     .priv_data_size = sizeof(VqaContext),
00624     .init           = vqa_decode_init,
00625     .close          = vqa_decode_end,
00626     .decode         = vqa_decode_frame,
00627     .capabilities   = CODEC_CAP_DR1,
00628     .long_name = NULL_IF_CONFIG_SMALL("Westwood Studios VQA (Vector Quantized Animation) video"),
00629 };