This is an automated email from the git hooks/post-receive script.
Git pushed a commit to branch master
in repository ffmpeg.
The following commit(s) were added to refs/heads/master by this push:
new 1d65e985b3 fftools/ffmpeg_demux: add options to override mastering
display and content light level metadata
1d65e985b3 is described below
commit 1d65e985b338fb0e65b768407df6ff4bcbf5d66e
Author: James Almer <[email protected]>
AuthorDate: Thu Mar 12 15:47:22 2026 -0300
Commit: James Almer <[email protected]>
CommitDate: Sun Mar 15 17:52:05 2026 -0300
fftools/ffmpeg_demux: add options to override mastering display and content
light level metadata
Signed-off-by: James Almer <[email protected]>
---
doc/ffmpeg.texi | 21 +++++++
fftools/ffmpeg.h | 2 +
fftools/ffmpeg_demux.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++--
fftools/ffmpeg_opt.c | 6 ++
4 files changed, 178 insertions(+), 6 deletions(-)
diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
index 2dae6632bc..533131adfc 100644
--- a/doc/ffmpeg.texi
+++ b/doc/ffmpeg.texi
@@ -1569,6 +1569,27 @@ Set whether on display the image should be vertically
flipped.
See the @code{-display_rotation} option for more details.
+@item -mastering_display[:@var{stream_specifier}]
@var{G(%u,%u)B(%u,%u)R(%u,%u)WP(%u,%u)L(%u,%u)} (@emph{input,per-stream})
+Set video mastering display metadata.
+
+@var{G(%u,%u)B(%u,%u)R(%u,%u)WP(%u,%u)L(%u,%u)} is a string specifying
+X,Y display primaries for GBR channels and white point (WP) in units of
+0.00002, and max-min luminance (L) values in units of 0.0001 candela per
+meter square. The values are unsigned integers representing the numerator
+of a rational with an implicit denominator of 50000 for GBR and (WP), and
+implicit denominator 10000 for (L).
+
+This option overrides the mastering display metadata stored in the file,
+if any.
+
+@item -content_light[:@var{stream_specifier}] @var{%u,%u}
(@emph{input,per-stream})
+Set video content light metadata.
+
+@var{%u,%u} is a string specifying max content light level and maximum picture
+average light level.
+
+This option overrides the content light metadata stored in the file, if any.
+
@item -vn (@emph{input/output})
As an input option, blocks all video streams of a file from being filtered or
being automatically selected or mapped for any output. See @code{-discard}
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index b4d3a2c2ac..3a19e5878d 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -218,6 +218,8 @@ typedef struct OptionsContext {
SpecifierOptList display_rotations;
SpecifierOptList display_hflips;
SpecifierOptList display_vflips;
+ SpecifierOptList mastering_displays;
+ SpecifierOptList content_lights;
SpecifierOptList rc_overrides;
SpecifierOptList intra_matrices;
SpecifierOptList inter_matrices;
diff --git a/fftools/ffmpeg_demux.c b/fftools/ffmpeg_demux.c
index 7c708ff0f3..c317987841 100644
--- a/fftools/ffmpeg_demux.c
+++ b/fftools/ffmpeg_demux.c
@@ -28,6 +28,7 @@
#include "libavutil/display.h"
#include "libavutil/error.h"
#include "libavutil/intreadwrite.h"
+#include "libavutil/mastering_display_metadata.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
@@ -68,6 +69,8 @@ typedef struct DemuxStream {
int autorotate;
int apply_cropping;
int force_display_matrix;
+ int force_mastering_display;
+ int force_content_light;
int drop_changed;
@@ -1248,6 +1251,125 @@ static int add_display_matrix_to_stream(const
OptionsContext *o,
return 0;
}
+static int add_mastering_display_to_stream(const OptionsContext *o,
+ AVFormatContext *ctx, InputStream
*ist)
+{
+ AVStream *st = ist->st;
+ DemuxStream *ds = ds_from_ist(ist);
+ AVMasteringDisplayMetadata *master_display;
+ AVPacketSideData *sd;
+ const char *p = NULL;
+ const int chroma_den = 50000;
+ const int luma_den = 10000;
+ size_t size;
+ int ret;
+
+ opt_match_per_stream_str(ist, &o->mastering_displays, ctx, st, &p);
+
+ if (!p)
+ return 0;
+
+ master_display = av_mastering_display_metadata_alloc_size(&size);
+ if (!master_display)
+ return AVERROR(ENOMEM);
+
+ ret = sscanf(p,
+ "G(%u,%u)B(%u,%u)R(%u,%u)WP(%u,%u)L(%u,%u)",
+ (unsigned*)&master_display->display_primaries[1][0].num,
+ (unsigned*)&master_display->display_primaries[1][1].num,
+ (unsigned*)&master_display->display_primaries[2][0].num,
+ (unsigned*)&master_display->display_primaries[2][1].num,
+ (unsigned*)&master_display->display_primaries[0][0].num,
+ (unsigned*)&master_display->display_primaries[0][1].num,
+ (unsigned*)&master_display->white_point[0].num,
+ (unsigned*)&master_display->white_point[1].num,
+ (unsigned*)&master_display->max_luminance.num,
+ (unsigned*)&master_display->min_luminance.num);
+
+ if (ret != 10 ||
+ (unsigned)(master_display->display_primaries[1][0].num |
master_display->display_primaries[1][1].num |
+ master_display->display_primaries[2][0].num |
master_display->display_primaries[2][1].num |
+ master_display->display_primaries[0][0].num |
master_display->display_primaries[0][1].num |
+ master_display->white_point[0].num |
master_display->white_point[1].num) > UINT16_MAX ||
+ (unsigned)(master_display->max_luminance.num |
master_display->min_luminance.num) > INT_MAX ||
+ master_display->min_luminance.num >
master_display->max_luminance.num) {
+ av_freep(&master_display);
+ av_log(ist, AV_LOG_ERROR, "Failed to parse mastering display
option\n");
+ return AVERROR(EINVAL);
+ }
+
+ master_display->display_primaries[1][0].den = chroma_den;
+ master_display->display_primaries[1][1].den = chroma_den;
+ master_display->display_primaries[2][0].den = chroma_den;
+ master_display->display_primaries[2][1].den = chroma_den;
+ master_display->display_primaries[0][0].den = chroma_den;
+ master_display->display_primaries[0][1].den = chroma_den;
+ master_display->white_point[0].den = chroma_den;
+ master_display->white_point[1].den = chroma_den;
+ master_display->max_luminance.den = luma_den;
+ master_display->min_luminance.den = luma_den;
+
+ master_display->has_primaries = 1;
+ master_display->has_luminance = 1;
+
+ sd = av_packet_side_data_add(&st->codecpar->coded_side_data,
+ &st->codecpar->nb_coded_side_data,
+ AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
+ (uint8_t *)master_display, size, 0);
+ if (!sd) {
+ av_freep(&master_display);
+ return AVERROR(ENOMEM);
+ }
+
+ ds->force_mastering_display = 1;
+
+ return 0;
+}
+
+static int add_content_light_to_stream(const OptionsContext *o,
+ AVFormatContext *ctx, InputStream *ist)
+{
+ AVStream *st = ist->st;
+ DemuxStream *ds = ds_from_ist(ist);
+ AVContentLightMetadata *cll;
+ AVPacketSideData *sd;
+ const char *p = NULL;
+ size_t size;
+ int ret;
+
+ opt_match_per_stream_str(ist, &o->content_lights, ctx, st, &p);
+
+ if (!p)
+ return 0;
+
+ cll = av_content_light_metadata_alloc(&size);
+ if (!cll)
+ return AVERROR(ENOMEM);
+
+ ret = sscanf(p, "%u,%u",
+ (unsigned*)&cll->MaxCLL,
+ (unsigned*)&cll->MaxFALL);
+
+ if (ret != 2 || (unsigned)(cll->MaxCLL | cll->MaxFALL) > UINT16_MAX) {
+ av_freep(&cll);
+ av_log(ist, AV_LOG_ERROR, "Failed to parse content light option\n");
+ return AVERROR(EINVAL);
+ }
+
+ sd = av_packet_side_data_add(&st->codecpar->coded_side_data,
+ &st->codecpar->nb_coded_side_data,
+ AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
+ (uint8_t *)cll, size, 0);
+ if (!sd) {
+ av_freep(&cll);
+ return AVERROR(ENOMEM);
+ }
+
+ ds->force_content_light = 1;
+
+ return 0;
+}
+
static const char *input_stream_item_name(void *obj)
{
const DemuxStream *ds = obj;
@@ -1301,6 +1423,7 @@ static int ist_add(const OptionsContext *o, Demuxer *d,
AVStream *st, AVDictiona
const char *bsfs = NULL;
char *next;
const char *discard_str = NULL;
+ AVBPrint bp;
int ret;
ds = demux_stream_alloc(d, st);
@@ -1366,6 +1489,14 @@ static int ist_add(const OptionsContext *o, Demuxer *d,
AVStream *st, AVDictiona
if (ret < 0)
return ret;
+ ret = add_mastering_display_to_stream(o, ic, ist);
+ if (ret < 0)
+ return ret;
+
+ ret = add_content_light_to_stream(o, ic, ist);
+ if (ret < 0)
+ return ret;
+
opt_match_per_stream_str(ist, &o->hwaccels, ic, st, &hwaccel);
opt_match_per_stream_str(ist, &o->hwaccel_output_formats, ic, st,
&hwaccel_output_format);
@@ -1483,15 +1614,27 @@ static int ist_add(const OptionsContext *o, Demuxer *d,
AVStream *st, AVDictiona
av_dict_set_int(&ds->decoder_opts, "apply_cropping",
ds->apply_cropping && ds->apply_cropping !=
CROP_CONTAINER, 0);
+ av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
if (ds->force_display_matrix) {
- char buf[32];
if (av_dict_get(ds->decoder_opts, "side_data_prefer_packet", NULL, 0))
- buf[0] = ',';
- else
- buf[0] = '\0';
- av_strlcat(buf, "displaymatrix", sizeof(buf));
- av_dict_set(&ds->decoder_opts, "side_data_prefer_packet", buf,
AV_DICT_APPEND);
+ av_bprintf(&bp, ",");
+ av_bprintf(&bp, "displaymatrix");
+ }
+ if (ds->force_mastering_display) {
+ if (bp.len || av_dict_get(ds->decoder_opts, "side_data_prefer_packet",
NULL, 0))
+ av_bprintf(&bp, ",");
+ av_bprintf(&bp, "mastering_display_metadata");
}
+ if (ds->force_content_light) {
+ if (bp.len || av_dict_get(ds->decoder_opts, "side_data_prefer_packet",
NULL, 0))
+ av_bprintf(&bp, ",");
+ av_bprintf(&bp, "content_light_level");
+ }
+ if (bp.len) {
+ av_bprint_finalize(&bp, NULL);
+ av_dict_set(&ds->decoder_opts, "side_data_prefer_packet", bp.str,
AV_DICT_APPEND);
+ }
+
/* Attached pics are sparse, therefore we would not want to delay their
decoding
* till EOF. */
if (ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC)
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index a0664e2964..48e6816c19 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -1940,6 +1940,12 @@ const OptionDef options[] = {
{ .off = OFFSET(display_vflips) },
"set display vertical flip for stream(s) "
"(overrides any display rotation if it is not set)"},
+ { "mastering_display", OPT_TYPE_STRING, OPT_VIDEO | OPT_PERSTREAM
| OPT_INPUT | OPT_EXPERT,
+ { .off = OFFSET(mastering_displays) },
+ "set SMPTE2084 mastering display color volume info" },
+ { "content_light", OPT_TYPE_STRING, OPT_VIDEO | OPT_PERSTREAM
| OPT_INPUT | OPT_EXPERT,
+ { .off = OFFSET(content_lights) },
+ "set SMPTE2084 Max CLL and Max FALL values" },
{ "vn", OPT_TYPE_BOOL, OPT_VIDEO | OPT_OFFSET |
OPT_INPUT | OPT_OUTPUT,
{ .off = OFFSET(video_disable) },
"disable video" },
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]