Re: [FFmpeg-devel] [PATCH 2/2] lavc/msrleenc: Add msrle encoder

2023-06-10 Thread Andreas Rheinhardt
Tomas Härdin:
> +typedef struct MSRLEContext {
> +const AVClass *class;
> +int curframe;
> +AVFrame *last_frame;
> +} MSRLEContext;
> +
> +static av_cold int msrle_encode_init(AVCodecContext *avctx)
> +{
> +avctx->bits_per_coded_sample = 8;
> +return 0;
> +}
> +
> +static void write_run(AVCodecContext *avctx, uint8_t **data, int len, int 
> value)
> +{
> +// we're allowed to write odd runs
> +while (len >= 255) {
> +bytestream_put_byte(data, 255);
> +bytestream_put_byte(data, value);
> +len -= 255;
> +}
> +if (len >= 1) {
> +// this is wasteful when len == 1 and sometimes when len == 2
> +// but sometimes we have no choice. also write_absolute()
> +// relies on this
> +bytestream_put_byte(data, len);
> +bytestream_put_byte(data, value);
> +}
> +}
> +
> +static void write_absolute(AVCodecContext *avctx, uint8_t **data, uint8_t 
> *line, int len)
> +{
> +// writing 255 would be wasteful here due to the padding requirement
> +while (len >= 254) {
> +bytestream_put_byte(data, 0);
> +bytestream_put_byte(data, 254);
> +bytestream_put_buffer(data, line, 254);
> +line += 254;
> +len -= 254;
> +}
> +if (len == 1) {
> +// it's less wasteful to write single pixels as runs
> +// not to mention that absolute mode requires >= 3 pixels
> +write_run(avctx, data, 1, line[0]);
> +} else if (len == 2) {
> +write_run(avctx, data, 1, line[0]);
> +write_run(avctx, data, 1, line[1]);
> +} else if (len > 0) {
> +bytestream_put_byte(data, 0);
> +bytestream_put_byte(data, len);
> +bytestream_put_buffer(data, line, len);
> +if (len & 1)
> +bytestream_put_byte(data, 0);
> +}
> +}
> +
> +static void write_delta(AVCodecContext *avctx, uint8_t **data, int delta)
> +{
> +// we let the yskip logic handle the case where we want to delta
> +// to following lines. it's not perfect but it's easier than finding
> +// the optimal combination of end-of-lines and deltas to reach any
> +// following position including places where dx < 0
> +while (delta >= 255) {
> +bytestream_put_byte(data, 0);
> +bytestream_put_byte(data, 2);
> +bytestream_put_byte(data, 255);
> +bytestream_put_byte(data, 0);
> +delta -= 255;
> +}
> +if (delta > 0) {
> +bytestream_put_byte(data, 0);
> +bytestream_put_byte(data, 2);
> +bytestream_put_byte(data, delta);
> +bytestream_put_byte(data, 0);
> +}
> +}
> +
> +static void write_yskip(AVCodecContext *avctx, uint8_t **data, int yskip)
> +{
> +if (yskip < 4)
> +return;
> +// we have yskip*2 nul bytess
> +*data -= 2*yskip;
> +// the end-of-line counts as one skip
> +yskip--;
> +while (yskip >= 255) {
> +bytestream_put_byte(data, 0);
> +bytestream_put_byte(data, 2);
> +bytestream_put_byte(data, 0);
> +bytestream_put_byte(data, 255);
> +yskip -= 255;
> +}
> +if (yskip > 0) {
> +bytestream_put_byte(data, 0);
> +bytestream_put_byte(data, 2);
> +bytestream_put_byte(data, 0);
> +bytestream_put_byte(data, yskip);
> +}
> +bytestream_put_be16(data, 0x);
> +}
> +
> +// used both to encode lines in keyframes and to encode lines between deltas
> +static void encode_line(AVCodecContext *avctx, uint8_t **data, uint8_t 
> *line, int length)
> +{
> +int run = 0, last = -1, absstart = 0;
> +if (length == 0)
> +return;
> +for (int x = 0; x < length; x++) {
> +if (last == line[x]) {
> +run++;
> +if (run == 3)
> +write_absolute(avctx, data, [absstart], x - absstart - 
> 2);
> +} else {
> +if (run >= 3) {
> +write_run(avctx, data, run, last);
> +absstart = x;
> +}
> +run = 1;
> +}
> +last = line[x];
> +}
> +if (run >= 3)
> +write_run(avctx, data, run, last);
> +else
> +write_absolute(avctx, data, [absstart], length - absstart);
> +}
> +
> +static int encode(AVCodecContext *avctx, AVPacket *pkt,
> +  const AVFrame *pict, int keyframe, int *got_keyframe)
> +{
> +MSRLEContext *s = avctx->priv_data;
> +uint8_t *data = pkt->data;
> +
> +/*  Compare the current frame to the last frame, or code the entire frame
> +if keyframe != 0. We're continually outputting pairs of bytes:
> +
> +00 00   end of line
> +00 01   end of bitmap
> +00 02 dx dy delta. move pointer to x+dx, y+dy
> +00 ll dd dd ..  absolute (verbatim) mode. ll >= 3
> +rr dd   run. rr >= 1
> +
> +For keyframes we only have absolute mode and runs at our disposal, 
> and
> +we are not allowed 

[FFmpeg-devel] [PATCH 2/2] lavc/msrleenc: Add msrle encoder

2023-06-08 Thread Tomas Härdin
Please check that I got the palette handling correct. Else this
producing output of similar size to the VfW encoder.

/Tomas
From ab9bb1aca7ddda8f4788b0a63460470fce021e72 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= 
Date: Thu, 8 Jun 2023 11:57:53 +0200
Subject: [PATCH 2/2] lavc/msrleenc: Add msrle encoder

Keyframes are marked automagically
---
 MAINTAINERS|   1 +
 doc/encoders.texi  |  14 ++
 libavcodec/Makefile|   1 +
 libavcodec/allcodecs.c |   1 +
 libavcodec/msrleenc.c  | 303 +
 tests/fate/vcodec.mak  |   3 +
 tests/ref/vsynth/vsynth1-msrle |   4 +
 tests/ref/vsynth/vsynth2-msrle |   4 +
 tests/ref/vsynth/vsynth3-msrle |   4 +
 tests/ref/vsynth/vsynth_lena-msrle |   4 +
 10 files changed, 339 insertions(+)
 create mode 100644 libavcodec/msrleenc.c
 create mode 100644 tests/ref/vsynth/vsynth1-msrle
 create mode 100644 tests/ref/vsynth/vsynth2-msrle
 create mode 100644 tests/ref/vsynth/vsynth3-msrle
 create mode 100644 tests/ref/vsynth/vsynth_lena-msrle

diff --git a/MAINTAINERS b/MAINTAINERS
index 07852486e4..3584a68442 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -210,6 +210,7 @@ Codecs:
   mqc*  Nicolas Bertrand
   msmpeg4.c, msmpeg4data.h  Michael Niedermayer
   msrle.c   Mike Melanson
+  msrleenc.cTomas Härdin
   msvideo1.cMike Melanson
   nuv.c Reimar Doeffinger
   nvdec*, nvenc*Timo Rothenpieler
diff --git a/doc/encoders.texi b/doc/encoders.texi
index 20cb8a1421..25d6b7f09e 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -3061,6 +3061,20 @@ Video encoders can take input in either of nv12 or yuv420p form
 (some encoders support both, some support only either - in practice,
 nv12 is the safer choice, especially among HW encoders).
 
+@section Microsoft RLE
+
+Microsoft RLE aka MSRLE encoder.
+Only 8-bit palette mode supported.
+Compatible with Windows 3.1 and Windows 95.
+
+@subsection Options
+
+@table @option
+@item g @var{integer}
+Keyframe interval.
+A keyframe is inserted at least every @code{-g} frames, sometimes sooner.
+@end table
+
 @section mpeg2
 
 MPEG-2 video encoder.
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 87a8b90037..2c88dd65d5 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -552,6 +552,7 @@ OBJS-$(CONFIG_MSA1_DECODER)+= mss3.o
 OBJS-$(CONFIG_MSCC_DECODER)+= mscc.o
 OBJS-$(CONFIG_MSNSIREN_DECODER)+= siren.o
 OBJS-$(CONFIG_MSP2_DECODER)+= msp2dec.o
+OBJS-$(CONFIG_MSRLE_ENCODER)   += msrleenc.o
 OBJS-$(CONFIG_MSRLE_DECODER)   += msrle.o msrledec.o
 OBJS-$(CONFIG_MSS1_DECODER)+= mss1.o mss12.o
 OBJS-$(CONFIG_MSS2_DECODER)+= mss2.o mss12.o mss2dsp.o wmv2data.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index a98c300da4..5d4889b968 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -228,6 +228,7 @@ extern const FFCodec ff_msmpeg4v3_encoder;
 extern const FFCodec ff_msmpeg4v3_decoder;
 extern const FFCodec ff_msmpeg4_crystalhd_decoder;
 extern const FFCodec ff_msp2_decoder;
+extern const FFCodec ff_msrle_encoder;
 extern const FFCodec ff_msrle_decoder;
 extern const FFCodec ff_mss1_decoder;
 extern const FFCodec ff_mss2_decoder;
diff --git a/libavcodec/msrleenc.c b/libavcodec/msrleenc.c
new file mode 100644
index 00..17d40cbd6a
--- /dev/null
+++ b/libavcodec/msrleenc.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2023 Tomas Härdin
+ *
+ * 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
+ * MSRLE encoder
+ * @see https://wiki.multimedia.cx/index.php?title=Microsoft_RLE
+ */
+
+// TODO: pal4 mode?
+
+#include "bytestream.h"
+#include "codec_internal.h"
+#include "encode.h"
+
+typedef struct MSRLEContext {
+const AVClass *class;
+int curframe;
+AVFrame *last_frame;
+} MSRLEContext;
+
+static av_cold int msrle_encode_init(AVCodecContext *avctx)
+{
+avctx->bits_per_coded_sample = 8;
+return 0;
+}
+
+static void write_run(AVCodecContext *avctx, uint8_t