[PR] Add the v216 pixel format (PR #24307)

charlymp via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <[email protected]>
PR #24307 opened by charlymp
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24307
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24307.patch

v216 is packed 16-bit 4:2:2 in UYVY component order, that is Y216 with the luma and chroma samples swapped inside each pair.

The last commit is an unrelated optimisation of that writer and can be dropped: hoisting the single-tap case gives 1.3x to 1.6x depending on resolution.


>From af560274738bdb34f746326784309eb06d7f91b0 Mon Sep 17 00:00:00 2001
From: Charly Morgand-Poyac <[email protected]>
Date: Fri, 28 Aug 2026 22:08:11 +0200
Subject: [PATCH 1/3] avutil/pixfmt: add AV_PIX_FMT_V216BE and
 AV_PIX_FMT_V216LE

Packed 16-bit 4:2:2 in UYVY component order, that is Y216 with the luma
and chroma samples swapped inside each pair.

Signed-off-by: Charly Morgand-Poyac <[email protected]>
---
 doc/APIchanges                   |  3 +++
 libavutil/pixdesc.c              | 23 +++++++++++++++++++++++
 libavutil/pixfmt.h               |  4 ++++
 libavutil/version.h              |  2 +-
 tests/ref/fate/imgutils          |  4 ++++
 tests/ref/fate/pixdesc           |  2 ++
 tests/ref/fate/sws-pixdesc-query |  7 +++++++
 7 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/doc/APIchanges b/doc/APIchanges
index 40b4ba3580..6cbdb490c4 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,9 @@ The last version increases of all libraries were on 2026-06-23.
 
 API changes, most recent first:
 
+2026-08-28 - xxxxxxxxxx - lavu 61.7.100 - pixfmt.h
+  Add AV_PIX_FMT_V216BE and AV_PIX_FMT_V216LE.
+
 2026-08-23 - xxxxxxxxxx - lavu 61.6.100 - channel_layout.h
   Add AV_CH_LAYOUT_5POINT1POINT4 and AV_CHANNEL_LAYOUT_5POINT1POINT4.
   Add AV_CH_LAYOUT_7POINT1POINT4 and AV_CHANNEL_LAYOUT_7POINT1POINT4.
diff --git a/libavutil/pixdesc.c b/libavutil/pixdesc.c
index f1e80cb31c..74cac8a4e6 100644
--- a/libavutil/pixdesc.c
+++ b/libavutil/pixdesc.c
@@ -3244,6 +3244,29 @@ static const AVPixFmtDescriptor av_pix_fmt_descriptors[AV_PIX_FMT_NB] = {
         },
         .flags = AV_PIX_FMT_FLAG_PLANAR,
     },
+    [AV_PIX_FMT_V216LE] = {
+        .name = "v216le",
+        .nb_components = 3,
+        .log2_chroma_w = 1,
+        .log2_chroma_h = 0,
+        .comp = {
+            { 0, 4, 2, 0, 16 },        /* Y */
+            { 0, 8, 0, 0, 16 },        /* U */
+            { 0, 8, 4, 0, 16 },        /* V */
+        },
+    },
+    [AV_PIX_FMT_V216BE] = {
+        .name = "v216be",
+        .nb_components = 3,
+        .log2_chroma_w = 1,
+        .log2_chroma_h = 0,
+        .comp = {
+            { 0, 4, 2, 0, 16 },        /* Y */
+            { 0, 8, 0, 0, 16 },        /* U */
+            { 0, 8, 4, 0, 16 },        /* V */
+        },
+        .flags = AV_PIX_FMT_FLAG_BE,
+    },
     [AV_PIX_FMT_Y216LE] = {
         .name = "y216le",
         .nb_components = 3,
diff --git a/libavutil/pixfmt.h b/libavutil/pixfmt.h
index b08881cc1f..bb8fe8f006 100644
--- a/libavutil/pixfmt.h
+++ b/libavutil/pixfmt.h
@@ -505,6 +505,9 @@ enum AVPixelFormat {
      */
     AV_PIX_FMT_CUARRAY,
 
+    AV_PIX_FMT_V216BE,      ///< packed YUV 4:2:2 like UYVY422, 32bpp, big-endian
+    AV_PIX_FMT_V216LE,      ///< packed YUV 4:2:2 like UYVY422, 32bpp, little-endian
+
     AV_PIX_FMT_NB         ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions
 };
 
@@ -612,6 +615,7 @@ enum AVPixelFormat {
 #define AV_PIX_FMT_Y210       AV_PIX_FMT_NE(Y210BE,  Y210LE)
 #define AV_PIX_FMT_Y212       AV_PIX_FMT_NE(Y212BE,  Y212LE)
 #define AV_PIX_FMT_Y216       AV_PIX_FMT_NE(Y216BE,  Y216LE)
+#define AV_PIX_FMT_V216       AV_PIX_FMT_NE(V216BE,  V216LE)
 #define AV_PIX_FMT_XV30       AV_PIX_FMT_NE(XV30BE,  XV30LE)
 #define AV_PIX_FMT_XV36       AV_PIX_FMT_NE(XV36BE,  XV36LE)
 #define AV_PIX_FMT_XV48       AV_PIX_FMT_NE(XV48BE,  XV48LE)
diff --git a/libavutil/version.h b/libavutil/version.h
index b065c39343..b83c74d755 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,7 +79,7 @@
  */
 
 #define LIBAVUTIL_VERSION_MAJOR  61
-#define LIBAVUTIL_VERSION_MINOR   6
+#define LIBAVUTIL_VERSION_MINOR   7
 #define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
diff --git a/tests/ref/fate/imgutils b/tests/ref/fate/imgutils
index ccd1522ef2..223fe2cfa9 100644
--- a/tests/ref/fate/imgutils
+++ b/tests/ref/fate/imgutils
@@ -306,6 +306,8 @@ gbrp10msbbe     planes: 3, linesizes: 128 128 128   0, plane_sizes:  6144  6144
 gbrp10msble     planes: 3, linesizes: 128 128 128   0, plane_sizes:  6144  6144  6144     0, plane_offsets:  6144  6144     0, total_size: 18432
 gbrp12msbbe     planes: 3, linesizes: 128 128 128   0, plane_sizes:  6144  6144  6144     0, plane_offsets:  6144  6144     0, total_size: 18432
 gbrp12msble     planes: 3, linesizes: 128 128 128   0, plane_sizes:  6144  6144  6144     0, plane_offsets:  6144  6144     0, total_size: 18432
+v216be          planes: 1, linesizes: 256   0   0   0, plane_sizes: 12288     0     0     0, plane_offsets:     0     0     0, total_size: 12288
+v216le          planes: 1, linesizes: 256   0   0   0, plane_sizes: 12288     0     0     0, plane_offsets:     0     0     0, total_size: 12288
 
 image_fill_black tests
 yuv420p         total_size:   4608,  black_unknown_crc: 0xd00f6cc6,  black_tv_crc: 0xd00f6cc6,  black_pc_crc: 0x234969af
@@ -559,3 +561,5 @@ gbrp10msbbe     total_size:  18432,  black_unknown_crc: 0x00000000,  black_tv_cr
 gbrp10msble     total_size:  18432,  black_unknown_crc: 0x00000000,  black_tv_crc: 0x00000000,  black_pc_crc: 0x00000000
 gbrp12msbbe     total_size:  18432,  black_unknown_crc: 0x00000000,  black_tv_crc: 0x00000000,  black_pc_crc: 0x00000000
 gbrp12msble     total_size:  18432,  black_unknown_crc: 0x00000000,  black_tv_crc: 0x00000000,  black_pc_crc: 0x00000000
+v216be          total_size:  12288,  black_unknown_crc: 0xb53352d5,  black_tv_crc: 0xb53352d5,  black_pc_crc: 0xf8461cf3
+v216le          total_size:  12288,  black_unknown_crc: 0xe39de4bd,  black_tv_crc: 0xe39de4bd,  black_pc_crc: 0x24b29add
diff --git a/tests/ref/fate/pixdesc b/tests/ref/fate/pixdesc
index 3d59e78ae7..5821eda565 100644
--- a/tests/ref/fate/pixdesc
+++ b/tests/ref/fate/pixdesc
@@ -281,3 +281,5 @@ Testing pixel format descriptors
   pix fmt gbrp12msble avg_bpp:48 planes:3
   pix fmt ohcodec avg_bpp:0 planes:0
   pix fmt cuarray avg_bpp:0 planes:0
+  pix fmt v216be avg_bpp:32 planes:1
+  pix fmt v216le avg_bpp:32 planes:1
diff --git a/tests/ref/fate/sws-pixdesc-query b/tests/ref/fate/sws-pixdesc-query
index a062088d54..f2754e59c5 100644
--- a/tests/ref/fate/sws-pixdesc-query
+++ b/tests/ref/fate/sws-pixdesc-query
@@ -31,6 +31,8 @@ is16BPS:
   rgbaf16le
   rgbf16be
   rgbf16le
+  v216be
+  v216le
   xv48be
   xv48le
   y216be
@@ -214,6 +216,7 @@ isBE:
   rgbaf32be
   rgbf16be
   rgbf32be
+  v216be
   v30xbe
   x2bgr10be
   x2rgb10be
@@ -290,6 +293,8 @@ isYUV:
   uyva
   uyvy422
   uyyvyy411
+  v216be
+  v216le
   v30xbe
   v30xle
   vuya
@@ -942,6 +947,8 @@ Packed:
   uyva
   uyvy422
   uyyvyy411
+  v216be
+  v216le
   v30xbe
   v30xle
   vuya
-- 
2.52.0


>From 146d2bf71f0d09268ad7271279496ab1fcfd8de4 Mon Sep 17 00:00:00 2001
From: Charly Morgand-Poyac <[email protected]>
Date: Fri, 28 Aug 2026 22:08:11 +0200
Subject: [PATCH 2/3] swscale: add v216 input and output support

Adds the readers for AV_PIX_FMT_V216LE, and factors the packed writer
with the Y216 one since they differ only in component order.

Signed-off-by: Charly Morgand-Poyac <[email protected]>
---
 libswscale/format.c                 |  1 +
 libswscale/input.c                  | 25 +++++++++++
 libswscale/output.c                 | 68 ++++++++++++++++++++++++-----
 tests/ref/fate/filter-pixfmts-copy  |  1 +
 tests/ref/fate/filter-pixfmts-scale |  1 +
 5 files changed, 84 insertions(+), 12 deletions(-)

diff --git a/libswscale/format.c b/libswscale/format.c
index cbd7e0263e..be3336d0d5 100644
--- a/libswscale/format.c
+++ b/libswscale/format.c
@@ -248,6 +248,7 @@ static const LegacyFormatEntry legacy_format_entries[] = {
     [AV_PIX_FMT_Y210LE]         = { 1, 1 },
     [AV_PIX_FMT_Y212LE]         = { 1, 1 },
     [AV_PIX_FMT_Y216LE]         = { 1, 1 },
+    [AV_PIX_FMT_V216LE]         = { 1, 1 },
     [AV_PIX_FMT_X2RGB10LE]      = { 1, 1 },
     [AV_PIX_FMT_X2BGR10LE]      = { 1, 1 },
     [AV_PIX_FMT_NV20BE]         = { 1, 1 },
diff --git a/libswscale/input.c b/libswscale/input.c
index 2e6dd6f413..28c5aa638b 100644
--- a/libswscale/input.c
+++ b/libswscale/input.c
@@ -605,6 +605,25 @@ y21xle_wrapper(10, 6)
 y21xle_wrapper(12, 4)
 y21xle_wrapper(16, 0)
 
+static void v216le_UV_c(uint8_t *dstU, uint8_t *dstV, const uint8_t *unused0,
+                        const uint8_t *src, const uint8_t *unused1, int width,
+                        uint32_t *unused2, void *opq)
+{
+    int i;
+    for (i = 0; i < width; i++) {
+        AV_WN16(dstU + i * 2, AV_RL16(src + i * 8 + 0));
+        AV_WN16(dstV + i * 2, AV_RL16(src + i * 8 + 4));
+    }
+}
+
+static void v216le_Y_c(uint8_t *dst, const uint8_t *src, const uint8_t *unused0,
+                       const uint8_t *unused1, int width, uint32_t *unused2, void *opq)
+{
+    int i;
+    for (i = 0; i < width; i++)
+        AV_WN16(dst + i * 2, AV_RL16(src + i * 4 + 2));
+}
+
 static void bswap16Y_c(uint8_t *_dst, const uint8_t *_src, const uint8_t *unused1, const uint8_t *unused2, int width,
                        uint32_t *unused, void *opq)
 {
@@ -2115,6 +2134,9 @@ av_cold void ff_sws_init_input_funcs(SwsInternal *c,
     case AV_PIX_FMT_Y216LE:
         *chrToYV12 = y216le_UV_c;
         break;
+    case AV_PIX_FMT_V216LE:
+        *chrToYV12 = v216le_UV_c;
+        break;
     case AV_PIX_FMT_RGBF32LE:
         *chrToYV12 = rgbf32le_to_uv_c;
         break;
@@ -2730,6 +2752,9 @@ av_cold void ff_sws_init_input_funcs(SwsInternal *c,
     case AV_PIX_FMT_Y216LE:
         *lumToYV12 = y216le_Y_c;
         break;
+    case AV_PIX_FMT_V216LE:
+        *lumToYV12 = v216le_Y_c;
+        break;
     case AV_PIX_FMT_X2RGB10LE:
         *lumToYV12 = rgb30leToY_c;
         break;
diff --git a/libswscale/output.c b/libswscale/output.c
index 760dca642e..3b6852918e 100644
--- a/libswscale/output.c
+++ b/libswscale/output.c
@@ -3126,14 +3126,28 @@ AYUVPACKEDWRAPPER(uyva, AV_PIX_FMT_UYVA)
 yuv2y2xx_wrapper(10)
 yuv2y2xx_wrapper(12)
 
-static void
-yuv2y216le_X_c(SwsInternal *c, const int16_t *lumFilter,
-               const int16_t **_lumSrc, int lumFilterSize,
-               const int16_t *chrFilter,
-               const int16_t **_chrUSrc,
-               const int16_t **_chrVSrc, int chrFilterSize,
-               const int16_t **_alpSrc,
-               uint8_t *dest, int dstW, int y)
+#define output_pixels(pos, Y1, U, Y2, V) \
+    if (target == AV_PIX_FMT_Y216LE) { \
+        AV_WL16(dest + pos + 0, Y1); \
+        AV_WL16(dest + pos + 2, U);  \
+        AV_WL16(dest + pos + 4, Y2); \
+        AV_WL16(dest + pos + 6, V);  \
+    } else { /* AV_PIX_FMT_V216LE */ \
+        AV_WL16(dest + pos + 0, U);  \
+        AV_WL16(dest + pos + 2, Y1); \
+        AV_WL16(dest + pos + 4, V);  \
+        AV_WL16(dest + pos + 6, Y2); \
+    }
+
+static av_always_inline void
+yuv2y216_X_c_template(SwsInternal *c, const int16_t *lumFilter,
+                      const int16_t **_lumSrc, int lumFilterSize,
+                      const int16_t *chrFilter,
+                      const int16_t **_chrUSrc,
+                      const int16_t **_chrVSrc, int chrFilterSize,
+                      const int16_t **_alpSrc,
+                      uint8_t *dest, int dstW, int y,
+                      enum AVPixelFormat target)
 {
     const int32_t **lumSrc  = (const int32_t **)_lumSrc;
     const int32_t **chrUSrc = (const int32_t **)_chrUSrc;
@@ -3160,13 +3174,40 @@ yuv2y216le_X_c(SwsInternal *c, const int16_t *lumFilter,
             V += chrVSrc[j][i] * (unsigned)chrFilter[j];
         }
 
-        AV_WL16(dest + 8 * i + 0, 0x8000 + av_clip_int16(Y1 >> shift));
-        AV_WL16(dest + 8 * i + 2, 0x8000 + av_clip_int16(U  >> shift));
-        AV_WL16(dest + 8 * i + 4, 0x8000 + av_clip_int16(Y2 >> shift));
-        AV_WL16(dest + 8 * i + 6, 0x8000 + av_clip_int16(V  >> shift));
+        output_pixels(8 * i,
+                      0x8000 + av_clip_int16(Y1 >> shift),
+                      0x8000 + av_clip_int16(U  >> shift),
+                      0x8000 + av_clip_int16(Y2 >> shift),
+                      0x8000 + av_clip_int16(V  >> shift));
     }
 }
 
+#undef output_pixels
+
+static void
+yuv2y216le_X_c(SwsInternal *c, const int16_t *lumFilter,
+               const int16_t **lumSrc, int lumFilterSize,
+               const int16_t *chrFilter, const int16_t **chrUSrc,
+               const int16_t **chrVSrc, int chrFilterSize,
+               const int16_t **alpSrc, uint8_t *dest, int dstW, int y)
+{
+    yuv2y216_X_c_template(c, lumFilter, lumSrc, lumFilterSize, chrFilter,
+                          chrUSrc, chrVSrc, chrFilterSize, alpSrc,
+                          dest, dstW, y, AV_PIX_FMT_Y216LE);
+}
+
+static void
+yuv2v216le_X_c(SwsInternal *c, const int16_t *lumFilter,
+               const int16_t **lumSrc, int lumFilterSize,
+               const int16_t *chrFilter, const int16_t **chrUSrc,
+               const int16_t **chrVSrc, int chrFilterSize,
+               const int16_t **alpSrc, uint8_t *dest, int dstW, int y)
+{
+    yuv2y216_X_c_template(c, lumFilter, lumSrc, lumFilterSize, chrFilter,
+                          chrUSrc, chrVSrc, chrFilterSize, alpSrc,
+                          dest, dstW, y, AV_PIX_FMT_V216LE);
+}
+
 static void
 yuv2vyu444_1_c(SwsInternal *c, const int16_t *buf0,
                const int16_t *ubuf[2], const int16_t *vbuf[2],
@@ -3881,5 +3922,8 @@ av_cold void ff_sws_init_output_funcs(SwsInternal *c,
     case AV_PIX_FMT_Y216LE:
         *yuv2packedX = yuv2y216le_X_c;
         break;
+    case AV_PIX_FMT_V216LE:
+        *yuv2packedX = yuv2v216le_X_c;
+        break;
     }
 }
diff --git a/tests/ref/fate/filter-pixfmts-copy b/tests/ref/fate/filter-pixfmts-copy
index 1b6e658fe8..192fcbb72f 100644
--- a/tests/ref/fate/filter-pixfmts-copy
+++ b/tests/ref/fate/filter-pixfmts-copy
@@ -106,6 +106,7 @@ rgba64be            ae2ae04b5efedca3505f47c4dd6ea6ea
 rgba64le            b91e1d77f799eb92241a2d2d28437b15
 uyva                8896d7525f12de08818a01a5d5cf84f1
 uyvy422             3bcf3c80047592f2211fae3260b1b65d
+v216le              ca2542f4fa856c4ce503918029230322
 v30xle              44503e6d78bc474c1b72c5ceb1779539
 vuya                3d5e934651cae1ce334001cb1829ad22
 vuyx                0af13a42f9d0932c5a9bb6a8a5d1c5ee
diff --git a/tests/ref/fate/filter-pixfmts-scale b/tests/ref/fate/filter-pixfmts-scale
index a6520f25ed..3b06f4c71a 100644
--- a/tests/ref/fate/filter-pixfmts-scale
+++ b/tests/ref/fate/filter-pixfmts-scale
@@ -106,6 +106,7 @@ rgba64be            ee73e57923af984b31cc7795d13929da
 rgba64le            783d2779adfafe3548bdb671ec0de69e
 uyva                2358038be6dac002a6c736e798a3d91c
 uyvy422             aeb4ba4f9f003ae21f6d18089198244f
+v216le              92208daa74523c0c61f6640e9fac7960
 v30xle              8cba1fdc0ba2c39eb6fb5885dcf628a7
 vuya                ffa817e283bf6a0b6fba21b07523ccaa
 vuyx                a6ff68f46c6b4b7595ec91b2a497df8e
-- 
2.52.0


>From 1c4c59eecb85c1425e800667d913f0424f66dbec Mon Sep 17 00:00:00 2001
From: Charly Morgand-Poyac <[email protected]>
Date: Fri, 28 Aug 2026 22:14:26 +0200
Subject: [PATCH 3/3] swscale: add a single-tap fast path to the y216 packed
 writer

The general filter loop cannot know that the filter size is one, which
is the case for every unscaled conversion. Hoisting the single tap gives
about 1.5x on y216 and v216 output, for bit-identical results.

Signed-off-by: Charly Morgand-Poyac <[email protected]>
---
 libswscale/output.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/libswscale/output.c b/libswscale/output.c
index 3b6852918e..509ec1f7e7 100644
--- a/libswscale/output.c
+++ b/libswscale/output.c
@@ -3154,6 +3154,29 @@ yuv2y216_X_c_template(SwsInternal *c, const int16_t *lumFilter,
     const int32_t **chrVSrc = (const int32_t **)_chrVSrc;
     int shift = 15;
 
+    /* See yuv2planeX_16_c_template for details on the bias. */
+    if (lumFilterSize == 1 && chrFilterSize == 1) {
+        const int32_t *ls = lumSrc[0], *us = chrUSrc[0], *vs = chrVSrc[0];
+        const unsigned lf = lumFilter[0], cf = chrFilter[0];
+        const int bias = (1 << (shift - 1)) - 0x40000000;
+
+        for (int i = 0; i < ((dstW + 1) >> 1); i++) {
+            int Y1 = bias, Y2 = bias, U = bias, V = bias;
+
+            Y1 += ls[i * 2]     * lf;
+            Y2 += ls[i * 2 + 1] * lf;
+            U  += us[i] * cf;
+            V  += vs[i] * cf;
+
+            output_pixels(8 * i,
+                          0x8000 + av_clip_int16(Y1 >> shift),
+                          0x8000 + av_clip_int16(U  >> shift),
+                          0x8000 + av_clip_int16(Y2 >> shift),
+                          0x8000 + av_clip_int16(V  >> shift));
+        }
+        return;
+    }
+
     for (int i = 0; i < ((dstW + 1) >> 1); i++) {
         int Y1 = 1 << (shift - 1), Y2 = 1 << (shift - 1);
         int U  = 1 << (shift - 1), V  = 1 << (shift - 1);
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.