PR #23557 opened by Zhao Zhili (quink)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23557
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23557.patch

Some encoders write VUI colour values that the H.264 specification marks
as reserved, or that are illegal for the stream's chroma format. Add
fix_colour_description which replaces such invalid values with
unspecified.


>From 12a59f95f2abe0a78527c96b99d30a2556899b3a Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Thu, 18 Jun 2026 22:09:12 +0800
Subject: [PATCH] avcodec/h264_metadata_bsf: add option to fix invalid colour
 description

Some encoders write VUI colour values that the H.264 specification marks
as reserved, or that are illegal for the stream's chroma format. Add
fix_colour_description which replaces such invalid values with
unspecified.
---
 doc/bitstream_filters.texi     |  6 ++++
 libavcodec/bsf/h264_metadata.c | 60 ++++++++++++++++++++++++++++++++++
 libavcodec/version.h           |  2 +-
 tests/fate/h264.mak            |  8 +++++
 4 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi
index c6705bbd39..72c2542f7f 100644
--- a/doc/bitstream_filters.texi
+++ b/doc/bitstream_filters.texi
@@ -391,6 +391,12 @@ table E-2).
 Set the colour description in the stream (see H.264 section E.2.1
 and tables E-3, E-4 and E-5).
 
+@item fix_colour_description
+If the stream already signals a colour description, replace any value
+that the H.264 specification marks as reserved, or that is illegal with
+the unspecified value. Values explicitly set by the options above are
+validated as well.
+
 @item chroma_sample_loc_type
 Set the chroma sample location in the stream (see H.264 section
 E.2.1 and figure E-1).
diff --git a/libavcodec/bsf/h264_metadata.c b/libavcodec/bsf/h264_metadata.c
index b5d81e41e5..58023e6e99 100644
--- a/libavcodec/bsf/h264_metadata.c
+++ b/libavcodec/bsf/h264_metadata.c
@@ -61,6 +61,8 @@ typedef struct H264MetadataContext {
     int transfer_characteristics;
     int matrix_coefficients;
 
+    int fix_colour_description;
+
     int chroma_sample_loc_type;
 
     AVRational tick_rate;
@@ -139,6 +141,58 @@ static int h264_metadata_insert_aud(AVBSFContext *bsf,
     return 0;
 }
 
+static int h264_metadata_colour_primaries_invalid(int cp)
+{
+    // H.264 table E-3.
+    // Reserved value, for future use by the spec.
+    return cp == AVCOL_PRI_RESERVED0 || cp == AVCOL_PRI_RESERVED ||
+           (cp >= 13 && cp <= 21);
+}
+
+static int h264_metadata_transfer_invalid(int trc)
+{
+    // H.264 table E-4.
+    return trc == AVCOL_TRC_RESERVED0 || trc == AVCOL_TRC_RESERVED;
+}
+
+static int h264_metadata_matrix_coeff_invalid(int mc, int chroma_format_idc)
+{
+    // H.264 table E-5. RGB requires chroma_format_idc is equal to 3 (4:4:4)
+    return mc == AVCOL_SPC_RESERVED ||
+           (mc == AVCOL_SPC_RGB && chroma_format_idc != 3);
+}
+
+static void h264_metadata_fix_colour_description(AVBSFContext *bsf,
+                                                 H264RawSPS *sps)
+{
+    H264MetadataContext *ctx = bsf->priv_data;
+
+    if (!ctx->fix_colour_description ||
+        !sps->vui.colour_description_present_flag)
+        return;
+
+    if (h264_metadata_colour_primaries_invalid(sps->vui.colour_primaries)) {
+        av_log(bsf, AV_LOG_INFO, "Replacing invalid colour_primaries "
+               "%d with unspecified.\n", sps->vui.colour_primaries);
+        sps->vui.colour_primaries = AVCOL_PRI_UNSPECIFIED;
+    }
+
+    if (h264_metadata_transfer_invalid(
+        sps->vui.transfer_characteristics)) {
+        av_log(bsf, AV_LOG_INFO, "Replacing invalid "
+               "transfer_characteristics %d with unspecified.\n",
+               sps->vui.transfer_characteristics);
+        sps->vui.transfer_characteristics = AVCOL_TRC_UNSPECIFIED;
+    }
+
+    if (h264_metadata_matrix_coeff_invalid(sps->vui.matrix_coefficients,
+                                           sps->chroma_format_idc)) {
+        av_log(bsf, AV_LOG_INFO, "Replacing invalid matrix_coefficients "
+               "%d with unspecified.\n", sps->vui.matrix_coefficients);
+        sps->vui.matrix_coefficients = AVCOL_SPC_UNSPECIFIED;
+    }
+}
+
 static int h264_metadata_update_sps(AVBSFContext *bsf,
                                     H264RawSPS *sps)
 {
@@ -203,6 +257,8 @@ static int h264_metadata_update_sps(AVBSFContext *bsf,
         sps->vui.video_signal_type_present_flag = 1;
     }
 
+    h264_metadata_fix_colour_description(bsf, sps);
+
     if (ctx->chroma_sample_loc_type >= 0) {
         sps->vui.chroma_sample_loc_type_top_field =
             ctx->chroma_sample_loc_type;
@@ -619,6 +675,10 @@ static const AVOption h264_metadata_options[] = {
         OFFSET(matrix_coefficients), AV_OPT_TYPE_INT,
         { .i64 = -1 }, -1, 255, FLAGS },
 
+    { "fix_colour_description", "Replace invalid VUI colour description values 
with unspecified",
+        OFFSET(fix_colour_description), AV_OPT_TYPE_BOOL,
+        { .i64 = 0 }, 0, 1, FLAGS },
+
     { "chroma_sample_loc_type", "Set chroma sample location type (figure E-1)",
         OFFSET(chroma_sample_loc_type), AV_OPT_TYPE_INT,
         { .i64 = -1 }, -1, 5, FLAGS },
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 381b278e81..c5b80dc383 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -30,7 +30,7 @@
 #include "version_major.h"
 
 #define LIBAVCODEC_VERSION_MINOR  36
-#define LIBAVCODEC_VERSION_MICRO 101
+#define LIBAVCODEC_VERSION_MICRO 102
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
                                                LIBAVCODEC_VERSION_MINOR, \
diff --git a/tests/fate/h264.mak b/tests/fate/h264.mak
index dacaaab274..1f73dc5773 100644
--- a/tests/fate/h264.mak
+++ b/tests/fate/h264.mak
@@ -227,6 +227,8 @@ FATE_H264-$(call FRAMECRC, MOV, H264) += 
fate-h264-twofields-packet
 
 FATE_H264-$(call FRAMECRC, MOV H264, H264, H264_PARSER H264_MUXER 
H264_MP4TOANNEXB_BSF H264_METADATA_BSF SCALE_FILTER) += 
fate-h264-bsf-mp4toannexb-new-extradata
 
+FATE_H264-$(call ALLYES, H264_DEMUXER H264_PARSER H264_MUXER 
H264_METADATA_BSF) += fate-h264-bsf-fix-colour-description
+
 FATE_H264-$(call FRAMECRC, MOV,, H264_MUXER H264_MP4TOANNEXB_BSF) += 
fate-h264-bsf-mp4toannexb \
                                                              
fate-h264-bsf-mp4toannexb-2 \
 
@@ -448,6 +450,12 @@ fate-h264-bsf-mp4toannexb-2:                      CMD = 
md5 -i $(TARGET_SAMPLES)
 fate-h264-bsf-mp4toannexb-2:                      CMP = oneline
 fate-h264-bsf-mp4toannexb-2:                      REF = 
cffcfa6a2d0b58c9de1f5785f099f41d
 fate-h264-bsf-mp4toannexb-new-extradata:          CMD = stream_remux mov 
$(TARGET_SAMPLES)/h264/extradata-reload-multi-stsd.mov "" h264 "-bsf 
h264_mp4toannexb,h264_metadata -map 0:v"
+# Inject reserved colour_primaries and transfer_characteristics plus the 
illegal identity (RGB) matrix on a 4:2:0 stream,
+# then verify that fix_colour_description rewrites all of them back to 
unspecified.
+fate-h264-bsf-fix-colour-description:             CMD = md5 -i 
$(TARGET_SAMPLES)/h264/intra_refresh.h264 -c:v copy \
+                                                        -bsf:v 
h264_metadata=colour_primaries=3:transfer_characteristics=3:matrix_coefficients=0,h264_metadata=fix_colour_description=1
 -f h264
+fate-h264-bsf-fix-colour-description:             CMP = oneline
+fate-h264-bsf-fix-colour-description:             REF = 
d2fb777060d9c1f4f9cc5578879aa79a
 fate-h264-bsf-dts2pts:                            CMD = transcode "h264" 
$(TARGET_SAMPLES)/h264-conformance/CAPAMA3_Sand_F.264 \
                                                         mov "-c:v copy -bsf:v 
dts2pts -frames:v 50" "-c:v copy"
 fate-h264_mp4toannexb_ticket5927:                 CMD = transcode "mp4" 
$(TARGET_SAMPLES)/h264/thezerotheorem-cut.mp4 \
-- 
2.52.0

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

Reply via email to