[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]