* [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 @ 2023-06-08 11:22 Tomas Härdin 2023-06-08 11:23 ` [FFmpeg-devel] [PATCH 2/2] lavc/msrleenc: Add msrle encoder Tomas Härdin ` (3 more replies) 0 siblings, 4 replies; 10+ messages in thread From: Tomas Härdin @ 2023-06-08 11:22 UTC (permalink / raw) To: ffmpeg-devel [-- Attachment #1: Type: text/plain, Size: 66 bytes --] This is important for the GoldSrc community among others. /Tomas [-- Attachment #2: 0001-lavc-riffenc-Fix-msrle-support-on-Windows-95.patch --] [-- Type: text/x-patch, Size: 1522 bytes --] From c5c2d535b3e5dc94b063e13359e475e9ce18e1b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= <git@haerdin.se> Date: Thu, 8 Jun 2023 11:55:28 +0200 Subject: [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 --- libavformat/riffenc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libavformat/riffenc.c b/libavformat/riffenc.c index 179b0f12cb..3325419b94 100644 --- a/libavformat/riffenc.c +++ b/libavformat/riffenc.c @@ -239,14 +239,16 @@ void ff_put_bmp_header(AVIOContext *pb, AVCodecParameters *par, /* depth */ avio_wl16(pb, par->bits_per_coded_sample ? par->bits_per_coded_sample : 24); /* compression type */ - avio_wl32(pb, par->codec_tag); + // MSRLE compatibility with Media Player 3.1 and Windows 95 + avio_wl32(pb, par->codec_id == AV_CODEC_ID_MSRLE ? 1 : par->codec_tag); avio_wl32(pb, (par->width * par->height * (par->bits_per_coded_sample ? par->bits_per_coded_sample : 24)+7) / 8); avio_wl32(pb, 0); avio_wl32(pb, 0); /* Number of color indices in the color table that are used. * A value of 0 means 2^biBitCount indices, but this doesn't work * with Windows Media Player and files containing xxpc chunks. */ - avio_wl32(pb, pal_avi ? 1 << par->bits_per_coded_sample : 0); + // MSRLE on Windows 95 requires a zero here + avio_wl32(pb, pal_avi && par->codec_id != AV_CODEC_ID_MSRLE ? 1 << par->bits_per_coded_sample : 0); avio_wl32(pb, 0); if (!ignore_extradata) { -- 2.30.2 [-- Attachment #3: Type: text/plain, Size: 251 bytes --] _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 10+ messages in thread
* [FFmpeg-devel] [PATCH 2/2] lavc/msrleenc: Add msrle encoder 2023-06-08 11:22 [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 Tomas Härdin @ 2023-06-08 11:23 ` Tomas Härdin 2023-06-10 18:34 ` Andreas Rheinhardt 2023-06-08 11:27 ` [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 Paul B Mahol ` (2 subsequent siblings) 3 siblings, 1 reply; 10+ messages in thread From: Tomas Härdin @ 2023-06-08 11:23 UTC (permalink / raw) To: FFmpeg development discussions and patches [-- Attachment #1: Type: text/plain, Size: 125 bytes --] Please check that I got the palette handling correct. Else this producing output of similar size to the VfW encoder. /Tomas [-- Attachment #2: 0002-lavc-msrleenc-Add-msrle-encoder.patch --] [-- Type: text/x-patch, Size: 16353 bytes --] From ab9bb1aca7ddda8f4788b0a63460470fce021e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= <git@haerdin.se> 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.c Tomas Härdin msvideo1.c Mike 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 0000000000..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 **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, 0x0000); +} + +// 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, &line[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, &line[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 to end a line early. If this happens when keyframe == 0 + then *got_keyframe is set to 1 and s->curframe is reset. + */ + *got_keyframe = 1; // set to zero whenever we use a feature that makes this a not-keyframe + + if (keyframe) { + for (int y = avctx->height-1; y >= 0; y--) { + uint8_t *line = &pict->data[0][y*pict->linesize[0]]; + encode_line(avctx, &data, line, avctx->width); + bytestream_put_be16(&data, 0x0000); // end of line + } + } else { + // compare to previous frame + int yskip = 0; // we can encode large skips using deltas + for (int y = avctx->height-1; y >= 0; y--) { + uint8_t *line = &pict->data[0][y*pict->linesize[0]]; + uint8_t *prev = &s->last_frame->data[0][y*s->last_frame->linesize[0]]; + // we need at least 5 pixels in a row for a delta to be worthwhile + int delta = 0, linestart = 0, encoded = 0; + for (int x = 0; x < avctx->width; x++) { + if (line[x] == prev[x]) { + delta++; + if (delta == 5) { + int len = x - linestart - 4; + if (len > 0) { + write_yskip(avctx, &data, yskip); + yskip = 0; + encode_line(avctx, &data, &line[linestart], len); + encoded = 1; + } + linestart = -1; + } + } else { + if (delta >= 5) { + write_yskip(avctx, &data, yskip); + yskip = 0; + write_delta(avctx, &data, delta); + *got_keyframe = 0; + encoded = 1; + } + delta = 0; + if (linestart == -1) + linestart = x; + } + } + if (delta < 5) { + write_yskip(avctx, &data, yskip); + yskip = 0; + encode_line(avctx, &data, &line[linestart], avctx->width - linestart); + encoded = 1; + } else + *got_keyframe = 0; + bytestream_put_be16(&data, 0x0000); // end of line + if (!encoded) + yskip++; + else + yskip = 0; + } + write_yskip(avctx, &data, yskip); + } + bytestream_put_be16(&data, 0x0001); // end of bitmap + pkt->size = data - pkt->data; + return 0;\r} + +static int msrle_encode_frame(AVCodecContext *avctx, AVPacket *pkt, + const AVFrame *pict, int *got_packet) +{ + MSRLEContext *s = avctx->priv_data; + int ret, got_keyframe; + + if ((ret = ff_alloc_packet(avctx, pkt, ( + avctx->width*2 /* worst case = rle every pixel */ + 2 /*end of line */ + ) * avctx->height + 2 /* end of bitmap */ + AV_INPUT_BUFFER_MIN_SIZE))) + return ret; + + if (pict->data[1]) { + uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); + memcpy(side_data, pict->data[1], AVPALETTE_SIZE); + } + + if ((ret = encode(avctx, pkt, pict, s->curframe == 0, &got_keyframe))) + return ret; + + if (got_keyframe) { + pkt->flags |= AV_PKT_FLAG_KEY; + s->curframe = 0; + } + if (++s->curframe >= avctx->gop_size) + s->curframe = 0; + *got_packet = 1; + + if (!s->last_frame) + s->last_frame = av_frame_alloc(); + else + av_frame_unref(s->last_frame); + + av_frame_ref(s->last_frame, pict); + return 0; +} + +static int msrle_encode_close(AVCodecContext *avctx) +{ + MSRLEContext *s = avctx->priv_data; + av_frame_free(&s->last_frame); + return 0; +} + +static const AVClass msrle_class = { + .class_name = "Microsoft RLE encoder", + .item_name = av_default_item_name, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFCodec ff_msrle_encoder = { + .p.name = "msrle", + CODEC_LONG_NAME("Microsoft RLE"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MSRLE, + .p.capabilities = AV_CODEC_CAP_DR1, + .priv_data_size = sizeof(MSRLEContext), + .init = msrle_encode_init, + FF_CODEC_ENCODE_CB(msrle_encode_frame), + .close = msrle_encode_close, + .p.pix_fmts = (const enum AVPixelFormat[]){ + AV_PIX_FMT_PAL8, AV_PIX_FMT_NONE + }, + .p.priv_class = &msrle_class, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, +}; diff --git a/tests/fate/vcodec.mak b/tests/fate/vcodec.mak index fbee264a9d..ef8904613a 100644 --- a/tests/fate/vcodec.mak +++ b/tests/fate/vcodec.mak @@ -338,6 +338,9 @@ fate-vsynth%-msmpeg4: ENCOPTS = -qscale 10 FATE_VCODEC-$(call ENCDEC, MSMPEG4V2, AVI) += msmpeg4v2 fate-vsynth%-msmpeg4v2: ENCOPTS = -qscale 10 +FATE_VCODEC_SCALE-$(call ENCDEC, MSRLE, AVI) += msrle +fate-vsynth%-msrle: CODEC = msrle + FATE_VCODEC_SCALE-$(call ENCDEC, PNG, AVI) += mpng fate-vsynth%-mpng: CODEC = png diff --git a/tests/ref/vsynth/vsynth1-msrle b/tests/ref/vsynth/vsynth1-msrle new file mode 100644 index 0000000000..6174ff57f8 --- /dev/null +++ b/tests/ref/vsynth/vsynth1-msrle @@ -0,0 +1,4 @@ +b19bc15e2c5866f3c7942aad47ce0261 *tests/data/fate/vsynth1-msrle.avi +5216296 tests/data/fate/vsynth1-msrle.avi +f142ee03bf9f37bb2e1902fe32366bbf *tests/data/fate/vsynth1-msrle.out.rawvideo +stddev: 8.69 PSNR: 29.34 MAXDIFF: 64 bytes: 7603200/ 7603200 diff --git a/tests/ref/vsynth/vsynth2-msrle b/tests/ref/vsynth/vsynth2-msrle new file mode 100644 index 0000000000..dd579b6ee9 --- /dev/null +++ b/tests/ref/vsynth/vsynth2-msrle @@ -0,0 +1,4 @@ +850744d6d38ab09adb2fbd685d5df740 *tests/data/fate/vsynth2-msrle.avi +4556642 tests/data/fate/vsynth2-msrle.avi +df26a524cad8ebf0cf5ba4376c5f115f *tests/data/fate/vsynth2-msrle.out.rawvideo +stddev: 7.57 PSNR: 30.54 MAXDIFF: 35 bytes: 7603200/ 7603200 diff --git a/tests/ref/vsynth/vsynth3-msrle b/tests/ref/vsynth/vsynth3-msrle new file mode 100644 index 0000000000..9cc92be036 --- /dev/null +++ b/tests/ref/vsynth/vsynth3-msrle @@ -0,0 +1,4 @@ +ee8f4d86f117d69919be69fbc976981a *tests/data/fate/vsynth3-msrle.avi +72866 tests/data/fate/vsynth3-msrle.avi +fa6042492a3116c1ae9a32b487caa677 *tests/data/fate/vsynth3-msrle.out.rawvideo +stddev: 8.88 PSNR: 29.16 MAXDIFF: 51 bytes: 86700/ 86700 diff --git a/tests/ref/vsynth/vsynth_lena-msrle b/tests/ref/vsynth/vsynth_lena-msrle new file mode 100644 index 0000000000..67226f05a2 --- /dev/null +++ b/tests/ref/vsynth/vsynth_lena-msrle @@ -0,0 +1,4 @@ +9654924690cbaf6348ea798e442e819c *tests/data/fate/vsynth_lena-msrle.avi +4671320 tests/data/fate/vsynth_lena-msrle.avi +db453693ceae6f65c173dd716ee2662e *tests/data/fate/vsynth_lena-msrle.out.rawvideo +stddev: 8.07 PSNR: 29.99 MAXDIFF: 32 bytes: 7603200/ 7603200 -- 2.30.2 [-- Attachment #3: Type: text/plain, Size: 251 bytes --] _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/2] lavc/msrleenc: Add msrle encoder 2023-06-08 11:23 ` [FFmpeg-devel] [PATCH 2/2] lavc/msrleenc: Add msrle encoder Tomas Härdin @ 2023-06-10 18:34 ` Andreas Rheinhardt 0 siblings, 0 replies; 10+ messages in thread From: Andreas Rheinhardt @ 2023-06-10 18:34 UTC (permalink / raw) To: ffmpeg-devel 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, 0x0000); > +} > + > +// 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, &line[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, &line[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 to end a line early. If this happens when keyframe == 0 > + then *got_keyframe is set to 1 and s->curframe is reset. > + */ > + *got_keyframe = 1; // set to zero whenever we use a feature that makes this a not-keyframe > + > + if (keyframe) { > + for (int y = avctx->height-1; y >= 0; y--) { > + uint8_t *line = &pict->data[0][y*pict->linesize[0]]; > + encode_line(avctx, &data, line, avctx->width); > + bytestream_put_be16(&data, 0x0000); // end of line > + } > + } else { > + // compare to previous frame > + int yskip = 0; // we can encode large skips using deltas > + for (int y = avctx->height-1; y >= 0; y--) { > + uint8_t *line = &pict->data[0][y*pict->linesize[0]]; > + uint8_t *prev = &s->last_frame->data[0][y*s->last_frame->linesize[0]]; Should be const. > + // we need at least 5 pixels in a row for a delta to be worthwhile > + int delta = 0, linestart = 0, encoded = 0; > + for (int x = 0; x < avctx->width; x++) { > + if (line[x] == prev[x]) { > + delta++; > + if (delta == 5) { > + int len = x - linestart - 4; > + if (len > 0) { > + write_yskip(avctx, &data, yskip); > + yskip = 0; > + encode_line(avctx, &data, &line[linestart], len); > + encoded = 1; > + } > + linestart = -1; > + } > + } else { > + if (delta >= 5) { > + write_yskip(avctx, &data, yskip); > + yskip = 0; > + write_delta(avctx, &data, delta); > + *got_keyframe = 0; > + encoded = 1; > + } > + delta = 0; > + if (linestart == -1) > + linestart = x; > + } > + } > + if (delta < 5) { > + write_yskip(avctx, &data, yskip); > + yskip = 0; > + encode_line(avctx, &data, &line[linestart], avctx->width - linestart); > + encoded = 1; > + } else > + *got_keyframe = 0; > + bytestream_put_be16(&data, 0x0000); // end of line > + if (!encoded) > + yskip++; > + else > + yskip = 0; > + } > + write_yskip(avctx, &data, yskip); > + } > + bytestream_put_be16(&data, 0x0001); // end of bitmap > + pkt->size = data - pkt->data; > + return 0; > } > + > +static int msrle_encode_frame(AVCodecContext *avctx, AVPacket *pkt, > + const AVFrame *pict, int *got_packet) > +{ > + MSRLEContext *s = avctx->priv_data; > + int ret, got_keyframe; > + > + if ((ret = ff_alloc_packet(avctx, pkt, ( > + avctx->width*2 /* worst case = rle every pixel */ + 2 /*end of line */ > + ) * avctx->height + 2 /* end of bitmap */ + AV_INPUT_BUFFER_MIN_SIZE))) > + return ret; > + > + if (pict->data[1]) { > + uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); > + memcpy(side_data, pict->data[1], AVPALETTE_SIZE); > + } > + > + if ((ret = encode(avctx, pkt, pict, s->curframe == 0, &got_keyframe))) > + return ret; > + > + if (got_keyframe) { > + pkt->flags |= AV_PKT_FLAG_KEY; > + s->curframe = 0; > + } > + if (++s->curframe >= avctx->gop_size) > + s->curframe = 0; > + *got_packet = 1; > + > + if (!s->last_frame) > + s->last_frame = av_frame_alloc(); > + else > + av_frame_unref(s->last_frame); > + > + av_frame_ref(s->last_frame, pict); Wouldn't it be simpler to allocate this frame during init and then use av_frame_replace() here? Apart from that: You need to check the av_frame_ref(). > + return 0; > +} > + > +static int msrle_encode_close(AVCodecContext *avctx) > +{ > + MSRLEContext *s = avctx->priv_data; > + av_frame_free(&s->last_frame); > + return 0; > +} > + > +static const AVClass msrle_class = { > + .class_name = "Microsoft RLE encoder", > + .item_name = av_default_item_name, > + .version = LIBAVUTIL_VERSION_INT, > +}; An AVClass is pointless without options, so you should just remove it (and the AVClass* from the context). > + > +const FFCodec ff_msrle_encoder = { > + .p.name = "msrle", > + CODEC_LONG_NAME("Microsoft RLE"), > + .p.type = AVMEDIA_TYPE_VIDEO, > + .p.id = AV_CODEC_ID_MSRLE, > + .p.capabilities = AV_CODEC_CAP_DR1, > + .priv_data_size = sizeof(MSRLEContext), > + .init = msrle_encode_init, > + FF_CODEC_ENCODE_CB(msrle_encode_frame), > + .close = msrle_encode_close, > + .p.pix_fmts = (const enum AVPixelFormat[]){ > + AV_PIX_FMT_PAL8, AV_PIX_FMT_NONE > + }, > + .p.priv_class = &msrle_class, > + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, > +}; _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 2023-06-08 11:22 [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 Tomas Härdin 2023-06-08 11:23 ` [FFmpeg-devel] [PATCH 2/2] lavc/msrleenc: Add msrle encoder Tomas Härdin @ 2023-06-08 11:27 ` Paul B Mahol 2023-06-08 11:44 ` Tomas Härdin 2023-06-16 13:20 ` [FFmpeg-devel] [PATCH 3/3] Add Changelog entry Tomas Härdin 2023-06-19 12:26 ` [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 Tomas Härdin 3 siblings, 1 reply; 10+ messages in thread From: Paul B Mahol @ 2023-06-08 11:27 UTC (permalink / raw) To: FFmpeg development discussions and patches But does this break it on other platforms? _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 2023-06-08 11:27 ` [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 Paul B Mahol @ 2023-06-08 11:44 ` Tomas Härdin 2023-06-19 15:42 ` Leo Izen 0 siblings, 1 reply; 10+ messages in thread From: Tomas Härdin @ 2023-06-08 11:44 UTC (permalink / raw) To: FFmpeg development discussions and patches tor 2023-06-08 klockan 13:27 +0200 skrev Paul B Mahol: > But does this break it on other platforms? That is a good question. What other RIFF decoders are there for which MSRLE support is important and depends on behavior different from VfW? mpv and vlc work just fine. /Tomas _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 2023-06-08 11:44 ` Tomas Härdin @ 2023-06-19 15:42 ` Leo Izen 2023-06-19 16:57 ` Tomas Härdin 0 siblings, 1 reply; 10+ messages in thread From: Leo Izen @ 2023-06-19 15:42 UTC (permalink / raw) To: ffmpeg-devel On 6/8/23 07:44, Tomas Härdin wrote: > tor 2023-06-08 klockan 13:27 +0200 skrev Paul B Mahol: >> But does this break it on other platforms? > > That is a good question. What other RIFF decoders are there for which > MSRLE support is important and depends on behavior different from VfW? > mpv and vlc work just fine. > > /Tomas > mpv's RIFF decoder is really avformat, so this doesn't say much. VLC has its own though, iirc. - Leo Izen _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 2023-06-19 15:42 ` Leo Izen @ 2023-06-19 16:57 ` Tomas Härdin 0 siblings, 0 replies; 10+ messages in thread From: Tomas Härdin @ 2023-06-19 16:57 UTC (permalink / raw) To: FFmpeg development discussions and patches mån 2023-06-19 klockan 11:42 -0400 skrev Leo Izen: > On 6/8/23 07:44, Tomas Härdin wrote: > > tor 2023-06-08 klockan 13:27 +0200 skrev Paul B Mahol: > > > But does this break it on other platforms? > > > > That is a good question. What other RIFF decoders are there for > > which > > MSRLE support is important and depends on behavior different from > > VfW? > > mpv and vlc work just fine. > > > > /Tomas > > > > mpv's RIFF decoder is really avformat, so this doesn't say much. VLC > has > its own though, iirc. Yeah. Also we'd be hard pressed to find an msrle decoder that isn't either ffmpeg, vlc or VfW It might be necessary to do this same thing for more codecs. We only tested msrle which was painful enough. I wouldn't be surprised if say msvideo1 requires similar hacks. This patch is conservative until we learn why VfW needs this. /Tomas _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 10+ messages in thread
* [FFmpeg-devel] [PATCH 3/3] Add Changelog entry 2023-06-08 11:22 [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 Tomas Härdin 2023-06-08 11:23 ` [FFmpeg-devel] [PATCH 2/2] lavc/msrleenc: Add msrle encoder Tomas Härdin 2023-06-08 11:27 ` [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 Paul B Mahol @ 2023-06-16 13:20 ` Tomas Härdin 2023-06-20 12:38 ` Tomas Härdin 2023-06-19 12:26 ` [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 Tomas Härdin 3 siblings, 1 reply; 10+ messages in thread From: Tomas Härdin @ 2023-06-16 13:20 UTC (permalink / raw) To: FFmpeg development discussions and patches [-- Attachment #1: Type: text/plain, Size: 41 bytes --] Bumping with a forgotten Changelog entry [-- Attachment #2: 0003-Add-Changelog-entry.patch --] [-- Type: text/x-patch, Size: 561 bytes --] From 33045f32da50390e6d58c34e95b3d344a53b6968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= <git@haerdin.se> Date: Fri, 16 Jun 2023 15:19:24 +0200 Subject: [PATCH 3/3] Add Changelog entry --- Changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog b/Changelog index d51e03b8eb..2d6b63bfa2 100644 --- a/Changelog +++ b/Changelog @@ -16,6 +16,7 @@ version <next>: - nlmeans_vulkan filter - RivaTuner video decoder - xfade_vulkan filter +- Microsoft RLE video encoder version 6.0: - Radiance HDR image support -- 2.30.2 [-- Attachment #3: Type: text/plain, Size: 251 bytes --] _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [FFmpeg-devel] [PATCH 3/3] Add Changelog entry 2023-06-16 13:20 ` [FFmpeg-devel] [PATCH 3/3] Add Changelog entry Tomas Härdin @ 2023-06-20 12:38 ` Tomas Härdin 0 siblings, 0 replies; 10+ messages in thread From: Tomas Härdin @ 2023-06-20 12:38 UTC (permalink / raw) To: FFmpeg development discussions and patches Squashed with patch 2/2, trailing whitespaces fixed and pushed /Tomas _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 2023-06-08 11:22 [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 Tomas Härdin ` (2 preceding siblings ...) 2023-06-16 13:20 ` [FFmpeg-devel] [PATCH 3/3] Add Changelog entry Tomas Härdin @ 2023-06-19 12:26 ` Tomas Härdin 3 siblings, 0 replies; 10+ messages in thread From: Tomas Härdin @ 2023-06-19 12:26 UTC (permalink / raw) To: FFmpeg development discussions and patches Will push later today /Tomas _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2023-06-20 12:39 UTC | newest] Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2023-06-08 11:22 [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 Tomas Härdin 2023-06-08 11:23 ` [FFmpeg-devel] [PATCH 2/2] lavc/msrleenc: Add msrle encoder Tomas Härdin 2023-06-10 18:34 ` Andreas Rheinhardt 2023-06-08 11:27 ` [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 Paul B Mahol 2023-06-08 11:44 ` Tomas Härdin 2023-06-19 15:42 ` Leo Izen 2023-06-19 16:57 ` Tomas Härdin 2023-06-16 13:20 ` [FFmpeg-devel] [PATCH 3/3] Add Changelog entry Tomas Härdin 2023-06-20 12:38 ` Tomas Härdin 2023-06-19 12:26 ` [FFmpeg-devel] [PATCH 1/2] lavc/riffenc: Fix msrle support on Windows 95 Tomas Härdin
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git