[Ffmpeg-devel] [RFC] ZMBV decoder

Kostya kostya.forjunk
Sun Feb 12 06:14:56 CET 2006


Here is Zip Blocks Motion Video decoder, codec used by DosBox to capture screen data. Description is in wiki.multimedia.cx, samples are at equinox.ltu.se, binary codec already supported by MPlayer.
-------------- next part --------------
diff -ruN --exclude CVS --exclude '*.o' --exclude '*.a' --exclude 'config.*' --exclude '*.pc' --exclude 'ffmpeg*' /home/kst/cvs-get/ffmpeg/Changelog ./Changelog
--- /home/kst/cvs-get/ffmpeg/Changelog	2006-02-10 07:17:13.000000000 +0200
+++ ./Changelog	2006-02-11 20:33:25.000000000 +0200
@@ -36,6 +36,7 @@
 - AIFF/AIFF-C audio format, encoding and decoding
 - ADTS AAC file reading and writing
 - Creative VOC file reading and writing
+- Zip Blocks Motion Video decoder
 
 version 0.4.9-pre1:
 
diff -ruN --exclude CVS --exclude '*.o' --exclude '*.a' --exclude 'config.*' --exclude '*.pc' --exclude 'ffmpeg*' /home/kst/cvs-get/ffmpeg/libavcodec/allcodecs.c ./libavcodec/allcodecs.c
--- /home/kst/cvs-get/ffmpeg/libavcodec/allcodecs.c	2006-02-10 07:18:12.000000000 +0200
+++ ./libavcodec/allcodecs.c	2006-02-10 08:08:50.000000000 +0200
@@ -444,6 +444,9 @@
 #ifdef CONFIG_ZLIB_DECODER
     register_avcodec(&zlib_decoder);
 #endif //CONFIG_ZLIB_DECODER
+#ifdef CONFIG_ZMBV_DECODER
+    register_avcodec(&zmbv_decoder);
+#endif //CONFIG_ZMBV_DECODER
 #ifdef CONFIG_SONIC_DECODER
     register_avcodec(&sonic_decoder);
 #endif //CONFIG_SONIC_DECODER
diff -ruN --exclude CVS --exclude '*.o' --exclude '*.a' --exclude 'config.*' --exclude '*.pc' --exclude 'ffmpeg*' /home/kst/cvs-get/ffmpeg/libavcodec/avcodec.h ./libavcodec/avcodec.h
--- /home/kst/cvs-get/ffmpeg/libavcodec/avcodec.h	2006-02-10 07:18:12.000000000 +0200
+++ ./libavcodec/avcodec.h	2006-02-11 20:35:16.000000000 +0200
@@ -21,8 +21,8 @@
 #define AV_STRINGIFY(s)         AV_TOSTRING(s)
 #define AV_TOSTRING(s) #s
 
-#define LIBAVCODEC_VERSION_INT  ((51<<16)+(2<<8)+0)
-#define LIBAVCODEC_VERSION      51.2.0
+#define LIBAVCODEC_VERSION_INT  ((51<<16)+(3<<8)+0)
+#define LIBAVCODEC_VERSION      51.3.0
 #define LIBAVCODEC_BUILD        LIBAVCODEC_VERSION_INT
 
 #define LIBAVCODEC_IDENT        "Lavc" AV_STRINGIFY(LIBAVCODEC_VERSION)
@@ -115,6 +115,7 @@
     CODEC_ID_TRUEMOTION2,
     CODEC_ID_BMP,
     CODEC_ID_CSCD,
+    CODEC_ID_ZMBV,
 
     /* various pcm "codecs" */
     CODEC_ID_PCM_S16LE= 0x10000,
@@ -2224,6 +2225,7 @@
 extern AVCodec libgsm_encoder;
 extern AVCodec libgsm_decoder;
 extern AVCodec bmp_decoder;
+extern AVCodec zmbv_decoder;
 
 /* pcm codecs */
 #define PCM_CODEC(id, name) \
diff -ruN --exclude CVS --exclude '*.o' --exclude '*.a' --exclude 'config.*' --exclude '*.pc' --exclude 'ffmpeg*' /home/kst/cvs-get/ffmpeg/libavcodec/Makefile ./libavcodec/Makefile
--- /home/kst/cvs-get/ffmpeg/libavcodec/Makefile	2006-02-10 07:18:10.000000000 +0200
+++ ./libavcodec/Makefile	2006-02-10 08:09:34.000000000 +0200
@@ -215,6 +215,9 @@
 ifeq ($(CONFIG_BMP_DECODER),yes)
 	OBJS+= bmp.o
 endif
+ifeq ($(CONFIG_ZMBV_DECODER),yes)
+	OBJS+= zmbv.o
+endif
 
 AMROBJS=
 ifeq ($(AMR_NB),yes)
diff -ruN --exclude CVS --exclude '*.o' --exclude '*.a' --exclude 'config.*' --exclude '*.pc' --exclude 'ffmpeg*' /home/kst/cvs-get/ffmpeg/libavcodec/zmbv.c ./libavcodec/zmbv.c
--- /home/kst/cvs-get/ffmpeg/libavcodec/zmbv.c	1970-01-01 03:00:00.000000000 +0300
+++ ./libavcodec/zmbv.c	2006-02-11 20:28:33.000000000 +0200
@@ -0,0 +1,470 @@
+/*
+ * Zip Motion Blocks Video (ZMBV) decoder
+ * Copyright (c) 2006 Konstantin Shishkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+/**
+ * @file zmbv.c
+ * Zip Motion Blocks Video decoder
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "common.h"
+#include "avcodec.h"
+
+#ifdef CONFIG_ZLIB
+#include <zlib.h>
+#endif
+
+#define ZMBV_KEYFRAME 1
+#define ZMBV_DELTAPAL 2
+
+enum ZmbvFormat {
+    ZMBV_FMT_NONE  = 0,
+    ZMBV_FMT_1BPP  = 1,
+    ZMBV_FMT_2BPP  = 2,
+    ZMBV_FMT_4BPP  = 3,
+    ZMBV_FMT_8BPP  = 4,
+    ZMBV_FMT_15BPP = 5,
+    ZMBV_FMT_16BPP = 6,
+    ZMBV_FMT_24BPP = 7,
+    ZMBV_FMT_32BPP = 8
+};
+
+/*
+ * Decoder context
+ */
+typedef struct ZmbvContext {
+    AVCodecContext *avctx;
+    AVFrame pic;
+
+    int bpp;
+    unsigned int decomp_size;
+    uint8_t* decomp_buf;
+    uint8_t pal[768];
+    uint8_t *prev, *cur;
+    int width, height;
+    int fmt;
+    int comp;
+    int flags;
+    int bw, bh, bx, by;
+    int decomp_len;
+#ifdef CONFIG_ZLIB
+    z_stream zstream;
+#endif
+    int (*decode_intra)(struct ZmbvContext *c);
+    int (*decode_xor)(struct ZmbvContext *c);
+} ZmbvContext;
+
+/**
+ * Decode XOR'ed frame - 8bpp version
+ */
+
+static int zmbv_decode_xor_8(ZmbvContext *c)
+{
+    uint8_t *src = c->decomp_buf;
+    uint8_t *output, *prev;
+    int8_t *mvec;
+    int x, y;
+    int d, dx, dy, bw2, bh2;
+    int block;
+    int i, j;
+    int mx, my;
+
+    output = c->cur;
+    prev = c->prev;
+
+    if(c->flags & ZMBV_DELTAPAL){
+        for(i = 0; i < 768; i++)
+            c->pal[i] ^= *src++;
+    }
+
+    mvec = (int8_t*)src;
+    src += ((c->bx * c->by * 2 + 3) & ~3);
+
+    block = 0;
+    for(y = 0; y < c->height; y += c->bh) {
+        bh2 = ((c->height - y) > c->bh) ? c->bh : (c->height - y);
+        for(x = 0; x < c->width; x += c->bw) {
+            uint8_t *out, *tprev;
+
+            d = mvec[block] & 1;
+            dx = mvec[block] >> 1;
+            dy = mvec[block + 1] >> 1;
+            block += 2;
+
+            bw2 = ((c->width - x) > c->bw) ? c->bw : (c->width - x);
+
+            /* copy block - motion vectors out of bounds are used to zero blocks */
+            out = output + x;
+            tprev = prev + x + dx + dy * c->width;
+            mx = x + dx;
+            my = y + dy;
+            for(j = 0; j < bh2; j++){
+                if((my + j < 0) || (my + j >= c->height)) {
+                    memset(out, 0, bw2);
+                } else {
+                    for(i = 0; i < bw2; i++){
+                        if((mx + i < 0) || (mx + i >= c->width))
+                            out[i] = 0;
+                        else
+                            out[i] = tprev[i];
+                    }
+                }
+                out += c->width;
+                tprev += c->width;
+            }
+
+            if(d) { /* apply XOR'ed difference */
+                out = output + x;
+                for(j = 0; j < bh2; j++){
+                    for(i = 0; i < bw2; i++)
+                        out[i] ^= *src++;
+                    out += c->width;
+                }
+            }
+        }
+        output += c->width * c->bh;
+        prev += c->width * c->bh;
+    }
+    if(src - c->decomp_buf != c->decomp_len)
+        av_log(c->avctx, AV_LOG_ERROR, "Used %i of %i bytes\n", src-c->decomp_buf, c->decomp_len);
+    return 0;
+}
+
+/**
+ * Decode XOR'ed frame - 24bpp version
+ */
+
+static int zmbv_decode_xor_24(ZmbvContext *c)
+{
+    uint8_t *src = c->decomp_buf;
+    uint8_t *output, *prev;
+    int8_t *mvec;
+    int x, y;
+    int d, dx, dy, bw2, bh2;
+    int block;
+    int i, j;
+    int mx, my;
+    int stride;
+
+    output = c->cur;
+    prev = c->prev;
+
+    stride = c->width * 3;
+    mvec = (int8_t*)src;
+    src += ((c->bx * c->by * 2 + 3) & ~3);
+
+    block = 0;
+    for(y = 0; y < c->height; y += c->bh) {
+        bh2 = ((c->height - y) > c->bh) ? c->bh : (c->height - y);
+        for(x = 0; x < c->width; x += c->bw) {
+            uint8_t *out, *tprev;
+
+            d = mvec[block] & 1;
+            dx = mvec[block] >> 1;
+            dy = mvec[block + 1] >> 1;
+            block += 2;
+
+            bw2 = ((c->width - x) > c->bw) ? c->bw : (c->width - x);
+
+            /* copy block - motion vectors out of bounds are used to zero blocks */
+            out = output + x * 3;
+            tprev = prev + (x + dx) * 3 + dy * stride;
+            mx = x + dx;
+            my = y + dy;
+            for(j = 0; j < bh2; j++){
+                if((my + j < 0) || (my + j >= c->height)) {
+                    memset(out, 0, bw2 * 3);
+                } else {
+                    for(i = 0; i < bw2; i++){
+                        if((mx + i < 0) || (mx + i >= c->width)) {
+                            out[i * 3 + 0] = 0;
+                            out[i * 3 + 1] = 0;
+                            out[i * 3 + 2] = 0;
+                        } else {
+                            out[i * 3 + 0] = tprev[i * 3 + 0];
+                            out[i * 3 + 1] = tprev[i * 3 + 1];
+                            out[i * 3 + 2] = tprev[i * 3 + 2];
+                        }
+                    }
+                }
+                out += stride;
+                tprev += stride;
+            }
+
+            if(d) { /* apply XOR'ed difference */
+                out = output + x * 3;
+                for(j = 0; j < bh2; j++){
+                    for(i = 0; i < bw2; i++) {
+                        out[i * 3 + 0] ^= *src++;
+                        out[i * 3 + 1] ^= *src++;
+                        out[i * 3 + 2] ^= *src++;
+                    }
+                    out += stride;
+                }
+            }
+        }
+        output += stride * c->bh;
+        prev += stride * c->bh;
+    }
+    if(src - c->decomp_buf != c->decomp_len)
+        av_log(c->avctx, AV_LOG_ERROR, "Used %i of %i bytes\n", src-c->decomp_buf, c->decomp_len);
+    return 0;
+}
+
+/**
+ * Decode intraframe
+ */
+static int zmbv_decode_intra(ZmbvContext *c)
+{
+    uint8_t *src = c->decomp_buf;
+
+    /* make the palette available on the way out */
+    if (c->fmt == ZMBV_FMT_8BPP) {
+        memcpy(c->pal, src, 768);
+        src += 768;
+    }
+
+    memcpy(c->cur, src, c->width * c->height * (c->bpp / 8));
+    return 0;
+}
+
+static int decode_frame(AVCodecContext *avctx, void *data, int *data_size, uint8_t *buf, int buf_size)
+{
+    ZmbvContext * const c = (ZmbvContext *)avctx->priv_data;
+    uint8_t *outptr;
+#ifdef CONFIG_ZLIB
+    int zret = Z_OK; // Zlib return code
+#endif
+    int len = buf_size;
+    int hi_ver, lo_ver;
+
+    if(c->pic.data[0])
+            avctx->release_buffer(avctx, &c->pic);
+
+    c->pic.reference = 1;
+    c->pic.buffer_hints = FF_BUFFER_HINTS_VALID;
+    if(avctx->get_buffer(avctx, &c->pic) < 0){
+        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
+        return -1;
+    }
+
+    outptr = c->pic.data[0]; // Output image pointer
+
+    /* parse header */
+    c->flags = buf[0];
+    buf++; len--;
+    if(c->flags & ZMBV_KEYFRAME) {
+        hi_ver = buf[0];
+        lo_ver = buf[1];
+        c->comp = buf[2];
+        c->fmt = buf[3];
+        c->bw = buf[4];
+        c->bh = buf[5];
+
+        buf += 6;
+        len -= 6;
+        av_log(avctx, AV_LOG_DEBUG, "Flags=%X ver=%i.%i comp=%i fmt=%i blk=%ix%i\n",c->flags,hi_ver,lo_ver,c->comp,c->fmt,c->bw,c->bh);
+        if(hi_ver != 0 || lo_ver != 1) {
+            av_log(avctx, AV_LOG_ERROR, "Unsupported version %i.%i\n", hi_ver, lo_ver);
+            return -1;
+        }
+        if(c->bw == 0 || c->bh == 0) {
+            av_log(avctx, AV_LOG_ERROR, "Unsupported block size %ix%i\n", c->bw, c->bh);
+        }
+        if(c->comp != 0 && c->comp != 1) {
+            av_log(avctx, AV_LOG_ERROR, "Unsupported compression type %i\n", c->comp);
+            return -1;
+        }
+        if(c->fmt != ZMBV_FMT_8BPP && c->fmt != ZMBV_FMT_24BPP) {
+            av_log(avctx, AV_LOG_ERROR, "Unsupported (for now) format %i\n", c->fmt);
+            return -1;
+        }
+        zret = inflateReset(&c->zstream);
+        if (zret != Z_OK) {
+            av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", zret);
+            return -1;
+        }
+        if(c->fmt == ZMBV_FMT_8BPP) {
+            c->bpp = 8;
+            c->decode_intra = zmbv_decode_intra;
+            c->decode_xor = zmbv_decode_xor_8;
+        } else {
+            c->bpp = 24;
+            c->decode_intra = zmbv_decode_intra;
+            c->decode_xor = zmbv_decode_xor_24;
+        }
+        c->cur = av_realloc(c->cur, avctx->width * avctx->height * (c->bpp / 8));
+        c->prev = av_realloc(c->prev, avctx->width * avctx->height * (c->bpp / 8));
+        c->bx = (c->width + c->bw - 1) / c->bw;
+        c->by = (c->height+ c->bh - 1) / c->bh;
+    }
+
+    if(c->fmt == 0) {
+        av_log(avctx, AV_LOG_ERROR, "Error! Got no format or no keyframe!\n");
+        return -1;
+    }
+
+    if(c->comp == 0) { //Uncompressed data
+        memcpy(c->decomp_buf, buf, len);
+        c->decomp_size = 1;
+    } else { // ZLIB-compressed data
+#ifdef CONFIG_ZLIB
+        c->zstream.total_in = c->zstream.total_out = 0;
+        c->zstream.next_in = buf;
+        c->zstream.avail_in = len;
+        c->zstream.next_out = c->decomp_buf;
+        c->zstream.avail_out = c->decomp_size;
+        inflate(&c->zstream, Z_FINISH);
+        c->decomp_len = c->zstream.total_out;
+#else
+        av_log(avctx, AV_LOG_ERROR, "BUG! Zlib support not compiled in frame decoder.\n");
+        return -1;
+#endif
+    }
+    if(c->flags & ZMBV_KEYFRAME) {
+        c->pic.key_frame = 1;
+        c->pic.pict_type = FF_I_TYPE;
+        c->decode_intra(c);
+    } else {
+        c->pic.key_frame = 0;
+        c->pic.pict_type = FF_P_TYPE;
+        c->decode_xor(c);
+    }
+
+    /* update frames */
+    {
+        uint8_t *out, *src;
+        int i, j;
+
+        out = c->pic.data[0];
+        src = c->cur;
+        for(j = 0; j < c->height; j++) {
+            for(i = 0; i < c->width; i++) {
+                out[i * 3 + 0] = c->pal[(*src) * 3 + 0];
+                out[i * 3 + 1] = c->pal[(*src) * 3 + 1];
+                out[i * 3 + 2] = c->pal[(*src) * 3 + 2];
+                *src++;
+            }
+            out += c->pic.linesize[0];
+        }
+        memcpy(c->prev, c->cur, c->width * c->height);
+    }
+    *data_size = sizeof(AVFrame);
+    *(AVFrame*)data = c->pic;
+
+    /* always report that the buffer was completely consumed */
+    return buf_size;
+}
+
+
+
+/*
+ *
+ * Init zmbv decoder
+ *
+ */
+static int decode_init(AVCodecContext *avctx)
+{
+    ZmbvContext * const c = (ZmbvContext *)avctx->priv_data;
+    int zret; // Zlib return code
+
+    c->avctx = avctx;
+    avctx->has_b_frames = 0;
+
+    c->pic.data[0] = NULL;
+    c->width = avctx->width;
+    c->height = avctx->height;
+
+    if (avcodec_check_dimensions(avctx, avctx->height, avctx->width) < 0) {
+        return 1;
+    }
+    c->bpp = avctx->bits_per_sample;
+
+#ifdef CONFIG_ZLIB
+    // Needed if zlib unused or init aborted before inflateInit
+    memset(&(c->zstream), 0, sizeof(z_stream));
+#else
+    av_log(avctx, AV_LOG_ERROR, "Zlib support not compiled.\n");
+    return 1;
+#endif
+    avctx->pix_fmt = PIX_FMT_RGB24;
+    c->decomp_size = (avctx->width + 255) * 4 * (avctx->height + 64);
+
+    /* Allocate decompression buffer */
+    if (c->decomp_size) {
+        if ((c->decomp_buf = av_malloc(c->decomp_size)) == NULL) {
+            av_log(avctx, AV_LOG_ERROR, "Can't allocate decompression buffer.\n");
+            return 1;
+        }
+    }
+
+#ifdef CONFIG_ZLIB
+    c->zstream.zalloc = Z_NULL;
+    c->zstream.zfree = Z_NULL;
+    c->zstream.opaque = Z_NULL;
+    zret = inflateInit(&(c->zstream));
+    if (zret != Z_OK) {
+        av_log(avctx, AV_LOG_ERROR, "Inflate init error: %d\n", zret);
+        return 1;
+    }
+#endif
+
+    return 0;
+}
+
+
+
+/*
+ *
+ * Uninit zmbv decoder
+ *
+ */
+static int decode_end(AVCodecContext *avctx)
+{
+    ZmbvContext * const c = (ZmbvContext *)avctx->priv_data;
+
+    av_freep(&c->decomp_buf);
+
+    if (c->pic.data[0])
+        avctx->release_buffer(avctx, &c->pic);
+#ifdef CONFIG_ZLIB
+    inflateEnd(&(c->zstream));
+#endif
+    if(c->cur)
+        av_freep(&c->cur);
+    if(c->prev)
+        av_freep(&c->prev);
+
+    return 0;
+}
+
+AVCodec zmbv_decoder = {
+    "zmbv",
+    CODEC_TYPE_VIDEO,
+    CODEC_ID_ZMBV,
+    sizeof(ZmbvContext),
+    decode_init,
+    NULL,
+    decode_end,
+    decode_frame
+};
+
diff -ruN --exclude CVS --exclude '*.o' --exclude '*.a' --exclude 'config.*' --exclude '*.pc' --exclude 'ffmpeg*' /home/kst/cvs-get/ffmpeg/libavformat/avienc.c ./libavformat/avienc.c
--- /home/kst/cvs-get/ffmpeg/libavformat/avienc.c	2006-02-10 07:22:00.000000000 +0200
+++ ./libavformat/avienc.c	2006-02-11 19:40:28.000000000 +0200
@@ -195,6 +195,7 @@
     { CODEC_ID_THEORA, MKTAG('t', 'h', 'e', 'o') },
     { CODEC_ID_TRUEMOTION2, MKTAG('T', 'M', '2', '0') },
     { CODEC_ID_CSCD, MKTAG('C', 'S', 'C', 'D') },
+    { CODEC_ID_ZMBV, MKTAG('Z', 'M', 'B', 'V') },
     { CODEC_ID_RAWVIDEO, 0 },
     { 0, 0 },
 };



More information about the ffmpeg-devel mailing list