diff mbox series

[FFmpeg-devel] avcodec: add VMX1 decoder

Message ID CAPYw7P50_JsZR5QnZT7z0_2aJVyqdZTGKD3iFaLPM0GPQdbhWQ@mail.gmail.com
State New
Headers show
Series [FFmpeg-devel] avcodec: add VMX1 decoder | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Paul B Mahol June 8, 2023, 5:39 p.m. UTC
Attached.

Comments

Jean-Baptiste Kempf June 9, 2023, 5:56 a.m. UTC | #1
On Thu, 8 Jun 2023, at 19:39, Paul B Mahol wrote:
> Attached.

Missing version bumping?
Paul B Mahol June 9, 2023, 9:20 a.m. UTC | #2
On Fri, Jun 9, 2023 at 7:57 AM Jean-Baptiste Kempf <jb@videolan.org> wrote:

> On Thu, 8 Jun 2023, at 19:39, Paul B Mahol wrote:
> > Attached.
>
> Missing version bumping?
>

That is done when committing.
To prevent potential need for rebase if in meantime someone changes version.



> --
> Jean-Baptiste Kempf -  President
> +33 672 704 734
> _______________________________________________
> 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".
>
Andreas Rheinhardt June 10, 2023, 10:24 a.m. UTC | #3
Paul B Mahol:
> +static av_cold int decode_init(AVCodecContext *avctx)
> +{
> +    VMIXContext *s = avctx->priv_data;
> +
> +    avctx->bits_per_raw_sample = 8;
> +    avctx->pix_fmt = AV_PIX_FMT_YUV422P;
> +
> +    avctx->coded_width = FFALIGN(avctx->width, 16);
> +    avctx->coded_height = FFALIGN(avctx->height, 16);
> +
> +    ff_idctdsp_init(&s->idsp, avctx);

Missing idctdsp configure dependency.

> +    ff_permute_scantable(s->scan, ff_zigzag_direct,
> +                         s->idsp.idct_permutation);
> +    return 0;
> +}
> +
> +static inline int get_se_golomb_vmix(GetBitContext *gb)
> +{
> +    unsigned int buf = get_ue_golomb_long(gb);
> +    int sign = (buf & 1) - 1;
> +    return ((buf >> 1) ^ (~sign));
> +}
> +
> +static int decode_dcac(AVCodecContext *avctx,
> +                       GetBitContext *dc_gb, GetBitContext *ac_gb,
> +                       unsigned *dcrun, unsigned *acrun,
> +                       AVFrame *frame, int width, int by, int plane)
> +{
> +    const ptrdiff_t linesize = frame->linesize[plane];
> +    uint8_t *dst = frame->data[plane] + by * linesize;
> +    unsigned dc_run = *dcrun, ac_run = *acrun;
> +    LOCAL_ALIGNED_32(int16_t, block, [64]);
> +    VMIXContext *s = avctx->priv_data;
> +    const int16_t *factors = s->factors;
> +    const uint8_t *scan = s->scan;
> +    const int add = plane ? 0 : 1024;
> +    int i, dc_v = 0, ac_v = 0, dc = 0;
> +
> +    for (int y = 0; y < 2; y++) {
> +        for (int x = 0; x < width; x += 8) {
> +            memset(block, 0, sizeof(*block)*64);
> +
> +            if (dc_run > 0) {
> +                dc_run--;
> +            } else {
> +                dc_v = get_se_golomb_vmix(dc_gb);
> +                dc += dc_v;
> +                if (!dc_v)
> +                    dc_run = get_ue_golomb_long(dc_gb);
> +            }
> +
> +            for (int n = 0; n < 64; n++) {
> +                if (ac_run > 0) {
> +                    ac_run--;
> +                    continue;
> +                }
> +
> +                ac_v = get_se_golomb_vmix(ac_gb);
> +                i = scan[n];
> +                block[i] = (ac_v * factors[i]) >> 4;
> +                if (!ac_v)
> +                    ac_run = get_ue_golomb_long(ac_gb);
> +            }
> +
> +            block[0] = ((dc + add) * 16) >> 4;
> +            s->idsp.idct_put(dst + x, linesize, block);
> +        }
> +
> +        dst += 8 * linesize;
> +    }
> +
> +    *dcrun = dc_run;
> +    *acrun = ac_run;
> +
> +    return 0;
> +}
> +
> +static int decode_slice(AVCodecContext *avctx, AVFrame *frame,
> +                        const uint8_t *dc_src, unsigned dc_slice_size,
> +                        const uint8_t *ac_src, unsigned ac_slice_size,
> +                        int by)
> +{
> +    unsigned dc_run = 0, ac_run = 0;
> +    GetBitContext dc_gb, ac_gb;
> +    int ret;
> +
> +    ret = init_get_bits8(&dc_gb, dc_src, dc_slice_size);
> +    if (ret < 0)
> +        return ret;
> +
> +    ret = init_get_bits8(&ac_gb, ac_src, ac_slice_size);
> +    if (ret < 0)
> +        return ret;
> +
> +    for (int p = 0; p < 3; p++) {
> +        const int rshift = !!p;
> +        ret = decode_dcac(avctx, &dc_gb, &ac_gb,
> +                          &dc_run, &ac_run, frame,
> +                          frame->width >> rshift, by, p);
> +        if (ret < 0)
> +            return ret;
> +
> +        if (get_bits_left(&dc_gb) < 0)
> +            return AVERROR_INVALIDDATA;
> +        if (get_bits_left(&ac_gb) < 0)
> +            return AVERROR_INVALIDDATA;
> +
> +        align_get_bits(&dc_gb);
> +        align_get_bits(&ac_gb);
> +    }
> +
> +    if (get_bits_left(&dc_gb) > 0)
> +        return AVERROR_INVALIDDATA;
> +    if (get_bits_left(&ac_gb) > 0)
> +        return AVERROR_INVALIDDATA;
> +
> +    return 0;
> +}
> +
> +static int decode_slices(AVCodecContext *avctx, void *arg,
> +                         int n, int thread_nb)
> +{
> +    VMIXContext *s = avctx->priv_data;
> +    const uint8_t *dc_slice_ptr = s->slices[n].dc_ptr;
> +    const uint8_t *ac_slice_ptr = s->slices[n].ac_ptr;
> +    unsigned dc_slice_size = s->slices[n].dc_size;
> +    unsigned ac_slice_size = s->slices[n].ac_size;
> +    AVFrame *frame = arg;
> +
> +    return decode_slice(avctx, frame, dc_slice_ptr, dc_slice_size,
> +                        ac_slice_ptr, ac_slice_size, n * 16);
> +}
> +
> +static int decode_frame(AVCodecContext *avctx,
> +                        AVFrame *frame, int *got_frame,
> +                        AVPacket *avpkt)
> +{
> +    VMIXContext *s = avctx->priv_data;
> +    unsigned offset = 3, q;
> +    int ret;
> +
> +    if (avpkt->size <= 7)
> +        return AVERROR_INVALIDDATA;
> +
> +    if (avpkt->data[0] != 0x01)
> +        return AVERROR_INVALIDDATA;
> +
> +    q = av_clip(99 - av_clip(avpkt->data[1], 0, 99), 0, FF_ARRAY_ELEMS(quality) - 1);

The inner av_clip() is redundant.

> +    for (int n = 0; n < 64; n++)
> +        s->factors[n] = quant[n] * quality[q];
> +
> +    s->nb_slices = avpkt->data[2];
> +    if (!s->nb_slices || s->nb_slices > (avctx->height + 15) / 16)
> +        return AVERROR_INVALIDDATA;
> +
> +    ret = ff_thread_get_buffer(avctx, frame, 0);

This should be moved after the loops below in order to avoid an
allocation in case of invalid input.

> +    if (ret < 0)
> +        return ret;
> +
> +    for (int n = 0; n < s->nb_slices; n++) {
> +        unsigned slice_size;
> +
> +        if (offset + 4 > avpkt->size)
> +            return AVERROR_INVALIDDATA;
> +
> +        slice_size = AV_RL32(avpkt->data + offset);
> +        if (slice_size > avpkt->size)
> +            return AVERROR_INVALIDDATA;
> +
> +        if (avpkt->size - slice_size - 4LL < offset)
> +            return AVERROR_INVALIDDATA;
> +
> +        s->slices[n].dc_size = slice_size;
> +        s->slices[n].dc_ptr = avpkt->data + offset + 4;
> +        offset += slice_size + 4;
> +    }
> +
> +    for (int n = 0; n < s->nb_slices; n++) {
> +        unsigned slice_size;
> +
> +        if (offset + 4 > avpkt->size)
> +            return AVERROR_INVALIDDATA;
> +
> +        slice_size = AV_RL32(avpkt->data + offset);
> +        if (slice_size > avpkt->size)
> +            return AVERROR_INVALIDDATA;
> +
> +        if (avpkt->size - slice_size - 4LL < offset)
> +            return AVERROR_INVALIDDATA;
> +
> +        s->slices[n].ac_size = slice_size;
> +        s->slices[n].ac_ptr = avpkt->data + offset + 4;
> +        offset += slice_size + 4;
> +    }

These loops could be combined if you modified the way you stored the
data by putting the dc information into slices[n] and the corresponding
ac information into slices[n + nb_slices]. In this case, you would need
to unpack this in decode_slices.
If you don't do this, then there is no need to put nb_slices in the
context. It can be local to this function.

> +
> +    avctx->execute2(avctx, decode_slices, frame, NULL, s->nb_slices);
> +
> +    frame->pict_type = AV_PICTURE_TYPE_I;
> +    frame->flags |= AV_FRAME_FLAG_KEY;
> +
> +    *got_frame = 1;
> +
> +    return avpkt->size;
> +}
Paul B Mahol June 12, 2023, 3:39 p.m. UTC | #4
On Sat, Jun 10, 2023 at 12:24 PM Andreas Rheinhardt <
andreas.rheinhardt@outlook.com> wrote:

> Paul B Mahol:
> > +static av_cold int decode_init(AVCodecContext *avctx)
> > +{
> > +    VMIXContext *s = avctx->priv_data;
> > +
> > +    avctx->bits_per_raw_sample = 8;
> > +    avctx->pix_fmt = AV_PIX_FMT_YUV422P;
> > +
> > +    avctx->coded_width = FFALIGN(avctx->width, 16);
> > +    avctx->coded_height = FFALIGN(avctx->height, 16);
> > +
> > +    ff_idctdsp_init(&s->idsp, avctx);
>
> Missing idctdsp configure dependency.
>

Added.


>
> > +    ff_permute_scantable(s->scan, ff_zigzag_direct,
> > +                         s->idsp.idct_permutation);
> > +    return 0;
> > +}
> > +
> > +static inline int get_se_golomb_vmix(GetBitContext *gb)
> > +{
> > +    unsigned int buf = get_ue_golomb_long(gb);
> > +    int sign = (buf & 1) - 1;
> > +    return ((buf >> 1) ^ (~sign));
> > +}
> > +
> > +static int decode_dcac(AVCodecContext *avctx,
> > +                       GetBitContext *dc_gb, GetBitContext *ac_gb,
> > +                       unsigned *dcrun, unsigned *acrun,
> > +                       AVFrame *frame, int width, int by, int plane)
> > +{
> > +    const ptrdiff_t linesize = frame->linesize[plane];
> > +    uint8_t *dst = frame->data[plane] + by * linesize;
> > +    unsigned dc_run = *dcrun, ac_run = *acrun;
> > +    LOCAL_ALIGNED_32(int16_t, block, [64]);
> > +    VMIXContext *s = avctx->priv_data;
> > +    const int16_t *factors = s->factors;
> > +    const uint8_t *scan = s->scan;
> > +    const int add = plane ? 0 : 1024;
> > +    int i, dc_v = 0, ac_v = 0, dc = 0;
> > +
> > +    for (int y = 0; y < 2; y++) {
> > +        for (int x = 0; x < width; x += 8) {
> > +            memset(block, 0, sizeof(*block)*64);
> > +
> > +            if (dc_run > 0) {
> > +                dc_run--;
> > +            } else {
> > +                dc_v = get_se_golomb_vmix(dc_gb);
> > +                dc += dc_v;
> > +                if (!dc_v)
> > +                    dc_run = get_ue_golomb_long(dc_gb);
> > +            }
> > +
> > +            for (int n = 0; n < 64; n++) {
> > +                if (ac_run > 0) {
> > +                    ac_run--;
> > +                    continue;
> > +                }
> > +
> > +                ac_v = get_se_golomb_vmix(ac_gb);
> > +                i = scan[n];
> > +                block[i] = (ac_v * factors[i]) >> 4;
> > +                if (!ac_v)
> > +                    ac_run = get_ue_golomb_long(ac_gb);
> > +            }
> > +
> > +            block[0] = ((dc + add) * 16) >> 4;
> > +            s->idsp.idct_put(dst + x, linesize, block);
> > +        }
> > +
> > +        dst += 8 * linesize;
> > +    }
> > +
> > +    *dcrun = dc_run;
> > +    *acrun = ac_run;
> > +
> > +    return 0;
> > +}
> > +
> > +static int decode_slice(AVCodecContext *avctx, AVFrame *frame,
> > +                        const uint8_t *dc_src, unsigned dc_slice_size,
> > +                        const uint8_t *ac_src, unsigned ac_slice_size,
> > +                        int by)
> > +{
> > +    unsigned dc_run = 0, ac_run = 0;
> > +    GetBitContext dc_gb, ac_gb;
> > +    int ret;
> > +
> > +    ret = init_get_bits8(&dc_gb, dc_src, dc_slice_size);
> > +    if (ret < 0)
> > +        return ret;
> > +
> > +    ret = init_get_bits8(&ac_gb, ac_src, ac_slice_size);
> > +    if (ret < 0)
> > +        return ret;
> > +
> > +    for (int p = 0; p < 3; p++) {
> > +        const int rshift = !!p;
> > +        ret = decode_dcac(avctx, &dc_gb, &ac_gb,
> > +                          &dc_run, &ac_run, frame,
> > +                          frame->width >> rshift, by, p);
> > +        if (ret < 0)
> > +            return ret;
> > +
> > +        if (get_bits_left(&dc_gb) < 0)
> > +            return AVERROR_INVALIDDATA;
> > +        if (get_bits_left(&ac_gb) < 0)
> > +            return AVERROR_INVALIDDATA;
> > +
> > +        align_get_bits(&dc_gb);
> > +        align_get_bits(&ac_gb);
> > +    }
> > +
> > +    if (get_bits_left(&dc_gb) > 0)
> > +        return AVERROR_INVALIDDATA;
> > +    if (get_bits_left(&ac_gb) > 0)
> > +        return AVERROR_INVALIDDATA;
> > +
> > +    return 0;
> > +}
> > +
> > +static int decode_slices(AVCodecContext *avctx, void *arg,
> > +                         int n, int thread_nb)
> > +{
> > +    VMIXContext *s = avctx->priv_data;
> > +    const uint8_t *dc_slice_ptr = s->slices[n].dc_ptr;
> > +    const uint8_t *ac_slice_ptr = s->slices[n].ac_ptr;
> > +    unsigned dc_slice_size = s->slices[n].dc_size;
> > +    unsigned ac_slice_size = s->slices[n].ac_size;
> > +    AVFrame *frame = arg;
> > +
> > +    return decode_slice(avctx, frame, dc_slice_ptr, dc_slice_size,
> > +                        ac_slice_ptr, ac_slice_size, n * 16);
> > +}
> > +
> > +static int decode_frame(AVCodecContext *avctx,
> > +                        AVFrame *frame, int *got_frame,
> > +                        AVPacket *avpkt)
> > +{
> > +    VMIXContext *s = avctx->priv_data;
> > +    unsigned offset = 3, q;
> > +    int ret;
> > +
> > +    if (avpkt->size <= 7)
> > +        return AVERROR_INVALIDDATA;
> > +
> > +    if (avpkt->data[0] != 0x01)
> > +        return AVERROR_INVALIDDATA;
> > +
> > +    q = av_clip(99 - av_clip(avpkt->data[1], 0, 99), 0,
> FF_ARRAY_ELEMS(quality) - 1);
>
> The inner av_clip() is redundant.
>

That is not very critical.


>
> > +    for (int n = 0; n < 64; n++)
> > +        s->factors[n] = quant[n] * quality[q];
> > +
> > +    s->nb_slices = avpkt->data[2];
> > +    if (!s->nb_slices || s->nb_slices > (avctx->height + 15) / 16)
> > +        return AVERROR_INVALIDDATA;
> > +
> > +    ret = ff_thread_get_buffer(avctx, frame, 0);
>
> This should be moved after the loops below in order to avoid an
> allocation in case of invalid input.
>

Moved.


>
> > +    if (ret < 0)
> > +        return ret;
> > +
> > +    for (int n = 0; n < s->nb_slices; n++) {
> > +        unsigned slice_size;
> > +
> > +        if (offset + 4 > avpkt->size)
> > +            return AVERROR_INVALIDDATA;
> > +
> > +        slice_size = AV_RL32(avpkt->data + offset);
> > +        if (slice_size > avpkt->size)
> > +            return AVERROR_INVALIDDATA;
> > +
> > +        if (avpkt->size - slice_size - 4LL < offset)
> > +            return AVERROR_INVALIDDATA;
> > +
> > +        s->slices[n].dc_size = slice_size;
> > +        s->slices[n].dc_ptr = avpkt->data + offset + 4;
> > +        offset += slice_size + 4;
> > +    }
> > +
> > +    for (int n = 0; n < s->nb_slices; n++) {
> > +        unsigned slice_size;
> > +
> > +        if (offset + 4 > avpkt->size)
> > +            return AVERROR_INVALIDDATA;
> > +
> > +        slice_size = AV_RL32(avpkt->data + offset);
> > +        if (slice_size > avpkt->size)
> > +            return AVERROR_INVALIDDATA;
> > +
> > +        if (avpkt->size - slice_size - 4LL < offset)
> > +            return AVERROR_INVALIDDATA;
> > +
> > +        s->slices[n].ac_size = slice_size;
> > +        s->slices[n].ac_ptr = avpkt->data + offset + 4;
> > +        offset += slice_size + 4;
> > +    }
>
> These loops could be combined if you modified the way you stored the
> data by putting the dc information into slices[n] and the corresponding
> ac information into slices[n + nb_slices]. In this case, you would need
> to unpack this in decode_slices.
> If you don't do this, then there is no need to put nb_slices in the
> context. It can be local to this function.
>

 Not very critical.


>
> > +
> > +    avctx->execute2(avctx, decode_slices, frame, NULL, s->nb_slices);
> > +
> > +    frame->pict_type = AV_PICTURE_TYPE_I;
> > +    frame->flags |= AV_FRAME_FLAG_KEY;
> > +
> > +    *got_frame = 1;
> > +
> > +    return avpkt->size;
> > +}
>
>

Will apply with local changes soon.


> _______________________________________________
> 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".
>
diff mbox series

Patch

From 2e0bbbc73afeac43e8cb7e2d5c78647725c764ba Mon Sep 17 00:00:00 2001
From: Paul B Mahol <onemda@gmail.com>
Date: Sun, 4 Jun 2023 20:30:59 +0200
Subject: [PATCH] avcodec: add VMX1 decoder

Signed-off-by: Paul B Mahol <onemda@gmail.com>
---
 libavcodec/Makefile     |   1 +
 libavcodec/allcodecs.c  |   1 +
 libavcodec/codec_desc.c |   7 +
 libavcodec/codec_id.h   |   1 +
 libavcodec/vmixdec.c    | 288 ++++++++++++++++++++++++++++++++++++++++
 libavformat/riff.c      |   1 +
 6 files changed, 299 insertions(+)
 create mode 100644 libavcodec/vmixdec.c

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 87a8b90037..2efab60d7d 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -753,6 +753,7 @@  OBJS-$(CONFIG_VC2_ENCODER)             += vc2enc.o vc2enc_dwt.o diractab.o
 OBJS-$(CONFIG_VCR1_DECODER)            += vcr1.o
 OBJS-$(CONFIG_VMDAUDIO_DECODER)        += vmdaudio.o
 OBJS-$(CONFIG_VMDVIDEO_DECODER)        += vmdvideo.o
+OBJS-$(CONFIG_VMIX_DECODER)            += vmixdec.o
 OBJS-$(CONFIG_VMNC_DECODER)            += vmnc.o
 OBJS-$(CONFIG_VNULL_DECODER)           += null.o
 OBJS-$(CONFIG_VNULL_ENCODER)           += null.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index a98c300da4..11c136ef59 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -370,6 +370,7 @@  extern const FFCodec ff_vc1_v4l2m2m_decoder;
 extern const FFCodec ff_vc2_encoder;
 extern const FFCodec ff_vcr1_decoder;
 extern const FFCodec ff_vmdvideo_decoder;
+extern const FFCodec ff_vmix_decoder;
 extern const FFCodec ff_vmnc_decoder;
 extern const FFCodec ff_vp3_decoder;
 extern const FFCodec ff_vp4_decoder;
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 41293a78dc..3e31a1eed6 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -1953,6 +1953,13 @@  static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("RTV1 (RivaTuner Video)"),
         .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
     },
+    {
+        .id        = AV_CODEC_ID_VMIX,
+        .type      = AVMEDIA_TYPE_VIDEO,
+        .name      = "vmix",
+        .long_name = NULL_IF_CONFIG_SMALL("vMix Video"),
+        .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
+    },
 
     /* various PCM "codecs" */
     {
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index 9a78cfabe2..d23549d7e0 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -323,6 +323,7 @@  enum AVCodecID {
     AV_CODEC_ID_PDV,
     AV_CODEC_ID_EVC,
     AV_CODEC_ID_RTV1,
+    AV_CODEC_ID_VMIX,
 
     /* various PCM "codecs" */
     AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/vmixdec.c b/libavcodec/vmixdec.c
new file mode 100644
index 0000000000..a9cb704964
--- /dev/null
+++ b/libavcodec/vmixdec.c
@@ -0,0 +1,288 @@ 
+/*
+ * vMix decoder
+ * Copyright (c) 2023 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 "libavutil/avassert.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/mem_internal.h"
+
+#include "avcodec.h"
+#include "codec_internal.h"
+#include "decode.h"
+#define CACHED_BITSTREAM_READER !ARCH_X86_32
+#include "golomb.h"
+#include "get_bits.h"
+#include "idctdsp.h"
+#include "thread.h"
+
+typedef struct SliceContext {
+    const uint8_t *dc_ptr;
+    const uint8_t *ac_ptr;
+    unsigned dc_size;
+    unsigned ac_size;
+} SliceContext;
+
+typedef struct VMIXContext {
+    int nb_slices;
+
+    int16_t factors[64];
+    uint8_t scan[64];
+
+    SliceContext slices[255];
+
+    IDCTDSPContext idsp;
+} VMIXContext;
+
+static const uint8_t quality[25] = {
+     1,  2,  3,  4,  5,  6,  7,  8, 10, 12, 14, 16,
+    18, 20, 22, 24, 28, 32, 36, 40, 44, 48, 52, 56, 64,
+};
+
+static const uint8_t quant[64] = {
+    16, 16, 19, 22, 22, 26, 26, 27,
+    16, 16, 22, 22, 26, 27, 27, 29,
+    19, 22, 26, 26, 27, 29, 29, 35,
+    22, 24, 27, 27, 29, 32, 34, 38,
+    26, 27, 29, 29, 32, 35, 38, 46,
+    27, 29, 34, 34, 35, 40, 46, 56,
+    29, 34, 34, 37, 40, 48, 56, 69,
+    34, 37, 38, 40, 48, 58, 69, 83,
+};
+
+static av_cold int decode_init(AVCodecContext *avctx)
+{
+    VMIXContext *s = avctx->priv_data;
+
+    avctx->bits_per_raw_sample = 8;
+    avctx->pix_fmt = AV_PIX_FMT_YUV422P;
+
+    avctx->coded_width = FFALIGN(avctx->width, 16);
+    avctx->coded_height = FFALIGN(avctx->height, 16);
+
+    ff_idctdsp_init(&s->idsp, avctx);
+    ff_permute_scantable(s->scan, ff_zigzag_direct,
+                         s->idsp.idct_permutation);
+    return 0;
+}
+
+static inline int get_se_golomb_vmix(GetBitContext *gb)
+{
+    unsigned int buf = get_ue_golomb_long(gb);
+    int sign = (buf & 1) - 1;
+    return ((buf >> 1) ^ (~sign));
+}
+
+static int decode_dcac(AVCodecContext *avctx,
+                       GetBitContext *dc_gb, GetBitContext *ac_gb,
+                       unsigned *dcrun, unsigned *acrun,
+                       AVFrame *frame, int width, int by, int plane)
+{
+    const ptrdiff_t linesize = frame->linesize[plane];
+    uint8_t *dst = frame->data[plane] + by * linesize;
+    unsigned dc_run = *dcrun, ac_run = *acrun;
+    LOCAL_ALIGNED_32(int16_t, block, [64]);
+    VMIXContext *s = avctx->priv_data;
+    const int16_t *factors = s->factors;
+    const uint8_t *scan = s->scan;
+    const int add = plane ? 0 : 1024;
+    int i, dc_v = 0, ac_v = 0, dc = 0;
+
+    for (int y = 0; y < 2; y++) {
+        for (int x = 0; x < width; x += 8) {
+            memset(block, 0, sizeof(*block)*64);
+
+            if (dc_run > 0) {
+                dc_run--;
+            } else {
+                dc_v = get_se_golomb_vmix(dc_gb);
+                dc += dc_v;
+                if (!dc_v)
+                    dc_run = get_ue_golomb_long(dc_gb);
+            }
+
+            for (int n = 0; n < 64; n++) {
+                if (ac_run > 0) {
+                    ac_run--;
+                    continue;
+                }
+
+                ac_v = get_se_golomb_vmix(ac_gb);
+                i = scan[n];
+                block[i] = (ac_v * factors[i]) >> 4;
+                if (!ac_v)
+                    ac_run = get_ue_golomb_long(ac_gb);
+            }
+
+            block[0] = ((dc + add) * 16) >> 4;
+            s->idsp.idct_put(dst + x, linesize, block);
+        }
+
+        dst += 8 * linesize;
+    }
+
+    *dcrun = dc_run;
+    *acrun = ac_run;
+
+    return 0;
+}
+
+static int decode_slice(AVCodecContext *avctx, AVFrame *frame,
+                        const uint8_t *dc_src, unsigned dc_slice_size,
+                        const uint8_t *ac_src, unsigned ac_slice_size,
+                        int by)
+{
+    unsigned dc_run = 0, ac_run = 0;
+    GetBitContext dc_gb, ac_gb;
+    int ret;
+
+    ret = init_get_bits8(&dc_gb, dc_src, dc_slice_size);
+    if (ret < 0)
+        return ret;
+
+    ret = init_get_bits8(&ac_gb, ac_src, ac_slice_size);
+    if (ret < 0)
+        return ret;
+
+    for (int p = 0; p < 3; p++) {
+        const int rshift = !!p;
+        ret = decode_dcac(avctx, &dc_gb, &ac_gb,
+                          &dc_run, &ac_run, frame,
+                          frame->width >> rshift, by, p);
+        if (ret < 0)
+            return ret;
+
+        if (get_bits_left(&dc_gb) < 0)
+            return AVERROR_INVALIDDATA;
+        if (get_bits_left(&ac_gb) < 0)
+            return AVERROR_INVALIDDATA;
+
+        align_get_bits(&dc_gb);
+        align_get_bits(&ac_gb);
+    }
+
+    if (get_bits_left(&dc_gb) > 0)
+        return AVERROR_INVALIDDATA;
+    if (get_bits_left(&ac_gb) > 0)
+        return AVERROR_INVALIDDATA;
+
+    return 0;
+}
+
+static int decode_slices(AVCodecContext *avctx, void *arg,
+                         int n, int thread_nb)
+{
+    VMIXContext *s = avctx->priv_data;
+    const uint8_t *dc_slice_ptr = s->slices[n].dc_ptr;
+    const uint8_t *ac_slice_ptr = s->slices[n].ac_ptr;
+    unsigned dc_slice_size = s->slices[n].dc_size;
+    unsigned ac_slice_size = s->slices[n].ac_size;
+    AVFrame *frame = arg;
+
+    return decode_slice(avctx, frame, dc_slice_ptr, dc_slice_size,
+                        ac_slice_ptr, ac_slice_size, n * 16);
+}
+
+static int decode_frame(AVCodecContext *avctx,
+                        AVFrame *frame, int *got_frame,
+                        AVPacket *avpkt)
+{
+    VMIXContext *s = avctx->priv_data;
+    unsigned offset = 3, q;
+    int ret;
+
+    if (avpkt->size <= 7)
+        return AVERROR_INVALIDDATA;
+
+    if (avpkt->data[0] != 0x01)
+        return AVERROR_INVALIDDATA;
+
+    q = av_clip(99 - av_clip(avpkt->data[1], 0, 99), 0, FF_ARRAY_ELEMS(quality) - 1);
+    for (int n = 0; n < 64; n++)
+        s->factors[n] = quant[n] * quality[q];
+
+    s->nb_slices = avpkt->data[2];
+    if (!s->nb_slices || s->nb_slices > (avctx->height + 15) / 16)
+        return AVERROR_INVALIDDATA;
+
+    ret = ff_thread_get_buffer(avctx, frame, 0);
+    if (ret < 0)
+        return ret;
+
+    for (int n = 0; n < s->nb_slices; n++) {
+        unsigned slice_size;
+
+        if (offset + 4 > avpkt->size)
+            return AVERROR_INVALIDDATA;
+
+        slice_size = AV_RL32(avpkt->data + offset);
+        if (slice_size > avpkt->size)
+            return AVERROR_INVALIDDATA;
+
+        if (avpkt->size - slice_size - 4LL < offset)
+            return AVERROR_INVALIDDATA;
+
+        s->slices[n].dc_size = slice_size;
+        s->slices[n].dc_ptr = avpkt->data + offset + 4;
+        offset += slice_size + 4;
+    }
+
+    for (int n = 0; n < s->nb_slices; n++) {
+        unsigned slice_size;
+
+        if (offset + 4 > avpkt->size)
+            return AVERROR_INVALIDDATA;
+
+        slice_size = AV_RL32(avpkt->data + offset);
+        if (slice_size > avpkt->size)
+            return AVERROR_INVALIDDATA;
+
+        if (avpkt->size - slice_size - 4LL < offset)
+            return AVERROR_INVALIDDATA;
+
+        s->slices[n].ac_size = slice_size;
+        s->slices[n].ac_ptr = avpkt->data + offset + 4;
+        offset += slice_size + 4;
+    }
+
+    avctx->execute2(avctx, decode_slices, frame, NULL, s->nb_slices);
+
+    frame->pict_type = AV_PICTURE_TYPE_I;
+    frame->flags |= AV_FRAME_FLAG_KEY;
+
+    *got_frame = 1;
+
+    return avpkt->size;
+}
+
+const FFCodec ff_vmix_decoder = {
+    .p.name           = "vmix",
+    CODEC_LONG_NAME("vMix Video"),
+    .p.type           = AVMEDIA_TYPE_VIDEO,
+    .p.id             = AV_CODEC_ID_VMIX,
+    .priv_data_size   = sizeof(VMIXContext),
+    .init             = decode_init,
+    FF_CODEC_DECODE_CB(decode_frame),
+    .p.capabilities   = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS |
+                        AV_CODEC_CAP_SLICE_THREADS,
+};
diff --git a/libavformat/riff.c b/libavformat/riff.c
index 4279071159..97708df6e3 100644
--- a/libavformat/riff.c
+++ b/libavformat/riff.c
@@ -502,6 +502,7 @@  const AVCodecTag ff_codec_bmp_tags[] = {
     { AV_CODEC_ID_VQC,          MKTAG('V', 'Q', 'C', '1') },
     { AV_CODEC_ID_VQC,          MKTAG('V', 'Q', 'C', '2') },
     { AV_CODEC_ID_RTV1,         MKTAG('R', 'T', 'V', '1') },
+    { AV_CODEC_ID_VMIX,         MKTAG('V', 'M', 'X', '1') },
     { AV_CODEC_ID_NONE,         0 }
 };
 
-- 
2.39.1