PR #21678 opened by Marton Balint (cus) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21678 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21678.patch
This patch series started as an experiment, because I was suprised that there is no proper support for RGB / YUV444 videos on the web with transparency. I could not find any documentation which disallows the use of transparency in WEBM files with VP9 codec for pixel formats other than YUV 4:2:0, so this patch series add support for creating and reading such files: 4:2:2, 4:4:4, RGB, and their high bit depth variants. It is not quite clear if the alpha channel should be encoded using the same pixel format as the normal channels, or it should always be YUV 4:2:0. I sticked to the same format as the normal color components. Unfortunately the browsers I tested (chrome, firefox) only support 4:2:0 properly, so let's require an experimental flag to encode VP9 with the new, more exotic pixel formats. Maybe browsers will pick up support if something at least can generate such files... >From d55a1694beed59c0f337366b56b1f2f9d5b4dff0 Mon Sep 17 00:00:00 2001 From: Marton Balint <[email protected]> Date: Sat, 7 Feb 2026 00:19:47 +0100 Subject: [PATCH 1/4] avformat/matroskaenc: only write AlphaMode for VP8 and VP9 AlphaMode should indicate whether the BlockAdditional Element with BlockAddID of "1" contains Alpha data, as defined by to the Codec Mapping for the `CodecID`. Only VP8 and VP9 Codec Mappings define this, so writing it for all codecs with a pixel format of YUVA420P was wrong. Signed-off-by: Marton Balint <[email protected]> --- libavformat/matroskaenc.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index 50188c396c..ef5f3d0259 100644 --- a/libavformat/matroskaenc.c +++ b/libavformat/matroskaenc.c @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include <stdbool.h> #include <stdint.h> #include "config_components.h" @@ -1757,6 +1758,14 @@ static void mkv_write_blockadditionmapping(AVFormatContext *s, const MatroskaMux #endif } +static bool codec_has_blockadditional_alpha(const AVCodecParameters *par) +{ + if (par->codec_id != AV_CODEC_ID_VP8 && + par->codec_id != AV_CODEC_ID_VP9) + return false; + return (par->format == AV_PIX_FMT_YUVA420P); +} + static int mkv_write_track_video(AVFormatContext *s, MatroskaMuxContext *mkv, const AVStream *st, const AVCodecParameters *par, AVIOContext *pb) @@ -1785,7 +1794,7 @@ static int mkv_write_track_video(AVFormatContext *s, MatroskaMuxContext *mkv, if (ret < 0) return ret; - if (par->format == AV_PIX_FMT_YUVA420P || + if (codec_has_blockadditional_alpha(par) || ((tag = av_dict_get(st->metadata, "alpha_mode", NULL, 0)) || (tag = av_dict_get( s->metadata, "alpha_mode", NULL, 0))) && strtol(tag->value, NULL, 0)) ebml_writer_add_uint(&writer, MATROSKA_ID_VIDEOALPHAMODE, 1); -- 2.52.0 >From e2e08682059c5aab9a68ff019c392ddc11bc47e6 Mon Sep 17 00:00:00 2001 From: Marton Balint <[email protected]> Date: Sat, 7 Feb 2026 00:36:07 +0100 Subject: [PATCH 2/4] avformat/matroskaenc: write AlphaMode flag for all VP8 and VP9 pixel formats with alpha Let's check the pixel format descriptor flags instead of hardcoding a specific pixel format. Signed-off-by: Marton Balint <[email protected]> --- libavformat/matroskaenc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index ef5f3d0259..2eec6cfb7f 100644 --- a/libavformat/matroskaenc.c +++ b/libavformat/matroskaenc.c @@ -1760,10 +1760,11 @@ static void mkv_write_blockadditionmapping(AVFormatContext *s, const MatroskaMux static bool codec_has_blockadditional_alpha(const AVCodecParameters *par) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(par->format); if (par->codec_id != AV_CODEC_ID_VP8 && par->codec_id != AV_CODEC_ID_VP9) return false; - return (par->format == AV_PIX_FMT_YUVA420P); + return desc && (desc->flags & AV_PIX_FMT_FLAG_ALPHA); } static int mkv_write_track_video(AVFormatContext *s, MatroskaMuxContext *mkv, -- 2.52.0 >From 80b04671d0bb0721f5966edc6f939e48c9c13bf0 Mon Sep 17 00:00:00 2001 From: Marton Balint <[email protected]> Date: Sat, 7 Feb 2026 00:58:37 +0100 Subject: [PATCH 3/4] avcodec/libvpxdec: add support for decoding pixel formats other than YUV420 with alpha Signed-off-by: Marton Balint <[email protected]> --- libavcodec/libvpxdec.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/libavcodec/libvpxdec.c b/libavcodec/libvpxdec.c index 1257a3607c..317725bf31 100644 --- a/libavcodec/libvpxdec.c +++ b/libavcodec/libvpxdec.c @@ -140,23 +140,28 @@ static int set_pix_fmt(AVCodecContext *avctx, struct vpx_image *img, #if CONFIG_LIBVPX_VP9_DECODER case VPX_IMG_FMT_I422: avctx->profile = AV_PROFILE_VP9_1; - avctx->pix_fmt = AV_PIX_FMT_YUV422P; + avctx->pix_fmt = + has_alpha_channel ? AV_PIX_FMT_YUVA422P : AV_PIX_FMT_YUV422P; return 0; case VPX_IMG_FMT_I440: + //TODO: Add alpha support once the pixel format becomes available avctx->profile = AV_PROFILE_VP9_1; avctx->pix_fmt = AV_PIX_FMT_YUV440P; return 0; case VPX_IMG_FMT_I444: avctx->profile = AV_PROFILE_VP9_1; avctx->pix_fmt = avctx->colorspace == AVCOL_SPC_RGB ? - AV_PIX_FMT_GBRP : AV_PIX_FMT_YUV444P; + (has_alpha_channel ? AV_PIX_FMT_GBRAP : AV_PIX_FMT_GBRP) : + (has_alpha_channel ? AV_PIX_FMT_YUVA444P : AV_PIX_FMT_YUV444P); return 0; case VPX_IMG_FMT_I42016: avctx->profile = AV_PROFILE_VP9_2; if (img->bit_depth == 10) { - avctx->pix_fmt = AV_PIX_FMT_YUV420P10; + avctx->pix_fmt = + has_alpha_channel ? AV_PIX_FMT_YUVA420P10 : AV_PIX_FMT_YUV420P10; return 0; } else if (img->bit_depth == 12) { + //TODO: Add alpha support once the pixel format becomes available avctx->pix_fmt = AV_PIX_FMT_YUV420P12; return 0; } else { @@ -165,15 +170,18 @@ static int set_pix_fmt(AVCodecContext *avctx, struct vpx_image *img, case VPX_IMG_FMT_I42216: avctx->profile = AV_PROFILE_VP9_3; if (img->bit_depth == 10) { - avctx->pix_fmt = AV_PIX_FMT_YUV422P10; + avctx->pix_fmt = + has_alpha_channel ? AV_PIX_FMT_YUVA422P10 : AV_PIX_FMT_YUV422P10; return 0; } else if (img->bit_depth == 12) { + //TODO: Add alpha support once the pixel format becomes available avctx->pix_fmt = AV_PIX_FMT_YUV422P12; return 0; } else { return AVERROR_INVALIDDATA; } case VPX_IMG_FMT_I44016: + //TODO: Add alpha support once the pixel format becomes available avctx->profile = AV_PROFILE_VP9_3; if (img->bit_depth == 10) { avctx->pix_fmt = AV_PIX_FMT_YUV440P10; @@ -188,11 +196,13 @@ static int set_pix_fmt(AVCodecContext *avctx, struct vpx_image *img, avctx->profile = AV_PROFILE_VP9_3; if (img->bit_depth == 10) { avctx->pix_fmt = avctx->colorspace == AVCOL_SPC_RGB ? - AV_PIX_FMT_GBRP10 : AV_PIX_FMT_YUV444P10; + (has_alpha_channel ? AV_PIX_FMT_GBRAP10 : AV_PIX_FMT_GBRP10) : + (has_alpha_channel ? AV_PIX_FMT_YUVA444P10 : AV_PIX_FMT_YUV444P10); return 0; } else if (img->bit_depth == 12) { avctx->pix_fmt = avctx->colorspace == AVCOL_SPC_RGB ? - AV_PIX_FMT_GBRP12 : AV_PIX_FMT_YUV444P12; + (has_alpha_channel ? AV_PIX_FMT_GBRAP12 : AV_PIX_FMT_GBRP12) : + (has_alpha_channel ? AV_PIX_FMT_YUVA444P12 : AV_PIX_FMT_YUV444P12); return 0; } else { return AVERROR_INVALIDDATA; -- 2.52.0 >From e8aac616fcc815adae46758999f42ea38c0a453c Mon Sep 17 00:00:00 2001 From: Marton Balint <[email protected]> Date: Sat, 7 Feb 2026 18:17:41 +0100 Subject: [PATCH 4/4] avcodec/libvpxenc: add experimental support for alpha pixel formats other than YUV 4:2:0. I could not find any documentation which disallows the use of transparency for pixel formats other than YUV 4:2:0, so this patch adds support for transparency using 4:2:2, 4:4:4, RGB, and their high bit depth variants. It is not quite clear if the alpha channel should be encoded using the same pixel format as the normal channels, or it should be always YUV 4:2:0. I sticked to the same format as the normal color components. Unfortunately the browsers I tested (chrome, firefox) only support 4:2:0 properly, so let's require an experimental flag to generate files with the new, more exotic pixel formats. Signed-off-by: Marton Balint <[email protected]> --- libavcodec/libvpxenc.c | 47 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/libavcodec/libvpxenc.c b/libavcodec/libvpxenc.c index 082709c41f..e54fb1b673 100644 --- a/libavcodec/libvpxenc.c +++ b/libavcodec/libvpxenc.c @@ -825,6 +825,7 @@ static int set_pix_fmt(AVCodecContext *avctx, vpx_codec_caps_t codec_caps, *img_fmt = VPX_IMG_FMT_I420; return 0; case AV_PIX_FMT_YUV422P: + case AV_PIX_FMT_YUVA422P: enccfg->g_profile = 1; *img_fmt = VPX_IMG_FMT_I422; return 0; @@ -833,12 +834,15 @@ static int set_pix_fmt(AVCodecContext *avctx, vpx_codec_caps_t codec_caps, *img_fmt = VPX_IMG_FMT_I440; return 0; case AV_PIX_FMT_GBRP: + case AV_PIX_FMT_GBRAP: ctx->vpx_cs = VPX_CS_SRGB; case AV_PIX_FMT_YUV444P: + case AV_PIX_FMT_YUVA444P: enccfg->g_profile = 1; *img_fmt = VPX_IMG_FMT_I444; return 0; case AV_PIX_FMT_YUV420P10: + case AV_PIX_FMT_YUVA420P10: case AV_PIX_FMT_YUV420P12: if (codec_caps & VPX_CODEC_CAP_HIGHBITDEPTH) { enccfg->g_profile = 2; @@ -848,6 +852,7 @@ static int set_pix_fmt(AVCodecContext *avctx, vpx_codec_caps_t codec_caps, } break; case AV_PIX_FMT_YUV422P10: + case AV_PIX_FMT_YUVA422P10: case AV_PIX_FMT_YUV422P12: if (codec_caps & VPX_CODEC_CAP_HIGHBITDEPTH) { enccfg->g_profile = 3; @@ -866,10 +871,14 @@ static int set_pix_fmt(AVCodecContext *avctx, vpx_codec_caps_t codec_caps, } break; case AV_PIX_FMT_GBRP10: + case AV_PIX_FMT_GBRAP10: case AV_PIX_FMT_GBRP12: + case AV_PIX_FMT_GBRAP12: ctx->vpx_cs = VPX_CS_SRGB; case AV_PIX_FMT_YUV444P10: + case AV_PIX_FMT_YUVA444P10: case AV_PIX_FMT_YUV444P12: + case AV_PIX_FMT_YUVA444P12: if (codec_caps & VPX_CODEC_CAP_HIGHBITDEPTH) { enccfg->g_profile = 3; *img_fmt = VPX_IMG_FMT_I44416; @@ -1004,12 +1013,21 @@ static av_cold int vpx_init(AVCodecContext *avctx, vpx_svc_extra_cfg_t svc_params; #endif const AVDictionaryEntry* en = NULL; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt); av_log(avctx, AV_LOG_INFO, "%s\n", vpx_codec_version_str()); av_log(avctx, AV_LOG_VERBOSE, "%s\n", vpx_codec_build_config()); - if (avctx->pix_fmt == AV_PIX_FMT_YUVA420P) + if (desc && (desc->flags & AV_PIX_FMT_FLAG_ALPHA)) { ctx->is_alpha = 1; + if (avctx->pix_fmt != AV_PIX_FMT_YUVA420P && avctx->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { + av_log(avctx, AV_LOG_ERROR, + "Pixel format '%s' is not widely supported. " + "Use -strict experimental to use it anyway, or use 'yuva420p' pixel format instead.\n", + av_get_pix_fmt_name(avctx->pix_fmt)); + return AVERROR(EINVAL); + } + } if ((res = vpx_codec_enc_config_default(iface, &enccfg, 0)) != VPX_CODEC_OK) { av_log(avctx, AV_LOG_ERROR, "Failed to get config: %s\n", @@ -1695,15 +1713,24 @@ static int realloc_alpha_uv(AVCodecContext *avctx, int width, int height) av_freep(&planes[VPX_PLANE_U]); av_freep(&planes[VPX_PLANE_V]); - vpx_img_wrap(rawimg_alpha, VPX_IMG_FMT_I420, width, height, 1, + vpx_img_wrap(rawimg_alpha, ctx->rawimg.fmt, width, height, 1, (unsigned char*)1); planes[VPX_PLANE_U] = av_malloc_array(stride[VPX_PLANE_U], height); planes[VPX_PLANE_V] = av_malloc_array(stride[VPX_PLANE_V], height); if (!planes[VPX_PLANE_U] || !planes[VPX_PLANE_V]) return AVERROR(ENOMEM); - memset(planes[VPX_PLANE_U], 0x80, stride[VPX_PLANE_U] * height); - memset(planes[VPX_PLANE_V], 0x80, stride[VPX_PLANE_V] * height); + if (ctx->rawimg.bit_depth > 8 && ctx->vpx_cs != VPX_CS_SRGB) { + int val = 0x80 << (ctx->rawimg.bit_depth - 8); + AV_WN16(planes[VPX_PLANE_U], val); + AV_WN16(planes[VPX_PLANE_V], val); + av_memcpy_backptr(planes[VPX_PLANE_U] + 2, 2, stride[VPX_PLANE_U] * height - 2); + av_memcpy_backptr(planes[VPX_PLANE_V] + 2, 2, stride[VPX_PLANE_V] * height - 2); + } else { + int val = (ctx->vpx_cs == VPX_CS_SRGB) ? 0x00 : 0x80; + memset(planes[VPX_PLANE_U], val, stride[VPX_PLANE_U] * height); + memset(planes[VPX_PLANE_V], val, stride[VPX_PLANE_V] * height); + } } return 0; @@ -2085,9 +2112,12 @@ static const enum AVPixelFormat vp9_pix_fmts_highcol[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUVA444P, AV_PIX_FMT_GBRP, + AV_PIX_FMT_GBRAP, AV_PIX_FMT_NONE }; @@ -2095,19 +2125,28 @@ static const enum AVPixelFormat vp9_pix_fmts_highbd[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV420P10, + AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUV422P10, + AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUV440P10, AV_PIX_FMT_YUV444P10, + AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV440P12, AV_PIX_FMT_YUV444P12, + AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_GBRP, + AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP10, + AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRP12, + AV_PIX_FMT_GBRAP12, AV_PIX_FMT_NONE }; -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
