Libav 0.7.1
libavcodec/kgv1dec.c
Go to the documentation of this file.
00001 /*
00002  * Kega Game Video (KGV1) decoder
00003  * Copyright (c) 2010 Daniel Verkamp
00004  *
00005  * This file is part of Libav.
00006  *
00007  * Libav 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  * Libav 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 Libav; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  */
00021 
00027 #include "libavutil/intreadwrite.h"
00028 #include "libavutil/imgutils.h"
00029 #include "avcodec.h"
00030 
00031 typedef struct {
00032     AVCodecContext *avctx;
00033     AVFrame pic;
00034     uint16_t *prev, *cur;
00035 } KgvContext;
00036 
00037 static int decode_frame(AVCodecContext *avctx, void *data, int *data_size, AVPacket *avpkt)
00038 {
00039     const uint8_t *buf = avpkt->data;
00040     const uint8_t *buf_end = buf + avpkt->size;
00041     KgvContext * const c = avctx->priv_data;
00042     int offsets[7];
00043     uint16_t *out, *prev;
00044     int outcnt = 0, maxcnt;
00045     int w, h, i;
00046 
00047     if (avpkt->size < 2)
00048         return -1;
00049 
00050     w = (buf[0] + 1) * 8;
00051     h = (buf[1] + 1) * 8;
00052     buf += 2;
00053 
00054     if (av_image_check_size(w, h, 0, avctx))
00055         return -1;
00056 
00057     if (w != avctx->width || h != avctx->height)
00058         avcodec_set_dimensions(avctx, w, h);
00059 
00060     maxcnt = w * h;
00061 
00062     out = av_realloc(c->cur, w * h * 2);
00063     if (!out)
00064         return -1;
00065     c->cur = out;
00066 
00067     prev = av_realloc(c->prev, w * h * 2);
00068     if (!prev)
00069         return -1;
00070     c->prev = prev;
00071 
00072     for (i = 0; i < 7; i++)
00073         offsets[i] = -1;
00074 
00075     while (outcnt < maxcnt && buf_end - 2 > buf) {
00076         int code = AV_RL16(buf);
00077         buf += 2;
00078 
00079         if (!(code & 0x8000)) {
00080             out[outcnt++] = code; // rgb555 pixel coded directly
00081         } else {
00082             int count;
00083             uint16_t *inp;
00084 
00085             if ((code & 0x6000) == 0x6000) {
00086                 // copy from previous frame
00087                 int oidx = (code >> 10) & 7;
00088                 int start;
00089 
00090                 count = (code & 0x3FF) + 3;
00091 
00092                 if (offsets[oidx] < 0) {
00093                     if (buf_end - 3 < buf)
00094                         break;
00095                     offsets[oidx] = AV_RL24(buf);
00096                     buf += 3;
00097                 }
00098 
00099                 start = (outcnt + offsets[oidx]) % maxcnt;
00100 
00101                 if (maxcnt - start < count)
00102                     break;
00103 
00104                 inp = prev + start;
00105             } else {
00106                 // copy from earlier in this frame
00107                 int offset = (code & 0x1FFF) + 1;
00108 
00109                 if (!(code & 0x6000)) {
00110                     count = 2;
00111                 } else if ((code & 0x6000) == 0x2000) {
00112                     count = 3;
00113                 } else {
00114                     if (buf_end - 1 < buf)
00115                         break;
00116                     count = 4 + *buf++;
00117                 }
00118 
00119                 if (outcnt < offset)
00120                     break;
00121 
00122                 inp = out + outcnt - offset;
00123             }
00124 
00125             if (maxcnt - outcnt < count)
00126                 break;
00127 
00128             for (i = 0; i < count; i++)
00129                 out[outcnt++] = inp[i];
00130         }
00131     }
00132 
00133     if (outcnt - maxcnt)
00134         av_log(avctx, AV_LOG_DEBUG, "frame finished with %d diff\n", outcnt - maxcnt);
00135 
00136     c->pic.data[0]     = (uint8_t *)c->cur;
00137     c->pic.linesize[0] = w * 2;
00138 
00139     *data_size = sizeof(AVFrame);
00140     *(AVFrame*)data = c->pic;
00141 
00142     FFSWAP(uint16_t *, c->cur, c->prev);
00143 
00144     return avpkt->size;
00145 }
00146 
00147 static av_cold int decode_init(AVCodecContext *avctx)
00148 {
00149     KgvContext * const c = avctx->priv_data;
00150 
00151     c->avctx = avctx;
00152     avctx->pix_fmt = PIX_FMT_RGB555;
00153 
00154     return 0;
00155 }
00156 
00157 static av_cold int decode_end(AVCodecContext *avctx)
00158 {
00159     KgvContext * const c = avctx->priv_data;
00160 
00161     av_freep(&c->cur);
00162     av_freep(&c->prev);
00163 
00164     return 0;
00165 }
00166 
00167 AVCodec ff_kgv1_decoder = {
00168     "kgv1",
00169     AVMEDIA_TYPE_VIDEO,
00170     CODEC_ID_KGV1,
00171     sizeof(KgvContext),
00172     decode_init,
00173     NULL,
00174     decode_end,
00175     decode_frame,
00176     .long_name = NULL_IF_CONFIG_SMALL("Kega Game Video"),
00177 };