[FFmpeg-cvslog] avcodec: add VMX1 decoder

Paul B Mahol git at videolan.org
Tue Jun 13 00:45:42 EEST 2023


ffmpeg | branch: master | Paul B Mahol <onemda at gmail.com> | Sun Jun  4 20:30:59 2023 +0200| [2d59ca0a668a0bdf9c83b35fe3ad357f87f8a15d] | committer: Paul B Mahol

avcodec: add VMX1 decoder

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=2d59ca0a668a0bdf9c83b35fe3ad357f87f8a15d
---

 Changelog               |   1 +
 configure               |   1 +
 libavcodec/Makefile     |   1 +
 libavcodec/allcodecs.c  |   1 +
 libavcodec/codec_desc.c |   7 ++
 libavcodec/codec_id.h   |   1 +
 libavcodec/version.h    |   2 +-
 libavcodec/vmixdec.c    | 288 ++++++++++++++++++++++++++++++++++++++++++++++++
 libavformat/riff.c      |   1 +
 9 files changed, 302 insertions(+), 1 deletion(-)

diff --git a/Changelog b/Changelog
index d51e03b8eb..cd872d9bf2 100644
--- a/Changelog
+++ b/Changelog
@@ -16,6 +16,7 @@ version <next>:
 - nlmeans_vulkan filter
 - RivaTuner video decoder
 - xfade_vulkan filter
+- vMix video decoder
 
 version 6.0:
 - Radiance HDR image support
diff --git a/configure b/configure
index 2fbe03921a..4ac7cc6c0b 100755
--- a/configure
+++ b/configure
@@ -3006,6 +3006,7 @@ utvideo_encoder_select="bswapdsp huffman llvidencdsp"
 vble_decoder_select="llviddsp"
 vbn_decoder_select="texturedsp"
 vbn_encoder_select="texturedspenc"
+vmix_decoder_select="idctdsp"
 vc1_decoder_select="blockdsp h264qpel intrax8 mpegvideodec qpeldsp vc1dsp"
 vc1image_decoder_select="vc1_decoder"
 vorbis_encoder_select="audio_frame_queue"
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/version.h b/libavcodec/version.h
index 2618016a83..65bc52fb24 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,7 +29,7 @@
 
 #include "version_major.h"
 
-#define LIBAVCODEC_VERSION_MINOR  17
+#define LIBAVCODEC_VERSION_MINOR  18
 #define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
diff --git a/libavcodec/vmixdec.c b/libavcodec/vmixdec.c
new file mode 100644
index 0000000000..d0f2219a67
--- /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;
+
+    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;
+    }
+
+    ret = ff_thread_get_buffer(avctx, frame, 0);
+    if (ret < 0)
+        return ret;
+
+    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 }
 };
 



More information about the ffmpeg-cvslog mailing list