• 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     /* 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 < s->size) {
00326 
00327         chunk_type = AV_RB32(&s->buf[index]);
00328         chunk_size = AV_RB32(&s->buf[index + 4]);
00329 
00330         switch (chunk_type) {
00331 
00332         case CBF0_TAG:
00333             cbf0_chunk = index;
00334             break;
00335 
00336         case CBFZ_TAG:
00337             cbfz_chunk = index;
00338             break;
00339 
00340         case CBP0_TAG:
00341             cbp0_chunk = index;
00342             break;
00343 
00344         case CBPZ_TAG:
00345             cbpz_chunk = index;
00346             break;
00347 
00348         case CPL0_TAG:
00349             cpl0_chunk = index;
00350             break;
00351 
00352         case CPLZ_TAG:
00353             cplz_chunk = index;
00354             break;
00355 
00356         case VPTZ_TAG:
00357             vptz_chunk = index;
00358             break;
00359 
00360         default:
00361             av_log(s->avctx, AV_LOG_ERROR, "  VQA video: Found unknown chunk type: %c%c%c%c (%08X)\n",
00362             (chunk_type >> 24) & 0xFF,
00363             (chunk_type >> 16) & 0xFF,
00364             (chunk_type >>  8) & 0xFF,
00365             (chunk_type >>  0) & 0xFF,
00366             chunk_type);
00367             break;
00368         }
00369 
00370         byte_skip = chunk_size & 0x01;
00371         index += (CHUNK_PREAMBLE_SIZE + chunk_size + byte_skip);
00372     }
00373 
00374     /* next, deal with the palette */
00375     if ((cpl0_chunk != -1) && (cplz_chunk != -1)) {
00376 
00377         /* a chunk should not have both chunk types */
00378         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: found both CPL0 and CPLZ chunks\n");
00379         return;
00380     }
00381 
00382     /* decompress the palette chunk */
00383     if (cplz_chunk != -1) {
00384 
00385 /* yet to be handled */
00386 
00387     }
00388 
00389     /* convert the RGB palette into the machine's endian format */
00390     if (cpl0_chunk != -1) {
00391 
00392         chunk_size = AV_RB32(&s->buf[cpl0_chunk + 4]);
00393         /* sanity check the palette size */
00394         if (chunk_size / 3 > 256) {
00395             av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: found a palette chunk with %d colors\n",
00396                 chunk_size / 3);
00397             return;
00398         }
00399         cpl0_chunk += CHUNK_PREAMBLE_SIZE;
00400         for (i = 0; i < chunk_size / 3; i++) {
00401             /* scale by 4 to transform 6-bit palette -> 8-bit */
00402             r = s->buf[cpl0_chunk++] * 4;
00403             g = s->buf[cpl0_chunk++] * 4;
00404             b = s->buf[cpl0_chunk++] * 4;
00405             s->palette[i] = (r << 16) | (g << 8) | (b);
00406         }
00407     }
00408 
00409     /* next, look for a full codebook */
00410     if ((cbf0_chunk != -1) && (cbfz_chunk != -1)) {
00411 
00412         /* a chunk should not have both chunk types */
00413         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: found both CBF0 and CBFZ chunks\n");
00414         return;
00415     }
00416 
00417     /* decompress the full codebook chunk */
00418     if (cbfz_chunk != -1) {
00419 
00420         chunk_size = AV_RB32(&s->buf[cbfz_chunk + 4]);
00421         cbfz_chunk += CHUNK_PREAMBLE_SIZE;
00422         decode_format80(&s->buf[cbfz_chunk], chunk_size,
00423             s->codebook, s->codebook_size, 0);
00424     }
00425 
00426     /* copy a full codebook */
00427     if (cbf0_chunk != -1) {
00428 
00429         chunk_size = AV_RB32(&s->buf[cbf0_chunk + 4]);
00430         /* sanity check the full codebook size */
00431         if (chunk_size > MAX_CODEBOOK_SIZE) {
00432             av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: CBF0 chunk too large (0x%X bytes)\n",
00433                 chunk_size);
00434             return;
00435         }
00436         cbf0_chunk += CHUNK_PREAMBLE_SIZE;
00437 
00438         memcpy(s->codebook, &s->buf[cbf0_chunk], chunk_size);
00439     }
00440 
00441     /* decode the frame */
00442     if (vptz_chunk == -1) {
00443 
00444         /* something is wrong if there is no VPTZ chunk */
00445         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: no VPTZ chunk found\n");
00446         return;
00447     }
00448 
00449     chunk_size = AV_RB32(&s->buf[vptz_chunk + 4]);
00450     vptz_chunk += CHUNK_PREAMBLE_SIZE;
00451     decode_format80(&s->buf[vptz_chunk], chunk_size,
00452         s->decode_buffer, s->decode_buffer_size, 1);
00453 
00454     /* render the final PAL8 frame */
00455     if (s->vector_height == 4)
00456         index_shift = 4;
00457     else
00458         index_shift = 3;
00459     for (y = 0; y < s->frame.linesize[0] * s->height;
00460         y += s->frame.linesize[0] * s->vector_height) {
00461 
00462         for (x = y; x < y + s->width; x += 4, lobytes++, hibytes++) {
00463             pixel_ptr = x;
00464 
00465             /* get the vector index, the method for which varies according to
00466              * VQA file version */
00467             switch (s->vqa_version) {
00468 
00469             case 1:
00470                 lobyte = s->decode_buffer[lobytes * 2];
00471                 hibyte = s->decode_buffer[(lobytes * 2) + 1];
00472                 vector_index = ((hibyte << 8) | lobyte) >> 3;
00473                 vector_index <<= index_shift;
00474                 lines = s->vector_height;
00475                 /* uniform color fill - a quick hack */
00476                 if (hibyte == 0xFF) {
00477                     while (lines--) {
00478                         s->frame.data[0][pixel_ptr + 0] = 255 - lobyte;
00479                         s->frame.data[0][pixel_ptr + 1] = 255 - lobyte;
00480                         s->frame.data[0][pixel_ptr + 2] = 255 - lobyte;
00481                         s->frame.data[0][pixel_ptr + 3] = 255 - lobyte;
00482                         pixel_ptr += s->frame.linesize[0];
00483                     }
00484                     lines=0;
00485                 }
00486                 break;
00487 
00488             case 2:
00489                 lobyte = s->decode_buffer[lobytes];
00490                 hibyte = s->decode_buffer[hibytes];
00491                 vector_index = (hibyte << 8) | lobyte;
00492                 vector_index <<= index_shift;
00493                 lines = s->vector_height;
00494                 break;
00495 
00496             case 3:
00497 /* not implemented yet */
00498                 lines = 0;
00499                 break;
00500             }
00501 
00502             while (lines--) {
00503                 s->frame.data[0][pixel_ptr + 0] = s->codebook[vector_index++];
00504                 s->frame.data[0][pixel_ptr + 1] = s->codebook[vector_index++];
00505                 s->frame.data[0][pixel_ptr + 2] = s->codebook[vector_index++];
00506                 s->frame.data[0][pixel_ptr + 3] = s->codebook[vector_index++];
00507                 pixel_ptr += s->frame.linesize[0];
00508             }
00509         }
00510     }
00511 
00512     /* handle partial codebook */
00513     if ((cbp0_chunk != -1) && (cbpz_chunk != -1)) {
00514         /* a chunk should not have both chunk types */
00515         av_log(s->avctx, AV_LOG_ERROR, "  VQA video: problem: found both CBP0 and CBPZ chunks\n");
00516         return;
00517     }
00518 
00519     if (cbp0_chunk != -1) {
00520 
00521         chunk_size = AV_RB32(&s->buf[cbp0_chunk + 4]);
00522         cbp0_chunk += CHUNK_PREAMBLE_SIZE;
00523 
00524         /* accumulate partial codebook */
00525         memcpy(&s->next_codebook_buffer[s->next_codebook_buffer_index],
00526             &s->buf[cbp0_chunk], chunk_size);
00527         s->next_codebook_buffer_index += chunk_size;
00528 
00529         s->partial_countdown--;
00530         if (s->partial_countdown == 0) {
00531 
00532             /* time to replace codebook */
00533             memcpy(s->codebook, s->next_codebook_buffer,
00534                 s->next_codebook_buffer_index);
00535 
00536             /* reset accounting */
00537             s->next_codebook_buffer_index = 0;
00538             s->partial_countdown = s->partial_count;
00539         }
00540     }
00541 
00542     if (cbpz_chunk != -1) {
00543 
00544         chunk_size = AV_RB32(&s->buf[cbpz_chunk + 4]);
00545         cbpz_chunk += CHUNK_PREAMBLE_SIZE;
00546 
00547         /* accumulate partial codebook */
00548         memcpy(&s->next_codebook_buffer[s->next_codebook_buffer_index],
00549             &s->buf[cbpz_chunk], chunk_size);
00550         s->next_codebook_buffer_index += chunk_size;
00551 
00552         s->partial_countdown--;
00553         if (s->partial_countdown == 0) {
00554 
00555             /* decompress codebook */
00556             decode_format80(s->next_codebook_buffer,
00557                 s->next_codebook_buffer_index,
00558                 s->codebook, s->codebook_size, 0);
00559 
00560             /* reset accounting */
00561             s->next_codebook_buffer_index = 0;
00562             s->partial_countdown = s->partial_count;
00563         }
00564     }
00565 }
00566 
00567 static int vqa_decode_frame(AVCodecContext *avctx,
00568                             void *data, int *data_size,
00569                             AVPacket *avpkt)
00570 {
00571     const uint8_t *buf = avpkt->data;
00572     int buf_size = avpkt->size;
00573     VqaContext *s = avctx->priv_data;
00574 
00575     s->buf = buf;
00576     s->size = buf_size;
00577 
00578     if (s->frame.data[0])
00579         avctx->release_buffer(avctx, &s->frame);
00580 
00581     if (avctx->get_buffer(avctx, &s->frame)) {
00582         av_log(s->avctx, AV_LOG_ERROR, "  VQA Video: get_buffer() failed\n");
00583         return -1;
00584     }
00585 
00586     vqa_decode_chunk(s);
00587 
00588     /* make the palette available on the way out */
00589     memcpy(s->frame.data[1], s->palette, PALETTE_COUNT * 4);
00590     s->frame.palette_has_changed = 1;
00591 
00592     *data_size = sizeof(AVFrame);
00593     *(AVFrame*)data = s->frame;
00594 
00595     /* report that the buffer was completely consumed */
00596     return buf_size;
00597 }
00598 
00599 static av_cold int vqa_decode_end(AVCodecContext *avctx)
00600 {
00601     VqaContext *s = avctx->priv_data;
00602 
00603     av_free(s->codebook);
00604     av_free(s->next_codebook_buffer);
00605     av_free(s->decode_buffer);
00606 
00607     if (s->frame.data[0])
00608         avctx->release_buffer(avctx, &s->frame);
00609 
00610     return 0;
00611 }
00612 
00613 AVCodec ff_vqa_decoder = {
00614     "vqavideo",
00615     AVMEDIA_TYPE_VIDEO,
00616     CODEC_ID_WS_VQA,
00617     sizeof(VqaContext),
00618     vqa_decode_init,
00619     NULL,
00620     vqa_decode_end,
00621     vqa_decode_frame,
00622     CODEC_CAP_DR1,
00623     .long_name = NULL_IF_CONFIG_SMALL("Westwood Studios VQA (Vector Quantized Animation) video"),
00624 };

Generated on Wed Apr 11 2012 07:31:35 for FFmpeg by  doxygen 1.7.1