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