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