Adds a native decoder for the VDEC codec used in AVI files produced by
VIDEC capture hardware. The codec uses a two-stage pipeline: a 9/13-bit
Huffman decode (patent fig. 4-3) followed by DPCM reconstruction
(patent fig. 4-6) as described in US patent 5,675,382. Output
resolution is inferred from pixel count since the container advertises
2x the encoded dimensions.

Based on the Python reference implementation at:
https://github.com/a-d-j-i/videc2

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: adji <[email protected]>
---
 libavcodec/Makefile     |   1 +
 libavcodec/allcodecs.c  |   1 +
 libavcodec/codec_desc.c |   7 +
 libavcodec/codec_id.h   |   1 +
 libavcodec/vdecdec.c    | 447 ++++++++++++++++++++++++++++++++++++++++
 libavformat/riff.c      |   1 +
 6 files changed, 458 insertions(+)
 create mode 100644 libavcodec/vdecdec.c

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index ffbacc2ed3..27b0aeee03 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -891,6 +891,7 @@ OBJS-$(CONFIG_ZLIB_DECODER)            += lcldec.o
 OBJS-$(CONFIG_ZLIB_ENCODER)            += lclenc.o
 OBJS-$(CONFIG_ZMBV_DECODER)            += zmbv.o
 OBJS-$(CONFIG_ZMBV_ENCODER)            += zmbvenc.o
+OBJS-$(CONFIG_VDEC_DECODER)            += vdecdec.o
  # (AD)PCM decoders/encoders
 OBJS-$(CONFIG_PCM_ALAW_DECODER)           += pcm.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 314cb230a4..0106bcea38 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -428,6 +428,7 @@ extern const FFCodec ff_zlib_encoder;
 extern const FFCodec ff_zlib_decoder;
 extern const FFCodec ff_zmbv_encoder;
 extern const FFCodec ff_zmbv_decoder;
+extern const FFCodec ff_vdec_decoder;
  /* audio codecs */
 extern const FFCodec ff_aac_encoder;
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 7a16d002b9..b6999eada3 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -2017,6 +2017,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .props     = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_LOSSLESS,
         .mime_types= MT("image/webp"),
     },
+    {
+        .id        = AV_CODEC_ID_VDEC,
+        .type      = AVMEDIA_TYPE_VIDEO,
+        .name      = "vdec",
+        .long_name = NULL_IF_CONFIG_SMALL("VDEC Huffman+DPCM 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 1aad9ba0e9..13442fb543 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -333,6 +333,7 @@ enum AVCodecID {
     AV_CODEC_ID_PRORES_RAW,
     AV_CODEC_ID_JPEGXS,
     AV_CODEC_ID_WEBP_ANIM,
+    AV_CODEC_ID_VDEC,
      /* various PCM "codecs" */
AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/vdecdec.c b/libavcodec/vdecdec.c
new file mode 100644
index 0000000000..1934e53b3b
--- /dev/null
+++ b/libavcodec/vdecdec.c
@@ -0,0 +1,447 @@
+/*
+ * VDEC video decoder
+ * Huffman + DPCM compression as described in US patent 5,675,382
+ * https://patentimages.storage.googleapis.com/e5/59/90/dbb789f1f1bdb5/US5675382.pdf
+ *
+ * Based on the Python reference implementation:
+ * https://github.com/a-d-j-i/videc2
+ */
+
+#include "avcodec.h"
+#include "codec_internal.h"
+#include "decode.h"
+#include "get_bits.h"
+#include "libavutil/mem.h"
+
+/* 9-bit Huffman table (patent fig. 4-3a).
+ * Entry format: bits[15:11]=nbits, bits[8:6]=b, bits[5:3]=g, bits[2:0]=r.
+ * Keys 0x000–0x03F (top 3 bits == 0) are never dispatched here;
+ * those go to table_430b instead, so those entries are 0x0000. */
+static const uint16_t table_430a[512] = {
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x401b, 0x401b, 0x4058, 0x4058, 0x4050, 0x4050, 0x4094, 0x4094,
+    0x4052, 0x4052, 0x4081, 0x4081, 0x40c0, 0x40c0, 0x4003, 0x4003,
+    0x401d, 0x401d, 0x405d, 0x405d, 0x4054, 0x4054, 0x40db, 0x40db,
+    0x405b, 0x405b, 0x4082, 0x4082, 0x4014, 0x4014, 0x400b, 0x400b,
+    0x38d9, 0x38d9, 0x38d9, 0x38d9, 0x384b, 0x384b, 0x384b, 0x384b,
+    0x3859, 0x3859, 0x3859, 0x3859, 0x3842, 0x3842, 0x3842, 0x3842,
+    0x38c8, 0x38c8, 0x38c8, 0x38c8, 0x38c9, 0x38c9, 0x38c9, 0x38c9,
+    0x3812, 0x3812, 0x3812, 0x3812, 0x3892, 0x3892, 0x3892, 0x3892,
+    0x2890, 0x2890, 0x2890, 0x2890, 0x2890, 0x2890, 0x2890, 0x2890,
+    0x2890, 0x2890, 0x2890, 0x2890, 0x2890, 0x2890, 0x2890, 0x2890,
+    0x2802, 0x2802, 0x2802, 0x2802, 0x2802, 0x2802, 0x2802, 0x2802,
+    0x2802, 0x2802, 0x2802, 0x2802, 0x2802, 0x2802, 0x2802, 0x2802,
+    0x2810, 0x2810, 0x2810, 0x2810, 0x2810, 0x2810, 0x2810, 0x2810,
+    0x2810, 0x2810, 0x2810, 0x2810, 0x2810, 0x2810, 0x2810, 0x2810,
+    0x2880, 0x2880, 0x2880, 0x2880, 0x2880, 0x2880, 0x2880, 0x2880,
+    0x2880, 0x2880, 0x2880, 0x2880, 0x2880, 0x2880, 0x2880, 0x2880,
+    0x2841, 0x2841, 0x2841, 0x2841, 0x2841, 0x2841, 0x2841, 0x2841,
+    0x2841, 0x2841, 0x2841, 0x2841, 0x2841, 0x2841, 0x2841, 0x2841,
+    0x2809, 0x2809, 0x2809, 0x2809, 0x2809, 0x2809, 0x2809, 0x2809,
+    0x2809, 0x2809, 0x2809, 0x2809, 0x2809, 0x2809, 0x2809, 0x2809,
+    0x2849, 0x2849, 0x2849, 0x2849, 0x2849, 0x2849, 0x2849, 0x2849,
+    0x2849, 0x2849, 0x2849, 0x2849, 0x2849, 0x2849, 0x2849, 0x2849,
+    0x2808, 0x2808, 0x2808, 0x2808, 0x2808, 0x2808, 0x2808, 0x2808,
+    0x2808, 0x2808, 0x2808, 0x2808, 0x2808, 0x2808, 0x2808, 0x2808,
+    0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001,
+    0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001,
+    0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001,
+    0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001,
+    0x2048, 0x2048, 0x2048, 0x2048, 0x2048, 0x2048, 0x2048, 0x2048,
+    0x2048, 0x2048, 0x2048, 0x2048, 0x2048, 0x2048, 0x2048, 0x2048,
+    0x2048, 0x2048, 0x2048, 0x2048, 0x2048, 0x2048, 0x2048, 0x2048,
+    0x2048, 0x2048, 0x2048, 0x2048, 0x2048, 0x2048, 0x2048, 0x2048,
+    0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840,
+    0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840,
+    0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840,
+    0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840,
+    0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840,
+    0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840,
+    0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840,
+    0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840, 0x1840,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000,
+};
+
+/* 13-bit Huffman table (patent fig. 4-3b).
+ * Only keys 0x0000–0x03FF are reachable (top 3 bits must all be 0). */
+static const uint16_t table_430b[1024] = {
+    0x69f7, 0x69f6, 0x69f3, 0x69f1, 0x69e7, 0x69e6, 0x69e3, 0x69de,
+    0x69d7, 0x69d6, 0x69d5, 0x69d3, 0x69cf, 0x69ce, 0x69c7, 0x69c6,
+    0x69c5, 0x69c3, 0x69c2, 0x69a7, 0x699f, 0x699e, 0x6997, 0x6996,
+    0x698e, 0x698d, 0x6987, 0x6986, 0x6984, 0x697e, 0x6957, 0x6947,
+    0x693f, 0x692f, 0x690f, 0x6907, 0x68bf, 0x68be, 0x68b7, 0x687e,
+    0x687c, 0x687a, 0x6847, 0x683e, 0x683a, 0x6838, 0x69f4, 0x69e5,
+    0x69e4, 0x69df, 0x69dc, 0x69d4, 0x69d1, 0x69cc, 0x69ca, 0x69c4,
+    0x69b8, 0x69aa, 0x698f, 0x698c, 0x698b, 0x6985, 0x694f, 0x691f,
+    0x6917, 0x68e7, 0x68a7, 0x6897, 0x6887, 0x6857, 0x683c, 0x6837,
+    0x69f5, 0x69ee, 0x69e2, 0x69cd, 0x69cb, 0x69c1, 0x69bf, 0x699d,
+    0x699a, 0x698a, 0x6983, 0x6981, 0x6976, 0x693e, 0x68fe, 0x68d7,
+    0x68c7, 0x68b8, 0x6877, 0x683f, 0x683d, 0x6817, 0x69fe, 0x69d2,
+    0x699c, 0x6995, 0x6991, 0x6989, 0x6982, 0x697c, 0x694e, 0x6946,
+    0x68cf, 0x68ce, 0x68b5, 0x68b3, 0x688f, 0x6876, 0x6867, 0x684f,
+    0x6827, 0x69ec, 0x69ea, 0x69e1, 0x69da, 0x69b1, 0x69af, 0x69a5,
+    0x699b, 0x6993, 0x6970, 0x6967, 0x6956, 0x693d, 0x6935, 0x6927,
+    0x68f7, 0x68bb, 0x688e, 0x6875, 0x6835, 0x69fc, 0x69f0, 0x69be,
+    0x69bd, 0x69b3, 0x695f, 0x6945, 0x690e, 0x68bd, 0x68bc, 0x68ba,
+    0x68ae, 0x686e, 0x6836, 0x680f, 0x680e, 0x69f8, 0x69bb, 0x69ac,
+    0x69a3, 0x6988, 0x6953, 0x68fc, 0x68f6, 0x68df, 0x6878, 0x6871,
+    0x683b, 0x6833, 0x6807, 0x69c0, 0x695e, 0x693b, 0x6937, 0x685e,
+    0x69ef, 0x69e0, 0x69b9, 0x6906, 0x68fa, 0x68c6, 0x6886, 0x6831,
+    0x682e, 0x681e, 0x69ba, 0x69b7, 0x69ab, 0x6955, 0x692e, 0x692a,
+    0x687b, 0x6870, 0x6839, 0x69f2, 0x6999, 0x6998, 0x6978, 0x6960,
+    0x689f, 0x69dd, 0x69a1, 0x693c, 0x6873, 0x69db, 0x6972, 0x6965,
+    0x6943, 0x693a, 0x6933, 0x68f3, 0x6879, 0x682c, 0x69bc, 0x6974,
+    0x6951, 0x6931, 0x6928, 0x691e, 0x6916, 0x687d, 0x6874, 0x684e,
+    0x681f, 0x69e8, 0x69ae, 0x69ad, 0x697a, 0x690c, 0x68f4, 0x6896,
+    0x6830, 0x69d0, 0x69a9, 0x6977, 0x692c, 0x68ee, 0x689e, 0x69eb,
+    0x6994, 0x6961, 0x6939, 0x68ff, 0x68f8, 0x68f5, 0x68de, 0x68b1,
+    0x68aa, 0x6872, 0x6971, 0x68f1, 0x682a, 0x69b5, 0x6938, 0x685f,
+    0x6806, 0x69fa, 0x6966, 0x69ff, 0x696e, 0x68ac, 0x68f2, 0x68f0,
+    0x68b9, 0x6846, 0x69ed, 0x697f, 0x6962, 0x686c, 0x6963, 0x68c5,
+    0x687f, 0x686a, 0x6973, 0x6915, 0x68ef, 0x69d8, 0x69d9, 0x69a6,
+    0x68fd, 0x6992, 0x6964, 0x695c, 0x69f9, 0x690a, 0x6950, 0x68b6,
+    0x6834, 0x69fd, 0x69a8, 0x6952, 0x68fb, 0x691c, 0x68a8, 0x686f,
+    0x68f9, 0x68ea, 0x69c8, 0x692b, 0x6980, 0x696f, 0x692d, 0x696a,
+    0x6941, 0x69fb, 0x69c9, 0x6975, 0x6905, 0x696c, 0x68ec, 0x69e9,
+    0x6918, 0x68af, 0x6832, 0x694c, 0x6929, 0x68d6, 0x68a6, 0x6904,
+    0x69a2, 0x6823, 0x697b, 0x6936, 0x691a, 0x6855, 0x6816, 0x69b0,
+    0x6861, 0x6821, 0x68d5, 0x69b6, 0x6942, 0x68a5, 0x68a9, 0x697d,
+    0x6925, 0x68b0, 0x6828, 0x6909, 0x6895, 0x6845, 0x68ab, 0x68a1,
+    0x681c, 0x6979, 0x6921, 0x6926, 0x6815, 0x69b4, 0x68b2, 0x688c,
+    0x6863, 0x694d, 0x6930, 0x6908, 0x68e0, 0x69b2, 0x6903, 0x690d,
+    0x6923, 0x68b4, 0x68e6, 0x6856, 0x6913, 0x69a4, 0x682f, 0x695a,
+    0x690b, 0x68a3, 0x6825, 0x6885, 0x6829, 0x68e1, 0x681a, 0x69a0,
+    0x689a, 0x6944, 0x68dc, 0x6860, 0x6853, 0x68d3, 0x6826, 0x6901,
+    0x6932, 0x6968, 0x6919, 0x6868, 0x685c, 0x695d, 0x682b, 0x689c,
+    0x68e8, 0x6954, 0x6865, 0x6934, 0x691d, 0x6990, 0x694a, 0x691b,
+    0x68e2, 0x68cc, 0x6805, 0x68e3, 0x680c, 0x6911, 0x6911, 0x6911,
+    0x5066, 0x5066, 0x5066, 0x5066, 0x5066, 0x5066, 0x5066, 0x5066,
+    0x50d1, 0x50d1, 0x50d1, 0x50d1, 0x50d1, 0x50d1, 0x50d1, 0x50d1,
+    0x50e5, 0x50e5, 0x50e5, 0x50e5, 0x50e5, 0x50e5, 0x50e5, 0x50e5,
+    0x5098, 0x5098, 0x5098, 0x5098, 0x5098, 0x5098, 0x5098, 0x5098,
+    0x5158, 0x5158, 0x5158, 0x5158, 0x5158, 0x5158, 0x5158, 0x5158,
+    0x5013, 0x5013, 0x5013, 0x5013, 0x5013, 0x5013, 0x5013, 0x5013,
+    0x5102, 0x5102, 0x5102, 0x5102, 0x5102, 0x5102, 0x5102, 0x5102,
+    0x5062, 0x5062, 0x5062, 0x5062, 0x5062, 0x5062, 0x5062, 0x5062,
+    0x508a, 0x508a, 0x508a, 0x508a, 0x508a, 0x508a, 0x508a, 0x508a,
+    0x5140, 0x5140, 0x5140, 0x5140, 0x5140, 0x5140, 0x5140, 0x5140,
+    0x50c3, 0x50c3, 0x50c3, 0x50c3, 0x50c3, 0x50c3, 0x50c3, 0x50c3,
+    0x505a, 0x505a, 0x505a, 0x505a, 0x505a, 0x505a, 0x505a, 0x505a,
+    0x504c, 0x504c, 0x504c, 0x504c, 0x504c, 0x504c, 0x504c, 0x504c,
+    0x5020, 0x5020, 0x5020, 0x5020, 0x5020, 0x5020, 0x5020, 0x5020,
+    0x50e4, 0x50e4, 0x50e4, 0x50e4, 0x50e4, 0x50e4, 0x50e4, 0x50e4,
+    0x5099, 0x5099, 0x5099, 0x5099, 0x5099, 0x5099, 0x5099, 0x5099,
+    0x5114, 0x5114, 0x5114, 0x5114, 0x5114, 0x5114, 0x5114, 0x5114,
+    0x5084, 0x5084, 0x5084, 0x5084, 0x5084, 0x5084, 0x5084, 0x5084,
+    0x5069, 0x5069, 0x5069, 0x5069, 0x5069, 0x5069, 0x5069, 0x5069,
+    0x514b, 0x514b, 0x514b, 0x514b, 0x514b, 0x514b, 0x514b, 0x514b,
+    0x5148, 0x5148, 0x5148, 0x5148, 0x5148, 0x5148, 0x5148, 0x5148,
+    0x50da, 0x50da, 0x50da, 0x50da, 0x50da, 0x50da, 0x50da, 0x50da,
+    0x506b, 0x506b, 0x506b, 0x506b, 0x506b, 0x506b, 0x506b, 0x506b,
+    0x50cd, 0x50cd, 0x50cd, 0x50cd, 0x50cd, 0x50cd, 0x50cd, 0x50cd,
+    0x5093, 0x5093, 0x5093, 0x5093, 0x5093, 0x5093, 0x5093, 0x5093,
+    0x50c4, 0x50c4, 0x50c4, 0x50c4, 0x50c4, 0x50c4, 0x50c4, 0x50c4,
+    0x50ca, 0x50ca, 0x50ca, 0x50ca, 0x50ca, 0x50ca, 0x50ca, 0x50ca,
+    0x509b, 0x509b, 0x509b, 0x509b, 0x509b, 0x509b, 0x509b, 0x509b,
+    0x5051, 0x5051, 0x5051, 0x5051, 0x5051, 0x5051, 0x5051, 0x5051,
+    0x50d2, 0x50d2, 0x50d2, 0x50d2, 0x50d2, 0x50d2, 0x50d2, 0x50d2,
+    0x515b, 0x515b, 0x515b, 0x515b, 0x515b, 0x515b, 0x515b, 0x515b,
+    0x50d0, 0x50d0, 0x50d0, 0x50d0, 0x50d0, 0x50d0, 0x50d0, 0x50d0,
+    0x5169, 0x5169, 0x5169, 0x5169, 0x5169, 0x5169, 0x5169, 0x5169,
+    0x50c2, 0x50c2, 0x50c2, 0x50c2, 0x50c2, 0x50c2, 0x50c2, 0x50c2,
+    0x516b, 0x516b, 0x516b, 0x516b, 0x516b, 0x516b, 0x516b, 0x516b,
+    0x50ed, 0x50ed, 0x50ed, 0x50ed, 0x50ed, 0x50ed, 0x50ed, 0x50ed,
+    0x5022, 0x5022, 0x5022, 0x5022, 0x5022, 0x5022, 0x5022, 0x5022,
+    0x50c1, 0x50c1, 0x50c1, 0x50c1, 0x50c1, 0x50c1, 0x50c1, 0x50c1,
+    0x50ad, 0x50ad, 0x50ad, 0x50ad, 0x50ad, 0x50ad, 0x50ad, 0x50ad,
+    0x5120, 0x5120, 0x5120, 0x5120, 0x5120, 0x5120, 0x5120, 0x5120,
+    0x504d, 0x504d, 0x504d, 0x504d, 0x504d, 0x504d, 0x504d, 0x504d,
+    0x5018, 0x5018, 0x5018, 0x5018, 0x5018, 0x5018, 0x5018, 0x5018,
+    0x506d, 0x506d, 0x506d, 0x506d, 0x506d, 0x506d, 0x506d, 0x506d,
+    0x5159, 0x5159, 0x5159, 0x5159, 0x5159, 0x5159, 0x5159, 0x5159,
+    0x508d, 0x508d, 0x508d, 0x508d, 0x508d, 0x508d, 0x508d, 0x508d,
+    0x5083, 0x5083, 0x5083, 0x5083, 0x5083, 0x5083, 0x5083, 0x5083,
+    0x50a0, 0x50a0, 0x50a0, 0x50a0, 0x50a0, 0x50a0, 0x50a0, 0x50a0,
+    0x50eb, 0x50eb, 0x50eb, 0x50eb, 0x50eb, 0x50eb, 0x50eb, 0x50eb,
+    0x5044, 0x5044, 0x5044, 0x5044, 0x5044, 0x5044, 0x5044, 0x5044,
+    0x508b, 0x508b, 0x508b, 0x508b, 0x508b, 0x508b, 0x508b, 0x508b,
+    0x5004, 0x5004, 0x5004, 0x5004, 0x5004, 0x5004, 0x5004, 0x5004,
+    0x500d, 0x500d, 0x500d, 0x500d, 0x500d, 0x500d, 0x500d, 0x500d,
+    0x5089, 0x5089, 0x5089, 0x5089, 0x5089, 0x5089, 0x5089, 0x5089,
+    0x5064, 0x5064, 0x5064, 0x5064, 0x5064, 0x5064, 0x5064, 0x5064,
+    0x50e9, 0x50e9, 0x50e9, 0x50e9, 0x50e9, 0x50e9, 0x50e9, 0x50e9,
+    0x516d, 0x516d, 0x516d, 0x516d, 0x516d, 0x516d, 0x516d, 0x516d,
+    0x5149, 0x5149, 0x5149, 0x5149, 0x5149, 0x5149, 0x5149, 0x5149,
+    0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122, 0x5122,
+    0x5024, 0x5024, 0x5024, 0x5024, 0x5024, 0x5024, 0x5024, 0x5024,
+    0x5112, 0x5112, 0x5112, 0x5112, 0x5112, 0x5112, 0x5112, 0x5112,
+    0x5100, 0x5100, 0x5100, 0x5100, 0x5100, 0x5100, 0x5100, 0x5100,
+    0x502d, 0x502d, 0x502d, 0x502d, 0x502d, 0x502d, 0x502d, 0x502d,
+    0x50a4, 0x50a4, 0x50a4, 0x50a4, 0x50a4, 0x50a4, 0x50a4, 0x50a4,
+    0x5088, 0x5088, 0x5088, 0x5088, 0x5088, 0x5088, 0x5088, 0x5088,
+    0x5091, 0x5091, 0x5091, 0x5091, 0x5091, 0x5091, 0x5091, 0x5091,
+    0x5043, 0x5043, 0x5043, 0x5043, 0x5043, 0x5043, 0x5043, 0x5043,
+    0x500a, 0x500a, 0x500a, 0x500a, 0x500a, 0x500a, 0x500a, 0x500a,
+    0x5124, 0x5124, 0x5124, 0x5124, 0x5124, 0x5124, 0x5124, 0x5124,
+    0x509d, 0x509d, 0x509d, 0x509d, 0x509d, 0x509d, 0x509d, 0x509d,
+    0x50dd, 0x50dd, 0x50dd, 0x50dd, 0x50dd, 0x50dd, 0x50dd, 0x50dd,
+    0x50d4, 0x50d4, 0x50d4, 0x50d4, 0x50d4, 0x50d4, 0x50d4, 0x50d4,
+    0x50d8, 0x50d8, 0x50d8, 0x50d8, 0x50d8, 0x50d8, 0x50d8, 0x50d8,
+    0x5019, 0x5019, 0x5019, 0x5019, 0x5019, 0x5019, 0x5019, 0x5019,
+    0x5110, 0x5110, 0x5110, 0x5110, 0x5110, 0x5110, 0x5110, 0x5110,
+    0x5011, 0x5011, 0x5011, 0x5011, 0x5011, 0x5011, 0x5011, 0x5011,
+    0x504a, 0x504a, 0x504a, 0x504a, 0x504a, 0x504a, 0x504a, 0x504a,
+    0x50a2, 0x50a2, 0x50a2, 0x50a2, 0x50a2, 0x50a2, 0x50a2, 0x50a2,
+    0x50cb, 0x50cb, 0x50cb, 0x50cb, 0x50cb, 0x50cb, 0x50cb, 0x50cb,
+};
+
+/* DPCM reconstruction tables (patent fig. 4-6a/b).
+ * Index: category (0–7) + predictor (0–31) * 8.
+ * Output: reconstructed pixel value 0–31. */
+static const uint8_t table_460a[256] = {
+    0x00, 0x01, 0x03, 0x08, 0x0d, 0x12, 0x17, 0x1d,
+    0x01, 0x02, 0x04, 0x09, 0x0e, 0x13, 0x18, 0x1d,
+    0x02, 0x03, 0x01, 0x05, 0x0a, 0x0f, 0x16, 0x1c,
+    0x03, 0x04, 0x01, 0x06, 0x0b, 0x10, 0x16, 0x1c,
+    0x04, 0x05, 0x02, 0x07, 0x0c, 0x11, 0x17, 0x1d,
+    0x05, 0x06, 0x03, 0x08, 0x0d, 0x12, 0x17, 0x1d,
+    0x06, 0x07, 0x04, 0x09, 0x0e, 0x13, 0x18, 0x1d,
+    0x07, 0x08, 0x05, 0x0a, 0x01, 0x0f, 0x15, 0x1c,
+    0x08, 0x09, 0x06, 0x0b, 0x01, 0x10, 0x16, 0x1d,
+    0x09, 0x0a, 0x07, 0x0c, 0x02, 0x11, 0x16, 0x1c,
+    0x0a, 0x0b, 0x08, 0x0d, 0x02, 0x12, 0x17, 0x1d,
+    0x0b, 0x0c, 0x09, 0x0e, 0x03, 0x13, 0x18, 0x1d,
+    0x0c, 0x0d, 0x0a, 0x0f, 0x05, 0x15, 0x01, 0x1c,
+    0x0d, 0x0e, 0x0b, 0x10, 0x06, 0x15, 0x01, 0x1c,
+    0x0e, 0x0f, 0x0c, 0x11, 0x07, 0x16, 0x02, 0x1c,
+    0x0f, 0x10, 0x0d, 0x12, 0x08, 0x17, 0x02, 0x1d,
+    0x10, 0x11, 0x0e, 0x13, 0x09, 0x18, 0x03, 0x1d,
+    0x11, 0x12, 0x0f, 0x14, 0x09, 0x19, 0x02, 0x1e,
+    0x12, 0x13, 0x10, 0x15, 0x0a, 0x1a, 0x03, 0x1e,
+    0x13, 0x14, 0x11, 0x16, 0x0c, 0x1c, 0x07, 0x01,
+    0x14, 0x15, 0x12, 0x17, 0x0d, 0x1c, 0x08, 0x02,
+    0x15, 0x16, 0x13, 0x18, 0x0e, 0x1d, 0x08, 0x02,
+    0x16, 0x17, 0x14, 0x19, 0x0f, 0x1e, 0x09, 0x02,
+    0x17, 0x18, 0x15, 0x1a, 0x10, 0x1e, 0x0a, 0x03,
+    0x18, 0x19, 0x16, 0x1b, 0x11, 0x1e, 0x0b, 0x03,
+    0x19, 0x1a, 0x17, 0x1c, 0x12, 0x0d, 0x07, 0x02,
+    0x1a, 0x1b, 0x18, 0x1d, 0x13, 0x0e, 0x08, 0x02,
+    0x1b, 0x1c, 0x19, 0x1e, 0x14, 0x0f, 0x09, 0x02,
+    0x1c, 0x1d, 0x1a, 0x15, 0x10, 0x0b, 0x06, 0x01,
+    0x1d, 0x1e, 0x1b, 0x16, 0x11, 0x0c, 0x07, 0x02,
+    0x1e, 0x1f, 0x1c, 0x17, 0x12, 0x0d, 0x08, 0x02,
+    0x1f, 0x1e, 0x1b, 0x17, 0x12, 0x0d, 0x08, 0x02,
+};
+
+static const uint8_t table_460b[256] = {
+    0x00, 0x01, 0x04, 0x08, 0x0d, 0x12, 0x17, 0x1d,
+    0x01, 0x00, 0x03, 0x08, 0x0d, 0x12, 0x17, 0x1d,
+    0x02, 0x01, 0x04, 0x09, 0x0e, 0x13, 0x18, 0x1d,
+    0x03, 0x02, 0x05, 0x0a, 0x0f, 0x14, 0x19, 0x1e,
+    0x04, 0x03, 0x06, 0x01, 0x0b, 0x10, 0x16, 0x1d,
+    0x05, 0x04, 0x07, 0x02, 0x0c, 0x11, 0x17, 0x1d,
+    0x06, 0x05, 0x08, 0x03, 0x0d, 0x12, 0x18, 0x1d,
+    0x07, 0x06, 0x09, 0x04, 0x0e, 0x01, 0x14, 0x1c,
+    0x08, 0x07, 0x0a, 0x05, 0x0f, 0x01, 0x15, 0x1c,
+    0x09, 0x08, 0x0b, 0x06, 0x10, 0x01, 0x16, 0x1d,
+    0x0a, 0x09, 0x0c, 0x07, 0x11, 0x02, 0x17, 0x1d,
+    0x0b, 0x0a, 0x0d, 0x08, 0x12, 0x03, 0x17, 0x1d,
+    0x0c, 0x0b, 0x0e, 0x09, 0x13, 0x03, 0x18, 0x1e,
+    0x0d, 0x0c, 0x0f, 0x0a, 0x15, 0x05, 0x1c, 0x01,
+    0x0e, 0x0d, 0x10, 0x0b, 0x16, 0x06, 0x1d, 0x01,
+    0x0f, 0x0e, 0x11, 0x0c, 0x16, 0x07, 0x1c, 0x02,
+    0x10, 0x0f, 0x12, 0x0d, 0x17, 0x08, 0x1d, 0x02,
+    0x11, 0x10, 0x13, 0x0e, 0x18, 0x09, 0x1d, 0x03,
+    0x12, 0x11, 0x14, 0x0f, 0x19, 0x0a, 0x1e, 0x03,
+    0x13, 0x12, 0x15, 0x10, 0x1a, 0x0a, 0x1e, 0x03,
+    0x14, 0x13, 0x16, 0x11, 0x1c, 0x0c, 0x07, 0x02,
+    0x15, 0x14, 0x17, 0x12, 0x1d, 0x0d, 0x08, 0x02,
+    0x16, 0x15, 0x18, 0x13, 0x1d, 0x0e, 0x09, 0x03,
+    0x17, 0x16, 0x19, 0x14, 0x1e, 0x0f, 0x09, 0x02,
+    0x18, 0x17, 0x1a, 0x15, 0x1e, 0x10, 0x0a, 0x03,
+    0x19, 0x18, 0x1b, 0x16, 0x11, 0x0c, 0x07, 0x02,
+    0x1a, 0x19, 0x1c, 0x17, 0x12, 0x0d, 0x08, 0x02,
+    0x1b, 0x1a, 0x1d, 0x18, 0x13, 0x0e, 0x08, 0x02,
+    0x1c, 0x1b, 0x1e, 0x19, 0x14, 0x0f, 0x09, 0x03,
+    0x1d, 0x1c, 0x1e, 0x1a, 0x15, 0x10, 0x09, 0x03,
+    0x1e, 0x1d, 0x1b, 0x16, 0x11, 0x0c, 0x07, 0x02,
+    0x1f, 0x1e, 0x1c, 0x17, 0x12, 0x0d, 0x08, 0x02,
+};
+
+/* Replicate seg3:0x5918 — 32-entry blue-channel gain LUT.
+ * Maps reconstructed 5-bit value (0..31) → gain-adjusted 5-bit value (0..31). */
+static void build_gain_lut(uint8_t lut[32], uint8_t gain_byte)
+{
+    uint32_t step = 0x300U * (uint32_t)gain_byte + (5U << 16);
+    uint32_t acc  = 0;
+    for (int i = 0; i < 32; i++) {
+        acc += step;
+        lut[i] = FFMIN(acc >> 19, 31);
+    }
+}
+
+static av_cold int vdec_decode_init(AVCodecContext *avctx)
+{
+    avctx->pix_fmt = AV_PIX_FMT_RGB24;
+    return 0;
+}
+
+static int vdec_decode_frame(AVCodecContext *avctx, AVFrame *frame,
+                             int *got_frame, AVPacket *avpkt)
+{
+    const uint8_t *buf  = avpkt->data;
+    int            size = avpkt->size;
+    uint8_t        gain_lut[32];
+    uint8_t       *codes = NULL;
+    uint8_t       *recon = NULL;
+    GetBitContext  gb;
+    int            ret;
+
+    if (size < 2) {
+        av_log(avctx, AV_LOG_ERROR, "packet too small (%d bytes)\n", size);
+        return AVERROR_INVALIDDATA;
+    }
+
+ /* Header: buf[0]+1 bytes to skip (type tag + gain byte + optional padding).
+     * buf[1] is always the blue-channel gain register. */
+    int skip = buf[0] + 1;
+    if (size <= skip) {
+        av_log(avctx, AV_LOG_ERROR, "header overruns packet\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    build_gain_lut(gain_lut, buf[1]);
+
+    ret = init_get_bits8(&gb, buf + skip, size - skip);
+    if (ret < 0)
+        return ret;
+
+    /* Step 1 — Huffman decode all codes.  Dimensions are inferred from the
+     * bitstream after decoding, so the container size is ignored entirely.
+     * 640×480 is the largest known VDEC resolution. */
+    int max_codes = 640 * 480;
+    codes = av_malloc(max_codes * 3);
+    if (!codes)
+        return AVERROR(ENOMEM);
+
+    int n_decoded = 0;
+    while (n_decoded < max_codes) {
+        uint16_t v;
+        if (get_bits_left(&gb) >= 13 && show_bits(&gb, 3) == 0) {
+            v = table_430b[show_bits(&gb, 13)];
+        } else if (get_bits_left(&gb) >= 9) {
+            v = table_430a[show_bits(&gb, 9)];
+        } else {
+            break;
+        }
+        int nbits = (v >> 11) & 0x1f;
+        if (nbits == 0)
+            break;
+        codes[n_decoded * 3]     = v & 0x7;
+        codes[n_decoded * 3 + 1] = (v >> 3) & 0x7;
+        codes[n_decoded * 3 + 2] = (v >> 6) & 0x7;
+        n_decoded++;
+        skip_bits(&gb, nbits);
+    }
+
+    if (n_decoded == 0) {
+        av_log(avctx, AV_LOG_ERROR, "no pixels decoded\n");
+        ret = AVERROR_INVALIDDATA;
+        goto end;
+    }
+
+    /* Infer encoded dimensions from pixel count (always 4:3 aspect ratio).
+ * The AVI container reports 2× the encoded size in each dimension, so the
+     * packet contains pixels for one of: 160×120, 320×240, or 640×480. */
+    int enc_w, enc_h;
+    if (n_decoded <= 48000)       { enc_w = 160; enc_h = 120; }
+    else if (n_decoded <= 192000) { enc_w = 320; enc_h = 240; }
+    else                          { enc_w = 640; enc_h = 480; }
+
+    ret = ff_set_dimensions(avctx, enc_w, enc_h);
+    if (ret < 0)
+        goto end;
+
+    /* Step 2 — DPCM reconstruction + write to output frame. */
+    recon = av_malloc(enc_w * enc_h * 3);
+    if (!recon) {
+        ret = AVERROR(ENOMEM);
+        goto end;
+    }
+
+    ret = ff_get_buffer(avctx, frame, 0);
+    if (ret < 0)
+        goto end;
+
+    int n = enc_w * enc_h;
+    for (int i = 0; i < n; i++) {
+        int col   = i % enc_w;
+        int row   = i / enc_w;
+        int carry = (row + col + 1) & 1;
+
+        int lr = col > 0 ? recon[(i - 1)     * 3]     : 16;
+        int lg = col > 0 ? recon[(i - 1)     * 3 + 1] : 16;
+        int lb = col > 0 ? recon[(i - 1)     * 3 + 2] : 16;
+        int ar = row > 0 ? recon[(i - enc_w) * 3]     : 16;
+        int ag = row > 0 ? recon[(i - enc_w) * 3 + 1] : 16;
+        int ab = row > 0 ? recon[(i - enc_w) * 3 + 2] : 16;
+
+        int pr = (lr + ar + carry) >> 1;
+        int pg = (lg + ag + carry) >> 1;
+        int pb = (lb + ab + carry) >> 1;
+
+        const uint8_t *tbl = carry ? table_460b : table_460a;
+        recon[i * 3]     = tbl[codes[i * 3]     + pr * 8];
+        recon[i * 3 + 1] = tbl[codes[i * 3 + 1] + pg * 8];
+        recon[i * 3 + 2] = tbl[codes[i * 3 + 2] + pb * 8];
+
+ /* Codec bit layout: bits 0-2 = Blue, bits 3-5 = Green, bits 6-8 = Red.
+         * Gain LUT is applied to the Red channel. */
+        uint8_t b5 = recon[i * 3];
+        uint8_t g5 = recon[i * 3 + 1];
+        uint8_t r5 = gain_lut[recon[i * 3 + 2]];
+        uint8_t *dst = frame->data[0] + row * frame->linesize[0] + col * 3;
+        dst[0] = (r5 << 3) | (r5 >> 2);
+        dst[1] = (g5 << 3) | (g5 >> 2);
+        dst[2] = (b5 << 3) | (b5 >> 2);
+    }
+
+    *got_frame = 1;
+    ret = avpkt->size;
+end:
+    av_freep(&codes);
+    av_freep(&recon);
+    return ret;
+}
+
+const FFCodec ff_vdec_decoder = {
+    .p.name         = "vdec",
+    CODEC_LONG_NAME("VDEC Huffman+DPCM video"),
+    .p.type         = AVMEDIA_TYPE_VIDEO,
+    .p.id           = AV_CODEC_ID_VDEC,
+    .init           = vdec_decode_init,
+    FF_CODEC_DECODE_CB(vdec_decode_frame),
+    .p.capabilities = AV_CODEC_CAP_DR1,
+};
\ No newline at end of file
diff --git a/libavformat/riff.c b/libavformat/riff.c
index fc79d0ac21..a11e5a059d 100644
--- a/libavformat/riff.c
+++ b/libavformat/riff.c
@@ -382,6 +382,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
     { AV_CODEC_ID_TRUEMOTION2RT,MKTAG('T', 'R', '2', '0') },
     { AV_CODEC_ID_CSCD,         MKTAG('C', 'S', 'C', 'D') },
     { AV_CODEC_ID_ZMBV,         MKTAG('Z', 'M', 'B', 'V') },
+    { AV_CODEC_ID_VDEC,         MKTAG('V', 'D', 'E', 'C') },
     { AV_CODEC_ID_KMVC,         MKTAG('K', 'M', 'V', 'C') },
     { AV_CODEC_ID_CAVS,         MKTAG('C', 'A', 'V', 'S') },
     { AV_CODEC_ID_AVS2,         MKTAG('A', 'V', 'S', '2') },
--
2.39.5

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to