diff mbox series

[FFmpeg-devel] avcodec/options: Add options for inputing HDR10-related metadata

Message ID 8A44004B-877D-4D63-94E2-040843E30445@me.com
State Superseded
Headers show
Series [FFmpeg-devel] avcodec/options: Add options for inputing HDR10-related metadata
Related show

Checks

Context Check Description
andriy/default pending
andriy/make_warn warning New warnings during build
andriy/make success Make finished
andriy/make_fate fail Make fate failed

Commit Message

Kenny McClive Aug. 11, 2020, 1:36 a.m. UTC
Previously, the only way to input the master display and content light metadata required for HDR10 was through x265-params. Obviously, that only worked with x265. If you wanted to use a different encoder like nvenc, you were out of luck. The options specified are written to the container (mov or matroska) only. Additional work would be required to write it to frames. The default values for the master display options may seem unorthodox, but it allows you to differentiate between 0 (which some movies use) and unspecified. The same was not done for content light metadata because 0 seems to mean unspecified in other systems such as x265.

Signed-off-by: Kenny McClive <kenny.mcclive@me.com>
---
 libavcodec/avcodec.h       |  84 +++++++++++++++++++++++
 libavcodec/codec_par.h     |   7 ++
 libavcodec/options_table.h |  12 ++++
 libavcodec/utils.c         | 133 +++++++++++++++++++++++++++++++++++++
 libavformat/matroskaenc.c  |  62 +++++++++++------
 libavformat/movenc.c       |  29 ++++++--
 6 files changed, 298 insertions(+), 29 deletions(-)

Comments

Kenny McClive Aug. 11, 2020, 1:42 a.m. UTC | #1
Sorry, please disregard this patch.  I just realized that my test changes are not in it.

Kenny

> On Aug 10, 2020, at 7:36 PM, Kenny McClive <kenny.mcclive@me.com> wrote:
> 
> Previously, the only way to input the master display and content light metadata required for HDR10 was through x265-params. Obviously, that only worked with x265. If you wanted to use a different encoder like nvenc, you were out of luck. The options specified are written to the container (mov or matroska) only. Additional work would be required to write it to frames. The default values for the master display options may seem unorthodox, but it allows you to differentiate between 0 (which some movies use) and unspecified. The same was not done for content light metadata because 0 seems to mean unspecified in other systems such as x265.
> 
> Signed-off-by: Kenny McClive <kenny.mcclive@me.com>
> ---
> libavcodec/avcodec.h       |  84 +++++++++++++++++++++++
> libavcodec/codec_par.h     |   7 ++
> libavcodec/options_table.h |  12 ++++
> libavcodec/utils.c         | 133 +++++++++++++++++++++++++++++++++++++
> libavformat/matroskaenc.c  |  62 +++++++++++------
> libavformat/movenc.c       |  29 ++++++--
> 6 files changed, 298 insertions(+), 29 deletions(-)
> 
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index c91b2fd169..24f97345b7 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -2352,6 +2352,90 @@ typedef struct AVCodecContext {
>      * - encoding: set by user
>      */
>     int export_side_data;
> +
> +    /**
> +     * CIE 1931 x chromaticity coord of red color primary for mastering display.
> +     * - encoding: Set by user
> +     * - decoding: unused
> +     */
> +    AVRational master_display_red_x;
> +
> +    /**
> +     * CIE 1931 y chromaticity coord of red color primary for mastering display.
> +     * - encoding: Set by user
> +     * - decoding: unused
> +     */
> +    AVRational master_display_red_y;
> +
> +    /**
> +     * CIE 1931 x chromaticity coord of green color primary for mastering display.
> +     * - encoding: Set by user
> +     * - decoding: unused
> +     */
> +    AVRational master_display_green_x;
> +
> +    /**
> +     * CIE 1931 y chromaticity coord of green color primary for mastering display.
> +     * - encoding: Set by user
> +     * - decoding: unused
> +     */
> +    AVRational master_display_green_y;
> +
> +    /**
> +     * CIE 1931 x chromaticity coord of blue color primary for mastering display.
> +     * - encoding: Set by user
> +     * - decoding: unused
> +     */
> +    AVRational master_display_blue_x;
> +
> +    /**
> +     * CIE 1931 y chromaticity coord of blue color primary for mastering display.
> +     * - encoding: Set by user
> +     * - decoding: unused
> +     */
> +    AVRational master_display_blue_y;
> +
> +    /**
> +     * CIE 1931 x chromaticity coord of white point for mastering display.
> +     * - encoding: Set by user
> +     * - decoding: unused
> +     */
> +    AVRational master_display_white_x;
> +
> +    /**
> +     * CIE 1931 y chromaticity coord of white point for mastering display.
> +     * - encoding: Set by user
> +     * - decoding: unused
> +     */
> +    AVRational master_display_white_y;
> +
> +    /**
> +     * Min luminance of mastering display (cd/m^2).
> +     * - encoding: Set by user
> +     * - decoding: unused
> +     */
> +    AVRational master_display_min_luminance;
> +
> +    /**
> +     * Max luminance of mastering display (cd/m^2).
> +     * - encoding: Set by user
> +     * - decoding: unused
> +     */
> +    AVRational master_display_max_luminance;
> +
> +    /**
> +     * Max content light level (cd/m^2).
> +     * - encoding: Set by user
> +     * - decoding: unused
> +     */
> +    unsigned max_cll;
> +
> +    /**
> +     * Max average light level per frame (cd/m^2).
> +     * - encoding: Set by user
> +     * - decoding: unused
> +     */
> +    unsigned max_fall;
> } AVCodecContext;
> 
> #if FF_API_CODEC_GET_SET
> diff --git a/libavcodec/codec_par.h b/libavcodec/codec_par.h
> index 948758e237..fe1fe3861b 100644
> --- a/libavcodec/codec_par.h
> +++ b/libavcodec/codec_par.h
> @@ -24,6 +24,7 @@
> #include <stdint.h>
> 
> #include "libavutil/avutil.h"
> +#include "libavutil/mastering_display_metadata.h"
> #include "libavutil/rational.h"
> #include "libavutil/pixfmt.h"
> 
> @@ -149,6 +150,12 @@ typedef struct AVCodecParameters {
>     enum AVColorSpace                  color_space;
>     enum AVChromaLocation              chroma_location;
> 
> +    /**
> +     * Video only. HDR metadata.
> +     */
> +    AVMasteringDisplayMetadata master_display_metadata;
> +    AVContentLightMetadata     content_light_metadata;
> +
>     /**
>      * Video only. Number of delayed frames.
>      */
> diff --git a/libavcodec/options_table.h b/libavcodec/options_table.h
> index 1d0db1b5a4..680d05469a 100644
> --- a/libavcodec/options_table.h
> +++ b/libavcodec/options_table.h
> @@ -412,6 +412,18 @@ static const AVOption avcodec_options[] = {
> {"bottomleft",  "Bottom-left", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_BOTTOMLEFT },  INT_MIN, INT_MAX, V|E|D, "chroma_sample_location_type"},
> {"bottom",      "Bottom",      0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_BOTTOM },      INT_MIN, INT_MAX, V|E|D, "chroma_sample_location_type"},
> {"unspecified", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, "chroma_sample_location_type"},
> +{"master_display_red_x", "mastering display x chromaticity coord for red color primary", OFFSET(master_display_red_x), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
> +{"master_display_red_y", "mastering display y chromaticity coord for red color primary", OFFSET(master_display_red_y), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
> +{"master_display_green_x", "mastering display x chromaticity coord for green color primary", OFFSET(master_display_green_x), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
> +{"master_display_green_y", "mastering display y chromaticity coord for green color primary", OFFSET(master_display_green_y), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
> +{"master_display_blue_x", "mastering display x chromaticity coord for blue color primary", OFFSET(master_display_blue_x), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
> +{"master_display_blue_y", "mastering display y chromaticity coord for blue color primary", OFFSET(master_display_blue_y), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
> +{"master_display_white_x", "mastering display x chromaticity coord for white point", OFFSET(master_display_white_x), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
> +{"master_display_white_y", "mastering display y chromaticity coord for white point", OFFSET(master_display_white_y), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
> +{"master_display_min_lum", "mastering display min luminance", OFFSET(master_display_min_luminance), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
> +{"master_display_max_lum", "mastering display max luminance", OFFSET(master_display_max_luminance), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
> +{"max_cll", "max content light level", OFFSET(max_cll), AV_OPT_TYPE_INT64, {.i64 = DEFAULT }, 0, INT_MAX, V|E},
> +{"max_fall", "max frame average light level", OFFSET(max_fall), AV_OPT_TYPE_INT64, {.i64 = DEFAULT }, 0, INT_MAX, V|E},
> {"log_level_offset", "set the log level offset", OFFSET(log_level_offset), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX },
> {"slices", "set the number of slices, used in parallelized encoding", OFFSET(slices), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, V|E},
> {"thread_type", "select multithreading type", OFFSET(thread_type), AV_OPT_TYPE_FLAGS, {.i64 = FF_THREAD_SLICE|FF_THREAD_FRAME }, 0, INT_MAX, V|A|E|D, "thread_type"},
> diff --git a/libavcodec/utils.c b/libavcodec/utils.c
> index 5a2a90b030..b31041a755 100644
> --- a/libavcodec/utils.c
> +++ b/libavcodec/utils.c
> @@ -35,6 +35,7 @@
> #include "libavutil/frame.h"
> #include "libavutil/hwcontext.h"
> #include "libavutil/internal.h"
> +#include "libavutil/mastering_display_metadata.h"
> #include "libavutil/mathematics.h"
> #include "libavutil/mem_internal.h"
> #include "libavutil/pixdesc.h"
> @@ -67,6 +68,7 @@
> const char av_codec_ffversion[] = "FFmpeg version " FFMPEG_VERSION;
> 
> static AVMutex codec_mutex = AV_MUTEX_INITIALIZER;
> +static int unspecified_coord = INT_MAX; 
> 
> void av_fast_padded_malloc(void *ptr, unsigned int *size, size_t min_size)
> {
> @@ -2031,6 +2033,34 @@ AVCPBProperties *ff_add_cpb_side_data(AVCodecContext *avctx)
>     return props;
> }
> 
> +static void master_display_metadata_reset(AVMasteringDisplayMetadata *metadata)
> +{
> +    if (!metadata)
> +        return;
> +
> +    metadata->display_primaries[0][0] = (AVRational){ 0, 1 };
> +    metadata->display_primaries[0][1] = (AVRational){ 0, 1 };
> +    metadata->display_primaries[1][0] = (AVRational){ 0, 1 };
> +    metadata->display_primaries[1][1] = (AVRational){ 0, 1 };
> +    metadata->display_primaries[2][0] = (AVRational){ 0, 1 };
> +    metadata->display_primaries[2][1] = (AVRational){ 0, 1 };
> +    metadata->white_point[0] = (AVRational){ 0, 1 };
> +    metadata->white_point[1] = (AVRational){ 0, 1 };
> +    metadata->has_primaries = 0;
> +    metadata->min_luminance = (AVRational){ 0, 1 };
> +    metadata->max_luminance = (AVRational){ 0, 1 };
> +    metadata->has_luminance = 0;
> +}
> +
> +static void content_light_metadata_reset(AVContentLightMetadata *metadata)
> +{
> +    if (!metadata)
> +        return;
> +
> +    metadata->MaxCLL = 0;
> +    metadata->MaxFALL = 0;
> +}
> +
> static void codec_parameters_reset(AVCodecParameters *par)
> {
>     av_freep(&par->extradata);
> @@ -2049,6 +2079,9 @@ static void codec_parameters_reset(AVCodecParameters *par)
>     par->sample_aspect_ratio = (AVRational){ 0, 1 };
>     par->profile             = FF_PROFILE_UNKNOWN;
>     par->level               = FF_LEVEL_UNKNOWN;
> +
> +    master_display_metadata_reset(&par->master_display_metadata);
> +    content_light_metadata_reset(&par->content_light_metadata);
> }
> 
> AVCodecParameters *avcodec_parameters_alloc(void)
> @@ -2090,6 +2123,53 @@ int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src
>     return 0;
> }
> 
> +static void mastering_display_metadata_from_context(AVMasteringDisplayMetadata *metadata,
> +                                                    const AVCodecContext *codec)
> +{
> +    if (!metadata || !codec)
> +        return;
> +    
> +    master_display_metadata_reset(metadata);
> +    
> +    if (codec->master_display_red_x.num != unspecified_coord &&
> +        codec->master_display_red_y.num != unspecified_coord &&
> +        codec->master_display_green_x.num != unspecified_coord &&
> +        codec->master_display_green_y.num != unspecified_coord &&
> +        codec->master_display_blue_x.num != unspecified_coord &&
> +        codec->master_display_blue_y.num != unspecified_coord &&
> +        codec->master_display_white_x.num != unspecified_coord &&
> +        codec->master_display_white_y.num != unspecified_coord)
> +    {
> +        metadata->display_primaries[0][0] = codec->master_display_red_x;
> +        metadata->display_primaries[0][1] = codec->master_display_red_y;
> +        metadata->display_primaries[1][0] = codec->master_display_green_x;
> +        metadata->display_primaries[1][1] = codec->master_display_green_y;
> +        metadata->display_primaries[2][0] = codec->master_display_blue_x;
> +        metadata->display_primaries[2][1] = codec->master_display_blue_y;
> +        metadata->white_point[0] = codec->master_display_white_x;
> +        metadata->white_point[1] = codec->master_display_white_y;
> +        metadata->has_primaries = 1;
> +    }
> +
> +    if (codec->master_display_min_luminance.den > 0 &&
> +        codec->master_display_max_luminance.den > 0)
> +    {
> +        metadata->min_luminance = codec->master_display_min_luminance;
> +        metadata->max_luminance = codec->master_display_max_luminance;
> +        metadata->has_luminance = 1;
> +    }
> +}
> +
> +static void content_light_metadata_from_context(AVContentLightMetadata *metadata,
> +                                                const AVCodecContext *codec)
> +{
> +    if (!metadata || !codec)
> +        return;
> +    
> +    metadata->MaxCLL = codec->max_cll;
> +    metadata->MaxFALL = codec->max_fall;
> +}
> +
> int avcodec_parameters_from_context(AVCodecParameters *par,
>                                     const AVCodecContext *codec)
> {
> @@ -2118,6 +2198,9 @@ int avcodec_parameters_from_context(AVCodecParameters *par,
>         par->chroma_location     = codec->chroma_sample_location;
>         par->sample_aspect_ratio = codec->sample_aspect_ratio;
>         par->video_delay         = codec->has_b_frames;
> +
> +        mastering_display_metadata_from_context(&par->master_display_metadata, codec);
> +        content_light_metadata_from_context(&par->content_light_metadata, codec);
>         break;
>     case AVMEDIA_TYPE_AUDIO:
>         par->format           = codec->sample_fmt;
> @@ -2147,6 +2230,53 @@ int avcodec_parameters_from_context(AVCodecParameters *par,
>     return 0;
> }
> 
> +static void mastering_display_metadata_to_context(AVCodecContext *codec,
> +                                                  const AVMasteringDisplayMetadata *metadata)
> +{
> +    if (!codec || !metadata)
> +        return;
> +    
> +    if (metadata->has_primaries)
> +    {
> +        codec->master_display_red_x = metadata->display_primaries[0][0];
> +        codec->master_display_red_y = metadata->display_primaries[0][1];
> +        codec->master_display_green_x = metadata->display_primaries[1][0];
> +        codec->master_display_green_y = metadata->display_primaries[1][1];
> +        codec->master_display_blue_x = metadata->display_primaries[2][0];
> +        codec->master_display_blue_y = metadata->display_primaries[2][1];
> +        codec->master_display_white_x = metadata->white_point[0];
> +        codec->master_display_white_y = metadata->white_point[1];
> +    } else {
> +        codec->master_display_red_x = (AVRational){ unspecified_coord, 1 };
> +        codec->master_display_red_y = (AVRational){ unspecified_coord, 1 };
> +        codec->master_display_green_x = (AVRational){ unspecified_coord, 1 };
> +        codec->master_display_green_y = (AVRational){ unspecified_coord, 1 };
> +        codec->master_display_blue_x = (AVRational){ unspecified_coord, 1 };
> +        codec->master_display_blue_y = (AVRational){ unspecified_coord, 1 };
> +        codec->master_display_white_x = (AVRational){ unspecified_coord, 1 };
> +        codec->master_display_white_y = (AVRational){ unspecified_coord, 1 };
> +    }
> +
> +    if (metadata->has_luminance)
> +    {
> +        codec->master_display_min_luminance = metadata->min_luminance;
> +        codec->master_display_max_luminance = metadata->max_luminance;
> +    } else {
> +        codec->master_display_min_luminance = (AVRational){ unspecified_coord, 1 };
> +        codec->master_display_max_luminance = (AVRational){ unspecified_coord, 1 };
> +    }
> +}
> +
> +static void content_light_metadata_to_context(AVCodecContext *codec,
> +                                              const AVContentLightMetadata *metadata)
> +{
> +    if (!codec || !metadata)
> +        return;
> +    
> +    codec->max_cll = metadata->MaxCLL;
> +    codec->max_fall = metadata->MaxFALL;
> +}
> +
> int avcodec_parameters_to_context(AVCodecContext *codec,
>                                   const AVCodecParameters *par)
> {
> @@ -2173,6 +2303,9 @@ int avcodec_parameters_to_context(AVCodecContext *codec,
>         codec->chroma_sample_location = par->chroma_location;
>         codec->sample_aspect_ratio    = par->sample_aspect_ratio;
>         codec->has_b_frames           = par->video_delay;
> +
> +        mastering_display_metadata_to_context(codec, &par->master_display_metadata);
> +        content_light_metadata_to_context(codec, &par->content_light_metadata);
>         break;
>     case AVMEDIA_TYPE_AUDIO:
>         codec->sample_fmt       = par->format;
> diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
> index 233c472b8f..dfe9d45f6f 100644
> --- a/libavformat/matroskaenc.c
> +++ b/libavformat/matroskaenc.c
> @@ -869,43 +869,61 @@ static void mkv_write_video_color(AVIOContext *pb, const AVStream *st,
>         put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORCHROMASITINGVERT, (ypos >> 7) + 1);
>     }
> 
> -    side_data = av_stream_get_side_data(st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
> -                                        NULL);
> -    if (side_data) {
> -        const AVContentLightMetadata *metadata = side_data;
> -        put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORMAXCLL,  metadata->MaxCLL);
> -        put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORMAXFALL, metadata->MaxFALL);
> +    const AVContentLightMetadata *content_light_metadata = &par->content_light_metadata;
> +
> +    if (content_light_metadata->MaxCLL <= 0 || content_light_metadata->MaxFALL <= 0) {
> +        side_data = av_stream_get_side_data(st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
> +                                            NULL);
> +        if (side_data) {
> +            content_light_metadata = side_data;
> +        } else {
> +            content_light_metadata = NULL;
> +        }
> +    }
> +    
> +    if (content_light_metadata) {
> +        put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORMAXCLL,  content_light_metadata->MaxCLL);
> +        put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORMAXFALL, content_light_metadata->MaxFALL);
>     }
> 
> -    side_data = av_stream_get_side_data(st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
> -                                        NULL);
> -    if (side_data) {
> +    const AVMasteringDisplayMetadata *master_display_metadata = &par->master_display_metadata;
> +
> +    if (!master_display_metadata->has_primaries || !master_display_metadata->has_luminance) {
> +        side_data = av_stream_get_side_data(st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
> +                                            NULL);
> +        if (side_data) {
> +            master_display_metadata = side_data;
> +        } else {
> +            master_display_metadata = NULL;
> +        }
> +    }
> +    
> +    if (master_display_metadata) {
>         ebml_master meta_element = start_ebml_master(
>             dyn_cp, MATROSKA_ID_VIDEOCOLORMASTERINGMETA, 10 * (2 + 1 + 8));
> -        const AVMasteringDisplayMetadata *metadata = side_data;
> -        if (metadata->has_primaries) {
> +        if (master_display_metadata->has_primaries) {
>             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_RX,
> -                           av_q2d(metadata->display_primaries[0][0]));
> +                           av_q2d(master_display_metadata->display_primaries[0][0]));
>             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_RY,
> -                           av_q2d(metadata->display_primaries[0][1]));
> +                           av_q2d(master_display_metadata->display_primaries[0][1]));
>             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_GX,
> -                           av_q2d(metadata->display_primaries[1][0]));
> +                           av_q2d(master_display_metadata->display_primaries[1][0]));
>             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_GY,
> -                           av_q2d(metadata->display_primaries[1][1]));
> +                           av_q2d(master_display_metadata->display_primaries[1][1]));
>             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_BX,
> -                           av_q2d(metadata->display_primaries[2][0]));
> +                           av_q2d(master_display_metadata->display_primaries[2][0]));
>             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_BY,
> -                           av_q2d(metadata->display_primaries[2][1]));
> +                           av_q2d(master_display_metadata->display_primaries[2][1]));
>             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_WHITEX,
> -                           av_q2d(metadata->white_point[0]));
> +                           av_q2d(master_display_metadata->white_point[0]));
>             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_WHITEY,
> -                           av_q2d(metadata->white_point[1]));
> +                           av_q2d(master_display_metadata->white_point[1]));
>         }
> -        if (metadata->has_luminance) {
> +        if (master_display_metadata->has_luminance) {
>             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_LUMINANCEMAX,
> -                           av_q2d(metadata->max_luminance));
> +                           av_q2d(master_display_metadata->max_luminance));
>             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_LUMINANCEMIN,
> -                           av_q2d(metadata->min_luminance));
> +                           av_q2d(master_display_metadata->min_luminance));
>         }
>         end_ebml_master(dyn_cp, meta_element);
>     }
> diff --git a/libavformat/movenc.c b/libavformat/movenc.c
> index 7db2e28840..074c463cee 100644
> --- a/libavformat/movenc.c
> +++ b/libavformat/movenc.c
> @@ -1947,14 +1947,21 @@ static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track, int prefer_icc)
> static int mov_write_clli_tag(AVIOContext *pb, MOVTrack *track)
> {
>     const uint8_t *side_data;
> -    const AVContentLightMetadata *content_light_metadata;
> +    const AVContentLightMetadata *content_light_metadata = &track->par->content_light_metadata;
> 
> -    side_data = av_stream_get_side_data(track->st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, NULL);
> -    if (!side_data) {
> +    if (content_light_metadata->MaxCLL <= 0 || content_light_metadata->MaxFALL <= 0) {
> +        side_data = av_stream_get_side_data(track->st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, NULL);
> +        if (side_data) {
> +            content_light_metadata = (const AVContentLightMetadata*)side_data;
> +        } else {
> +            content_light_metadata = NULL;
> +        }
> +    }
> +
> +    if (!content_light_metadata) {
>         av_log(NULL, AV_LOG_VERBOSE, "Not writing 'clli' atom. No content light level info.\n");
>         return 0;
>     }
> -    content_light_metadata = (const AVContentLightMetadata*)side_data;
> 
>     avio_wb32(pb, 12); // size
>     ffio_wfourcc(pb, "clli");
> @@ -1973,10 +1980,18 @@ static int mov_write_mdcv_tag(AVIOContext *pb, MOVTrack *track)
>     const int chroma_den = 50000;
>     const int luma_den = 10000;
>     const uint8_t *side_data;
> -    const AVMasteringDisplayMetadata *metadata;
> +    const AVMasteringDisplayMetadata *metadata = &track->par->master_display_metadata;
> 
> -    side_data = av_stream_get_side_data(track->st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, NULL);
> -    metadata = (const AVMasteringDisplayMetadata*)side_data;
> +    if (!metadata->has_primaries || !metadata->has_luminance) {
> +        side_data = av_stream_get_side_data(track->st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, NULL);
> +
> +        if (side_data) {
> +            metadata = (const AVMasteringDisplayMetadata*)side_data;
> +        } else {
> +            metadata = NULL;
> +        }
> +    }
> +    
>     if (!metadata || !metadata->has_primaries || !metadata->has_luminance) {
>         av_log(NULL, AV_LOG_VERBOSE, "Not writing 'mdcv' atom. Missing mastering metadata.\n");
>         return 0;
> -- 
> 2.28.0
> 
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
diff mbox series

Patch

diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index c91b2fd169..24f97345b7 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -2352,6 +2352,90 @@  typedef struct AVCodecContext {
      * - encoding: set by user
      */
     int export_side_data;
+
+    /**
+     * CIE 1931 x chromaticity coord of red color primary for mastering display.
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    AVRational master_display_red_x;
+
+    /**
+     * CIE 1931 y chromaticity coord of red color primary for mastering display.
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    AVRational master_display_red_y;
+
+    /**
+     * CIE 1931 x chromaticity coord of green color primary for mastering display.
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    AVRational master_display_green_x;
+
+    /**
+     * CIE 1931 y chromaticity coord of green color primary for mastering display.
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    AVRational master_display_green_y;
+
+    /**
+     * CIE 1931 x chromaticity coord of blue color primary for mastering display.
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    AVRational master_display_blue_x;
+
+    /**
+     * CIE 1931 y chromaticity coord of blue color primary for mastering display.
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    AVRational master_display_blue_y;
+
+    /**
+     * CIE 1931 x chromaticity coord of white point for mastering display.
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    AVRational master_display_white_x;
+
+    /**
+     * CIE 1931 y chromaticity coord of white point for mastering display.
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    AVRational master_display_white_y;
+
+    /**
+     * Min luminance of mastering display (cd/m^2).
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    AVRational master_display_min_luminance;
+
+    /**
+     * Max luminance of mastering display (cd/m^2).
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    AVRational master_display_max_luminance;
+
+    /**
+     * Max content light level (cd/m^2).
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    unsigned max_cll;
+
+    /**
+     * Max average light level per frame (cd/m^2).
+     * - encoding: Set by user
+     * - decoding: unused
+     */
+    unsigned max_fall;
 } AVCodecContext;
 
 #if FF_API_CODEC_GET_SET
diff --git a/libavcodec/codec_par.h b/libavcodec/codec_par.h
index 948758e237..fe1fe3861b 100644
--- a/libavcodec/codec_par.h
+++ b/libavcodec/codec_par.h
@@ -24,6 +24,7 @@ 
 #include <stdint.h>
 
 #include "libavutil/avutil.h"
+#include "libavutil/mastering_display_metadata.h"
 #include "libavutil/rational.h"
 #include "libavutil/pixfmt.h"
 
@@ -149,6 +150,12 @@  typedef struct AVCodecParameters {
     enum AVColorSpace                  color_space;
     enum AVChromaLocation              chroma_location;
 
+    /**
+     * Video only. HDR metadata.
+     */
+    AVMasteringDisplayMetadata master_display_metadata;
+    AVContentLightMetadata     content_light_metadata;
+
     /**
      * Video only. Number of delayed frames.
      */
diff --git a/libavcodec/options_table.h b/libavcodec/options_table.h
index 1d0db1b5a4..680d05469a 100644
--- a/libavcodec/options_table.h
+++ b/libavcodec/options_table.h
@@ -412,6 +412,18 @@  static const AVOption avcodec_options[] = {
 {"bottomleft",  "Bottom-left", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_BOTTOMLEFT },  INT_MIN, INT_MAX, V|E|D, "chroma_sample_location_type"},
 {"bottom",      "Bottom",      0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_BOTTOM },      INT_MIN, INT_MAX, V|E|D, "chroma_sample_location_type"},
 {"unspecified", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, "chroma_sample_location_type"},
+{"master_display_red_x", "mastering display x chromaticity coord for red color primary", OFFSET(master_display_red_x), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
+{"master_display_red_y", "mastering display y chromaticity coord for red color primary", OFFSET(master_display_red_y), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
+{"master_display_green_x", "mastering display x chromaticity coord for green color primary", OFFSET(master_display_green_x), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
+{"master_display_green_y", "mastering display y chromaticity coord for green color primary", OFFSET(master_display_green_y), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
+{"master_display_blue_x", "mastering display x chromaticity coord for blue color primary", OFFSET(master_display_blue_x), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
+{"master_display_blue_y", "mastering display y chromaticity coord for blue color primary", OFFSET(master_display_blue_y), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
+{"master_display_white_x", "mastering display x chromaticity coord for white point", OFFSET(master_display_white_x), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
+{"master_display_white_y", "mastering display y chromaticity coord for white point", OFFSET(master_display_white_y), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
+{"master_display_min_lum", "mastering display min luminance", OFFSET(master_display_min_luminance), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
+{"master_display_max_lum", "mastering display max luminance", OFFSET(master_display_max_luminance), AV_OPT_TYPE_RATIONAL, {.dbl = INT_MAX}, 0, INT_MAX, V|E},
+{"max_cll", "max content light level", OFFSET(max_cll), AV_OPT_TYPE_INT64, {.i64 = DEFAULT }, 0, INT_MAX, V|E},
+{"max_fall", "max frame average light level", OFFSET(max_fall), AV_OPT_TYPE_INT64, {.i64 = DEFAULT }, 0, INT_MAX, V|E},
 {"log_level_offset", "set the log level offset", OFFSET(log_level_offset), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX },
 {"slices", "set the number of slices, used in parallelized encoding", OFFSET(slices), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, V|E},
 {"thread_type", "select multithreading type", OFFSET(thread_type), AV_OPT_TYPE_FLAGS, {.i64 = FF_THREAD_SLICE|FF_THREAD_FRAME }, 0, INT_MAX, V|A|E|D, "thread_type"},
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index 5a2a90b030..b31041a755 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -35,6 +35,7 @@ 
 #include "libavutil/frame.h"
 #include "libavutil/hwcontext.h"
 #include "libavutil/internal.h"
+#include "libavutil/mastering_display_metadata.h"
 #include "libavutil/mathematics.h"
 #include "libavutil/mem_internal.h"
 #include "libavutil/pixdesc.h"
@@ -67,6 +68,7 @@ 
 const char av_codec_ffversion[] = "FFmpeg version " FFMPEG_VERSION;
 
 static AVMutex codec_mutex = AV_MUTEX_INITIALIZER;
+static int unspecified_coord = INT_MAX; 
 
 void av_fast_padded_malloc(void *ptr, unsigned int *size, size_t min_size)
 {
@@ -2031,6 +2033,34 @@  AVCPBProperties *ff_add_cpb_side_data(AVCodecContext *avctx)
     return props;
 }
 
+static void master_display_metadata_reset(AVMasteringDisplayMetadata *metadata)
+{
+    if (!metadata)
+        return;
+
+    metadata->display_primaries[0][0] = (AVRational){ 0, 1 };
+    metadata->display_primaries[0][1] = (AVRational){ 0, 1 };
+    metadata->display_primaries[1][0] = (AVRational){ 0, 1 };
+    metadata->display_primaries[1][1] = (AVRational){ 0, 1 };
+    metadata->display_primaries[2][0] = (AVRational){ 0, 1 };
+    metadata->display_primaries[2][1] = (AVRational){ 0, 1 };
+    metadata->white_point[0] = (AVRational){ 0, 1 };
+    metadata->white_point[1] = (AVRational){ 0, 1 };
+    metadata->has_primaries = 0;
+    metadata->min_luminance = (AVRational){ 0, 1 };
+    metadata->max_luminance = (AVRational){ 0, 1 };
+    metadata->has_luminance = 0;
+}
+
+static void content_light_metadata_reset(AVContentLightMetadata *metadata)
+{
+    if (!metadata)
+        return;
+
+    metadata->MaxCLL = 0;
+    metadata->MaxFALL = 0;
+}
+
 static void codec_parameters_reset(AVCodecParameters *par)
 {
     av_freep(&par->extradata);
@@ -2049,6 +2079,9 @@  static void codec_parameters_reset(AVCodecParameters *par)
     par->sample_aspect_ratio = (AVRational){ 0, 1 };
     par->profile             = FF_PROFILE_UNKNOWN;
     par->level               = FF_LEVEL_UNKNOWN;
+
+    master_display_metadata_reset(&par->master_display_metadata);
+    content_light_metadata_reset(&par->content_light_metadata);
 }
 
 AVCodecParameters *avcodec_parameters_alloc(void)
@@ -2090,6 +2123,53 @@  int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src
     return 0;
 }
 
+static void mastering_display_metadata_from_context(AVMasteringDisplayMetadata *metadata,
+                                                    const AVCodecContext *codec)
+{
+    if (!metadata || !codec)
+        return;
+    
+    master_display_metadata_reset(metadata);
+    
+    if (codec->master_display_red_x.num != unspecified_coord &&
+        codec->master_display_red_y.num != unspecified_coord &&
+        codec->master_display_green_x.num != unspecified_coord &&
+        codec->master_display_green_y.num != unspecified_coord &&
+        codec->master_display_blue_x.num != unspecified_coord &&
+        codec->master_display_blue_y.num != unspecified_coord &&
+        codec->master_display_white_x.num != unspecified_coord &&
+        codec->master_display_white_y.num != unspecified_coord)
+    {
+        metadata->display_primaries[0][0] = codec->master_display_red_x;
+        metadata->display_primaries[0][1] = codec->master_display_red_y;
+        metadata->display_primaries[1][0] = codec->master_display_green_x;
+        metadata->display_primaries[1][1] = codec->master_display_green_y;
+        metadata->display_primaries[2][0] = codec->master_display_blue_x;
+        metadata->display_primaries[2][1] = codec->master_display_blue_y;
+        metadata->white_point[0] = codec->master_display_white_x;
+        metadata->white_point[1] = codec->master_display_white_y;
+        metadata->has_primaries = 1;
+    }
+
+    if (codec->master_display_min_luminance.den > 0 &&
+        codec->master_display_max_luminance.den > 0)
+    {
+        metadata->min_luminance = codec->master_display_min_luminance;
+        metadata->max_luminance = codec->master_display_max_luminance;
+        metadata->has_luminance = 1;
+    }
+}
+
+static void content_light_metadata_from_context(AVContentLightMetadata *metadata,
+                                                const AVCodecContext *codec)
+{
+    if (!metadata || !codec)
+        return;
+    
+    metadata->MaxCLL = codec->max_cll;
+    metadata->MaxFALL = codec->max_fall;
+}
+
 int avcodec_parameters_from_context(AVCodecParameters *par,
                                     const AVCodecContext *codec)
 {
@@ -2118,6 +2198,9 @@  int avcodec_parameters_from_context(AVCodecParameters *par,
         par->chroma_location     = codec->chroma_sample_location;
         par->sample_aspect_ratio = codec->sample_aspect_ratio;
         par->video_delay         = codec->has_b_frames;
+
+        mastering_display_metadata_from_context(&par->master_display_metadata, codec);
+        content_light_metadata_from_context(&par->content_light_metadata, codec);
         break;
     case AVMEDIA_TYPE_AUDIO:
         par->format           = codec->sample_fmt;
@@ -2147,6 +2230,53 @@  int avcodec_parameters_from_context(AVCodecParameters *par,
     return 0;
 }
 
+static void mastering_display_metadata_to_context(AVCodecContext *codec,
+                                                  const AVMasteringDisplayMetadata *metadata)
+{
+    if (!codec || !metadata)
+        return;
+    
+    if (metadata->has_primaries)
+    {
+        codec->master_display_red_x = metadata->display_primaries[0][0];
+        codec->master_display_red_y = metadata->display_primaries[0][1];
+        codec->master_display_green_x = metadata->display_primaries[1][0];
+        codec->master_display_green_y = metadata->display_primaries[1][1];
+        codec->master_display_blue_x = metadata->display_primaries[2][0];
+        codec->master_display_blue_y = metadata->display_primaries[2][1];
+        codec->master_display_white_x = metadata->white_point[0];
+        codec->master_display_white_y = metadata->white_point[1];
+    } else {
+        codec->master_display_red_x = (AVRational){ unspecified_coord, 1 };
+        codec->master_display_red_y = (AVRational){ unspecified_coord, 1 };
+        codec->master_display_green_x = (AVRational){ unspecified_coord, 1 };
+        codec->master_display_green_y = (AVRational){ unspecified_coord, 1 };
+        codec->master_display_blue_x = (AVRational){ unspecified_coord, 1 };
+        codec->master_display_blue_y = (AVRational){ unspecified_coord, 1 };
+        codec->master_display_white_x = (AVRational){ unspecified_coord, 1 };
+        codec->master_display_white_y = (AVRational){ unspecified_coord, 1 };
+    }
+
+    if (metadata->has_luminance)
+    {
+        codec->master_display_min_luminance = metadata->min_luminance;
+        codec->master_display_max_luminance = metadata->max_luminance;
+    } else {
+        codec->master_display_min_luminance = (AVRational){ unspecified_coord, 1 };
+        codec->master_display_max_luminance = (AVRational){ unspecified_coord, 1 };
+    }
+}
+
+static void content_light_metadata_to_context(AVCodecContext *codec,
+                                              const AVContentLightMetadata *metadata)
+{
+    if (!codec || !metadata)
+        return;
+    
+    codec->max_cll = metadata->MaxCLL;
+    codec->max_fall = metadata->MaxFALL;
+}
+
 int avcodec_parameters_to_context(AVCodecContext *codec,
                                   const AVCodecParameters *par)
 {
@@ -2173,6 +2303,9 @@  int avcodec_parameters_to_context(AVCodecContext *codec,
         codec->chroma_sample_location = par->chroma_location;
         codec->sample_aspect_ratio    = par->sample_aspect_ratio;
         codec->has_b_frames           = par->video_delay;
+
+        mastering_display_metadata_to_context(codec, &par->master_display_metadata);
+        content_light_metadata_to_context(codec, &par->content_light_metadata);
         break;
     case AVMEDIA_TYPE_AUDIO:
         codec->sample_fmt       = par->format;
diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index 233c472b8f..dfe9d45f6f 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -869,43 +869,61 @@  static void mkv_write_video_color(AVIOContext *pb, const AVStream *st,
         put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORCHROMASITINGVERT, (ypos >> 7) + 1);
     }
 
-    side_data = av_stream_get_side_data(st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
-                                        NULL);
-    if (side_data) {
-        const AVContentLightMetadata *metadata = side_data;
-        put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORMAXCLL,  metadata->MaxCLL);
-        put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORMAXFALL, metadata->MaxFALL);
+    const AVContentLightMetadata *content_light_metadata = &par->content_light_metadata;
+
+    if (content_light_metadata->MaxCLL <= 0 || content_light_metadata->MaxFALL <= 0) {
+        side_data = av_stream_get_side_data(st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
+                                            NULL);
+        if (side_data) {
+            content_light_metadata = side_data;
+        } else {
+            content_light_metadata = NULL;
+        }
+    }
+    
+    if (content_light_metadata) {
+        put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORMAXCLL,  content_light_metadata->MaxCLL);
+        put_ebml_uint(dyn_cp, MATROSKA_ID_VIDEOCOLORMAXFALL, content_light_metadata->MaxFALL);
     }
 
-    side_data = av_stream_get_side_data(st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
-                                        NULL);
-    if (side_data) {
+    const AVMasteringDisplayMetadata *master_display_metadata = &par->master_display_metadata;
+
+    if (!master_display_metadata->has_primaries || !master_display_metadata->has_luminance) {
+        side_data = av_stream_get_side_data(st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
+                                            NULL);
+        if (side_data) {
+            master_display_metadata = side_data;
+        } else {
+            master_display_metadata = NULL;
+        }
+    }
+    
+    if (master_display_metadata) {
         ebml_master meta_element = start_ebml_master(
             dyn_cp, MATROSKA_ID_VIDEOCOLORMASTERINGMETA, 10 * (2 + 1 + 8));
-        const AVMasteringDisplayMetadata *metadata = side_data;
-        if (metadata->has_primaries) {
+        if (master_display_metadata->has_primaries) {
             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_RX,
-                           av_q2d(metadata->display_primaries[0][0]));
+                           av_q2d(master_display_metadata->display_primaries[0][0]));
             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_RY,
-                           av_q2d(metadata->display_primaries[0][1]));
+                           av_q2d(master_display_metadata->display_primaries[0][1]));
             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_GX,
-                           av_q2d(metadata->display_primaries[1][0]));
+                           av_q2d(master_display_metadata->display_primaries[1][0]));
             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_GY,
-                           av_q2d(metadata->display_primaries[1][1]));
+                           av_q2d(master_display_metadata->display_primaries[1][1]));
             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_BX,
-                           av_q2d(metadata->display_primaries[2][0]));
+                           av_q2d(master_display_metadata->display_primaries[2][0]));
             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_BY,
-                           av_q2d(metadata->display_primaries[2][1]));
+                           av_q2d(master_display_metadata->display_primaries[2][1]));
             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_WHITEX,
-                           av_q2d(metadata->white_point[0]));
+                           av_q2d(master_display_metadata->white_point[0]));
             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_WHITEY,
-                           av_q2d(metadata->white_point[1]));
+                           av_q2d(master_display_metadata->white_point[1]));
         }
-        if (metadata->has_luminance) {
+        if (master_display_metadata->has_luminance) {
             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_LUMINANCEMAX,
-                           av_q2d(metadata->max_luminance));
+                           av_q2d(master_display_metadata->max_luminance));
             put_ebml_float(dyn_cp, MATROSKA_ID_VIDEOCOLOR_LUMINANCEMIN,
-                           av_q2d(metadata->min_luminance));
+                           av_q2d(master_display_metadata->min_luminance));
         }
         end_ebml_master(dyn_cp, meta_element);
     }
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 7db2e28840..074c463cee 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -1947,14 +1947,21 @@  static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track, int prefer_icc)
 static int mov_write_clli_tag(AVIOContext *pb, MOVTrack *track)
 {
     const uint8_t *side_data;
-    const AVContentLightMetadata *content_light_metadata;
+    const AVContentLightMetadata *content_light_metadata = &track->par->content_light_metadata;
 
-    side_data = av_stream_get_side_data(track->st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, NULL);
-    if (!side_data) {
+    if (content_light_metadata->MaxCLL <= 0 || content_light_metadata->MaxFALL <= 0) {
+        side_data = av_stream_get_side_data(track->st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, NULL);
+        if (side_data) {
+            content_light_metadata = (const AVContentLightMetadata*)side_data;
+        } else {
+            content_light_metadata = NULL;
+        }
+    }
+
+    if (!content_light_metadata) {
         av_log(NULL, AV_LOG_VERBOSE, "Not writing 'clli' atom. No content light level info.\n");
         return 0;
     }
-    content_light_metadata = (const AVContentLightMetadata*)side_data;
 
     avio_wb32(pb, 12); // size
     ffio_wfourcc(pb, "clli");
@@ -1973,10 +1980,18 @@  static int mov_write_mdcv_tag(AVIOContext *pb, MOVTrack *track)
     const int chroma_den = 50000;
     const int luma_den = 10000;
     const uint8_t *side_data;
-    const AVMasteringDisplayMetadata *metadata;
+    const AVMasteringDisplayMetadata *metadata = &track->par->master_display_metadata;
 
-    side_data = av_stream_get_side_data(track->st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, NULL);
-    metadata = (const AVMasteringDisplayMetadata*)side_data;
+    if (!metadata->has_primaries || !metadata->has_luminance) {
+        side_data = av_stream_get_side_data(track->st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, NULL);
+
+        if (side_data) {
+            metadata = (const AVMasteringDisplayMetadata*)side_data;
+        } else {
+            metadata = NULL;
+        }
+    }
+    
     if (!metadata || !metadata->has_primaries || !metadata->has_luminance) {
         av_log(NULL, AV_LOG_VERBOSE, "Not writing 'mdcv' atom. Missing mastering metadata.\n");
         return 0;