LCOV - code coverage report
Current view: top level - libavcodec - yop.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 84 102 82.4 %
Date: 2017-12-11 20:48:03 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Psygnosis YOP decoder
       3             :  *
       4             :  * Copyright (C) 2010 Mohamed Naufal Basheer <naufal11@gmail.com>
       5             :  * derived from the code by
       6             :  * Copyright (C) 2009 Thomas P. Higdon <thomas.p.higdon@gmail.com>
       7             :  *
       8             :  * This file is part of FFmpeg.
       9             :  *
      10             :  * FFmpeg is free software; you can redistribute it and/or
      11             :  * modify it under the terms of the GNU Lesser General Public
      12             :  * License as published by the Free Software Foundation; either
      13             :  * version 2.1 of the License, or (at your option) any later version.
      14             :  *
      15             :  * FFmpeg is distributed in the hope that it will be useful,
      16             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      18             :  * Lesser General Public License for more details.
      19             :  *
      20             :  * You should have received a copy of the GNU Lesser General Public
      21             :  * License along with FFmpeg; if not, write to the Free Software
      22             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      23             :  */
      24             : 
      25             : #include <string.h>
      26             : 
      27             : #include "libavutil/imgutils.h"
      28             : #include "libavutil/internal.h"
      29             : #include "libavutil/intreadwrite.h"
      30             : 
      31             : #include "avcodec.h"
      32             : #include "internal.h"
      33             : 
      34             : typedef struct YopDecContext {
      35             :     AVCodecContext *avctx;
      36             :     AVFrame *frame;
      37             : 
      38             :     int num_pal_colors;
      39             :     int first_color[2];
      40             :     int frame_data_length;
      41             : 
      42             :     uint8_t *low_nibble;
      43             :     uint8_t *srcptr;
      44             :     uint8_t *src_end;
      45             :     uint8_t *dstptr;
      46             :     uint8_t *dstbuf;
      47             : } YopDecContext;
      48             : 
      49             : // These tables are taken directly from:
      50             : // http://wiki.multimedia.cx/index.php?title=Psygnosis_YOP
      51             : 
      52             : /**
      53             :  * Lookup table for painting macroblocks. Bytes 0-2 of each entry contain
      54             :  * the macroblock positions to be painted (taken as (0, B0, B1, B2)).
      55             :  * Byte 3 contains the number of bytes consumed on the input,
      56             :  * equal to max(bytes 0-2) + 1.
      57             :  */
      58             : static const uint8_t paint_lut[15][4] =
      59             :     {{1, 2, 3, 4}, {1, 2, 0, 3},
      60             :      {1, 2, 1, 3}, {1, 2, 2, 3},
      61             :      {1, 0, 2, 3}, {1, 0, 0, 2},
      62             :      {1, 0, 1, 2}, {1, 1, 2, 3},
      63             :      {0, 1, 2, 3}, {0, 1, 0, 2},
      64             :      {1, 1, 0, 2}, {0, 1, 1, 2},
      65             :      {0, 0, 1, 2}, {0, 0, 0, 1},
      66             :      {1, 1, 1, 2},
      67             :     };
      68             : 
      69             : /**
      70             :  * Lookup table for copying macroblocks. Each entry contains the respective
      71             :  * x and y pixel offset for the copy source.
      72             :  */
      73             : static const int8_t motion_vector[16][2] =
      74             :     {{-4, -4}, {-2, -4},
      75             :      { 0, -4}, { 2, -4},
      76             :      {-4, -2}, {-4,  0},
      77             :      {-3, -3}, {-1, -3},
      78             :      { 1, -3}, { 3, -3},
      79             :      {-3, -1}, {-2, -2},
      80             :      { 0, -2}, { 2, -2},
      81             :      { 4, -2}, {-2,  0},
      82             :     };
      83             : 
      84           2 : static av_cold int yop_decode_close(AVCodecContext *avctx)
      85             : {
      86           2 :     YopDecContext *s = avctx->priv_data;
      87             : 
      88           2 :     av_frame_free(&s->frame);
      89             : 
      90           2 :     return 0;
      91             : }
      92             : 
      93           2 : static av_cold int yop_decode_init(AVCodecContext *avctx)
      94             : {
      95           2 :     YopDecContext *s = avctx->priv_data;
      96           2 :     s->avctx = avctx;
      97             : 
      98           4 :     if (avctx->width & 1 || avctx->height & 1 ||
      99           2 :         av_image_check_size(avctx->width, avctx->height, 0, avctx) < 0) {
     100           0 :         av_log(avctx, AV_LOG_ERROR, "YOP has invalid dimensions\n");
     101           0 :         return AVERROR_INVALIDDATA;
     102             :     }
     103             : 
     104           2 :     if (avctx->extradata_size < 3) {
     105           0 :         av_log(avctx, AV_LOG_ERROR, "Missing or incomplete extradata.\n");
     106           0 :         return AVERROR_INVALIDDATA;
     107             :     }
     108             : 
     109           2 :     avctx->pix_fmt = AV_PIX_FMT_PAL8;
     110             : 
     111           2 :     s->num_pal_colors = avctx->extradata[0];
     112           2 :     s->first_color[0] = avctx->extradata[1];
     113           2 :     s->first_color[1] = avctx->extradata[2];
     114             : 
     115           4 :     if (s->num_pal_colors + s->first_color[0] > 256 ||
     116           2 :         s->num_pal_colors + s->first_color[1] > 256) {
     117           0 :         av_log(avctx, AV_LOG_ERROR,
     118             :                "Palette parameters invalid, header probably corrupt\n");
     119           0 :         return AVERROR_INVALIDDATA;
     120             :     }
     121             : 
     122           2 :     s->frame = av_frame_alloc();
     123           2 :     if (!s->frame)
     124           0 :         return AVERROR(ENOMEM);
     125             : 
     126           2 :     return 0;
     127             : }
     128             : 
     129             : /**
     130             :  * Paint a macroblock using the pattern in paint_lut.
     131             :  * @param s codec context
     132             :  * @param tag the tag that was in the nibble
     133             :  */
     134       76866 : static int yop_paint_block(YopDecContext *s, int linesize, int tag)
     135             : {
     136       76866 :     if (s->src_end - s->srcptr < paint_lut[tag][3]) {
     137           1 :         av_log(s->avctx, AV_LOG_ERROR, "Packet too small.\n");
     138           1 :         return AVERROR_INVALIDDATA;
     139             :     }
     140             : 
     141       76865 :     s->dstptr[0]            = s->srcptr[0];
     142       76865 :     s->dstptr[1]            = s->srcptr[paint_lut[tag][0]];
     143       76865 :     s->dstptr[linesize]     = s->srcptr[paint_lut[tag][1]];
     144       76865 :     s->dstptr[linesize + 1] = s->srcptr[paint_lut[tag][2]];
     145             : 
     146             :     // The number of src bytes consumed is in the last part of the lut entry.
     147       76865 :     s->srcptr += paint_lut[tag][3];
     148       76865 :     return 0;
     149             : }
     150             : 
     151             : /**
     152             :  * Copy a previously painted macroblock to the current_block.
     153             :  * @param copy_tag the tag that was in the nibble
     154             :  */
     155       90934 : static int yop_copy_previous_block(YopDecContext *s, int linesize, int copy_tag)
     156             : {
     157             :     uint8_t *bufptr;
     158             : 
     159             :     // Calculate position for the copy source
     160      181868 :     bufptr = s->dstptr + motion_vector[copy_tag][0] +
     161       90934 :              linesize * motion_vector[copy_tag][1];
     162       90934 :     if (bufptr < s->dstbuf) {
     163           0 :         av_log(s->avctx, AV_LOG_ERROR, "File probably corrupt\n");
     164           0 :         return AVERROR_INVALIDDATA;
     165             :     }
     166             : 
     167       90934 :     s->dstptr[0]            = bufptr[0];
     168       90934 :     s->dstptr[1]            = bufptr[1];
     169       90934 :     s->dstptr[linesize]     = bufptr[linesize];
     170       90934 :     s->dstptr[linesize + 1] = bufptr[linesize + 1];
     171             : 
     172       90934 :     return 0;
     173             : }
     174             : 
     175             : /**
     176             :  * Return the next nibble in sequence, consuming a new byte on the input
     177             :  * only if necessary.
     178             :  */
     179      258734 : static uint8_t yop_get_next_nibble(YopDecContext *s)
     180             : {
     181             :     int ret;
     182             : 
     183      258734 :     if (s->low_nibble) {
     184      129365 :         ret           = *s->low_nibble & 0xf;
     185      129365 :         s->low_nibble = NULL;
     186             :     }else {
     187      129369 :         s->low_nibble = s->srcptr++;
     188      129369 :         ret           = *s->low_nibble >> 4;
     189             :     }
     190      258734 :     return ret;
     191             : }
     192             : 
     193           7 : static int yop_decode_frame(AVCodecContext *avctx, void *data, int *got_frame,
     194             :                             AVPacket *avpkt)
     195             : {
     196           7 :     YopDecContext *s = avctx->priv_data;
     197           7 :     AVFrame *frame = s->frame;
     198             :     int tag, firstcolor, is_odd_frame;
     199             :     int ret, i, x, y;
     200             :     uint32_t *palette;
     201             : 
     202           7 :     if (avpkt->size < 4 + 3 * s->num_pal_colors) {
     203           0 :         av_log(avctx, AV_LOG_ERROR, "Packet too small.\n");
     204           0 :         return AVERROR_INVALIDDATA;
     205             :     }
     206             : 
     207           7 :     if ((ret = ff_reget_buffer(avctx, frame)) < 0)
     208           0 :         return ret;
     209             : 
     210           7 :     if (!avctx->frame_number)
     211           1 :         memset(frame->data[1], 0, AVPALETTE_SIZE);
     212             : 
     213           7 :     s->dstbuf     = frame->data[0];
     214           7 :     s->dstptr     = frame->data[0];
     215           7 :     s->srcptr     = avpkt->data + 4;
     216           7 :     s->src_end    = avpkt->data + avpkt->size;
     217           7 :     s->low_nibble = NULL;
     218             : 
     219           7 :     is_odd_frame = avpkt->data[0];
     220           7 :     if(is_odd_frame>1){
     221           0 :         av_log(avctx, AV_LOG_ERROR, "frame is too odd %d\n", is_odd_frame);
     222           0 :         return AVERROR_INVALIDDATA;
     223             :     }
     224           7 :     firstcolor   = s->first_color[is_odd_frame];
     225           7 :     palette      = (uint32_t *)frame->data[1];
     226             : 
     227         847 :     for (i = 0; i < s->num_pal_colors; i++, s->srcptr += 3) {
     228        3360 :         palette[i + firstcolor] = (s->srcptr[0] << 18) |
     229        2520 :                                   (s->srcptr[1] << 10) |
     230         840 :                                   (s->srcptr[2] << 2);
     231        1680 :         palette[i + firstcolor] |= 0xFFU << 24 |
     232         840 :                                    (palette[i + firstcolor] >> 6) & 0x30303;
     233             :     }
     234             : 
     235           7 :     frame->palette_has_changed = 1;
     236             : 
     237         585 :     for (y = 0; y < avctx->height; y += 2) {
     238      168378 :         for (x = 0; x < avctx->width; x += 2) {
     239      167800 :             if (s->srcptr - avpkt->data >= avpkt->size) {
     240           0 :                 av_log(avctx, AV_LOG_ERROR, "Packet too small.\n");
     241           0 :                 return AVERROR_INVALIDDATA;
     242             :             }
     243             : 
     244      167800 :             tag = yop_get_next_nibble(s);
     245             : 
     246      167800 :             if (tag != 0xf) {
     247       76866 :                 ret = yop_paint_block(s, frame->linesize[0], tag);
     248       76866 :                 if (ret < 0)
     249           1 :                     return ret;
     250             :             } else {
     251       90934 :                 tag = yop_get_next_nibble(s);
     252       90934 :                 ret = yop_copy_previous_block(s, frame->linesize[0], tag);
     253       90934 :                 if (ret < 0)
     254           0 :                     return ret;
     255             :             }
     256      167799 :             s->dstptr += 2;
     257             :         }
     258         578 :         s->dstptr += 2*frame->linesize[0] - x;
     259             :     }
     260             : 
     261           6 :     if ((ret = av_frame_ref(data, s->frame)) < 0)
     262           0 :         return ret;
     263             : 
     264           6 :     *got_frame = 1;
     265           6 :     return avpkt->size;
     266             : }
     267             : 
     268             : AVCodec ff_yop_decoder = {
     269             :     .name           = "yop",
     270             :     .long_name      = NULL_IF_CONFIG_SMALL("Psygnosis YOP Video"),
     271             :     .type           = AVMEDIA_TYPE_VIDEO,
     272             :     .id             = AV_CODEC_ID_YOP,
     273             :     .priv_data_size = sizeof(YopDecContext),
     274             :     .init           = yop_decode_init,
     275             :     .close          = yop_decode_close,
     276             :     .decode         = yop_decode_frame,
     277             : };

Generated by: LCOV version 1.13