libavcodec/vb.c
Go to the documentation of this file.
00001 /*
00002  * Beam Software VB decoder
00003  * Copyright (c) 2007 Konstantin Shishkov
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 
00027 #include <stdio.h>
00028 #include <stdlib.h>
00029 
00030 #include "avcodec.h"
00031 #include "bytestream.h"
00032 
00033 enum VBFlags{
00034     VB_HAS_GMC     = 0x01,
00035     VB_HAS_AUDIO   = 0x04,
00036     VB_HAS_VIDEO   = 0x08,
00037     VB_HAS_PALETTE = 0x10,
00038     VB_HAS_LENGTH  = 0x20
00039 };
00040 
00041 typedef struct VBDecContext {
00042     AVCodecContext *avctx;
00043     AVFrame pic;
00044 
00045     uint8_t *frame, *prev_frame;
00046     uint32_t pal[AVPALETTE_COUNT];
00047     GetByteContext stream;
00048 } VBDecContext;
00049 
00050 static const uint16_t vb_patterns[64] = {
00051     0x0660, 0xFF00, 0xCCCC, 0xF000, 0x8888, 0x000F, 0x1111, 0xFEC8,
00052     0x8CEF, 0x137F, 0xF731, 0xC800, 0x008C, 0x0013, 0x3100, 0xCC00,
00053     0x00CC, 0x0033, 0x3300, 0x0FF0, 0x6666, 0x00F0, 0x0F00, 0x2222,
00054     0x4444, 0xF600, 0x8CC8, 0x006F, 0x1331, 0x318C, 0xC813, 0x33CC,
00055     0x6600, 0x0CC0, 0x0066, 0x0330, 0xF900, 0xC88C, 0x009F, 0x3113,
00056     0x6000, 0x0880, 0x0006, 0x0110, 0xCC88, 0xFC00, 0x00CF, 0x88CC,
00057     0x003F, 0x1133, 0x3311, 0xF300, 0x6FF6, 0x0603, 0x08C6, 0x8C63,
00058     0xC631, 0x6310, 0xC060, 0x0136, 0x136C, 0x36C8, 0x6C80, 0x324C
00059 };
00060 
00061 static void vb_decode_palette(VBDecContext *c, int data_size)
00062 {
00063     int start, size, i;
00064 
00065     start = bytestream2_get_byte(&c->stream);
00066     size = (bytestream2_get_byte(&c->stream) - 1) & 0xFF;
00067     if(start + size > 255){
00068         av_log(c->avctx, AV_LOG_ERROR, "Palette change runs beyond entry 256\n");
00069         return;
00070     }
00071     if(size*3+2 > data_size){
00072         av_log(c->avctx, AV_LOG_ERROR, "Palette data runs beyond chunk size\n");
00073         return;
00074     }
00075     for(i = start; i <= start + size; i++)
00076         c->pal[i] = 0xFFU << 24 | bytestream2_get_be24(&c->stream);
00077 }
00078 
00079 static inline int check_pixel(uint8_t *buf, uint8_t *start, uint8_t *end)
00080 {
00081     return buf >= start && buf < end;
00082 }
00083 
00084 static inline int check_line(uint8_t *buf, uint8_t *start, uint8_t *end)
00085 {
00086     return buf >= start && (buf + 4) <= end;
00087 }
00088 
00089 static int vb_decode_framedata(VBDecContext *c, int offset)
00090 {
00091     GetByteContext g;
00092     uint8_t *prev, *cur;
00093     int blk, blocks, t, blk2;
00094     int blocktypes = 0;
00095     int x, y, a, b;
00096     int pattype, pattern;
00097     const int width = c->avctx->width;
00098     uint8_t *pstart = c->prev_frame;
00099     uint8_t *pend = c->prev_frame + width*c->avctx->height;
00100 
00101     g = c->stream;
00102 
00103     prev = c->prev_frame + offset;
00104     cur = c->frame;
00105 
00106     blocks = (c->avctx->width >> 2) * (c->avctx->height >> 2);
00107     blk2 = 0;
00108     for(blk = 0; blk < blocks; blk++){
00109         if(!(blk & 3)) {
00110             blocktypes = bytestream2_get_byte(&g);
00111         }
00112         switch(blocktypes & 0xC0){
00113         case 0x00: //skip
00114             for(y = 0; y < 4; y++)
00115                 if(check_line(prev + y*width, pstart, pend))
00116                     memcpy(cur + y*width, prev + y*width, 4);
00117                 else
00118                     memset(cur + y*width, 0, 4);
00119             break;
00120         case 0x40:
00121             t = bytestream2_get_byte(&g);
00122             if(!t){ //raw block
00123                 if (bytestream2_get_bytes_left(&g) < 16) {
00124                     av_log(c->avctx, AV_LOG_ERROR, "Insufficient data\n");
00125                     return -1;
00126                 }
00127                 for(y = 0; y < 4; y++)
00128                     bytestream2_get_buffer(&g, cur + y * width, 4);
00129             }else{ // motion compensation
00130                 x = ((t & 0xF)^8) - 8;
00131                 y = ((t >> 4) ^8) - 8;
00132                 t = x + y*width;
00133                 for(y = 0; y < 4; y++)
00134                     if(check_line(prev + t + y*width, pstart, pend))
00135                         memcpy(cur + y*width, prev + t + y*width, 4);
00136                     else
00137                         memset(cur + y*width, 0, 4);
00138             }
00139             break;
00140         case 0x80: // fill
00141             t = bytestream2_get_byte(&g);
00142             for(y = 0; y < 4; y++)
00143                 memset(cur + y*width, t, 4);
00144             break;
00145         case 0xC0: // pattern fill
00146             t = bytestream2_get_byte(&g);
00147             pattype = t >> 6;
00148             pattern = vb_patterns[t & 0x3F];
00149             switch(pattype){
00150             case 0:
00151                 a = bytestream2_get_byte(&g);
00152                 b = bytestream2_get_byte(&g);
00153                 for(y = 0; y < 4; y++)
00154                     for(x = 0; x < 4; x++, pattern >>= 1)
00155                         cur[x + y*width] = (pattern & 1) ? b : a;
00156                 break;
00157             case 1:
00158                 pattern = ~pattern;
00159             case 2:
00160                 a = bytestream2_get_byte(&g);
00161                 for(y = 0; y < 4; y++)
00162                     for(x = 0; x < 4; x++, pattern >>= 1)
00163                         if(pattern & 1 && check_pixel(prev + x + y*width, pstart, pend))
00164                             cur[x + y*width] = prev[x + y*width];
00165                         else
00166                             cur[x + y*width] = a;
00167                 break;
00168             case 3:
00169                 av_log(c->avctx, AV_LOG_ERROR, "Invalid opcode seen @%d\n",blk);
00170                 return -1;
00171             }
00172             break;
00173         }
00174         blocktypes <<= 2;
00175         cur  += 4;
00176         prev += 4;
00177         blk2++;
00178         if(blk2 == (width >> 2)){
00179             blk2 = 0;
00180             cur  += width * 3;
00181             prev += width * 3;
00182         }
00183     }
00184     return 0;
00185 }
00186 
00187 static int decode_frame(AVCodecContext *avctx, void *data, int *data_size, AVPacket *avpkt)
00188 {
00189     VBDecContext * const c = avctx->priv_data;
00190     uint8_t *outptr, *srcptr;
00191     int i, j;
00192     int flags;
00193     uint32_t size;
00194     int offset = 0;
00195 
00196     bytestream2_init(&c->stream, avpkt->data, avpkt->size);
00197 
00198     if(c->pic.data[0])
00199         avctx->release_buffer(avctx, &c->pic);
00200     c->pic.reference = 3;
00201     if(avctx->get_buffer(avctx, &c->pic) < 0){
00202         av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
00203         return -1;
00204     }
00205 
00206     flags = bytestream2_get_le16(&c->stream);
00207 
00208     if(flags & VB_HAS_GMC){
00209         i = (int16_t)bytestream2_get_le16(&c->stream);
00210         j = (int16_t)bytestream2_get_le16(&c->stream);
00211         offset = i + j * avctx->width;
00212     }
00213     if(flags & VB_HAS_VIDEO){
00214         size = bytestream2_get_le32(&c->stream);
00215         if(size > bytestream2_get_bytes_left(&c->stream)+4 || size<4){
00216             av_log(avctx, AV_LOG_ERROR, "Frame size invalid\n");
00217             return -1;
00218         }
00219         vb_decode_framedata(c, offset);
00220         bytestream2_skip(&c->stream, size - 4);
00221     }
00222     if(flags & VB_HAS_PALETTE){
00223         size = bytestream2_get_le32(&c->stream);
00224         vb_decode_palette(c, size);
00225     }
00226 
00227     memcpy(c->pic.data[1], c->pal, AVPALETTE_SIZE);
00228     c->pic.palette_has_changed = flags & VB_HAS_PALETTE;
00229 
00230     outptr = c->pic.data[0];
00231     srcptr = c->frame;
00232 
00233     for(i = 0; i < avctx->height; i++){
00234         memcpy(outptr, srcptr, avctx->width);
00235         srcptr += avctx->width;
00236         outptr += c->pic.linesize[0];
00237     }
00238 
00239     FFSWAP(uint8_t*, c->frame, c->prev_frame);
00240 
00241     *data_size = sizeof(AVFrame);
00242     *(AVFrame*)data = c->pic;
00243 
00244     /* always report that the buffer was completely consumed */
00245     return avpkt->size;
00246 }
00247 
00248 static av_cold int decode_init(AVCodecContext *avctx)
00249 {
00250     VBDecContext * const c = avctx->priv_data;
00251 
00252     c->avctx = avctx;
00253     avctx->pix_fmt = PIX_FMT_PAL8;
00254     avcodec_get_frame_defaults(&c->pic);
00255 
00256     c->frame      = av_mallocz(avctx->width * avctx->height);
00257     c->prev_frame = av_mallocz(avctx->width * avctx->height);
00258 
00259     return 0;
00260 }
00261 
00262 static av_cold int decode_end(AVCodecContext *avctx)
00263 {
00264     VBDecContext *c = avctx->priv_data;
00265 
00266     av_freep(&c->frame);
00267     av_freep(&c->prev_frame);
00268     if(c->pic.data[0])
00269         avctx->release_buffer(avctx, &c->pic);
00270 
00271     return 0;
00272 }
00273 
00274 AVCodec ff_vb_decoder = {
00275     .name           = "vb",
00276     .type           = AVMEDIA_TYPE_VIDEO,
00277     .id             = CODEC_ID_VB,
00278     .priv_data_size = sizeof(VBDecContext),
00279     .init           = decode_init,
00280     .close          = decode_end,
00281     .decode         = decode_frame,
00282     .long_name = NULL_IF_CONFIG_SMALL("Beam Software VB"),
00283 };
00284