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]
