[FFmpeg-devel] [PATCH] MS Video 1 encoder, take 2

Kostya kostya.shishkov
Wed Mar 11 07:11:39 CET 2009


$subj

It is quality-based encoder since this codec is not fit for bitrate-based encoding,
so it should be run as ffmpeg -i infile -vcodec msvideo1 -qscale 3 output.avi
-------------- next part --------------
Index: Changelog
===================================================================
--- Changelog	(revision 17880)
+++ Changelog	(working copy)
@@ -4,6 +4,7 @@
 - deprecated vhook subsystem removed
 - deprecated old scaler removed
 - VQF demuxer
+- MS Video 1 15-bpp encoder
 
 
 
Index: libavcodec/Makefile
===================================================================
--- libavcodec/Makefile	(revision 17880)
+++ libavcodec/Makefile	(working copy)
@@ -152,6 +152,7 @@
 OBJS-$(CONFIG_MSMPEG4V3_ENCODER)       += msmpeg4.o msmpeg4data.o mpegvideo_enc.o motion_est.o ratecontrol.o h263.o mpeg12data.o mpegvideo.o error_resilience.o
 OBJS-$(CONFIG_MSRLE_DECODER)           += msrle.o msrledec.o
 OBJS-$(CONFIG_MSVIDEO1_DECODER)        += msvideo1.o
+OBJS-$(CONFIG_MSVIDEO1_ENCODER)        += msvideo1enc.o elbg.o
 OBJS-$(CONFIG_MSZH_DECODER)            += lcldec.o
 OBJS-$(CONFIG_NELLYMOSER_DECODER)      += nellymoserdec.o nellymoser.o
 OBJS-$(CONFIG_NELLYMOSER_ENCODER)      += nellymoserenc.o nellymoser.o
Index: libavcodec/msvideo1enc.c
===================================================================
--- libavcodec/msvideo1enc.c	(revision 0)
+++ libavcodec/msvideo1enc.c	(revision 0)
@@ -0,0 +1,297 @@
+/*
+ * Microsoft Video-1 Encoder
+ * Copyright (c) 2009 Konstantin Shishkov
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file msvideo1enc.c
+ * Microsoft Video-1 encoder
+ */
+
+#include "avcodec.h"
+#include "bytestream.h"
+#include "libavutil/lfg.h"
+#include "elbg.h"
+
+/**
+ * Encoder context
+ */
+typedef struct Msvideo1EncContext {
+    AVCodecContext *avctx;
+    AVFrame pic;
+    AVLFG rnd;
+    uint8_t *prev;
+
+    int block[16*3];
+    int block2[16*3];
+    int codebook[8*3];
+    int codebook2[8*3];
+    int output[16*3];
+    int output2[16*3];
+    int avg[3];
+    int keyint;
+} Msvideo1EncContext;
+
+enum MSV1Mode{
+    MODE_SKIP = 0,
+    MODE_FILL,
+    MODE_2COL,
+    MODE_8COL,
+};
+
+#define SKIP_PREFIX 0x8400
+#define SKIPS_MAX 0x03FF
+#define MKRGB555(in, off) ((in[off] << 10) | (in[off + 1] << 5) | (in[off + 2]))
+
+static const uint8_t remap[16] = { 0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15 };
+
+static int encode_frame(AVCodecContext *avctx, uint8_t *buf, int buf_size, void *data)
+{
+    Msvideo1EncContext * const c = avctx->priv_data;
+    AVFrame *pict = data;
+    AVFrame * const p = &c->pic;
+    const uint16_t *src;
+    uint8_t *prevptr;
+    uint8_t *dst = buf;
+    int keyframe = 0;
+    int no_skips = 1;
+    int i, j, k, x, y;
+    int skips = 0;
+    int quality;
+
+    *p = *pict;
+    if(!c->prev)
+        c->prev = av_malloc(((avctx->width + 3) & ~3) * ((avctx->height + 3) & ~3) * 3);
+    prevptr = c->prev;
+    src = (uint16_t*)(p->data[0] + p->linesize[0]*(((avctx->height + 3)&~3) - 1));
+    if(c->keyint >= avctx->keyint_min)
+        keyframe = 1;
+
+    p->quality = avctx->global_quality;
+    quality = p->quality;
+
+    for(y = 0; y < avctx->height; y += 4){
+        for(x = 0; x < avctx->width; x += 4){
+            int bestmode = MODE_SKIP;
+            int bestscore = INT_MAX;
+            int flags = 0;
+            int score;
+
+            for(j = 0; j < 4; j++){
+                for(i = 0; i < 4; i++){
+                    uint16_t val = src[x + i - j*p->linesize[0]/2];
+                    for(k = 0; k < 3; k++){
+                        c->block[(i + j*4)*3 + k] = (val >> (10-k*5)) & 0x1F;
+                        c->block2[remap[i + j*4]*3 + k] = (val >> (10-k*5)) & 0x1F;
+                    }
+                }
+            }
+            if(!keyframe){
+                bestscore = 0;
+                for(i = 0; i < 4*4*3; i++){
+                    int t = prevptr[i] - c->block[i];
+                    bestscore += t*t;
+                }
+                if(!skips)
+                    bestscore += 2;
+            }
+            if(bestscore > quality){
+                // try to find optimal value to fill whole 4x4 block
+                score = 0;
+                memset(c->avg, 0, sizeof(c->avg));
+                for(i = 0; i < 4*4*3; i += 3)
+                    for(k = 0; k < 3; k++)
+                        c->avg[k] += c->block[i+k];
+                for(i = 0; i < 3; i++)
+                    c->avg[i] = (c->avg[i] + 8) >> 4;
+                if(c->avg[0] == 1) // red component = 1 will be written as skip code
+                    c->avg[0] = 0;
+                for(i = 0; i < 4*4*3; i += 3){
+                    for(k = 0; k < 3; k++){
+                        int t = c->avg[k] - c->block[i+k];
+                        score += t*t;
+                    }
+                }
+                score += 2;
+                if(score < bestscore){
+                    bestscore = score;
+                    bestmode = MODE_FILL;
+                }
+            }
+            if(bestscore > quality){
+                // search for optimal filling of 2-color block
+                score = 0;
+                ff_init_elbg(c->block, 3, 16, c->codebook, 2, 1, c->output, &c->rnd);
+                ff_do_elbg  (c->block, 3, 16, c->codebook, 2, 1, c->output, &c->rnd);
+                // last output value should be always 1, swap codebooks if needed
+                if(!c->output[15]){
+                    for(i = 0; i < 3; i++)
+                        FFSWAP(uint8_t, c->codebook[i], c->codebook[i+3]);
+                    for(i = 0; i < 16; i++)
+                        c->output[i] ^= 1;
+                }
+                for(i = 0; i < 4*4; i++){
+                    for(k = 0; k < 3; k++){
+                        int t = c->codebook[c->output[i]*3 + k] - c->block[i*3+k];
+                        score += t*t;
+                    }
+                }
+                score += 6;
+                if(score < bestscore){
+                    bestscore = score;
+                    bestmode = MODE_2COL;
+                }
+            }
+            if(bestscore > quality){
+                // search for optimal filling of 2-color 2x2 subblocks
+                score = 0;
+                for(i = 0; i < 4; i++){
+                    ff_init_elbg(c->block2 + i*4*3, 3, 4, c->codebook2 + i*2*3, 2, 1, c->output2 + i*4, &c->rnd);
+                    ff_do_elbg  (c->block2 + i*4*3, 3, 4, c->codebook2 + i*2*3, 2, 1, c->output2 + i*4, &c->rnd);
+                }
+                // last value should be always 1, swap codebooks if needed
+                if(!c->output2[15]){
+                    for(i = 0; i < 3; i++)
+                        FFSWAP(uint8_t, c->codebook2[i+18], c->codebook2[i+21]);
+                    for(i = 12; i < 16; i++)
+                        c->output2[i] ^= 1;
+                }
+                for(i = 0; i < 4*4; i++){
+                    for(k = 0; k < 3; k++){
+                        int t = c->codebook2[(c->output2[remap[i]] + (i&2) + ((i&8)>>1))*3+k] - c->block[i*3+k];
+                        score += t*t;
+                    }
+                }
+                score += 18;
+                if(score < bestscore){
+                    bestscore = score;
+                    bestmode = MODE_8COL;
+                }
+            }
+
+            if(bestmode == MODE_SKIP){
+                skips++;
+                no_skips = 0;
+            }
+            if((bestmode != MODE_SKIP && skips) || skips == SKIPS_MAX){
+                bytestream_put_le16(&dst, skips | SKIP_PREFIX);
+                skips = 0;
+            }
+
+            switch(bestmode){
+            case MODE_FILL:
+                bytestream_put_le16(&dst, MKRGB555(c->avg,0) | 0x8000);
+                for(i = 0; i < 4*4*3; i += 3)
+                    for(k = 0; k < 3; k++)
+                        prevptr[i+k] = c->avg[k];
+                break;
+            case MODE_2COL:
+                for(i = 0; i < 4*4; i++){
+                    flags |= (c->output[i]^1) << i;
+                    for(k = 0; k < 3; k++)
+                        prevptr[i*3 + k] = c->codebook[c->output[i]*3 + k];
+                }
+                bytestream_put_le16(&dst, flags);
+                bytestream_put_le16(&dst, MKRGB555(c->codebook, 0));
+                bytestream_put_le16(&dst, MKRGB555(c->codebook, 3));
+                break;
+            case MODE_8COL:
+                for(i = 0; i < 4*4; i++){
+                    int val = c->output2[remap[i]];
+                    flags |= (val ^ 1) << i;
+                    for(k = 0; k < 3; k++)
+                        prevptr[i*3 + k] = c->codebook2[(val + (i&2) + ((i&8)>>1))*3 + k];
+                }
+                bytestream_put_le16(&dst, flags);
+                bytestream_put_le16(&dst, MKRGB555(c->codebook2, 0) | 0x8000);
+                for(i = 3; i < 24; i += 3)
+                    bytestream_put_le16(&dst, MKRGB555(c->codebook2, i));
+                break;
+            }
+            prevptr += 4*4*3;
+        }
+        src -= p->linesize[0] << 1;
+    }
+    if(skips)
+        bytestream_put_le16(&dst, skips | SKIP_PREFIX);
+    //EOF
+    bytestream_put_le16(&dst, 0);
+
+    if(no_skips)
+        keyframe = 1;
+    if(keyframe)
+        c->keyint = 0;
+    else
+        c->keyint++;
+    p->pict_type = keyframe ? FF_I_TYPE : FF_P_TYPE;
+    p->key_frame = keyframe;
+
+    return dst - buf;
+}
+
+
+/**
+ * init encoder
+ */
+static av_cold int encode_init(AVCodecContext *avctx)
+{
+    Msvideo1EncContext * const c = avctx->priv_data;
+
+    if (!(avctx->flags&CODEC_FLAG_QSCALE)) {
+        av_log(avctx, AV_LOG_ERROR, "This encoder works only with set quality, not bitrate\n");
+        return -1;
+    }
+
+    c->avctx = avctx;
+    if (avcodec_check_dimensions(avctx, avctx->width, avctx->height) < 0) {
+        return -1;
+    }
+
+    avctx->coded_frame = (AVFrame*)&c->pic;
+
+    c->keyint = avctx->keyint_min;
+    av_lfg_init(&c->rnd, 0xDEADBEEF);
+
+    return 0;
+}
+
+
+/**
+ * Uninit encoder
+ */
+static av_cold int encode_end(AVCodecContext *avctx)
+{
+    Msvideo1EncContext * const c = avctx->priv_data;
+
+    av_freep(&c->prev);
+
+    return 0;
+}
+
+AVCodec msvideo1_encoder = {
+    "msvideo1",
+    CODEC_TYPE_VIDEO,
+    CODEC_ID_MSVIDEO1,
+    sizeof(Msvideo1EncContext),
+    encode_init,
+    encode_frame,
+    encode_end,
+    .pix_fmts = (enum PixelFormat[]){PIX_FMT_RGB555, PIX_FMT_NONE},
+    .long_name = NULL_IF_CONFIG_SMALL("Microsoft Video-1"),
+};
Index: libavcodec/allcodecs.c
===================================================================
--- libavcodec/allcodecs.c	(revision 17880)
+++ libavcodec/allcodecs.c	(working copy)
@@ -116,7 +116,7 @@
     REGISTER_ENCDEC  (MSMPEG4V2, msmpeg4v2);
     REGISTER_ENCDEC  (MSMPEG4V3, msmpeg4v3);
     REGISTER_DECODER (MSRLE, msrle);
-    REGISTER_DECODER (MSVIDEO1, msvideo1);
+    REGISTER_ENCDEC  (MSVIDEO1, msvideo1);
     REGISTER_DECODER (MSZH, mszh);
     REGISTER_DECODER (NUV, nuv);
     REGISTER_ENCODER (PAM, pam);
Index: doc/general.texi
===================================================================
--- doc/general.texi	(revision 17880)
+++ doc/general.texi	(working copy)
@@ -373,7 +373,7 @@
 @item LOCO                   @tab     @tab  X
 @item lossless MJPEG         @tab  X  @tab  X
 @item Microsoft RLE          @tab     @tab  X
- at item Microsoft Video 1      @tab     @tab  X
+ at item Microsoft Video 1      @tab  X  @tab  X
 @item Mimic                  @tab     @tab  X
     @tab Used in MSN Messenger Webcam streams.
 @item Miro VideoXL           @tab     @tab  X



More information about the ffmpeg-devel mailing list