LCOV - code coverage report
Current view: top level - libavcodec - vmdvideo.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 146 243 60.1 %
Date: 2017-12-16 21:16:39 Functions: 5 6 83.3 %

          Line data    Source code
       1             : /*
       2             :  * Sierra VMD video decoder
       3             :  * Copyright (c) 2004 The FFmpeg Project
       4             :  *
       5             :  * This file is part of FFmpeg.
       6             :  *
       7             :  * FFmpeg is free software; you can redistribute it and/or
       8             :  * modify it under the terms of the GNU Lesser General Public
       9             :  * License as published by the Free Software Foundation; either
      10             :  * version 2.1 of the License, or (at your option) any later version.
      11             :  *
      12             :  * FFmpeg is distributed in the hope that it will be useful,
      13             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15             :  * Lesser General Public License for more details.
      16             :  *
      17             :  * You should have received a copy of the GNU Lesser General Public
      18             :  * License along with FFmpeg; if not, write to the Free Software
      19             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      20             :  */
      21             : 
      22             : /**
      23             :  * @file
      24             :  * Sierra VMD video decoder
      25             :  * by Vladimir "VAG" Gneushev (vagsoft at mail.ru)
      26             :  * for more information on the Sierra VMD format, visit:
      27             :  *   http://www.pcisys.net/~melanson/codecs/
      28             :  *
      29             :  * The video decoder outputs PAL8 colorspace data. The decoder expects
      30             :  * a 0x330-byte VMD file header to be transmitted via extradata during
      31             :  * codec initialization. Each encoded frame that is sent to this decoder
      32             :  * is expected to be prepended with the appropriate 16-byte frame
      33             :  * information record from the VMD file.
      34             :  */
      35             : 
      36             : #include <string.h>
      37             : 
      38             : #include "libavutil/common.h"
      39             : #include "libavutil/intreadwrite.h"
      40             : 
      41             : #include "avcodec.h"
      42             : #include "internal.h"
      43             : #include "bytestream.h"
      44             : 
      45             : #define VMD_HEADER_SIZE 0x330
      46             : #define PALETTE_COUNT 256
      47             : 
      48             : typedef struct VmdVideoContext {
      49             : 
      50             :     AVCodecContext *avctx;
      51             :     AVFrame *prev_frame;
      52             : 
      53             :     const unsigned char *buf;
      54             :     int size;
      55             : 
      56             :     unsigned char palette[PALETTE_COUNT * 4];
      57             :     unsigned char *unpack_buffer;
      58             :     int unpack_buffer_size;
      59             : 
      60             :     int x_off, y_off;
      61             : } VmdVideoContext;
      62             : 
      63             : #define QUEUE_SIZE 0x1000
      64             : #define QUEUE_MASK 0x0FFF
      65             : 
      66         128 : static int lz_unpack(const unsigned char *src, int src_len,
      67             :                       unsigned char *dest, int dest_len)
      68             : {
      69             :     unsigned char *d;
      70             :     unsigned char *d_end;
      71             :     unsigned char queue[QUEUE_SIZE];
      72             :     unsigned int qpos;
      73             :     unsigned int dataleft;
      74             :     unsigned int chainofs;
      75             :     unsigned int chainlen;
      76             :     unsigned int speclen;
      77             :     unsigned char tag;
      78             :     unsigned int i, j;
      79             :     GetByteContext gb;
      80             : 
      81         128 :     bytestream2_init(&gb, src, src_len);
      82         128 :     d = dest;
      83         128 :     d_end = d + dest_len;
      84         128 :     dataleft = bytestream2_get_le32(&gb);
      85         128 :     memset(queue, 0x20, QUEUE_SIZE);
      86         128 :     if (bytestream2_get_bytes_left(&gb) < 4)
      87           0 :         return AVERROR_INVALIDDATA;
      88         128 :     if (bytestream2_peek_le32(&gb) == 0x56781234) {
      89          84 :         bytestream2_skipu(&gb, 4);
      90          84 :         qpos = 0x111;
      91          84 :         speclen = 0xF + 3;
      92             :     } else {
      93          44 :         qpos = 0xFEE;
      94          44 :         speclen = 100;  /* no speclen */
      95             :     }
      96             : 
      97       20219 :     while (dataleft > 0 && bytestream2_get_bytes_left(&gb) > 0) {
      98       19963 :         tag = bytestream2_get_byteu(&gb);
      99       19963 :         if ((tag == 0xFF) && (dataleft > 8)) {
     100        1814 :             if (d_end - d < 8 || bytestream2_get_bytes_left(&gb) < 8)
     101           0 :                 return AVERROR_INVALIDDATA;
     102       16326 :             for (i = 0; i < 8; i++) {
     103       14512 :                 queue[qpos++] = *d++ = bytestream2_get_byteu(&gb);
     104       14512 :                 qpos &= QUEUE_MASK;
     105             :             }
     106        1814 :             dataleft -= 8;
     107             :         } else {
     108      162858 :             for (i = 0; i < 8; i++) {
     109      144819 :                 if (dataleft == 0)
     110         110 :                     break;
     111      144709 :                 if (tag & 0x01) {
     112       84919 :                     if (d_end - d < 1 || bytestream2_get_bytes_left(&gb) < 1)
     113           0 :                         return AVERROR_INVALIDDATA;
     114       84919 :                     queue[qpos++] = *d++ = bytestream2_get_byteu(&gb);
     115       84919 :                     qpos &= QUEUE_MASK;
     116       84919 :                     dataleft--;
     117             :                 } else {
     118       59790 :                     chainofs = bytestream2_get_byte(&gb);
     119       59790 :                     chainofs |= ((bytestream2_peek_byte(&gb) & 0xF0) << 4);
     120       59790 :                     chainlen = (bytestream2_get_byte(&gb) & 0x0F) + 3;
     121       59790 :                     if (chainlen == speclen) {
     122        7696 :                         chainlen = bytestream2_get_byte(&gb) + 0xF + 3;
     123             :                     }
     124       59790 :                     if (d_end - d < chainlen)
     125           0 :                         return AVERROR_INVALIDDATA;
     126      739328 :                     for (j = 0; j < chainlen; j++) {
     127      679538 :                         *d = queue[chainofs++ & QUEUE_MASK];
     128      679538 :                         queue[qpos++] = *d++;
     129      679538 :                         qpos &= QUEUE_MASK;
     130             :                     }
     131       59790 :                     dataleft -= chainlen;
     132             :                 }
     133      144709 :                 tag >>= 1;
     134             :             }
     135             :         }
     136             :     }
     137         128 :     return d - dest;
     138             : }
     139           0 : static int rle_unpack(const unsigned char *src, unsigned char *dest,
     140             :                       int src_count, int src_size, int dest_len)
     141             : {
     142             :     unsigned char *pd;
     143           0 :     int i, l, used = 0;
     144           0 :     unsigned char *dest_end = dest + dest_len;
     145             :     GetByteContext gb;
     146             :     uint16_t run_val;
     147             : 
     148           0 :     bytestream2_init(&gb, src, src_size);
     149           0 :     pd = dest;
     150           0 :     if (src_count & 1) {
     151           0 :         if (bytestream2_get_bytes_left(&gb) < 1)
     152           0 :             return 0;
     153           0 :         *pd++ = bytestream2_get_byteu(&gb);
     154           0 :         used++;
     155             :     }
     156             : 
     157             :     do {
     158           0 :         if (bytestream2_get_bytes_left(&gb) < 1)
     159           0 :             break;
     160           0 :         l = bytestream2_get_byteu(&gb);
     161           0 :         if (l & 0x80) {
     162           0 :             l = (l & 0x7F) * 2;
     163           0 :             if (dest_end - pd < l || bytestream2_get_bytes_left(&gb) < l)
     164           0 :                 return bytestream2_tell(&gb);
     165           0 :             bytestream2_get_bufferu(&gb, pd, l);
     166           0 :             pd += l;
     167             :         } else {
     168           0 :             if (dest_end - pd < 2*l || bytestream2_get_bytes_left(&gb) < 2)
     169           0 :                 return bytestream2_tell(&gb);
     170           0 :             run_val = bytestream2_get_ne16(&gb);
     171           0 :             for (i = 0; i < l; i++) {
     172           0 :                 AV_WN16(pd, run_val);
     173           0 :                 pd += 2;
     174             :             }
     175           0 :             l *= 2;
     176             :         }
     177           0 :         used += l;
     178           0 :     } while (used < src_count);
     179             : 
     180           0 :     return bytestream2_tell(&gb);
     181             : }
     182             : 
     183         137 : static int vmd_decode(VmdVideoContext *s, AVFrame *frame)
     184             : {
     185             :     int i;
     186             :     unsigned int *palette32;
     187             :     unsigned char r, g, b;
     188             : 
     189             :     GetByteContext gb;
     190             : 
     191             :     unsigned char meth;
     192             :     unsigned char *dp;   /* pointer to current frame */
     193             :     unsigned char *pp;   /* pointer to previous frame */
     194             :     unsigned char len;
     195             :     int ofs;
     196             : 
     197             :     int frame_x, frame_y;
     198             :     int frame_width, frame_height;
     199             : 
     200         137 :     frame_x = AV_RL16(&s->buf[6]);
     201         137 :     frame_y = AV_RL16(&s->buf[8]);
     202         137 :     frame_width = AV_RL16(&s->buf[10]) - frame_x + 1;
     203         137 :     frame_height = AV_RL16(&s->buf[12]) - frame_y + 1;
     204             : 
     205         137 :     if ((frame_width == s->avctx->width && frame_height == s->avctx->height) &&
     206           6 :         (frame_x || frame_y)) {
     207             : 
     208           0 :         s->x_off = frame_x;
     209           0 :         s->y_off = frame_y;
     210             :     }
     211         137 :     frame_x -= s->x_off;
     212         137 :     frame_y -= s->y_off;
     213             : 
     214         274 :     if (frame_x < 0 || frame_width < 0 ||
     215         274 :         frame_x >= s->avctx->width ||
     216         274 :         frame_width > s->avctx->width ||
     217         137 :         frame_x + frame_width > s->avctx->width) {
     218           0 :         av_log(s->avctx, AV_LOG_ERROR,
     219             :                "Invalid horizontal range %d-%d\n",
     220             :                frame_x, frame_width);
     221           0 :         return AVERROR_INVALIDDATA;
     222             :     }
     223         274 :     if (frame_y < 0 || frame_height < 0 ||
     224         274 :         frame_y >= s->avctx->height ||
     225         274 :         frame_height > s->avctx->height ||
     226         137 :         frame_y + frame_height > s->avctx->height) {
     227           0 :         av_log(s->avctx, AV_LOG_ERROR,
     228             :                "Invalid vertical range %d-%d\n",
     229             :                frame_x, frame_width);
     230           0 :         return AVERROR_INVALIDDATA;
     231             :     }
     232             : 
     233             :     /* if only a certain region will be updated, copy the entire previous
     234             :      * frame before the decode */
     235         137 :     if (s->prev_frame->data[0] &&
     236          13 :         (frame_x || frame_y || (frame_width != s->avctx->width) ||
     237           5 :         (frame_height != s->avctx->height))) {
     238             : 
     239         131 :         memcpy(frame->data[0], s->prev_frame->data[0],
     240         131 :             s->avctx->height * frame->linesize[0]);
     241             :     }
     242             : 
     243             :     /* check if there is a new palette */
     244         137 :     bytestream2_init(&gb, s->buf + 16, s->size - 16);
     245         137 :     if (s->buf[15] & 0x02) {
     246           0 :         bytestream2_skip(&gb, 2);
     247           0 :         palette32 = (unsigned int *)s->palette;
     248           0 :         if (bytestream2_get_bytes_left(&gb) >= PALETTE_COUNT * 3) {
     249           0 :             for (i = 0; i < PALETTE_COUNT; i++) {
     250           0 :                 r = bytestream2_get_byteu(&gb) * 4;
     251           0 :                 g = bytestream2_get_byteu(&gb) * 4;
     252           0 :                 b = bytestream2_get_byteu(&gb) * 4;
     253           0 :                 palette32[i] = 0xFFU << 24 | (r << 16) | (g << 8) | (b);
     254           0 :                 palette32[i] |= palette32[i] >> 6 & 0x30303;
     255             :             }
     256             :         } else {
     257           0 :             av_log(s->avctx, AV_LOG_ERROR, "Incomplete palette\n");
     258           0 :             return AVERROR_INVALIDDATA;
     259             :         }
     260             :     }
     261             : 
     262         137 :     if (!s->size)
     263           0 :         return 0;
     264             : 
     265             :     /* originally UnpackFrame in VAG's code */
     266         137 :     if (bytestream2_get_bytes_left(&gb) < 1)
     267           0 :         return AVERROR_INVALIDDATA;
     268         137 :     meth = bytestream2_get_byteu(&gb);
     269         137 :     if (meth & 0x80) {
     270             :         int size;
     271         128 :         if (!s->unpack_buffer_size) {
     272           0 :             av_log(s->avctx, AV_LOG_ERROR,
     273             :                    "Trying to unpack LZ-compressed frame with no LZ buffer\n");
     274           0 :             return AVERROR_INVALIDDATA;
     275             :         }
     276         128 :         size = lz_unpack(gb.buffer, bytestream2_get_bytes_left(&gb),
     277             :                          s->unpack_buffer, s->unpack_buffer_size);
     278         128 :         if (size < 0)
     279           0 :             return size;
     280         128 :         meth &= 0x7F;
     281         128 :         bytestream2_init(&gb, s->unpack_buffer, size);
     282             :     }
     283             : 
     284         137 :     dp = &frame->data[0][frame_y * frame->linesize[0] + frame_x];
     285         137 :     pp = &s->prev_frame->data[0][frame_y * s->prev_frame->linesize[0] + frame_x];
     286         137 :     switch (meth) {
     287         131 :     case 1:
     288       11919 :         for (i = 0; i < frame_height; i++) {
     289       11788 :             ofs = 0;
     290             :             do {
     291       56026 :                 len = bytestream2_get_byte(&gb);
     292       56026 :                 if (len & 0x80) {
     293       21508 :                     len = (len & 0x7F) + 1;
     294       43016 :                     if (ofs + len > frame_width ||
     295       21508 :                         bytestream2_get_bytes_left(&gb) < len)
     296           0 :                         return AVERROR_INVALIDDATA;
     297       21508 :                     bytestream2_get_bufferu(&gb, &dp[ofs], len);
     298       21508 :                     ofs += len;
     299             :                 } else {
     300             :                     /* interframe pixel copy */
     301       34518 :                     if (ofs + len + 1 > frame_width || !s->prev_frame->data[0])
     302           0 :                         return AVERROR_INVALIDDATA;
     303       34518 :                     memcpy(&dp[ofs], &pp[ofs], len + 1);
     304       34518 :                     ofs += len + 1;
     305             :                 }
     306       56026 :             } while (ofs < frame_width);
     307       11788 :             if (ofs > frame_width) {
     308           0 :                 av_log(s->avctx, AV_LOG_ERROR,
     309             :                        "offset > width (%d > %d)\n",
     310             :                        ofs, frame_width);
     311           0 :                 return AVERROR_INVALIDDATA;
     312             :             }
     313       11788 :             dp += frame->linesize[0];
     314       11788 :             pp += s->prev_frame->linesize[0];
     315             :         }
     316         131 :         break;
     317             : 
     318           6 :     case 2:
     319         771 :         for (i = 0; i < frame_height; i++) {
     320         765 :             bytestream2_get_buffer(&gb, dp, frame_width);
     321         765 :             dp += frame->linesize[0];
     322         765 :             pp += s->prev_frame->linesize[0];
     323             :         }
     324           6 :         break;
     325             : 
     326           0 :     case 3:
     327           0 :         for (i = 0; i < frame_height; i++) {
     328           0 :             ofs = 0;
     329             :             do {
     330           0 :                 len = bytestream2_get_byte(&gb);
     331           0 :                 if (len & 0x80) {
     332           0 :                     len = (len & 0x7F) + 1;
     333           0 :                     if (bytestream2_peek_byte(&gb) == 0xFF) {
     334           0 :                         int slen = len;
     335           0 :                         bytestream2_get_byte(&gb);
     336           0 :                         len = rle_unpack(gb.buffer, &dp[ofs],
     337           0 :                                          len, bytestream2_get_bytes_left(&gb),
     338             :                                          frame_width - ofs);
     339           0 :                         ofs += slen;
     340           0 :                         bytestream2_skip(&gb, len);
     341             :                     } else {
     342           0 :                         if (ofs + len > frame_width ||
     343           0 :                             bytestream2_get_bytes_left(&gb) < len)
     344           0 :                             return AVERROR_INVALIDDATA;
     345           0 :                         bytestream2_get_buffer(&gb, &dp[ofs], len);
     346           0 :                         ofs += len;
     347             :                     }
     348             :                 } else {
     349             :                     /* interframe pixel copy */
     350           0 :                     if (ofs + len + 1 > frame_width || !s->prev_frame->data[0])
     351           0 :                         return AVERROR_INVALIDDATA;
     352           0 :                     memcpy(&dp[ofs], &pp[ofs], len + 1);
     353           0 :                     ofs += len + 1;
     354             :                 }
     355           0 :             } while (ofs < frame_width);
     356           0 :             if (ofs > frame_width) {
     357           0 :                 av_log(s->avctx, AV_LOG_ERROR,
     358             :                        "offset > width (%d > %d)\n",
     359             :                        ofs, frame_width);
     360           0 :                 return AVERROR_INVALIDDATA;
     361             :             }
     362           0 :             dp += frame->linesize[0];
     363           0 :             pp += s->prev_frame->linesize[0];
     364             :         }
     365           0 :         break;
     366             :     }
     367         137 :     return 0;
     368             : }
     369             : 
     370           5 : static av_cold int vmdvideo_decode_end(AVCodecContext *avctx)
     371             : {
     372           5 :     VmdVideoContext *s = avctx->priv_data;
     373             : 
     374           5 :     av_frame_free(&s->prev_frame);
     375           5 :     av_freep(&s->unpack_buffer);
     376           5 :     s->unpack_buffer_size = 0;
     377             : 
     378           5 :     return 0;
     379             : }
     380             : 
     381           5 : static av_cold int vmdvideo_decode_init(AVCodecContext *avctx)
     382             : {
     383           5 :     VmdVideoContext *s = avctx->priv_data;
     384             :     int i;
     385             :     unsigned int *palette32;
     386           5 :     int palette_index = 0;
     387             :     unsigned char r, g, b;
     388             :     unsigned char *vmd_header;
     389             :     unsigned char *raw_palette;
     390             : 
     391           5 :     s->avctx = avctx;
     392           5 :     avctx->pix_fmt = AV_PIX_FMT_PAL8;
     393             : 
     394             :     /* make sure the VMD header made it */
     395           5 :     if (s->avctx->extradata_size != VMD_HEADER_SIZE) {
     396           0 :         av_log(s->avctx, AV_LOG_ERROR, "expected extradata size of %d\n",
     397             :             VMD_HEADER_SIZE);
     398           0 :         return AVERROR_INVALIDDATA;
     399             :     }
     400           5 :     vmd_header = (unsigned char *)avctx->extradata;
     401             : 
     402           5 :     s->unpack_buffer_size = AV_RL32(&vmd_header[800]);
     403           5 :     if (s->unpack_buffer_size) {
     404           5 :         s->unpack_buffer = av_malloc(s->unpack_buffer_size);
     405           5 :         if (!s->unpack_buffer)
     406           0 :             return AVERROR(ENOMEM);
     407             :     }
     408             : 
     409             :     /* load up the initial palette */
     410           5 :     raw_palette = &vmd_header[28];
     411           5 :     palette32 = (unsigned int *)s->palette;
     412        1285 :     for (i = 0; i < PALETTE_COUNT; i++) {
     413        1280 :         r = raw_palette[palette_index++] * 4;
     414        1280 :         g = raw_palette[palette_index++] * 4;
     415        1280 :         b = raw_palette[palette_index++] * 4;
     416        1280 :         palette32[i] = 0xFFU << 24 | (r << 16) | (g << 8) | (b);
     417        1280 :         palette32[i] |= palette32[i] >> 6 & 0x30303;
     418             :     }
     419             : 
     420           5 :     s->prev_frame = av_frame_alloc();
     421           5 :     if (!s->prev_frame) {
     422           0 :         vmdvideo_decode_end(avctx);
     423           0 :         return AVERROR(ENOMEM);
     424             :     }
     425             : 
     426           5 :     return 0;
     427             : }
     428             : 
     429         137 : static int vmdvideo_decode_frame(AVCodecContext *avctx,
     430             :                                  void *data, int *got_frame,
     431             :                                  AVPacket *avpkt)
     432             : {
     433         137 :     const uint8_t *buf = avpkt->data;
     434         137 :     int buf_size = avpkt->size;
     435         137 :     VmdVideoContext *s = avctx->priv_data;
     436         137 :     AVFrame *frame = data;
     437             :     int ret;
     438             : 
     439         137 :     s->buf = buf;
     440         137 :     s->size = buf_size;
     441             : 
     442         137 :     if (buf_size < 16)
     443           0 :         return AVERROR_INVALIDDATA;
     444             : 
     445         137 :     if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0)
     446           0 :         return ret;
     447             : 
     448         137 :     if ((ret = vmd_decode(s, frame)) < 0)
     449           0 :         return ret;
     450             : 
     451             :     /* make the palette available on the way out */
     452         137 :     memcpy(frame->data[1], s->palette, PALETTE_COUNT * 4);
     453             : 
     454             :     /* shuffle frames */
     455         137 :     av_frame_unref(s->prev_frame);
     456         137 :     if ((ret = av_frame_ref(s->prev_frame, frame)) < 0)
     457           0 :         return ret;
     458             : 
     459         137 :     *got_frame      = 1;
     460             : 
     461             :     /* report that the buffer was completely consumed */
     462         137 :     return buf_size;
     463             : }
     464             : 
     465             : AVCodec ff_vmdvideo_decoder = {
     466             :     .name           = "vmdvideo",
     467             :     .long_name      = NULL_IF_CONFIG_SMALL("Sierra VMD video"),
     468             :     .type           = AVMEDIA_TYPE_VIDEO,
     469             :     .id             = AV_CODEC_ID_VMDVIDEO,
     470             :     .priv_data_size = sizeof(VmdVideoContext),
     471             :     .init           = vmdvideo_decode_init,
     472             :     .close          = vmdvideo_decode_end,
     473             :     .decode         = vmdvideo_decode_frame,
     474             :     .capabilities   = AV_CODEC_CAP_DR1,
     475             : };

Generated by: LCOV version 1.13