diff mbox series

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

Message ID 472C4480-6B92-43EE-B91A-AA16EA81F735@me.com
State New
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 success Make fate finished

Commit Message

Kenny McClive Aug. 11, 2020, 1:47 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                 |  90 ++++++++++++++++++
 libavcodec/codec_par.h               |   7 ++
 libavcodec/options_table.h           |  12 +++
 libavcodec/utils.c                   | 134 ++++++++++++++++++++++++++-
 libavformat/matroskaenc.c            |  62 ++++++++-----
 libavformat/movenc.c                 |  29 ++++--
 tests/ref/fate/api-mjpeg-codec-param |  24 +++++
 tests/ref/fate/api-png-codec-param   |  24 +++++
 8 files changed, 352 insertions(+), 30 deletions(-)

Comments

Paul B Mahol Aug. 11, 2020, 10:16 a.m. UTC | #1
I think this should use some sort of side data instead or adding new
fields to AVCodecContext.

On 8/11/20, 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                 |  90 ++++++++++++++++++
>  libavcodec/codec_par.h               |   7 ++
>  libavcodec/options_table.h           |  12 +++
>  libavcodec/utils.c                   | 134 ++++++++++++++++++++++++++-
>  libavformat/matroskaenc.c            |  62 ++++++++-----
>  libavformat/movenc.c                 |  29 ++++--
>  tests/ref/fate/api-mjpeg-codec-param |  24 +++++
>  tests/ref/fate/api-png-codec-param   |  24 +++++
>  8 files changed, 352 insertions(+), 30 deletions(-)
>
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index c91b2fd169..6f8b42b048 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -411,6 +411,12 @@ typedef struct RcOverride{
>   */
>  #define AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS (1 << 2)
>
> +/**
> + * Encoding only.
> + * Indicates that a mastering display option was not specified.
> + */
> +#define AV_CODEC_MASTER_DISPLAY_UNSPECIFIED INT_MAX
> +
>  /**
>   * Pan Scan area.
>   * This specifies the area which should be displayed.
> @@ -2352,6 +2358,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..5c1cfc77b4 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 =
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 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 =
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 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 = AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 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 = AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 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 =
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 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 =
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 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 =
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 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 =
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 0, INT_MAX, V|E},
> +{"master_display_min_lum", "mastering display min luminance",
> OFFSET(master_display_min_luminance), AV_OPT_TYPE_RATIONAL, {.dbl =
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 0, INT_MAX, V|E},
> +{"master_display_max_lum", "mastering display max luminance",
> OFFSET(master_display_max_luminance), AV_OPT_TYPE_RATIONAL, {.dbl =
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 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..ef90ad0513 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"
> @@ -66,7 +67,7 @@
>  #include "libavutil/ffversion.h"
>  const char av_codec_ffversion[] = "FFmpeg version " FFMPEG_VERSION;
>
> -static AVMutex codec_mutex = AV_MUTEX_INITIALIZER;
> +static AVMutex codec_mutex = AV_MUTEX_INITIALIZER;
>
>  void av_fast_padded_malloc(void *ptr, unsigned int *size, size_t min_size)
>  {
> @@ -2031,6 +2032,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 +2078,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 +2122,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 !=
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED &&
> +        codec->master_display_red_y.num !=
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED &&
> +        codec->master_display_green_x.num !=
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED &&
> +        codec->master_display_green_y.num !=
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED &&
> +        codec->master_display_blue_x.num !=
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED &&
> +        codec->master_display_blue_y.num !=
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED &&
> +        codec->master_display_white_x.num !=
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED &&
> +        codec->master_display_white_y.num !=
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED)
> +    {
> +        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 +2197,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 +2229,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){
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 1 };
> +        codec->master_display_red_y = (AVRational){
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 1 };
> +        codec->master_display_green_x = (AVRational){
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 1 };
> +        codec->master_display_green_y = (AVRational){
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 1 };
> +        codec->master_display_blue_x = (AVRational){
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 1 };
> +        codec->master_display_blue_y = (AVRational){
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 1 };
> +        codec->master_display_white_x = (AVRational){
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 1 };
> +        codec->master_display_white_y = (AVRational){
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 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){
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 1 };
> +        codec->master_display_max_luminance = (AVRational){
> AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 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 +2302,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;
> diff --git a/tests/ref/fate/api-mjpeg-codec-param
> b/tests/ref/fate/api-mjpeg-codec-param
> index 82e3313aa9..48a28aa4e4 100644
> --- a/tests/ref/fate/api-mjpeg-codec-param
> +++ b/tests/ref/fate/api-mjpeg-codec-param
> @@ -118,6 +118,18 @@ stream=0, decode=0
>      colorspace=5
>      color_range=2
>      chroma_sample_location=2
> +    master_display_red_x=2147483647/1
> +    master_display_red_y=2147483647/1
> +    master_display_green_x=2147483647/1
> +    master_display_green_y=2147483647/1
> +    master_display_blue_x=2147483647/1
> +    master_display_blue_y=2147483647/1
> +    master_display_white_x=2147483647/1
> +    master_display_white_y=2147483647/1
> +    master_display_min_lum=2147483647/1
> +    master_display_max_lum=2147483647/1
> +    max_cll=0
> +    max_fall=0
>      log_level_offset=0
>      slices=0
>      thread_type=0x00000003
> @@ -261,6 +273,18 @@ stream=0, decode=1
>      colorspace=5
>      color_range=2
>      chroma_sample_location=2
> +    master_display_red_x=2147483647/1
> +    master_display_red_y=2147483647/1
> +    master_display_green_x=2147483647/1
> +    master_display_green_y=2147483647/1
> +    master_display_blue_x=2147483647/1
> +    master_display_blue_y=2147483647/1
> +    master_display_white_x=2147483647/1
> +    master_display_white_y=2147483647/1
> +    master_display_min_lum=2147483647/1
> +    master_display_max_lum=2147483647/1
> +    max_cll=0
> +    max_fall=0
>      log_level_offset=0
>      slices=0
>      thread_type=0x00000003
> diff --git a/tests/ref/fate/api-png-codec-param
> b/tests/ref/fate/api-png-codec-param
> index 7adaa5260d..84d5ae722b 100644
> --- a/tests/ref/fate/api-png-codec-param
> +++ b/tests/ref/fate/api-png-codec-param
> @@ -118,6 +118,18 @@ stream=0, decode=0
>      colorspace=2
>      color_range=2
>      chroma_sample_location=0
> +    master_display_red_x=2147483647/1
> +    master_display_red_y=2147483647/1
> +    master_display_green_x=2147483647/1
> +    master_display_green_y=2147483647/1
> +    master_display_blue_x=2147483647/1
> +    master_display_blue_y=2147483647/1
> +    master_display_white_x=2147483647/1
> +    master_display_white_y=2147483647/1
> +    master_display_min_lum=2147483647/1
> +    master_display_max_lum=2147483647/1
> +    max_cll=0
> +    max_fall=0
>      log_level_offset=0
>      slices=0
>      thread_type=0x00000003
> @@ -261,6 +273,18 @@ stream=0, decode=1
>      colorspace=2
>      color_range=2
>      chroma_sample_location=0
> +    master_display_red_x=2147483647/1
> +    master_display_red_y=2147483647/1
> +    master_display_green_x=2147483647/1
> +    master_display_green_y=2147483647/1
> +    master_display_blue_x=2147483647/1
> +    master_display_blue_y=2147483647/1
> +    master_display_white_x=2147483647/1
> +    master_display_white_y=2147483647/1
> +    master_display_min_lum=2147483647/1
> +    master_display_max_lum=2147483647/1
> +    max_cll=0
> +    max_fall=0
>      log_level_offset=0
>      slices=0
>      thread_type=0x00000003
> --
> 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".
Lynne Aug. 11, 2020, 10:19 a.m. UTC | #2
Aug 11, 2020, 03:47 by kenny.mcclive@me.com:

> 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.
>

I don't like this as-is.
For HDR transcoding/tonemapping we rely purely on frame side data atm. How would adding
new metadata to avcodeccontext work with this? Would the avcodec metadata overwrite the
avframe side data? We cannot break existing behavior as its actually used in production.
Furthermore, considering all codecs which support HDR carry data rather than rely on the
container, remuxing would break. Some ideas were discussed about adding a bsf to deal with this,
and if we were to add a bsf, it would make this patch largely redundant, apart from setting
the container fields, which is only somewhat useful.
For now, what's the problem with just using the AVFrame fields for the container as well?
Then you'll only need a filter to set the metadata.
Kenny McClive Aug. 11, 2020, 10:23 p.m. UTC | #3
Lynne,

Please see my responses below.  I am not opposed to taking a different approach, but I could use some guidance on how to go about that.  This is my first contribution to FFmpeg, and I struggled to get this done.

> For HDR transcoding/tonemapping we rely purely on frame side data atm. How would adding
> new metadata to avcodeccontext work with this?

I don’t have enough context to say.  I am only aware of FFmpeg reading side data.  I haven’t seen any options that explicitly write to it.

> Would the avcodec metadata overwrite the
> avframe side data?

I’m not sure.  The code that I modified was retrieving side data from somewhere and writing that to the container.  I thought that might be coming from the source container; I’m not sure how you get frame-level data at the container-level.  The code that I added will override the data retrieved here if the options are set.

> We cannot break existing behavior as its actually used in production.
> Furthermore, considering all codecs which support HDR carry data rather than rely on the
> container, remuxing would break.

I disagree.  The change that I made does not have any effect unless the options are set.

> Some ideas were discussed about adding a bsf to deal with this,
> and if we were to add a bsf, it would make this patch largely redundant, apart from setting
> the container fields, which is only somewhat useful.

What is a bsf? Is that a filter? Is someone else working on that? I don’t care who implements this functionality as long as it gets done.

> For now, what's the problem with just using the AVFrame fields for the container as well?
> Then you'll only need a filter to set the metadata.

Can you access the AVFrame fields from the container functions in movenc and matroskaenc?  If we are talking about using a filter, it seems like it would be more conventional to write the data to the frames instead of the container.  However, I could use some guidance on how to get started with either of these approaches.

Thanks,
Kenny

> On Aug 11, 2020, at 4:19 AM, Lynne <dev@lynne.ee> wrote:
> 
> Aug 11, 2020, 03:47 by kenny.mcclive@me.com:
> 
>> 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.
>> 
> 
> I don't like this as-is.
> For HDR transcoding/tonemapping we rely purely on frame side data atm. How would adding
> new metadata to avcodeccontext work with this? Would the avcodec metadata overwrite the
> avframe side data? We cannot break existing behavior as its actually used in production.
> Furthermore, considering all codecs which support HDR carry data rather than rely on the
> container, remuxing would break. Some ideas were discussed about adding a bsf to deal with this,
> and if we were to add a bsf, it would make this patch largely redundant, apart from setting
> the container fields, which is only somewhat useful.
> For now, what's the problem with just using the AVFrame fields for the container as well?
> Then you'll only need a filter to set the metadata.
> _______________________________________________
> 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..6f8b42b048 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -411,6 +411,12 @@  typedef struct RcOverride{
  */
 #define AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS (1 << 2)
 
+/**
+ * Encoding only.
+ * Indicates that a mastering display option was not specified.
+ */
+#define AV_CODEC_MASTER_DISPLAY_UNSPECIFIED INT_MAX
+
 /**
  * Pan Scan area.
  * This specifies the area which should be displayed.
@@ -2352,6 +2358,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..5c1cfc77b4 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 = AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 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 = AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 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 = AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 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 = AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 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 = AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 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 = AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 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 = AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 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 = AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 0, INT_MAX, V|E},
+{"master_display_min_lum", "mastering display min luminance", OFFSET(master_display_min_luminance), AV_OPT_TYPE_RATIONAL, {.dbl = AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 0, INT_MAX, V|E},
+{"master_display_max_lum", "mastering display max luminance", OFFSET(master_display_max_luminance), AV_OPT_TYPE_RATIONAL, {.dbl = AV_CODEC_MASTER_DISPLAY_UNSPECIFIED}, 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..ef90ad0513 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"
@@ -66,7 +67,7 @@ 
 #include "libavutil/ffversion.h"
 const char av_codec_ffversion[] = "FFmpeg version " FFMPEG_VERSION;
 
-static AVMutex codec_mutex = AV_MUTEX_INITIALIZER;
+static AVMutex codec_mutex = AV_MUTEX_INITIALIZER; 
 
 void av_fast_padded_malloc(void *ptr, unsigned int *size, size_t min_size)
 {
@@ -2031,6 +2032,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 +2078,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 +2122,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 != AV_CODEC_MASTER_DISPLAY_UNSPECIFIED &&
+        codec->master_display_red_y.num != AV_CODEC_MASTER_DISPLAY_UNSPECIFIED &&
+        codec->master_display_green_x.num != AV_CODEC_MASTER_DISPLAY_UNSPECIFIED &&
+        codec->master_display_green_y.num != AV_CODEC_MASTER_DISPLAY_UNSPECIFIED &&
+        codec->master_display_blue_x.num != AV_CODEC_MASTER_DISPLAY_UNSPECIFIED &&
+        codec->master_display_blue_y.num != AV_CODEC_MASTER_DISPLAY_UNSPECIFIED &&
+        codec->master_display_white_x.num != AV_CODEC_MASTER_DISPLAY_UNSPECIFIED &&
+        codec->master_display_white_y.num != AV_CODEC_MASTER_DISPLAY_UNSPECIFIED)
+    {
+        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 +2197,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 +2229,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){ AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 1 };
+        codec->master_display_red_y = (AVRational){ AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 1 };
+        codec->master_display_green_x = (AVRational){ AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 1 };
+        codec->master_display_green_y = (AVRational){ AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 1 };
+        codec->master_display_blue_x = (AVRational){ AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 1 };
+        codec->master_display_blue_y = (AVRational){ AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 1 };
+        codec->master_display_white_x = (AVRational){ AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 1 };
+        codec->master_display_white_y = (AVRational){ AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 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){ AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 1 };
+        codec->master_display_max_luminance = (AVRational){ AV_CODEC_MASTER_DISPLAY_UNSPECIFIED, 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 +2302,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;
diff --git a/tests/ref/fate/api-mjpeg-codec-param b/tests/ref/fate/api-mjpeg-codec-param
index 82e3313aa9..48a28aa4e4 100644
--- a/tests/ref/fate/api-mjpeg-codec-param
+++ b/tests/ref/fate/api-mjpeg-codec-param
@@ -118,6 +118,18 @@  stream=0, decode=0
     colorspace=5
     color_range=2
     chroma_sample_location=2
+    master_display_red_x=2147483647/1
+    master_display_red_y=2147483647/1
+    master_display_green_x=2147483647/1
+    master_display_green_y=2147483647/1
+    master_display_blue_x=2147483647/1
+    master_display_blue_y=2147483647/1
+    master_display_white_x=2147483647/1
+    master_display_white_y=2147483647/1
+    master_display_min_lum=2147483647/1
+    master_display_max_lum=2147483647/1
+    max_cll=0
+    max_fall=0
     log_level_offset=0
     slices=0
     thread_type=0x00000003
@@ -261,6 +273,18 @@  stream=0, decode=1
     colorspace=5
     color_range=2
     chroma_sample_location=2
+    master_display_red_x=2147483647/1
+    master_display_red_y=2147483647/1
+    master_display_green_x=2147483647/1
+    master_display_green_y=2147483647/1
+    master_display_blue_x=2147483647/1
+    master_display_blue_y=2147483647/1
+    master_display_white_x=2147483647/1
+    master_display_white_y=2147483647/1
+    master_display_min_lum=2147483647/1
+    master_display_max_lum=2147483647/1
+    max_cll=0
+    max_fall=0
     log_level_offset=0
     slices=0
     thread_type=0x00000003
diff --git a/tests/ref/fate/api-png-codec-param b/tests/ref/fate/api-png-codec-param
index 7adaa5260d..84d5ae722b 100644
--- a/tests/ref/fate/api-png-codec-param
+++ b/tests/ref/fate/api-png-codec-param
@@ -118,6 +118,18 @@  stream=0, decode=0
     colorspace=2
     color_range=2
     chroma_sample_location=0
+    master_display_red_x=2147483647/1
+    master_display_red_y=2147483647/1
+    master_display_green_x=2147483647/1
+    master_display_green_y=2147483647/1
+    master_display_blue_x=2147483647/1
+    master_display_blue_y=2147483647/1
+    master_display_white_x=2147483647/1
+    master_display_white_y=2147483647/1
+    master_display_min_lum=2147483647/1
+    master_display_max_lum=2147483647/1
+    max_cll=0
+    max_fall=0
     log_level_offset=0
     slices=0
     thread_type=0x00000003
@@ -261,6 +273,18 @@  stream=0, decode=1
     colorspace=2
     color_range=2
     chroma_sample_location=0
+    master_display_red_x=2147483647/1
+    master_display_red_y=2147483647/1
+    master_display_green_x=2147483647/1
+    master_display_green_y=2147483647/1
+    master_display_blue_x=2147483647/1
+    master_display_blue_y=2147483647/1
+    master_display_white_x=2147483647/1
+    master_display_white_y=2147483647/1
+    master_display_min_lum=2147483647/1
+    master_display_max_lum=2147483647/1
+    max_cll=0
+    max_fall=0
     log_level_offset=0
     slices=0
     thread_type=0x00000003