From: Tomas Härdin <tomas.hardin at codemill.se>

Signed-off-by: Diego Biurrun <di...@biurrun.de>
---

The whole set squashed together, to ease Luca's review.

 Changelog                        |    1 +
 doc/general.texi                 |    2 +-
 libavcodec/Makefile              |    1 +
 libavcodec/allcodecs.c           |    2 +-
 libavcodec/cinepakenc.c          | 1238 ++++++++++++++++++++++++++++++++++++++
 tests/fate/vcodec.mak            |    4 +
 tests/ref/vsynth/vsynth1-cinepak |    4 +
 tests/ref/vsynth/vsynth2-cinepak |    4 +
 8 files changed, 1254 insertions(+), 2 deletions(-)
 create mode 100644 libavcodec/cinepakenc.c
 create mode 100644 tests/ref/vsynth/vsynth1-cinepak
 create mode 100644 tests/ref/vsynth/vsynth2-cinepak

diff --git a/Changelog b/Changelog
index e44df54..adcca3f 100644
--- a/Changelog
+++ b/Changelog
@@ -16,6 +16,7 @@ version <next>:
 - FM Screen Capture Codec decoder
 - ClearVideo decoder (I-frames only)
 - support for decoding through D3D11VA in avconv
+- Cinepak encoder
 
 
 version 12:
diff --git a/doc/general.texi b/doc/general.texi
index 905e2d1..adf5b4e 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -631,7 +631,7 @@ following image formats are supported:
     @tab Codec used in Delphine Software International games.
 @item Discworld II BMV Video @tab     @tab  X
 @item Canopus Lossless Codec @tab     @tab  X
-@item Cinepak                @tab     @tab  X
+@item Cinepak                @tab  X  @tab  X
 @item Cirrus Logic AccuPak   @tab  X  @tab  X
     @tab fourcc: CLJR
 @item Creative YUV (CYUV)    @tab     @tab  X
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 240f7d8..2b91588 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -186,6 +186,7 @@ OBJS-$(CONFIG_CDGRAPHICS_DECODER)      += cdgraphics.o
 OBJS-$(CONFIG_CDXL_DECODER)            += cdxl.o
 OBJS-$(CONFIG_CFHD_DECODER)            += cfhd.o cfhddata.o
 OBJS-$(CONFIG_CINEPAK_DECODER)         += cinepak.o
+OBJS-$(CONFIG_CINEPAK_ENCODER)         += cinepakenc.o
 OBJS-$(CONFIG_CLEARVIDEO_DECODER)      += clearvideo.o
 OBJS-$(CONFIG_CLJR_DECODER)            += cljrdec.o
 OBJS-$(CONFIG_CLJR_ENCODER)            += cljrenc.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 70c35e9..5635ae1 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -133,7 +133,7 @@ void avcodec_register_all(void)
     REGISTER_DECODER(CDGRAPHICS,        cdgraphics);
     REGISTER_DECODER(CDXL,              cdxl);
     REGISTER_DECODER(CFHD,              cfhd);
-    REGISTER_DECODER(CINEPAK,           cinepak);
+    REGISTER_ENCDEC (CINEPAK,           cinepak);
     REGISTER_DECODER(CLEARVIDEO,        clearvideo);
     REGISTER_ENCDEC (CLJR,              cljr);
     REGISTER_DECODER(CLLC,              cllc);
diff --git a/libavcodec/cinepakenc.c b/libavcodec/cinepakenc.c
new file mode 100644
index 0000000..5466a1e
--- /dev/null
+++ b/libavcodec/cinepakenc.c
@@ -0,0 +1,1238 @@
+/*
+ * Cinepak encoder (c) 2011 Tomas Härdin
+ * http://titan.codemill.se/~tomhar/cinepakenc.patch
+ *
+ * Fixes and improvements, vintage decoders compatibility
+ *  (c) 2013, 2014 Rl, Aetey Global Technologies AB
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* MAYBE:
+ * - "optimally" split the frame into several non-regular areas
+ *   using a separate codebook pair for each area and approximating
+ *   the area by several rectangular strips (generally not full width ones)
+ *   (use quadtree splitting? a simple fixed-granularity grid?)
+ *
+ *
+ * version 2014-01-23 Rl
+ * - added option handling for flexibility
+ *
+ * version 2014-01-21 Rl
+ * - believe it or not, now we get even smaller files, with better quality
+ *   (which means I missed an optimization earlier :)
+ *
+ * version 2014-01-20 Rl
+ * - made the encoder compatible with vintage decoders
+ *   and added some yet unused code for possible future
+ *   incremental codebook updates
+ * - fixed a small memory leak
+ *
+ * version 2013-04-28 Rl
+ * - bugfixed codebook optimization logic
+ *
+ * version 2013-02-14 Rl
+ * "Valentine's Day" version:
+ * - made strip division more robust
+ * - minimized bruteforcing the number of strips,
+ *   (costs some R/D but speeds up compession a lot), the heuristic
+ *   assumption is that score as a function of the number of strips has
+ *   one wide minimum which moves slowly, of course not fully true
+ * - simplified codebook generation,
+ *   the old code was meant for other optimizations than we actually do
+ * - optimized the codebook generation / error estimation for MODE_MC
+ *
+ * version 2013-02-12 Rl
+ * - separated codebook training sets, avoided the transfer of wasted bytes,
+ *   which yields both better quality and smaller files
+ * - now using the correct colorspace (TODO: move conversion to libswscale)
+ *
+ * version 2013-02-08 Rl
+ * - fixes/optimization in multistrip encoding and codebook size choice,
+ *   quality/bitrate is now better than that of the binary proprietary encoder
+ */
+
+#include <string.h>
+
+#include "libavutil/avassert.h"
+#include "libavutil/common.h"
+#include "libavutil/internal.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/lfg.h"
+#include "libavutil/opt.h"
+
+#include "avcodec.h"
+#include "elbg.h"
+#include "internal.h"
+
+#define CVID_HEADER_SIZE 10
+#define STRIP_HEADER_SIZE 12
+#define CHUNK_HEADER_SIZE 4
+
+#define MB_SIZE 4           //4x4 MBs
+#define MB_AREA (MB_SIZE * MB_SIZE)
+
+#define VECTOR_MAX     6    // six or four entries per vector depending on 
format
+#define CODEBOOK_MAX 256    // size of a codebook
+
+#define MAX_STRIPS  32      // Note: having fewer choices regarding the number 
of strips speeds up encoding (obviously)
+#define MIN_STRIPS   1      // Note: having more strips speeds up encoding the 
frame (this is less obvious)
+// MAX_STRIPS limits the maximum quality you can reach
+//            when you want high quality on high resolutions,
+// MIN_STRIPS limits the minimum efficiently encodable bit rate
+//            on low resolutions
+// the numbers are only used for brute force optimization for the first frame,
+// for the following frames they are adaptively readjusted
+// NOTE the decoder in ffmpeg has its own arbitrary limitation on the number
+// of strips, currently 32
+
+typedef enum CinepakMode {
+    MODE_V1_ONLY = 0,
+    MODE_V1_V4,
+    MODE_MC,
+
+    MODE_COUNT,
+} CinepakMode;
+
+typedef enum mb_encoding {
+    ENC_V1,
+    ENC_V4,
+    ENC_SKIP,
+
+    ENC_UNCERTAIN
+} mb_encoding;
+
+typedef struct mb_info {
+    int v1_vector;              // index into v1 codebook
+    int v1_error;               // error when using V1 encoding
+    int v4_vector[4];           // indices into v4 codebook
+    int v4_error;               // error when using V4 encoding
+    int skip_error;             // error when block is skipped (aka copied 
from last frame)
+    mb_encoding best_encoding;  // last result from calculate_mode_score()
+} mb_info;
+
+typedef struct strip_info {
+    int v1_codebook[CODEBOOK_MAX * VECTOR_MAX];
+    int v4_codebook[CODEBOOK_MAX * VECTOR_MAX];
+    int v1_size;
+    int v4_size;
+    CinepakMode mode;
+} strip_info;
+
+typedef struct CinepakEncContext {
+    AVCodecContext *avctx;
+    unsigned char *pict_bufs[4], *strip_buf, *frame_buf;
+    AVFrame *last_frame;
+    AVFrame *best_frame;
+    AVFrame *scratch_frame;
+    AVFrame *input_frame;
+    enum AVPixelFormat pix_fmt;
+    int w, h;
+    int frame_buf_size;
+    int curframe, keyint;
+    AVLFG randctx;
+    uint64_t lambda;
+    int *codebook_input;
+    int *codebook_closest;
+    mb_info *mb;                // MB RD state
+    int min_strips;             // the current limit
+    int max_strips;             // the current limit
+    // options
+    int max_extra_cb_iterations;
+    int skip_empty_cb;
+    int min_min_strips;
+    int max_max_strips;
+    int strip_number_delta_range;
+} CinepakEncContext;
+
+#define OFFSET(x) offsetof(CinepakEncContext, x)
+#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+    { "max_extra_cb_iterations", "Max extra codebook recalculation passes, 
more is better and slower",
+      OFFSET(max_extra_cb_iterations),  AV_OPT_TYPE_INT, { .i64 =          2 
},          0, INT_MAX,                 VE },
+    { "skip_empty_cb",           "Avoid wasting bytes, ignore vintage MacOS 
decoder",
+      OFFSET(skip_empty_cb),            AV_OPT_TYPE_INT, { .i64 =          0 
},          0, 1,                       VE },
+    { "max_strips",              "Limit strips/frame, vintage compatible is 
1..3, otherwise the more the better",
+      OFFSET(max_max_strips),           AV_OPT_TYPE_INT, { .i64 =          3 
}, MIN_STRIPS, MAX_STRIPS,              VE },
+    { "min_strips",              "Enforce min strips/frame, more is worse and 
faster, must be <= max_strips",
+      OFFSET(min_min_strips),           AV_OPT_TYPE_INT, { .i64 = MIN_STRIPS 
}, MIN_STRIPS, MAX_STRIPS,              VE },
+    { "strip_number_adaptivity", "How fast the strip number adapts, more is 
slightly better, much slower",
+      OFFSET(strip_number_delta_range), AV_OPT_TYPE_INT, { .i64 =          0 
},          0, MAX_STRIPS - MIN_STRIPS, VE },
+    { NULL },
+};
+
+static const AVClass cinepak_class = {
+    .class_name = "cinepak",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+static av_cold int cinepak_encode_init(AVCodecContext *avctx)
+{
+    CinepakEncContext *s = avctx->priv_data;
+    int x, mb_count, strip_buf_size, frame_buf_size;
+
+    if (avctx->width & 3 || avctx->height & 3) {
+        av_log(avctx, AV_LOG_ERROR, "width and height must be multiples of 
four (got %ix%i)\n",
+               avctx->width, avctx->height);
+        return AVERROR(EINVAL);
+    }
+
+    if (s->min_min_strips > s->max_max_strips) {
+        av_log(avctx, AV_LOG_ERROR, "minimum number of strips must not exceed 
maximum (got %i and %i)\n",
+               s->min_min_strips, s->max_max_strips);
+        return AVERROR(EINVAL);
+    }
+
+    if (!(s->last_frame = av_frame_alloc()))
+        return AVERROR(ENOMEM);
+    if (!(s->best_frame = av_frame_alloc()))
+        goto enomem;
+    if (!(s->scratch_frame = av_frame_alloc()))
+        goto enomem;
+    if (avctx->pix_fmt == AV_PIX_FMT_RGB24)
+        if (!(s->input_frame = av_frame_alloc()))
+            goto enomem;
+
+    if (!(s->codebook_input = av_malloc_array((avctx->pix_fmt == 
AV_PIX_FMT_RGB24 ? 6 : 4) * (avctx->width * avctx->height) >> 2, 
sizeof(*s->codebook_input))))
+        goto enomem;
+
+    if (!(s->codebook_closest = av_malloc_array((avctx->width * avctx->height) 
>> 2, sizeof(*s->codebook_closest))))
+        goto enomem;
+
+    for (x = 0; x < (avctx->pix_fmt == AV_PIX_FMT_RGB24 ? 4 : 3); x++)
+        if (!(s->pict_bufs[x] = av_malloc((avctx->pix_fmt == AV_PIX_FMT_RGB24 
? 6 : 4) * (avctx->width * avctx->height) >> 2)))
+            goto enomem;
+
+    mb_count = avctx->width * avctx->height / MB_AREA;
+
+    // the largest possible chunk is 0x31 with all MBs encoded in V4 mode
+    // and full codebooks being replaced in INTER mode,
+    // which is 34 bits per MB
+    // and 2*256 extra flag bits per strip
+    strip_buf_size = STRIP_HEADER_SIZE + 3 * CHUNK_HEADER_SIZE + 2 * 
VECTOR_MAX * CODEBOOK_MAX + 4 * (mb_count + (mb_count + 15) / 16) + (2 * 
CODEBOOK_MAX) / 8;
+
+    frame_buf_size = CVID_HEADER_SIZE + s->max_max_strips * strip_buf_size;
+
+    if (!(s->strip_buf = av_malloc(strip_buf_size)))
+        goto enomem;
+
+    if (!(s->frame_buf = av_malloc(frame_buf_size)))
+        goto enomem;
+
+    if (!(s->mb = av_malloc(mb_count * sizeof(mb_info))))
+        goto enomem;
+
+    av_lfg_init(&s->randctx, 1);
+    s->avctx          = avctx;
+    s->w              = avctx->width;
+    s->h              = avctx->height;
+    s->frame_buf_size = frame_buf_size;
+    s->curframe       = 0;
+    s->keyint         = avctx->keyint_min;
+    s->pix_fmt        = avctx->pix_fmt;
+
+    // set up AVFrames
+    s->last_frame->data[0]        = s->pict_bufs[0];
+    s->last_frame->linesize[0]    = s->w;
+    s->best_frame->data[0]        = s->pict_bufs[1];
+    s->best_frame->linesize[0]    = s->w;
+    s->scratch_frame->data[0]     = s->pict_bufs[2];
+    s->scratch_frame->linesize[0] = s->w;
+
+    if (s->pix_fmt == AV_PIX_FMT_RGB24) {
+        s->last_frame->data[1]     = s->last_frame->data[0] +   s->w * s->h;
+        s->last_frame->data[2]     = s->last_frame->data[1] + ((s->w * s->h) 
>> 2);
+        s->last_frame->linesize[1] =
+        s->last_frame->linesize[2] = s->w >> 1;
+
+        s->best_frame->data[1]     = s->best_frame->data[0] +   s->w * s->h;
+        s->best_frame->data[2]     = s->best_frame->data[1] + ((s->w * s->h) 
>> 2);
+        s->best_frame->linesize[1] =
+        s->best_frame->linesize[2] = s->w >> 1;
+
+        s->scratch_frame->data[1]     = s->scratch_frame->data[0] +   s->w * 
s->h;
+        s->scratch_frame->data[2]     = s->scratch_frame->data[1] + ((s->w * 
s->h) >> 2);
+        s->scratch_frame->linesize[1] =
+        s->scratch_frame->linesize[2] = s->w >> 1;
+
+        s->input_frame->data[0]     = s->pict_bufs[3];
+        s->input_frame->linesize[0] = s->w;
+        s->input_frame->data[1]     = s->input_frame->data[0] +   s->w * s->h;
+        s->input_frame->data[2]     = s->input_frame->data[1] + ((s->w * s->h) 
>> 2);
+        s->input_frame->linesize[1] =
+        s->input_frame->linesize[2] = s->w >> 1;
+    }
+
+    s->min_strips = s->min_min_strips;
+    s->max_strips = s->max_max_strips;
+
+    return 0;
+
+enomem:
+    av_frame_free(&s->last_frame);
+    av_frame_free(&s->best_frame);
+    av_frame_free(&s->scratch_frame);
+    if (avctx->pix_fmt == AV_PIX_FMT_RGB24)
+        av_frame_free(&s->input_frame);
+    av_freep(&s->codebook_input);
+    av_freep(&s->codebook_closest);
+    av_freep(&s->strip_buf);
+    av_freep(&s->frame_buf);
+    av_freep(&s->mb);
+
+    for (x = 0; x < (avctx->pix_fmt == AV_PIX_FMT_RGB24 ? 4 : 3); x++)
+        av_freep(&s->pict_bufs[x]);
+
+    return AVERROR(ENOMEM);
+}
+
+static int64_t calculate_mode_score(CinepakEncContext *s, int h,
+                                    strip_info *info, int report,
+                                    int *training_set_v1_shrunk,
+                                    int *training_set_v4_shrunk)
+{
+    // score = FF_LAMBDA_SCALE * error + lambda * bits
+    int x;
+    int entry_size = s->pix_fmt == AV_PIX_FMT_RGB24 ? 6 : 4;
+    int mb_count   = s->w * h / MB_AREA;
+    mb_info *mb;
+    int64_t score1, score2, score3;
+    int64_t ret = s->lambda * ((info->v1_size ? CHUNK_HEADER_SIZE + 
info->v1_size * entry_size : 0) +
+                               (info->v4_size ? CHUNK_HEADER_SIZE + 
info->v4_size * entry_size : 0) +
+                               CHUNK_HEADER_SIZE) << 3;
+
+    switch (info->mode) {
+    case MODE_V1_ONLY:
+        // one byte per MB
+        ret += s->lambda * 8 * mb_count;
+
+        // while calculating we assume all blocks are ENC_V1
+        for (x = 0; x < mb_count; x++) {
+            mb   = &s->mb[x];
+            ret += FF_LAMBDA_SCALE * mb->v1_error;
+            // this function is never called for report in MODE_V1_ONLY
+            // if (!report)
+            mb->best_encoding = ENC_V1;
+        }
+
+        break;
+    case MODE_V1_V4:
+        // 9 or 33 bits per MB
+        if (report) {
+            // no moves between the corresponding training sets are allowed
+            *training_set_v1_shrunk = *training_set_v4_shrunk = 0;
+            for (x = 0; x < mb_count; x++) {
+                int mberr;
+                mb = &s->mb[x];
+                if (mb->best_encoding == ENC_V1)
+                    score1 = s->lambda * 9 + FF_LAMBDA_SCALE * (mberr = 
mb->v1_error);
+                else
+                    score1 = s->lambda * 33 + FF_LAMBDA_SCALE * (mberr = 
mb->v4_error);
+                ret += score1;
+            }
+        } else { // find best mode per block
+            for (x = 0; x < mb_count; x++) {
+                mb     = &s->mb[x];
+                score1 = s->lambda * 9 + FF_LAMBDA_SCALE * mb->v1_error;
+                score2 = s->lambda * 33 + FF_LAMBDA_SCALE * mb->v4_error;
+
+                if (score1 <= score2) {
+                    ret += score1;
+                    mb->best_encoding = ENC_V1;
+                } else {
+                    ret += score2;
+                    mb->best_encoding = ENC_V4;
+                }
+            }
+        }
+
+        break;
+    case MODE_MC:
+        // 1, 10 or 34 bits per MB
+        if (report) {
+            int v1_shrunk = 0, v4_shrunk = 0;
+            for (x = 0; x < mb_count; x++) {
+                mb = &s->mb[x];
+                // it is OK to move blocks to ENC_SKIP here
+                // but not to any codebook encoding!
+                score1 = s->lambda * 1 + FF_LAMBDA_SCALE * mb->skip_error;
+                if (mb->best_encoding == ENC_SKIP) {
+                    ret += score1;
+                } else if (mb->best_encoding == ENC_V1) {
+                    if ((score2 = s->lambda * 10 + FF_LAMBDA_SCALE * 
mb->v1_error) >= score1) {
+                        mb->best_encoding = ENC_SKIP;
+                        ++v1_shrunk;
+                        ret += score1;
+                    } else {
+                        ret += score2;
+                    }
+                } else {
+                    if ((score3 = s->lambda * 34 + FF_LAMBDA_SCALE * 
mb->v4_error) >= score1) {
+                        mb->best_encoding = ENC_SKIP;
+                        ++v4_shrunk;
+                        ret += score1;
+                    } else {
+                        ret += score3;
+                    }
+                }
+            }
+            *training_set_v1_shrunk = v1_shrunk;
+            *training_set_v4_shrunk = v4_shrunk;
+        } else { // find best mode per block
+            for (x = 0; x < mb_count; x++) {
+                mb     = &s->mb[x];
+                score1 = s->lambda * 1 + FF_LAMBDA_SCALE * mb->skip_error;
+                score2 = s->lambda * 10 + FF_LAMBDA_SCALE * mb->v1_error;
+                score3 = s->lambda * 34 + FF_LAMBDA_SCALE * mb->v4_error;
+
+                if (score1 <= score2 && score1 <= score3) {
+                    ret += score1;
+                    mb->best_encoding = ENC_SKIP;
+                } else if (score2 <= score3) {
+                    ret += score2;
+                    mb->best_encoding = ENC_V1;
+                } else {
+                    ret += score3;
+                    mb->best_encoding = ENC_V4;
+                }
+            }
+        }
+
+        break;
+    }
+
+    return ret;
+}
+
+static int write_chunk_header(unsigned char *buf, int chunk_type, int 
chunk_size)
+{
+    buf[0] = chunk_type;
+    AV_WB24(&buf[1], chunk_size + CHUNK_HEADER_SIZE);
+    return CHUNK_HEADER_SIZE;
+}
+
+static int encode_codebook(CinepakEncContext *s, int *codebook, int size,
+                           int chunk_type_yuv, int chunk_type_gray,
+                           unsigned char *buf)
+{
+    int x, y, ret, entry_size = s->pix_fmt == AV_PIX_FMT_RGB24 ? 6 : 4;
+    int incremental_codebook_replacement_mode = 0; // hardcoded here,
+    // the compiler should notice that this is a constant -- rl
+
+    ret = write_chunk_header(buf,
+                             s->pix_fmt == AV_PIX_FMT_RGB24 ?
+                             chunk_type_yuv  + 
(incremental_codebook_replacement_mode ? 1 : 0) :
+                             chunk_type_gray + 
(incremental_codebook_replacement_mode ? 1 : 0),
+                             entry_size * size +
+                             (incremental_codebook_replacement_mode ? (size + 
31) / 32 * 4 : 0));
+
+    // we do codebook encoding according to the "intra" mode
+    // but we keep the "dead" code for reference in case we will want
+    // to use incremental codebook updates (which actually would give us
+    // "kind of" motion compensation, especially in 1 strip/frame case) -- rl
+    // (of course, the code will be not useful as-is)
+    if (incremental_codebook_replacement_mode) {
+        int flags = 0;
+        int flagsind;
+        for (x = 0; x < size; x++) {
+            if (flags == 0) {
+                flagsind = ret;
+                ret     += 4;
+                flags    = 0x80000000;
+            } else
+                flags = ((flags >> 1) | 0x80000000);
+            for (y = 0; y < entry_size; y++)
+                buf[ret++] = codebook[y + x * entry_size] ^ (y >= 4 ? 0x80 : 
0);
+            if ((flags & 0xffffffff) == 0xffffffff) {
+                AV_WB32(&buf[flagsind], flags);
+                flags = 0;
+            }
+        }
+        if (flags)
+            AV_WB32(&buf[flagsind], flags);
+    } else
+        for (x = 0; x < size; x++)
+            for (y = 0; y < entry_size; y++)
+                buf[ret++] = codebook[y + x * entry_size] ^ (y >= 4 ? 0x80 : 
0);
+
+    return ret;
+}
+
+// sets out to the sub picture starting at (x,y) in in
+static void get_sub_picture(CinepakEncContext *s, int x, int y,
+                            uint8_t * in_data[4], int  in_linesize[4],
+                            uint8_t *out_data[4], int out_linesize[4])
+{
+    out_data[0]     = in_data[0] + x + y * in_linesize[0];
+    out_linesize[0] = in_linesize[0];
+
+    if (s->pix_fmt == AV_PIX_FMT_RGB24) {
+        out_data[1]     = in_data[1] + (x >> 1) + (y >> 1) * in_linesize[1];
+        out_linesize[1] = in_linesize[1];
+
+        out_data[2]     = in_data[2] + (x >> 1) + (y >> 1) * in_linesize[2];
+        out_linesize[2] = in_linesize[2];
+    }
+}
+
+// decodes the V1 vector in mb into the 4x4 MB pointed to by data
+static void decode_v1_vector(CinepakEncContext *s, uint8_t *data[4],
+                             int linesize[4], int v1_vector, strip_info *info)
+{
+    int entry_size = s->pix_fmt == AV_PIX_FMT_RGB24 ? 6 : 4;
+
+    data[0][0] =
+    data[0][1] =
+    data[0][    linesize[0]] =
+    data[0][1 + linesize[0]] = info->v1_codebook[v1_vector * entry_size];
+
+    data[0][2] =
+    data[0][3] =
+    data[0][2 + linesize[0]] =
+    data[0][3 + linesize[0]] = info->v1_codebook[v1_vector * entry_size + 1];
+
+    data[0][    2 * linesize[0]] =
+    data[0][1 + 2 * linesize[0]] =
+    data[0][    3 * linesize[0]] =
+    data[0][1 + 3 * linesize[0]] = info->v1_codebook[v1_vector * entry_size + 
2];
+
+    data[0][2 + 2 * linesize[0]] =
+    data[0][3 + 2 * linesize[0]] =
+    data[0][2 + 3 * linesize[0]] =
+    data[0][3 + 3 * linesize[0]] = info->v1_codebook[v1_vector * entry_size + 
3];
+
+    if (s->pix_fmt == AV_PIX_FMT_RGB24) {
+        data[1][0] =
+        data[1][1] =
+        data[1][    linesize[1]] =
+        data[1][1 + linesize[1]] = info->v1_codebook[v1_vector * entry_size + 
4];
+
+        data[2][0] =
+        data[2][1] =
+        data[2][    linesize[2]] =
+        data[2][1 + linesize[2]] = info->v1_codebook[v1_vector * entry_size + 
5];
+    }
+}
+
+// decodes the V4 vectors in mb into the 4x4 MB pointed to by data
+static void decode_v4_vector(CinepakEncContext *s, uint8_t *data[4],
+                             int linesize[4], int *v4_vector, strip_info *info)
+{
+    int i, x, y, entry_size = s->pix_fmt == AV_PIX_FMT_RGB24 ? 6 : 4;
+
+    for (i = y = 0; y < 4; y += 2) {
+        for (x = 0; x < 4; x += 2, i++) {
+            data[0][x     +  y      * linesize[0]] = 
info->v4_codebook[v4_vector[i] * entry_size];
+            data[0][x + 1 +  y      * linesize[0]] = 
info->v4_codebook[v4_vector[i] * entry_size + 1];
+            data[0][x     + (y + 1) * linesize[0]] = 
info->v4_codebook[v4_vector[i] * entry_size + 2];
+            data[0][x + 1 + (y + 1) * linesize[0]] = 
info->v4_codebook[v4_vector[i] * entry_size + 3];
+
+            if (s->pix_fmt == AV_PIX_FMT_RGB24) {
+                data[1][(x >> 1) + (y >> 1) * linesize[1]] = 
info->v4_codebook[v4_vector[i] * entry_size + 4];
+                data[2][(x >> 1) + (y >> 1) * linesize[2]] = 
info->v4_codebook[v4_vector[i] * entry_size + 5];
+            }
+        }
+    }
+}
+
+static void copy_mb(CinepakEncContext *s,
+                    uint8_t *a_data[4], int a_linesize[4],
+                    uint8_t *b_data[4], int b_linesize[4])
+{
+    int y, p;
+
+    for (y = 0; y < MB_SIZE; y++)
+        memcpy(a_data[0] + y * a_linesize[0], b_data[0] + y * b_linesize[0],
+               MB_SIZE);
+
+    if (s->pix_fmt == AV_PIX_FMT_RGB24) {
+        for (p = 1; p <= 2; p++)
+            for (y = 0; y < MB_SIZE / 2; y++)
+                memcpy(a_data[p] + y * a_linesize[p],
+                       b_data[p] + y * b_linesize[p],
+                       MB_SIZE / 2);
+    }
+}
+
+static int encode_mode(CinepakEncContext *s, int h,
+                       uint8_t *scratch_data[4], int scratch_linesize[4],
+                       uint8_t *last_data[4], int last_linesize[4],
+                       strip_info *info, unsigned char *buf)
+{
+    int x, y, z, flags, bits, temp_size, header_ofs, ret = 0, mb_count = s->w 
* h / MB_AREA;
+    int needs_extra_bit, should_write_temp;
+    unsigned char temp[64]; // 32/2 = 16 V4 blocks at 4 B each -> 64 B
+    mb_info *mb;
+    uint8_t *sub_scratch_data[4] = { 0 }, *sub_last_data[4] = { 0 };
+    int sub_scratch_linesize[4] = { 0 }, sub_last_linesize[4] = { 0 };
+
+    // encode codebooks
+    ////// MacOS vintage decoder compatibility dictates the presence of
+    ////// the codebook chunk even when the codebook is empty - pretty dumb...
+    ////// and also the certain order of the codebook chunks -- rl
+    if (info->v4_size || !s->skip_empty_cb)
+        ret += encode_codebook(s, info->v4_codebook, info->v4_size, 0x20, 
0x24, buf + ret);
+
+    if (info->v1_size || !s->skip_empty_cb)
+        ret += encode_codebook(s, info->v1_codebook, info->v1_size, 0x22, 
0x26, buf + ret);
+
+    // update scratch picture
+    for (z = y = 0; y < h; y += MB_SIZE)
+        for (x = 0; x < s->w; x += MB_SIZE, z++) {
+            mb = &s->mb[z];
+
+            get_sub_picture(s, x, y, scratch_data, scratch_linesize,
+                            sub_scratch_data, sub_scratch_linesize);
+
+            if (info->mode == MODE_MC && mb->best_encoding == ENC_SKIP) {
+                get_sub_picture(s, x, y, last_data, last_linesize,
+                                sub_last_data, sub_last_linesize);
+                copy_mb(s, sub_scratch_data, sub_scratch_linesize,
+                        sub_last_data, sub_last_linesize);
+            } else if (info->mode == MODE_V1_ONLY || mb->best_encoding == 
ENC_V1)
+                decode_v1_vector(s, sub_scratch_data, sub_scratch_linesize,
+                                 mb->v1_vector, info);
+            else
+                decode_v4_vector(s, sub_scratch_data, sub_scratch_linesize,
+                                 mb->v4_vector, info);
+        }
+
+    switch (info->mode) {
+    case MODE_V1_ONLY:
+        ret += write_chunk_header(buf + ret, 0x32, mb_count);
+
+        for (x = 0; x < mb_count; x++)
+            buf[ret++] = s->mb[x].v1_vector;
+
+        break;
+    case MODE_V1_V4:
+        // remember header position
+        header_ofs = ret;
+        ret       += CHUNK_HEADER_SIZE;
+
+        for (x = 0; x < mb_count; x += 32) {
+            flags = 0;
+            for (y = x; y < FFMIN(x + 32, mb_count); y++)
+                if (s->mb[y].best_encoding == ENC_V4)
+                    flags |= 1 << (31 - y + x);
+
+            AV_WB32(&buf[ret], flags);
+            ret += 4;
+
+            for (y = x; y < FFMIN(x + 32, mb_count); y++) {
+                mb = &s->mb[y];
+
+                if (mb->best_encoding == ENC_V1)
+                    buf[ret++] = mb->v1_vector;
+                else
+                    for (z = 0; z < 4; z++)
+                        buf[ret++] = mb->v4_vector[z];
+            }
+        }
+
+        write_chunk_header(buf + header_ofs, 0x30, ret - header_ofs - 
CHUNK_HEADER_SIZE);
+
+        break;
+    case MODE_MC:
+        // remember header position
+        header_ofs = ret;
+        ret       += CHUNK_HEADER_SIZE;
+        flags      = bits = temp_size = 0;
+
+        for (x = 0; x < mb_count; x++) {
+            mb                = &s->mb[x];
+            flags            |= (mb->best_encoding != ENC_SKIP) << (31 - 
bits++);
+            needs_extra_bit   = 0;
+            should_write_temp = 0;
+
+            if (mb->best_encoding != ENC_SKIP) {
+                if (bits < 32)
+                    flags |= (mb->best_encoding == ENC_V4) << (31 - bits++);
+                else
+                    needs_extra_bit = 1;
+            }
+
+            if (bits == 32) {
+                AV_WB32(&buf[ret], flags);
+                ret  += 4;
+                flags = bits = 0;
+
+                if (mb->best_encoding == ENC_SKIP || needs_extra_bit) {
+                    memcpy(&buf[ret], temp, temp_size);
+                    ret      += temp_size;
+                    temp_size = 0;
+                } else
+                    should_write_temp = 1;
+            }
+
+            if (needs_extra_bit) {
+                flags = (mb->best_encoding == ENC_V4) << 31;
+                bits  = 1;
+            }
+
+            if (mb->best_encoding == ENC_V1)
+                temp[temp_size++] = mb->v1_vector;
+            else if (mb->best_encoding == ENC_V4)
+                for (z = 0; z < 4; z++)
+                    temp[temp_size++] = mb->v4_vector[z];
+
+            if (should_write_temp) {
+                memcpy(&buf[ret], temp, temp_size);
+                ret      += temp_size;
+                temp_size = 0;
+            }
+        }
+
+        if (bits > 0) {
+            AV_WB32(&buf[ret], flags);
+            ret += 4;
+            memcpy(&buf[ret], temp, temp_size);
+            ret += temp_size;
+        }
+
+        write_chunk_header(buf + header_ofs, 0x31, ret - header_ofs - 
CHUNK_HEADER_SIZE);
+
+        break;
+    }
+
+    return ret;
+}
+
+// computes distortion of 4x4 MB in b compared to a
+static int compute_mb_distortion(CinepakEncContext *s,
+                                 uint8_t *a_data[4], int a_linesize[4],
+                                 uint8_t *b_data[4], int b_linesize[4])
+{
+    int x, y, p, d, ret = 0;
+
+    for (y = 0; y < MB_SIZE; y++)
+        for (x = 0; x < MB_SIZE; x++) {
+            d = a_data[0][x + y * a_linesize[0]] - b_data[0][x + y * 
b_linesize[0]];
+            ret += d * d;
+        }
+
+    if (s->pix_fmt == AV_PIX_FMT_RGB24) {
+        for (p = 1; p <= 2; p++) {
+            for (y = 0; y < MB_SIZE / 2; y++)
+                for (x = 0; x < MB_SIZE / 2; x++) {
+                    d = a_data[p][x + y * a_linesize[p]] - b_data[p][x + y * 
b_linesize[p]];
+                    ret += d * d;
+                }
+        }
+    }
+
+    return ret;
+}
+
+// return the possibly adjusted size of the codebook
+#define CERTAIN(x) ((x) != ENC_UNCERTAIN)
+static int quantize(CinepakEncContext *s, int h, uint8_t *data[4],
+                    int linesize[4], int v1mode, strip_info *info,
+                    mb_encoding encoding)
+{
+    int x, y, i, j, k, x2, y2, x3, y3, plane, shift, mbn;
+    int entry_size      = s->pix_fmt == AV_PIX_FMT_RGB24 ? 6 : 4;
+    int *codebook       = v1mode ? info->v1_codebook : info->v4_codebook;
+    int size            = v1mode ? info->v1_size : info->v4_size;
+    int64_t total_error = 0;
+    uint8_t vq_pict_buf[(MB_AREA * 3) / 2];
+    uint8_t     *sub_data[4],     *vq_data[4];
+    int      sub_linesize[4],  vq_linesize[4];
+
+    for (mbn = i = y = 0; y < h; y += MB_SIZE) {
+        for (x = 0; x < s->w; x += MB_SIZE, ++mbn) {
+            int *base;
+
+            if (CERTAIN(encoding)) {
+                // use for the training only the blocks known to be to be 
encoded [sic:-]
+                if (s->mb[mbn].best_encoding != encoding)
+                    continue;
+            }
+
+            base = s->codebook_input + i * entry_size;
+            if (v1mode) {
+                // subsample
+                for (j = y2 = 0; y2 < entry_size; y2 += 2)
+                    for (x2 = 0; x2 < 4; x2 += 2, j++) {
+                        plane   = y2 < 4 ? 0 : 1 + (x2 >> 1);
+                        shift   = y2 < 4 ? 0 : 1;
+                        x3      = shift ? 0 : x2;
+                        y3      = shift ? 0 : y2;
+                        base[j] = (data[plane][((x + x3) >> shift) +      ((y 
+ y3) >> shift)      * linesize[plane]] +
+                                   data[plane][((x + x3) >> shift) + 1 +  ((y 
+ y3) >> shift)      * linesize[plane]] +
+                                   data[plane][((x + x3) >> shift) +     (((y 
+ y3) >> shift) + 1) * linesize[plane]] +
+                                   data[plane][((x + x3) >> shift) + 1 + (((y 
+ y3) >> shift) + 1) * linesize[plane]]) >> 2;
+                    }
+            } else {
+                // copy
+                for (j = y2 = 0; y2 < MB_SIZE; y2 += 2) {
+                    for (x2 = 0; x2 < MB_SIZE; x2 += 2)
+                        for (k = 0; k < entry_size; k++, j++) {
+                            plane = k >= 4 ? k - 3 : 0;
+
+                            if (k >= 4) {
+                                x3 = (x + x2) >> 1;
+                                y3 = (y + y2) >> 1;
+                            } else {
+                                x3 = x + x2 + (k & 1);
+                                y3 = y + y2 + (k >> 1);
+                            }
+
+                            base[j] = data[plane][x3 + y3 * linesize[plane]];
+                        }
+                }
+            }
+            i += v1mode ? 1 : 4;
+        }
+    }
+
+    if (i == 0) // empty training set, nothing to do
+        return 0;
+    if (i < size)
+        size = i;
+
+    ff_init_elbg(s->codebook_input, entry_size, i, codebook, size, 1, 
s->codebook_closest, &s->randctx);
+    ff_do_elbg(s->codebook_input, entry_size, i, codebook, size, 1, 
s->codebook_closest, &s->randctx);
+
+    // set up vq_data, which contains a single MB
+    vq_data[0]     = vq_pict_buf;
+    vq_linesize[0] = MB_SIZE;
+    vq_data[1]     = &vq_pict_buf[MB_AREA];
+    vq_data[2]     = vq_data[1] + (MB_AREA >> 2);
+    vq_linesize[1] =
+    vq_linesize[2] = MB_SIZE >> 1;
+
+    // copy indices
+    for (i = j = y = 0; y < h; y += MB_SIZE)
+        for (x = 0; x < s->w; x += MB_SIZE, j++) {
+            mb_info *mb = &s->mb[j];
+            // skip uninteresting blocks if we know their preferred encoding
+            if (CERTAIN(encoding) && mb->best_encoding != encoding)
+                continue;
+
+            // point sub_data to current MB
+            get_sub_picture(s, x, y, data, linesize, sub_data, sub_linesize);
+
+            if (v1mode) {
+                mb->v1_vector = s->codebook_closest[i];
+
+                // fill in vq_data with V1 data
+                decode_v1_vector(s, vq_data, vq_linesize, mb->v1_vector, info);
+
+                mb->v1_error = compute_mb_distortion(s, sub_data, sub_linesize,
+                                                     vq_data, vq_linesize);
+                total_error += mb->v1_error;
+            } else {
+                for (k = 0; k < 4; k++)
+                    mb->v4_vector[k] = s->codebook_closest[i + k];
+
+                // fill in vq_data with V4 data
+                decode_v4_vector(s, vq_data, vq_linesize, mb->v4_vector, info);
+
+                mb->v4_error = compute_mb_distortion(s, sub_data, sub_linesize,
+                                                     vq_data, vq_linesize);
+                total_error += mb->v4_error;
+            }
+            i += v1mode ? 1 : 4;
+        }
+    // check that we did it right in the beginning of the function
+    av_assert0(i >= size); // training set is no smaller than the codebook
+
+    return size;
+}
+
+static void calculate_skip_errors(CinepakEncContext *s, int h,
+                                  uint8_t *last_data[4], int last_linesize[4],
+                                  uint8_t *data[4], int linesize[4],
+                                  strip_info *info)
+{
+    int x, y, i;
+    uint8_t *sub_last_data    [4], *sub_pict_data    [4];
+    int      sub_last_linesize[4],  sub_pict_linesize[4];
+
+    for (i = y = 0; y < h; y += MB_SIZE)
+        for (x = 0; x < s->w; x += MB_SIZE, i++) {
+            get_sub_picture(s, x, y, last_data, last_linesize,
+                            sub_last_data, sub_last_linesize);
+            get_sub_picture(s, x, y, data, linesize,
+                            sub_pict_data, sub_pict_linesize);
+
+            s->mb[i].skip_error =
+                compute_mb_distortion(s,
+                                      sub_last_data, sub_last_linesize,
+                                      sub_pict_data, sub_pict_linesize);
+        }
+}
+
+static void write_strip_header(CinepakEncContext *s, int y, int h, int 
keyframe,
+                               unsigned char *buf, int strip_size)
+{
+    // actually we are exclusively using intra strip coding (how much can we 
win
+    // otherwise? how to choose which part of a codebook to update?),
+    // keyframes are different only because we disallow ENC_SKIP on them -- rl
+    // (besides, the logic here used to be inverted: )
+    //    buf[0] = keyframe ? 0x11: 0x10;
+    buf[0] = keyframe ? 0x10 : 0x11;
+    AV_WB24(&buf[1], strip_size + STRIP_HEADER_SIZE);
+    // AV_WB16(&buf[4], y); /* using absolute y values works -- rl */
+    AV_WB16(&buf[4], 0); /* using relative values works as well -- rl */
+    AV_WB16(&buf[6], 0);
+    // AV_WB16(&buf[8], y + h); /* using absolute y values works -- rl */
+    AV_WB16(&buf[8], h); /* using relative values works as well -- rl */
+    AV_WB16(&buf[10], s->w);
+}
+
+static int rd_strip(CinepakEncContext *s, int y, int h, int keyframe,
+                    uint8_t *last_data[4], int last_linesize[4],
+                    uint8_t *data[4], int linesize[4],
+                    uint8_t *scratch_data[4], int scratch_linesize[4],
+                    unsigned char *buf, int64_t *best_score)
+{
+    int64_t score = 0;
+    int best_size = 0;
+    strip_info info;
+    // for codebook optimization:
+    int v1enough, v1_size, v4enough, v4_size;
+    int new_v1_size, new_v4_size;
+    int v1shrunk, v4shrunk;
+
+    if (!keyframe)
+        calculate_skip_errors(s, h, last_data, last_linesize, data, linesize,
+                              &info);
+
+    // try some powers of 4 for the size of the codebooks
+    // constraint the v4 codebook to be no bigger than v1 one,
+    // (and no less than v1_size/4)
+    // thus making v1 preferable and possibly losing small details? should be 
ok
+#define SMALLEST_CODEBOOK 1
+    for (v1enough = 0, v1_size = SMALLEST_CODEBOOK; v1_size <= CODEBOOK_MAX && 
!v1enough; v1_size <<= 2) {
+        for (v4enough = 0, v4_size = 0; v4_size <= v1_size && !v4enough; 
v4_size = v4_size ? v4_size << 2 : v1_size >= SMALLEST_CODEBOOK << 2 ? v1_size 
>> 2 : SMALLEST_CODEBOOK) {
+            // try all modes
+            for (CinepakMode mode = 0; mode < MODE_COUNT; mode++) {
+                // don't allow MODE_MC in intra frames
+                if (keyframe && mode == MODE_MC)
+                    continue;
+
+                if (mode == MODE_V1_ONLY) {
+                    info.v1_size = v1_size;
+                    // the size may shrink even before optimizations if the 
input is short:
+                    info.v1_size = quantize(s, h, data, linesize, 1,
+                                            &info, ENC_UNCERTAIN);
+                    if (info.v1_size < v1_size)
+                        // too few eligible blocks, no sense in trying bigger 
sizes
+                        v1enough = 1;
+
+                    info.v4_size = 0;
+                } else { // mode != MODE_V1_ONLY
+                    // if v4 codebook is empty then only allow V1-only mode
+                    if (!v4_size)
+                        continue;
+
+                    if (mode == MODE_V1_V4) {
+                        info.v4_size = v4_size;
+                        info.v4_size = quantize(s, h, data, linesize, 0,
+                                                &info, ENC_UNCERTAIN);
+                        if (info.v4_size < v4_size)
+                            // too few eligible blocks, no sense in trying 
bigger sizes
+                            v4enough = 1;
+                    }
+                }
+
+                info.mode = mode;
+                // choose the best encoding per block, based on current 
experience
+                score = calculate_mode_score(s, h, &info, 0,
+                                             &v1shrunk, &v4shrunk);
+
+                if (mode != MODE_V1_ONLY) {
+                    int extra_iterations_limit = s->max_extra_cb_iterations;
+                    // recompute the codebooks, omitting the extra blocks
+                    // we assume we _may_ come here with more blocks to encode 
than before
+                    info.v1_size = v1_size;
+                    new_v1_size = quantize(s, h, data, linesize, 1, &info, 
ENC_V1);
+                    if (new_v1_size < info.v1_size)
+                        info.v1_size = new_v1_size;
+                    // we assume we _may_ come here with more blocks to encode 
than before
+                    info.v4_size = v4_size;
+                    new_v4_size = quantize(s, h, data, linesize, 0, &info, 
ENC_V4);
+                    if (new_v4_size < info.v4_size)
+                        info.v4_size = new_v4_size;
+                    // calculate the resulting score
+                    // (do not move blocks to codebook encodings now, as some 
blocks may have
+                    // got bigger errors despite a smaller training set - but 
we do not
+                    // ever grow the training sets back)
+                    for (;;) {
+                        score = calculate_mode_score(s, h, &info, 1,
+                                                     &v1shrunk, &v4shrunk);
+                        // do we have a reason to reiterate? if so, have we 
reached the limit?
+                        if ((!v1shrunk && !v4shrunk) || 
!extra_iterations_limit--)
+                            break;
+                        // recompute the codebooks, omitting the extra blocks
+                        if (v1shrunk) {
+                            info.v1_size = v1_size;
+                            new_v1_size = quantize(s, h, data, linesize, 1, 
&info, ENC_V1);
+                            if (new_v1_size < info.v1_size)
+                                info.v1_size = new_v1_size;
+                        }
+                        if (v4shrunk) {
+                            info.v4_size = v4_size;
+                            new_v4_size = quantize(s, h, data, linesize, 0, 
&info, ENC_V4);
+                            if (new_v4_size < info.v4_size)
+                                info.v4_size = new_v4_size;
+                        }
+                    }
+                }
+
+                if (best_size == 0 || score < *best_score) {
+                    *best_score = score;
+                    best_size = encode_mode(s, h,
+                                            scratch_data, scratch_linesize,
+                                            last_data, last_linesize, &info,
+                                            s->strip_buf + STRIP_HEADER_SIZE);
+                    write_strip_header(s, y, h, keyframe, s->strip_buf, 
best_size);
+                }
+            }
+        }
+    }
+
+    best_size += STRIP_HEADER_SIZE;
+    memcpy(buf, s->strip_buf, best_size);
+
+    return best_size;
+}
+
+static int write_cvid_header(CinepakEncContext *s, unsigned char *buf,
+                             int num_strips, int data_size, int isakeyframe)
+{
+    buf[0] = isakeyframe ? 0 : 1;
+    AV_WB24(&buf[1], data_size + CVID_HEADER_SIZE);
+    AV_WB16(&buf[4], s->w);
+    AV_WB16(&buf[6], s->h);
+    AV_WB16(&buf[8], num_strips);
+
+    return CVID_HEADER_SIZE;
+}
+
+static int rd_frame(CinepakEncContext *s, const AVFrame *frame,
+                    int isakeyframe, unsigned char *buf, int buf_size)
+{
+    int num_strips, strip, i, y, nexty, size, temp_size;
+    uint8_t *last_data    [4], *data    [4], *scratch_data    [4];
+    int      last_linesize[4],  linesize[4],  scratch_linesize[4];
+    int64_t best_score = 0, score, score_temp;
+    int best_nstrips = -1, best_size = -1; // mark as uninitialzed
+
+    if (s->pix_fmt == AV_PIX_FMT_RGB24) {
+        int x;
+        // build a copy of the given frame in the correct colorspace
+        for (y = 0; y < s->h; y += 2)
+            for (x = 0; x < s->w; x += 2) {
+                uint8_t *ir[2];
+                int32_t r, g, b, rr, gg, bb;
+                ir[0] = frame->data[0] + x * 3 + y * frame->linesize[0];
+                ir[1] = ir[0] + frame->linesize[0];
+                get_sub_picture(s, x, y,
+                                s->input_frame->data, s->input_frame->linesize,
+                                scratch_data, scratch_linesize);
+                r = g = b = 0;
+                for (i = 0; i < 4; ++i) {
+                    int i1, i2;
+                    i1 = (i & 1);
+                    i2 = (i >= 2);
+                    rr = ir[i2][i1 * 3 + 0];
+                    gg = ir[i2][i1 * 3 + 1];
+                    bb = ir[i2][i1 * 3 + 2];
+                    r += rr;
+                    g += gg;
+                    b += bb;
+                    // using fixed point arithmetic for portable 
repeatability, scaling by 2^23
+                    // "Y"
+                    // rr = 0.2857 * rr + 0.5714 * gg + 0.1429 * bb;
+                    rr = (2396625 * rr + 4793251 * gg + 1198732 * bb) >> 23;
+                    if (rr < 0)
+                        rr = 0;
+                    else if (rr > 255)
+                        rr = 255;
+                    scratch_data[0][i1 + i2 * scratch_linesize[0]] = rr;
+                }
+                // let us scale down as late as possible
+                //                r /= 4; g /= 4; b /= 4;
+                // "U"
+                // rr = -0.1429 * r - 0.2857 * g + 0.4286 * b;
+                rr = (-299683 * r - 599156 * g + 898839 * b) >> 23;
+                if (rr < -128)
+                    rr = -128;
+                else if (rr > 127)
+                    rr = 127;
+                scratch_data[1][0] = rr + 128; // quantize needs unsigned
+                // "V"
+                // rr = 0.3571 * r - 0.2857 * g - 0.0714 * b;
+                rr = (748893 * r - 599156 * g - 149737 * b) >> 23;
+                if (rr < -128)
+                    rr = -128;
+                else if (rr > 127)
+                    rr = 127;
+                scratch_data[2][0] = rr + 128; // quantize needs unsigned
+            }
+    }
+
+    // would be nice but quite certainly incompatible with vintage players:
+    // support encoding zero strips (meaning skip the whole frame)
+    for (num_strips = s->min_strips; num_strips <= s->max_strips && num_strips 
<= s->h / MB_SIZE; num_strips++) {
+        score = 0;
+        size  = 0;
+
+        for (y = 0, strip = 1; y < s->h; strip++, y = nexty) {
+            int strip_height;
+
+            nexty = strip * s->h / num_strips; // <= s->h
+            // make nexty the next multiple of 4 if not already there
+            if (nexty & 3)
+                nexty += 4 - (nexty & 3);
+
+            strip_height = nexty - y;
+            if (strip_height <= 0) { // can this ever happen?
+                av_log(s->avctx, AV_LOG_INFO, "skipping zero height strip %i 
of %i\n", strip, num_strips);
+                continue;
+            }
+
+            if (s->pix_fmt == AV_PIX_FMT_RGB24)
+                get_sub_picture(s, 0, y,
+                                s->input_frame->data, s->input_frame->linesize,
+                                data, linesize);
+            else
+                get_sub_picture(s, 0, y,
+                                (uint8_t **)frame->data, (int 
*)frame->linesize,
+                                data, linesize);
+            get_sub_picture(s, 0, y,
+                            s->last_frame->data, s->last_frame->linesize,
+                            last_data, last_linesize);
+            get_sub_picture(s, 0, y,
+                            s->scratch_frame->data, s->scratch_frame->linesize,
+                            scratch_data, scratch_linesize);
+
+            if ((temp_size = rd_strip(s, y, strip_height, isakeyframe,
+                                      last_data, last_linesize, data, linesize,
+                                      scratch_data, scratch_linesize,
+                                      s->frame_buf + size + CVID_HEADER_SIZE,
+                                      &score_temp)) < 0)
+                return temp_size;
+
+            score += score_temp;
+            size += temp_size;
+        }
+
+        if (best_score == 0 || score < best_score) {
+            best_score = score;
+            best_size = size + write_cvid_header(s, s->frame_buf, num_strips, 
size, isakeyframe);
+            FFSWAP(AVFrame *, s->best_frame, s->scratch_frame);
+            memcpy(buf, s->frame_buf, best_size);
+            best_nstrips = num_strips;
+        }
+        // avoid trying too many strip numbers without a real reason
+        // (this makes the processing of the very first frame faster)
+        if (num_strips - best_nstrips > 4)
+            break;
+    }
+
+    // let the number of strips slowly adapt to the changes in the contents,
+    // compared to full bruteforcing every time this will occasionally lead
+    // to some r/d performance loss but makes encoding up to several times 
faster
+    if (!s->strip_number_delta_range) {
+        if (best_nstrips == s->max_strips) { // let us try to step up
+            s->max_strips = best_nstrips + 1;
+            if (s->max_strips >= s->max_max_strips)
+                s->max_strips = s->max_max_strips;
+        } else { // try to step down
+            s->max_strips = best_nstrips;
+        }
+        s->min_strips = s->max_strips - 1;
+        if (s->min_strips < s->min_min_strips)
+            s->min_strips = s->min_min_strips;
+    } else {
+        s->max_strips = best_nstrips + s->strip_number_delta_range;
+        if (s->max_strips >= s->max_max_strips)
+            s->max_strips = s->max_max_strips;
+        s->min_strips = best_nstrips - s->strip_number_delta_range;
+        if (s->min_strips < s->min_min_strips)
+            s->min_strips = s->min_min_strips;
+    }
+
+    return best_size;
+}
+
+static int cinepak_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
+                                const AVFrame *frame, int *got_packet)
+{
+    CinepakEncContext *s = avctx->priv_data;
+    int ret;
+
+    s->lambda = frame->quality ? frame->quality - 1 : 2 * FF_LAMBDA_SCALE;
+
+    if ((ret = ff_alloc_packet(pkt, s->frame_buf_size)) < 0)
+        return ret;
+    ret       = rd_frame(s, frame, (s->curframe == 0), pkt->data, 
s->frame_buf_size);
+    pkt->size = ret;
+    if (s->curframe == 0)
+        pkt->flags |= AV_PKT_FLAG_KEY;
+    *got_packet = 1;
+
+    FFSWAP(AVFrame *, s->last_frame, s->best_frame);
+
+    if (++s->curframe >= s->keyint)
+        s->curframe = 0;
+
+    return 0;
+}
+
+static av_cold int cinepak_encode_end(AVCodecContext *avctx)
+{
+    CinepakEncContext *s = avctx->priv_data;
+    int x;
+
+    av_frame_free(&s->last_frame);
+    av_frame_free(&s->best_frame);
+    av_frame_free(&s->scratch_frame);
+    if (avctx->pix_fmt == AV_PIX_FMT_RGB24)
+        av_frame_free(&s->input_frame);
+    av_freep(&s->codebook_input);
+    av_freep(&s->codebook_closest);
+    av_freep(&s->strip_buf);
+    av_freep(&s->frame_buf);
+    av_freep(&s->mb);
+
+    for (x = 0; x < (avctx->pix_fmt == AV_PIX_FMT_RGB24 ? 4 : 3); x++)
+        av_freep(&s->pict_bufs[x]);
+
+    return 0;
+}
+
+AVCodec ff_cinepak_encoder = {
+    .name           = "cinepak",
+    .long_name      = NULL_IF_CONFIG_SMALL("Cinepak"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_CINEPAK,
+    .priv_data_size = sizeof(CinepakEncContext),
+    .init           = cinepak_encode_init,
+    .encode2        = cinepak_encode_frame,
+    .close          = cinepak_encode_end,
+    .pix_fmts       = (const enum AVPixelFormat[]) { AV_PIX_FMT_RGB24, 
AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE },
+    .priv_class     = &cinepak_class,
+};
diff --git a/tests/fate/vcodec.mak b/tests/fate/vcodec.mak
index e195254..490da55 100644
--- a/tests/fate/vcodec.mak
+++ b/tests/fate/vcodec.mak
@@ -12,6 +12,10 @@ fate-vsynth%-asv1:               ENCOPTS = -qscale 10
 FATE_VCODEC-$(call ENCDEC, ASV2, AVI)   += asv2
 fate-vsynth%-asv2:               ENCOPTS = -qscale 10
 
+FATE_VCODEC-$(call ENCDEC, CINEPAK, AVI) += cinepak
+fate-vsynth%-cinepak:            ENCOPTS = -s sqcif -strip_number_adaptivity 1
+fate-vsynth%-cinepak:            DECOPTS = -s sqcif
+
 FATE_VCODEC-$(call ENCDEC, CLJR, AVI)   += cljr
 
 FATE_VCODEC-$(call ENCDEC, DNXHD, DNXHD) += dnxhd-720p                  \
diff --git a/tests/ref/vsynth/vsynth1-cinepak b/tests/ref/vsynth/vsynth1-cinepak
new file mode 100644
index 0000000..09ba81a
--- /dev/null
+++ b/tests/ref/vsynth/vsynth1-cinepak
@@ -0,0 +1,4 @@
+c2198279f3ccf18eb029875c98231073 *tests/data/fate/vsynth1-cinepak.avi
+408612 tests/data/fate/vsynth1-cinepak.avi
+fdcc7e515ff0bea5203d1ed9bfa7514c *tests/data/fate/vsynth1-cinepak.out.rawvideo
+stddev:   64.04 PSNR: 12.00 MAXDIFF:  238 bytes:  7603200/   921600
diff --git a/tests/ref/vsynth/vsynth2-cinepak b/tests/ref/vsynth/vsynth2-cinepak
new file mode 100644
index 0000000..c1ff48d
--- /dev/null
+++ b/tests/ref/vsynth/vsynth2-cinepak
@@ -0,0 +1,4 @@
+722bab34b13b33e46dbbdd47985049f3 *tests/data/fate/vsynth2-cinepak.avi
+400398 tests/data/fate/vsynth2-cinepak.avi
+c59f0419068f0071476d037abd2ccde2 *tests/data/fate/vsynth2-cinepak.out.rawvideo
+stddev:   87.20 PSNR:  9.32 MAXDIFF:  247 bytes:  7603200/   921600
-- 
2.1.4

_______________________________________________
libav-devel mailing list
libav-devel@libav.org
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to