• 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 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     if (s->width  & (s->vector_width  - 1) ||
00163         s->height & (s->vector_height - 1)) {
00164         av_log(avctx, AV_LOG_ERROR, "Image size not multiple of block size\n");
00165         return AVERROR_INVALIDDATA;
00166     }
00167 
00168     /* allocate codebooks */
00169     s->codebook_size = MAX_CODEBOOK_SIZE;
00170     s->codebook = av_malloc(s->codebook_size);
00171     s->next_codebook_buffer = av_malloc(s->codebook_size);
00172 
00173     /* initialize the solid-color vectors */
00174     if (s->vector_height == 4) {
00175         codebook_index = 0xFF00 * 16;
00176         for (i = 0; i < 256; i++)
00177             for (j = 0; j < 16; j++)
00178                 s->codebook[codebook_index++] = i;
00179     } else {
00180         codebook_index = 0xF00 * 8;
00181         for (i = 0; i < 256; i++)
00182             for (j = 0; j < 8; j++)
00183                 s->codebook[codebook_index++] = i;
00184     }
00185     s->next_codebook_buffer_index = 0;
00186 
00187     /* allocate decode buffer */
00188     s->decode_buffer_size = (s->width / s->vector_width) *
00189         (s->height / s->vector_height) * 2;
00190     s->decode_buffer = av_malloc(s->decode_buffer_size);
00191 
00192     avcodec_get_frame_defaults(&s->frame);
00193     s->frame.data[0] = NULL;
00194 
00195     return 0;
00196 }
00197 
00198 #define CHECK_COUNT() \
00199     if (dest_index + count > dest_size) { \
00200         av_log(NULL, AV_LOG_ERROR, "  VQA video: decode_format80 problem: next op would overflow dest_index\n"); \
00201         av_log(NULL, AV_LOG_ERROR, "  VQA video: current dest_index = %d, count = %d, dest_size = %d\n", \
00202             dest_index, count, dest_size); \
00203         return; \
00204     }
00205 
00206 static void decode_format80(const unsigned char *src, int src_size,
00207     unsigned char *dest, int dest_size, int check_size) {
00208 
00209     int src_index = 0;
00210     int dest_index = 0;
00211     int count;
00212     int src_pos;
00213     unsigned char color;
00214     int i;
00215 
00216     while (src_index < src_size) {
00217 
00218         av_dlog(NULL, "      opcode %02X: ", src[src_index]);
00219 
00220         /* 0x80 means that frame is finished */
00221         if (src[src_index] == 0x80)
00222             return;
00223 
00224         if (dest_index >= dest_size) {
00225             av_log(NULL, AV_LOG_ERROR, "  VQA video: decode_format80 problem: dest_index (%d) exceeded dest_size (%d)\n",
00226                 dest_index, dest_size);
00227             return;
00228         }
00229 
00230         if (src[src_index] == 0xFF) {
00231 
00232             src_index++;
00233             count = AV_RL16(&src[src_index]);
00234             src_index += 2;
00235             src_pos = AV_RL16(&src[src_index]);
00236             src_index += 2;
00237             av_dlog(NULL, "(1) copy %X bytes from absolute pos %X\n", count, src_pos);
00238             CHECK_COUNT();
00239             if (src_pos + count > dest_size)
00240                 return;
00241             for (i = 0; i < count; i++)
00242                 dest[dest_index + i] = dest[src_pos + i];
00243             dest_index += count;
00244 
00245         } else if (src[src_index] == 0xFE) {
00246 
00247             src_index++;
00248             count = AV_RL16(&src[src_index]);
00249             src_index += 2;
00250             color = src[src_index++];
00251             av_dlog(NULL, "(2) set %X bytes to %02X\n", count, color);
00252             CHECK_COUNT();
00253             memset(&dest[dest_index], color, count);
00254             dest_index += count;
00255 
00256         } else if ((src[src_index] & 0xC0) == 0xC0) {
00257 
00258             count = (src[src_index++] & 0x3F) + 3;
00259             src_pos = AV_RL16(&src[src_index]);
00260             src_index += 2;
00261             av_dlog(NULL, "(3) copy %X bytes from absolute pos %X\n", count, src_pos);
00262             CHECK_COUNT();
00263             if (src_pos + count > dest_size)
00264                 return;
00265             for (i = 0; i < count; i++)
00266                 dest[dest_index + i] = dest[src_pos + i];
00267             dest_index += count;
00268 
00269         } else if (src[src_index] > 0x80) {
00270 
00271             count = src[src_index++] & 0x3F;
00272             av_dlog(NULL, "(4) copy %X bytes from source to dest\n", count);
00273             CHECK_COUNT();
00274             memcpy(&dest[dest_index], &src[src_index], count);
00275             src_index += count;
00276             dest_index += count;
00277 
00278         } else {
00279 
00280             count = ((src[src_index] & 0x70) >> 4) + 3;
00281             src_pos = AV_RB16(&src[src_index]) & 0x0FFF;
00282             src_index += 2;
00283             av_dlog(NULL, "(5) copy %X bytes from relpos %X\n", count, src_pos);
00284             CHECK_COUNT();
00285             if (dest_index < src_pos)
00286                 return;
00287             for (i = 0; i < count; i++)
00288                 dest[dest_index + i] = dest[dest_index - src_pos + i];
00289             dest_index += count;
00290         }
00291     }
00292 
00293     /* validate that the entire destination buffer was filled; this is
00294      * important for decoding frame maps since each vector needs to have a
00295      * codebook entry; it is not important for compressed codebooks because
00296      * not every entry needs to be filled */
00297     if (check_size)
00298         if (dest_index < dest_size)
00299             av_log(NULL, AV_LOG_ERROR, "  VQA video: decode_format80 problem: decode finished with dest_index (%d) < dest_size (%d)\n",
00300                 dest_index, dest_size);
00301 }
00302 
00303 static void vqa_decode_chunk(VqaContext *s)
00304 {
00305     unsigned int chunk_type;
00306     unsigned int chunk_size;
00307     int byte_skip;
00308     unsigned int index = 0;
00309     int i;
00310     unsigned char r, g, b;
00311     int index_shift;
00312 
00313     int cbf0_chunk = -1;
00314     int cbfz_chunk = -1;
00315     int cbp0_chunk = -1;
00316     int cbpz_chunk = -1;
00317     int cpl0_chunk = -1;
00318     int cplz_chunk = -1;
00319     int vptz_chunk = -1;
00320 
00321     int x, y;
00322     int lines = 0;
00323     int pixel_ptr;
00324     int vector_index = 0;
00325     int lobyte = 0;
00326     int hibyte = 0;
00327     int lobytes = 0;
00328     int hibytes = s->decode_buffer_size / 2;
00329 
00330     /* first, traverse through the frame and find the subchunks */
00331     while (index < s->size) {
00332 
00333         chunk_type = AV_RB32(&s->buf[index]);
00334         chunk_size = AV_RB32(&s->buf[index + 4]);
00335 
00336         switch (chunk_type) {
00337 
00338         case CBF0_TAG:
00339             cbf0_chunk = index;
00340             break;
00341 
00342         case CBFZ_TAG:
00343             cbfz_chunk = index;
00344             break;
00345 
00346         case CBP0_TAG:
00347             cbp0_chunk = index;
00348             break;
00349 
00350         case CBPZ_TAG:
00351             cbpz_chunk = index;
00352             break;
00353 
00354         case CPL0_TAG:
00355             cpl0_chunk = index;
00356             break;
00357 
00358         case CPLZ_TAG:
00359             cplz_chunk = index;
00360             break;
00361 
00362         case VPTZ_TAG:
00363             vptz_chunk = index;
00364             break;
00365 
00366         default:
00367             av_log(s->avctx, AV_LOG_ERROR, "  VQA video: Found unknown chunk type: %c%c%c%c (%08X)\n",
00368             (chunk_type >> 24) & 0xFF,
00369             (chunk_type >> 16) & 0xFF,
00370             (chunk_type >>  8) & 0xFF,
00371             (chunk_type >>  0) & 0xFF,
00372             chunk_type);
00373             break;
00374         }
00375 
00376         byte_skip = chunk_size & 0x01;
00377         index += (CHUNK_PREAMBLE_SIZE + chunk_size + byte_skip);
00378     }
00379 
00380     /* next, deal with the palette */
00381     if ((cpl0_chunk != -1) && (cplz_chunk != -1)) {
00382 
00383         /* a chunk should not have both chunk types */
00384         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: found both CPL0 and CPLZ chunks\n");
00385         return;
00386     }
00387 
00388     /* decompress the palette chunk */
00389     if (cplz_chunk != -1) {
00390 
00391 /* yet to be handled */
00392 
00393     }
00394 
00395     /* convert the RGB palette into the machine's endian format */
00396     if (cpl0_chunk != -1) {
00397 
00398         chunk_size = AV_RB32(&s->buf[cpl0_chunk + 4]);
00399         /* sanity check the palette size */
00400         if (chunk_size / 3 > 256) {
00401             av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: found a palette chunk with %d colors\n",
00402                 chunk_size / 3);
00403             return;
00404         }
00405         cpl0_chunk += CHUNK_PREAMBLE_SIZE;
00406         for (i = 0; i < chunk_size / 3; i++) {
00407             /* scale by 4 to transform 6-bit palette -> 8-bit */
00408             r = s->buf[cpl0_chunk++] * 4;
00409             g = s->buf[cpl0_chunk++] * 4;
00410             b = s->buf[cpl0_chunk++] * 4;
00411             s->palette[i] = (r << 16) | (g << 8) | (b);
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         if (chunk_size > MAX_CODEBOOK_SIZE - s->next_codebook_buffer_index) {
00531             av_log(s->avctx, AV_LOG_ERROR, "cbp0 chunk too large (0x%X bytes)\n", chunk_size);
00532             return;
00533         }
00534 
00535         /* accumulate partial codebook */
00536         memcpy(&s->next_codebook_buffer[s->next_codebook_buffer_index],
00537             &s->buf[cbp0_chunk], chunk_size);
00538         s->next_codebook_buffer_index += chunk_size;
00539 
00540         s->partial_countdown--;
00541         if (s->partial_countdown == 0) {
00542 
00543             /* time to replace codebook */
00544             memcpy(s->codebook, s->next_codebook_buffer,
00545                 s->next_codebook_buffer_index);
00546 
00547             /* reset accounting */
00548             s->next_codebook_buffer_index = 0;
00549             s->partial_countdown = s->partial_count;
00550         }
00551     }
00552 
00553     if (cbpz_chunk != -1) {
00554 
00555         chunk_size = AV_RB32(&s->buf[cbpz_chunk + 4]);
00556         cbpz_chunk += CHUNK_PREAMBLE_SIZE;
00557 
00558         if (chunk_size > MAX_CODEBOOK_SIZE - s->next_codebook_buffer_index) {
00559             av_log(s->avctx, AV_LOG_ERROR, "cbpz chunk too large (0x%X bytes)\n", chunk_size);
00560             return;
00561         }
00562 
00563         /* accumulate partial codebook */
00564         memcpy(&s->next_codebook_buffer[s->next_codebook_buffer_index],
00565             &s->buf[cbpz_chunk], chunk_size);
00566         s->next_codebook_buffer_index += chunk_size;
00567 
00568         s->partial_countdown--;
00569         if (s->partial_countdown == 0) {
00570 
00571             /* decompress codebook */
00572             decode_format80(s->next_codebook_buffer,
00573                 s->next_codebook_buffer_index,
00574                 s->codebook, s->codebook_size, 0);
00575 
00576             /* reset accounting */
00577             s->next_codebook_buffer_index = 0;
00578             s->partial_countdown = s->partial_count;
00579         }
00580     }
00581 }
00582 
00583 static int vqa_decode_frame(AVCodecContext *avctx,
00584                             void *data, int *data_size,
00585                             AVPacket *avpkt)
00586 {
00587     const uint8_t *buf = avpkt->data;
00588     int buf_size = avpkt->size;
00589     VqaContext *s = avctx->priv_data;
00590 
00591     s->buf = buf;
00592     s->size = buf_size;
00593 
00594     if (s->frame.data[0])
00595         avctx->release_buffer(avctx, &s->frame);
00596 
00597     if (avctx->get_buffer(avctx, &s->frame)) {
00598         av_log(s->avctx, AV_LOG_ERROR, "  VQA Video: get_buffer() failed\n");
00599         return -1;
00600     }
00601 
00602     vqa_decode_chunk(s);
00603 
00604     /* make the palette available on the way out */
00605     memcpy(s->frame.data[1], s->palette, PALETTE_COUNT * 4);
00606     s->frame.palette_has_changed = 1;
00607 
00608     *data_size = sizeof(AVFrame);
00609     *(AVFrame*)data = s->frame;
00610 
00611     /* report that the buffer was completely consumed */
00612     return buf_size;
00613 }
00614 
00615 static av_cold int vqa_decode_end(AVCodecContext *avctx)
00616 {
00617     VqaContext *s = avctx->priv_data;
00618 
00619     av_free(s->codebook);
00620     av_free(s->next_codebook_buffer);
00621     av_free(s->decode_buffer);
00622 
00623     if (s->frame.data[0])
00624         avctx->release_buffer(avctx, &s->frame);
00625 
00626     return 0;
00627 }
00628 
00629 AVCodec ff_vqa_decoder = {
00630     "vqavideo",
00631     AVMEDIA_TYPE_VIDEO,
00632     CODEC_ID_WS_VQA,
00633     sizeof(VqaContext),
00634     vqa_decode_init,
00635     NULL,
00636     vqa_decode_end,
00637     vqa_decode_frame,
00638     CODEC_CAP_DR1,
00639     .long_name = NULL_IF_CONFIG_SMALL("Westwood Studios VQA (Vector Quantized Animation) video"),
00640 };

Generated on Fri Feb 22 2013 07:24:29 for FFmpeg by  doxygen 1.7.1