diff mbox

[FFmpeg-devel] avcodec: add MatchWare Screen Capture Codec

Message ID 20180824200652.19481-1-onemda@gmail.com
State Superseded
Headers show

Commit Message

Paul B Mahol Aug. 24, 2018, 8:06 p.m. UTC
Signed-off-by: Paul B Mahol <onemda@gmail.com>
---
 configure               |   1 +
 libavcodec/Makefile     |   1 +
 libavcodec/allcodecs.c  |   1 +
 libavcodec/avcodec.h    |   1 +
 libavcodec/codec_desc.c |   7 ++
 libavcodec/mwsc.c       | 182 ++++++++++++++++++++++++++++++++++++++++
 libavformat/riff.c      |   1 +
 7 files changed, 194 insertions(+)
 create mode 100644 libavcodec/mwsc.c

Comments

Rostislav Pehlivanov Aug. 24, 2018, 9:22 p.m. UTC | #1
On Fri, 24 Aug 2018 at 21:07, Paul B Mahol <onemda@gmail.com> wrote:

> Signed-off-by: Paul B Mahol <onemda@gmail.com>
> ---
>  configure               |   1 +
>  libavcodec/Makefile     |   1 +
>  libavcodec/allcodecs.c  |   1 +
>  libavcodec/avcodec.h    |   1 +
>  libavcodec/codec_desc.c |   7 ++
>  libavcodec/mwsc.c       | 182 ++++++++++++++++++++++++++++++++++++++++
>  libavformat/riff.c      |   1 +
>  7 files changed, 194 insertions(+)
>  create mode 100644 libavcodec/mwsc.c
>
> diff --git a/configure b/configure
> index b9c9d0b307..0c1f6a79a7 100755
> --- a/configure
> +++ b/configure
> @@ -2679,6 +2679,7 @@ msmpeg4v3_decoder_select="h263_decoder"
>  msmpeg4v3_encoder_select="h263_encoder"
>  mss2_decoder_select="mpegvideo qpeldsp vc1_decoder"
>  mts2_decoder_select="mss34dsp"
> +mwsc_decoder_deps="zlib"
>  mxpeg_decoder_select="mjpeg_decoder"
>  nellymoser_decoder_select="mdct sinewin"
>  nellymoser_encoder_select="audio_frame_queue mdct sinewin"
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 9a309c348e..aee4f5431a 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -482,6 +482,7 @@ OBJS-$(CONFIG_MSZH_DECODER)            += lcldec.o
>  OBJS-$(CONFIG_MTS2_DECODER)            += mss4.o
>  OBJS-$(CONFIG_MVC1_DECODER)            += mvcdec.o
>  OBJS-$(CONFIG_MVC2_DECODER)            += mvcdec.o
> +OBJS-$(CONFIG_MWSC_DECODER)            += mwsc.o
>  OBJS-$(CONFIG_MXPEG_DECODER)           += mxpegdec.o
>  OBJS-$(CONFIG_NELLYMOSER_DECODER)      += nellymoserdec.o nellymoser.o
>  OBJS-$(CONFIG_NELLYMOSER_ENCODER)      += nellymoserenc.o nellymoser.o
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index b1d1ef26c0..d41868c591 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -212,6 +212,7 @@ extern AVCodec ff_mszh_decoder;
>  extern AVCodec ff_mts2_decoder;
>  extern AVCodec ff_mvc1_decoder;
>  extern AVCodec ff_mvc2_decoder;
> +extern AVCodec ff_mwsc_decoder;
>  extern AVCodec ff_mxpeg_decoder;
>  extern AVCodec ff_nuv_decoder;
>  extern AVCodec ff_paf_video_decoder;
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index 2a4be2ca4f..b327284449 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -449,6 +449,7 @@ enum AVCodecID {
>      AV_CODEC_ID_FITS,
>      AV_CODEC_ID_IMM4,
>      AV_CODEC_ID_PROSUMER,
> +    AV_CODEC_ID_MWSC,
>
>      /* various PCM "codecs" */
>      AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at
> the start of audio codecs
> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> index e611183599..129d0f1aac 100644
> --- a/libavcodec/codec_desc.c
> +++ b/libavcodec/codec_desc.c
> @@ -1668,6 +1668,13 @@ static const AVCodecDescriptor codec_descriptors[]
> = {
>          .long_name = NULL_IF_CONFIG_SMALL("Brooktree ProSumer Video"),
>          .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
>      },
> +    {
> +        .id        = AV_CODEC_ID_MWSC,
> +        .type      = AVMEDIA_TYPE_VIDEO,
> +        .name      = "mwsc",
> +        .long_name = NULL_IF_CONFIG_SMALL("MatchWare Screen Capture
> Codec"),
> +        .props     = AV_CODEC_PROP_LOSSLESS,
> +    },
>
>      /* various PCM "codecs" */
>      {
> diff --git a/libavcodec/mwsc.c b/libavcodec/mwsc.c
> new file mode 100644
> index 0000000000..632d1cc30c
> --- /dev/null
> +++ b/libavcodec/mwsc.c
> @@ -0,0 +1,182 @@
> +/*
> + * MatchWare Screen Capture Codec decoder
> + *
> + * Copyright (c) 2018 Paul B Mahol
> + *
> + * 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
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include "avcodec.h"
> +#include "bytestream.h"
> +#include "internal.h"
> +
> +#include <zlib.h>
> +
> +typedef struct MWSCContext {
> +    unsigned          bpp;
> +    unsigned int      decomp_size;
> +    uint8_t          *decomp_buf;
> +    unsigned int      uncomp_size;
> +    uint8_t          *uncomp_buf;
> +    z_stream          zstream;
> +} MWSCContext;
> +
> +static int rle_uncompress(AVCodecContext *avctx, GetByteContext *gb,
> PutByteContext *pb, int bpp)
> +{
> +    int intra = 1;
> +
> +    while (bytestream2_get_bytes_left(gb) > 0) {
> +        uint32_t fill = bytestream2_get_le24(gb);
> +        unsigned run = bytestream2_get_byte(gb);
> +        int j;
>

Save a line, for (int j...


+
> +        if (run == 0) {
> +            run = bytestream2_get_le32(gb);
> +            for (j = 0; j < run; j++) {
> +                bytestream2_put_le24(pb, fill);
> +            }
>

No need for bracket, 1 line loop.



> +        } else if (run == 255) {
> +            bytestream2_skip_p(pb, fill * 3);
> +            intra = 0;
> +        } else {
> +            for (j = 0; j < run; j++) {
> +                bytestream2_put_le24(pb, fill);
>

Same.



> +            }
> +        }
> +    }
> +
> +    return intra;
> +}
> +
> +static int decode_frame(AVCodecContext *avctx,
> +                        void *data, int *got_frame,
> +                        AVPacket *avpkt)
> +{
> +    MWSCContext *s = avctx->priv_data;
> +    AVFrame *frame = data;
> +    uint8_t *buf = avpkt->data;
> +    int buf_size = avpkt->size;
> +    GetByteContext gb;
> +    PutByteContext pb;
> +    int ret, j;
> +
> +    ret = inflateReset(&s->zstream);
> +    if (ret != Z_OK) {
> +        av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", ret);
> +        return AVERROR_EXTERNAL;
> +    }
> +    s->zstream.next_in   = buf;
> +    s->zstream.avail_in  = buf_size;
> +    s->zstream.next_out  = s->decomp_buf;
> +    s->zstream.avail_out = s->decomp_size;
> +    ret = inflate(&s->zstream, Z_FINISH);
> +    if (ret != Z_STREAM_END) {
> +        av_log(avctx, AV_LOG_ERROR, "Inflate error: %d\n", ret);
> +        return AVERROR_EXTERNAL;
> +    }
> +
> +    bytestream2_init(&gb, s->decomp_buf, s->zstream.total_out);
> +    bytestream2_init_writer(&pb, s->uncomp_buf, s->uncomp_size);
> +
> +    frame->key_frame = rle_uncompress(avctx, &gb, &pb, s->bpp);
> +
> +    if ((ret = ff_reget_buffer(avctx, frame)) < 0)
> +        return ret;
>

ff_get_buffer()



> +

+    for (j = 0; j < avctx->height; j++) {
> +        memcpy(frame->data[0] + (avctx->height - j - 1) *
> frame->linesize[0],
> +               s->uncomp_buf + s->bpp * j * avctx->width, s->bpp *
> avctx->width);
> +    }
>

You should save needing a third copy (bitstream -> deflated bitstream ->
run length decoded bitstream -> avframe) by writing to the AVFrame directly
in rle_uncompress, by tracking how many pixels you've decoded and deciding
whether to go on the next line or not.


+
> +    frame->pict_type = frame->key_frame ? AV_PICTURE_TYPE_I :
> AV_PICTURE_TYPE_P;
> +
> +    *got_frame = 1;
> +
> +    return avpkt->size;
> +}
> +
> +static av_cold int decode_init(AVCodecContext *avctx)
> +{
> +    MWSCContext *s = avctx->priv_data;
> +    int64_t size;
> +    int zret;
> +
> +    switch (avctx->bits_per_coded_sample) {
> +    case 24: avctx->pix_fmt = AV_PIX_FMT_BGR24;  break;
> +    default:
> +        av_log(avctx, AV_LOG_ERROR, "Unsupported bitdepth %d\n",
> avctx->bits_per_coded_sample);
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    s->bpp = avctx->bits_per_coded_sample >> 3;
>

You're giving users the option to decode to whatever pixel format they want
because there's no limit to what the bitstream can encode and there's no
way to know since its not signaled? Just always output BGR24. Make sure to
clip values using av_clip_uintp2. You could output to BGR48 too, but since
this works for BGR24 we can assume that the data is pretty much always
going to be within an 8 bit range.



> +
> +    size = 8LL * avctx->height * ((avctx->width *
> avctx->bits_per_coded_sample + 31) / 32);
> +    if (size >= INT32_MAX)
> +        return AVERROR_INVALIDDATA;
> +    s->decomp_size = size;
> +    if (!(s->decomp_buf = av_malloc(s->decomp_size)))
> +        return AVERROR(ENOMEM);
> +
> +    size = 4LL * avctx->height * ((avctx->width *
> avctx->bits_per_coded_sample + 31) / 32);
> +    if (size >= INT32_MAX)
> +        return AVERROR_INVALIDDATA;
> +    s->uncomp_size = size;
> +    if (!(s->uncomp_buf = av_malloc(s->uncomp_size)))
> +        return AVERROR(ENOMEM);
>

You can remove this once you optimize rle_uncompress.


+
> +    s->zstream.zalloc = Z_NULL;
> +    s->zstream.zfree = Z_NULL;
> +    s->zstream.opaque = Z_NULL;
> +    zret = inflateInit(&s->zstream);
> +    if (zret != Z_OK) {
> +        av_log(avctx, AV_LOG_ERROR, "Inflate init error: %d\n", zret);
> +        return AVERROR_EXTERNAL;
> +    }
> +
> +    return 0;
> +}
> +
> +static av_cold int decode_close(AVCodecContext *avctx)
> +{
> +    MWSCContext *s = avctx->priv_data;
> +
> +    av_freep(&s->decomp_buf);
> +    s->decomp_size = 0;
> +    av_freep(&s->uncomp_buf);
> +    s->uncomp_size = 0;
>

Same.


+    inflateEnd(&s->zstream);
> +
> +    return 0;
> +}
> +
> +AVCodec ff_mwsc_decoder = {
> +    .name             = "mwsc",
> +    .long_name        = NULL_IF_CONFIG_SMALL("MatchWare Screen Capture
> Codec"),
> +    .type             = AVMEDIA_TYPE_VIDEO,
> +    .id               = AV_CODEC_ID_MWSC,
> +    .priv_data_size   = sizeof(MWSCContext),
> +    .init             = decode_init,
> +    .close            = decode_close,
> +    .decode           = decode_frame,
> +    .capabilities     = AV_CODEC_CAP_DR1,
> +    .caps_internal    = FF_CODEC_CAP_INIT_THREADSAFE |
> +                        FF_CODEC_CAP_INIT_CLEANUP,
> +};
> diff --git a/libavformat/riff.c b/libavformat/riff.c
> index cf27d0dfd5..aef3c047ac 100644
> --- a/libavformat/riff.c
> +++ b/libavformat/riff.c
> @@ -472,6 +472,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
>      { AV_CODEC_ID_SRGC,         MKTAG('S', 'R', 'G', 'C') },
>      { AV_CODEC_ID_IMM4,         MKTAG('I', 'M', 'M', '4') },
>      { AV_CODEC_ID_PROSUMER,     MKTAG('B', 'T', '2', '0') },
> +    { AV_CODEC_ID_MWSC,         MKTAG('M', 'W', 'S', 'C') },
>      { AV_CODEC_ID_NONE,         0 }
>  };
>
> --
> 2.17.1
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
diff mbox

Patch

diff --git a/configure b/configure
index b9c9d0b307..0c1f6a79a7 100755
--- a/configure
+++ b/configure
@@ -2679,6 +2679,7 @@  msmpeg4v3_decoder_select="h263_decoder"
 msmpeg4v3_encoder_select="h263_encoder"
 mss2_decoder_select="mpegvideo qpeldsp vc1_decoder"
 mts2_decoder_select="mss34dsp"
+mwsc_decoder_deps="zlib"
 mxpeg_decoder_select="mjpeg_decoder"
 nellymoser_decoder_select="mdct sinewin"
 nellymoser_encoder_select="audio_frame_queue mdct sinewin"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 9a309c348e..aee4f5431a 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -482,6 +482,7 @@  OBJS-$(CONFIG_MSZH_DECODER)            += lcldec.o
 OBJS-$(CONFIG_MTS2_DECODER)            += mss4.o
 OBJS-$(CONFIG_MVC1_DECODER)            += mvcdec.o
 OBJS-$(CONFIG_MVC2_DECODER)            += mvcdec.o
+OBJS-$(CONFIG_MWSC_DECODER)            += mwsc.o
 OBJS-$(CONFIG_MXPEG_DECODER)           += mxpegdec.o
 OBJS-$(CONFIG_NELLYMOSER_DECODER)      += nellymoserdec.o nellymoser.o
 OBJS-$(CONFIG_NELLYMOSER_ENCODER)      += nellymoserenc.o nellymoser.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index b1d1ef26c0..d41868c591 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -212,6 +212,7 @@  extern AVCodec ff_mszh_decoder;
 extern AVCodec ff_mts2_decoder;
 extern AVCodec ff_mvc1_decoder;
 extern AVCodec ff_mvc2_decoder;
+extern AVCodec ff_mwsc_decoder;
 extern AVCodec ff_mxpeg_decoder;
 extern AVCodec ff_nuv_decoder;
 extern AVCodec ff_paf_video_decoder;
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 2a4be2ca4f..b327284449 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -449,6 +449,7 @@  enum AVCodecID {
     AV_CODEC_ID_FITS,
     AV_CODEC_ID_IMM4,
     AV_CODEC_ID_PROSUMER,
+    AV_CODEC_ID_MWSC,
 
     /* various PCM "codecs" */
     AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index e611183599..129d0f1aac 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -1668,6 +1668,13 @@  static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("Brooktree ProSumer Video"),
         .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
     },
+    {
+        .id        = AV_CODEC_ID_MWSC,
+        .type      = AVMEDIA_TYPE_VIDEO,
+        .name      = "mwsc",
+        .long_name = NULL_IF_CONFIG_SMALL("MatchWare Screen Capture Codec"),
+        .props     = AV_CODEC_PROP_LOSSLESS,
+    },
 
     /* various PCM "codecs" */
     {
diff --git a/libavcodec/mwsc.c b/libavcodec/mwsc.c
new file mode 100644
index 0000000000..632d1cc30c
--- /dev/null
+++ b/libavcodec/mwsc.c
@@ -0,0 +1,182 @@ 
+/*
+ * MatchWare Screen Capture Codec decoder
+ *
+ * Copyright (c) 2018 Paul B Mahol
+ *
+ * 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
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "avcodec.h"
+#include "bytestream.h"
+#include "internal.h"
+
+#include <zlib.h>
+
+typedef struct MWSCContext {
+    unsigned          bpp;
+    unsigned int      decomp_size;
+    uint8_t          *decomp_buf;
+    unsigned int      uncomp_size;
+    uint8_t          *uncomp_buf;
+    z_stream          zstream;
+} MWSCContext;
+
+static int rle_uncompress(AVCodecContext *avctx, GetByteContext *gb, PutByteContext *pb, int bpp)
+{
+    int intra = 1;
+
+    while (bytestream2_get_bytes_left(gb) > 0) {
+        uint32_t fill = bytestream2_get_le24(gb);
+        unsigned run = bytestream2_get_byte(gb);
+        int j;
+
+        if (run == 0) {
+            run = bytestream2_get_le32(gb);
+            for (j = 0; j < run; j++) {
+                bytestream2_put_le24(pb, fill);
+            }
+        } else if (run == 255) {
+            bytestream2_skip_p(pb, fill * 3);
+            intra = 0;
+        } else {
+            for (j = 0; j < run; j++) {
+                bytestream2_put_le24(pb, fill);
+            }
+        }
+    }
+
+    return intra;
+}
+
+static int decode_frame(AVCodecContext *avctx,
+                        void *data, int *got_frame,
+                        AVPacket *avpkt)
+{
+    MWSCContext *s = avctx->priv_data;
+    AVFrame *frame = data;
+    uint8_t *buf = avpkt->data;
+    int buf_size = avpkt->size;
+    GetByteContext gb;
+    PutByteContext pb;
+    int ret, j;
+
+    ret = inflateReset(&s->zstream);
+    if (ret != Z_OK) {
+        av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", ret);
+        return AVERROR_EXTERNAL;
+    }
+    s->zstream.next_in   = buf;
+    s->zstream.avail_in  = buf_size;
+    s->zstream.next_out  = s->decomp_buf;
+    s->zstream.avail_out = s->decomp_size;
+    ret = inflate(&s->zstream, Z_FINISH);
+    if (ret != Z_STREAM_END) {
+        av_log(avctx, AV_LOG_ERROR, "Inflate error: %d\n", ret);
+        return AVERROR_EXTERNAL;
+    }
+
+    bytestream2_init(&gb, s->decomp_buf, s->zstream.total_out);
+    bytestream2_init_writer(&pb, s->uncomp_buf, s->uncomp_size);
+
+    frame->key_frame = rle_uncompress(avctx, &gb, &pb, s->bpp);
+
+    if ((ret = ff_reget_buffer(avctx, frame)) < 0)
+        return ret;
+
+    for (j = 0; j < avctx->height; j++) {
+        memcpy(frame->data[0] + (avctx->height - j - 1) * frame->linesize[0],
+               s->uncomp_buf + s->bpp * j * avctx->width, s->bpp * avctx->width);
+    }
+
+    frame->pict_type = frame->key_frame ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P;
+
+    *got_frame = 1;
+
+    return avpkt->size;
+}
+
+static av_cold int decode_init(AVCodecContext *avctx)
+{
+    MWSCContext *s = avctx->priv_data;
+    int64_t size;
+    int zret;
+
+    switch (avctx->bits_per_coded_sample) {
+    case 24: avctx->pix_fmt = AV_PIX_FMT_BGR24;  break;
+    default:
+        av_log(avctx, AV_LOG_ERROR, "Unsupported bitdepth %d\n", avctx->bits_per_coded_sample);
+        return AVERROR_INVALIDDATA;
+    }
+
+    s->bpp = avctx->bits_per_coded_sample >> 3;
+
+    size = 8LL * avctx->height * ((avctx->width * avctx->bits_per_coded_sample + 31) / 32);
+    if (size >= INT32_MAX)
+        return AVERROR_INVALIDDATA;
+    s->decomp_size = size;
+    if (!(s->decomp_buf = av_malloc(s->decomp_size)))
+        return AVERROR(ENOMEM);
+
+    size = 4LL * avctx->height * ((avctx->width * avctx->bits_per_coded_sample + 31) / 32);
+    if (size >= INT32_MAX)
+        return AVERROR_INVALIDDATA;
+    s->uncomp_size = size;
+    if (!(s->uncomp_buf = av_malloc(s->uncomp_size)))
+        return AVERROR(ENOMEM);
+
+    s->zstream.zalloc = Z_NULL;
+    s->zstream.zfree = Z_NULL;
+    s->zstream.opaque = Z_NULL;
+    zret = inflateInit(&s->zstream);
+    if (zret != Z_OK) {
+        av_log(avctx, AV_LOG_ERROR, "Inflate init error: %d\n", zret);
+        return AVERROR_EXTERNAL;
+    }
+
+    return 0;
+}
+
+static av_cold int decode_close(AVCodecContext *avctx)
+{
+    MWSCContext *s = avctx->priv_data;
+
+    av_freep(&s->decomp_buf);
+    s->decomp_size = 0;
+    av_freep(&s->uncomp_buf);
+    s->uncomp_size = 0;
+    inflateEnd(&s->zstream);
+
+    return 0;
+}
+
+AVCodec ff_mwsc_decoder = {
+    .name             = "mwsc",
+    .long_name        = NULL_IF_CONFIG_SMALL("MatchWare Screen Capture Codec"),
+    .type             = AVMEDIA_TYPE_VIDEO,
+    .id               = AV_CODEC_ID_MWSC,
+    .priv_data_size   = sizeof(MWSCContext),
+    .init             = decode_init,
+    .close            = decode_close,
+    .decode           = decode_frame,
+    .capabilities     = AV_CODEC_CAP_DR1,
+    .caps_internal    = FF_CODEC_CAP_INIT_THREADSAFE |
+                        FF_CODEC_CAP_INIT_CLEANUP,
+};
diff --git a/libavformat/riff.c b/libavformat/riff.c
index cf27d0dfd5..aef3c047ac 100644
--- a/libavformat/riff.c
+++ b/libavformat/riff.c
@@ -472,6 +472,7 @@  const AVCodecTag ff_codec_bmp_tags[] = {
     { AV_CODEC_ID_SRGC,         MKTAG('S', 'R', 'G', 'C') },
     { AV_CODEC_ID_IMM4,         MKTAG('I', 'M', 'M', '4') },
     { AV_CODEC_ID_PROSUMER,     MKTAG('B', 'T', '2', '0') },
+    { AV_CODEC_ID_MWSC,         MKTAG('M', 'W', 'S', 'C') },
     { AV_CODEC_ID_NONE,         0 }
 };