From 7faa6ee2aa01e3bd747ce6c55e6d44c2ba7b0bb8 Mon Sep 17 00:00:00 2001 From: Vignesh Venkat Date: Tue, 14 Apr 2026 15:12:10 -0700 Subject: [PATCH] libavformat/matroska: Support smpte 2094-50 metadata Add support for parsing and muxing smpte 2094-50 metadata. It will be stored as an ITUT-T35 message in the BlockAdditional element with an AddId type of 4 (which is reserved for ITUT-T35 in the matroska spec). https://www.matroska.org/technical/codec_specs.html#itu-t35-metadata Signed-off-by: Vignesh Venkatasubramanian --- doc/APIchanges | 3 +++ libavcodec/decode.c | 13 +++++++------ libavcodec/packet.c | 1 + libavcodec/packet.h | 7 +++++++ libavcodec/version.h | 4 ++-- libavformat/matroskadec.c | 25 +++++++++++++++++++++++++ libavformat/matroskaenc.c | 24 ++++++++++++++++++++++++ 7 files changed, 69 insertions(+), 8 deletions(-) diff --git a/doc/APIchanges b/doc/APIchanges index c57321cc39..ffa142a078 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -2,6 +2,9 @@ The last version increases of all libraries were on 2025-03-28 API changes, most recent first: +2026-04-xx - xxxxxxxxxx - lavc 60.30.100 - packet.h + Add AV_PKT_DATA_DYNAMIC_HDR_SMPTE_2094_APP5 side data type. + 2026-04-09 - 6ba6db4f19 - lavu 60.30.100 - hdr_dynamic_metadata.h frame.h Add AVDynamicHDRSmpte2094App5 struct and functions. Add AV_FRAME_DATA_DYNAMIC_HDR_SMPTE_2094_APP5 side data type. diff --git a/libavcodec/decode.c b/libavcodec/decode.c index ac654f1f4c..d64f089b82 100644 --- a/libavcodec/decode.c +++ b/libavcodec/decode.c @@ -1542,12 +1542,13 @@ int ff_decode_frame_props_from_pkt(const AVCodecContext *avctx, AVFrame *frame, const AVPacket *pkt) { static const SideDataMap sd[] = { - { AV_PKT_DATA_A53_CC, AV_FRAME_DATA_A53_CC }, - { AV_PKT_DATA_AFD, AV_FRAME_DATA_AFD }, - { AV_PKT_DATA_DYNAMIC_HDR10_PLUS, AV_FRAME_DATA_DYNAMIC_HDR_PLUS }, - { AV_PKT_DATA_S12M_TIMECODE, AV_FRAME_DATA_S12M_TIMECODE }, - { AV_PKT_DATA_SKIP_SAMPLES, AV_FRAME_DATA_SKIP_SAMPLES }, - { AV_PKT_DATA_LCEVC, AV_FRAME_DATA_LCEVC }, + { AV_PKT_DATA_A53_CC, AV_FRAME_DATA_A53_CC }, + { AV_PKT_DATA_AFD, AV_FRAME_DATA_AFD }, + { AV_PKT_DATA_DYNAMIC_HDR10_PLUS, AV_FRAME_DATA_DYNAMIC_HDR_PLUS }, + { AV_PKT_DATA_DYNAMIC_HDR_SMPTE_2094_APP5, AV_FRAME_DATA_DYNAMIC_HDR_SMPTE_2094_APP5 }, + { AV_PKT_DATA_S12M_TIMECODE, AV_FRAME_DATA_S12M_TIMECODE }, + { AV_PKT_DATA_SKIP_SAMPLES, AV_FRAME_DATA_SKIP_SAMPLES }, + { AV_PKT_DATA_LCEVC, AV_FRAME_DATA_LCEVC }, { AV_PKT_DATA_NB } }; diff --git a/libavcodec/packet.c b/libavcodec/packet.c index a2d453a4b3..37defa4a45 100644 --- a/libavcodec/packet.c +++ b/libavcodec/packet.c @@ -301,6 +301,7 @@ const char *av_packet_side_data_name(enum AVPacketSideDataType type) case AV_PKT_DATA_DOVI_CONF: return "DOVI configuration record"; case AV_PKT_DATA_S12M_TIMECODE: return "SMPTE ST 12-1:2014 timecode"; case AV_PKT_DATA_DYNAMIC_HDR10_PLUS: return "HDR10+ Dynamic Metadata (SMPTE 2094-40)"; + case AV_PKT_DATA_DYNAMIC_HDR_SMPTE_2094_APP5:return "HDR Dynamic Metadata (SMPTE 2094-50)"; case AV_PKT_DATA_AMBIENT_VIEWING_ENVIRONMENT:return "Ambient viewing environment"; case AV_PKT_DATA_IAMF_MIX_GAIN_PARAM: return "IAMF Mix Gain Parameter Data"; case AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM: return "IAMF Demixing Info Parameter Data"; diff --git a/libavcodec/packet.h b/libavcodec/packet.h index 59bfddf4cc..d2895bfe5d 100644 --- a/libavcodec/packet.h +++ b/libavcodec/packet.h @@ -368,6 +368,13 @@ enum AVPacketSideDataType { */ AV_PKT_DATA_EXIF, + /** + * HDR dynamic metadata associated with a video frame. The payload is an + * AVDynamicHDRSmpte2094App5 type and contains information for color volume + * transform as specified in the SMPTE 2094-50 standard. + */ + AV_PKT_DATA_DYNAMIC_HDR_SMPTE_2094_APP5, + /** * The number of side data types. * This is not part of the public API/ABI in the sense that it may diff --git a/libavcodec/version.h b/libavcodec/version.h index e60d3c1afc..aeb58e3fed 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -29,8 +29,8 @@ #include "version_major.h" -#define LIBAVCODEC_VERSION_MINOR 29 -#define LIBAVCODEC_VERSION_MICRO 101 +#define LIBAVCODEC_VERSION_MINOR 30 +#define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ LIBAVCODEC_VERSION_MINOR, \ diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c index 1900a93210..de14ff96b8 100644 --- a/libavformat/matroskadec.c +++ b/libavformat/matroskadec.c @@ -3964,6 +3964,31 @@ static int matroska_parse_block_additional(MatroskaDemuxContext *matroska, break; } + case ITU_T_T35_PROVIDER_CODE_SMPTE: { + AVDynamicHDRSmpte2094App5 *hdr_smpte_2094_app5; + size_t hdr_smpte_2094_app5_size; + + provider_oriented_code = bytestream2_get_be16u(&bc); + if (provider_oriented_code != 1) + break; // ignore + + hdr_smpte_2094_app5 = av_dynamic_hdr_smpte2094_app5_alloc(&hdr_smpte_2094_app5_size); + if (!hdr_smpte_2094_app5_size) + return AVERROR(ENOMEM); + + if ((res = av_dynamic_hdr_smpte2094_app5_from_t35(hdr_smpte_2094_app5, + bc.buffer, + bytestream2_get_bytes_left(&bc))) < 0 || + (res = av_packet_add_side_data(pkt, + AV_PKT_DATA_DYNAMIC_HDR_SMPTE_2094_APP5, + (uint8_t *)hdr_smpte_2094_app5, + hdr_smpte_2094_app5_size)) < 0) { + av_free(hdr_smpte_2094_app5); + return res; + } + + break; + } default: break; } diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index cb19584b2b..a3b45257db 100644 --- a/libavformat/matroskaenc.c +++ b/libavformat/matroskaenc.c @@ -2939,6 +2939,30 @@ static int mkv_write_block(void *logctx, MatroskaMuxContext *mkv, track->max_blockaddid = FFMAX(track->max_blockaddid, MATROSKA_BLOCK_ADD_ID_ITU_T_T35); } + side_data = av_packet_get_side_data(pkt, + AV_PKT_DATA_DYNAMIC_HDR_SMPTE_2094_APP5, + &side_data_size); + if (side_data && side_data_size) { + // The maximum possible payload size for SMPTE_2094_APP5 metadata is + // 855 bytes. So it will fit into the t35_buf which is of size + // 6 + AV_HDR_PLUS_MAX_PAYLOAD_SIZE = 913. + uint8_t *payload = t35_buf; + size_t payload_size = sizeof(t35_buf) - 5; + + bytestream_put_byte(&payload, ITU_T_T35_COUNTRY_CODE_US); + bytestream_put_be16(&payload, ITU_T_T35_PROVIDER_CODE_SMPTE); + bytestream_put_be16(&payload, 0x01); // provider_oriented_code + + ret = av_dynamic_hdr_smpte2094_app5_to_t35((AVDynamicHDRSmpte2094App5 *)side_data, + &payload, &payload_size); + if (ret < 0) + return ret; + + mkv_write_blockadditional(&writer, t35_buf, payload_size + 5, + MATROSKA_BLOCK_ADD_ID_ITU_T_T35); + track->max_blockaddid = FFMAX(track->max_blockaddid, + MATROSKA_BLOCK_ADD_ID_ITU_T_T35); + } } ebml_writer_close_or_discard_master(&writer);