diff mbox series

[FFmpeg-devel] Support HDR10+ metadata for HEVC.

Message ID 20201120035711.2357694-1-izadi@google.com
State Superseded
Headers show
Series [FFmpeg-devel] Support HDR10+ metadata for HEVC. | expand

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate fail Make fate failed
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate warning Make fate failed

Commit Message

Mohammad Izadi Nov. 20, 2020, 3:57 a.m. UTC
From: Mohammad Izadi <moh.izadi@gmail.com>

HDR10+ is dynamic metadata (A/341 Amendment - SMPTE2094-40) that needs to be decoded from ITU-T T.35 in HEVC bitstream. The HDR10+ is transferred to side data packet to be used or passed through.
---
The fate test file can be found here: https://drive.google.com/file/d/1vcT0ohzpoyVFcxQN32jKIhUrBaBwZweT/view?usp=sharing
The video file needs to be copied to fate-suite/mov/                                                           
 configure                                     |   1 +
 fftools/ffprobe.c                             | 106 +++++++++
 libavcodec/Makefile                           |   3 +
 libavcodec/avpacket.c                         |   1 +
 libavcodec/decode.c                           |   1 +
 libavcodec/dynamic_hdr10_plus.c               | 223 ++++++++++++++++++
 .../dynamic_hdr10_plus.h                      |  22 +-
 libavcodec/hevc_sei.c                         |  62 ++++-
 libavcodec/hevc_sei.h                         |   5 +
 libavcodec/hevcdec.c                          |  18 ++
 libavcodec/packet.h                           |   8 +
 libavcodec/version.h                          |   2 +-
 libavfilter/vf_showinfo.c                     | 106 ++++++++-
 libavutil/Makefile                            |   2 -
 libavutil/hdr_dynamic_metadata.c              |  47 ----
 tests/fate/mov.mak                            |   3 +
 tests/ref/fate/mov-hdr10-plus-metadata        |  90 +++++++
 17 files changed, 629 insertions(+), 71 deletions(-)
 create mode 100644 libavcodec/dynamic_hdr10_plus.c
 rename libavutil/hdr_dynamic_metadata.h => libavcodec/dynamic_hdr10_plus.h (95%)
 delete mode 100644 libavutil/hdr_dynamic_metadata.c
 create mode 100644 tests/ref/fate/mov-hdr10-plus-metadata

Comments

Anton Khirnov Nov. 20, 2020, 1:43 p.m. UTC | #1
Quoting Mohammad Izadi (2020-11-20 04:57:11)
> From: Mohammad Izadi <moh.izadi@gmail.com>
> 
> HDR10+ is dynamic metadata (A/341 Amendment - SMPTE2094-40) that needs to be decoded from ITU-T T.35 in HEVC bitstream. The HDR10+ is transferred to side data packet to be used or passed through.
> ---
> The fate test file can be found here: https://drive.google.com/file/d/1vcT0ohzpoyVFcxQN32jKIhUrBaBwZweT/view?usp=sharing
> The video file needs to be copied to fate-suite/mov/                                                           
>  configure                                     |   1 +
>  fftools/ffprobe.c                             | 106 +++++++++
>  libavcodec/Makefile                           |   3 +
>  libavcodec/avpacket.c                         |   1 +
>  libavcodec/decode.c                           |   1 +
>  libavcodec/dynamic_hdr10_plus.c               | 223 ++++++++++++++++++
>  .../dynamic_hdr10_plus.h                      |  22 +-
>  libavcodec/hevc_sei.c                         |  62 ++++-
>  libavcodec/hevc_sei.h                         |   5 +
>  libavcodec/hevcdec.c                          |  18 ++
>  libavcodec/packet.h                           |   8 +
>  libavcodec/version.h                          |   2 +-
>  libavfilter/vf_showinfo.c                     | 106 ++++++++-
>  libavutil/Makefile                            |   2 -
>  libavutil/hdr_dynamic_metadata.c              |  47 ----
>  tests/fate/mov.mak                            |   3 +
>  tests/ref/fate/mov-hdr10-plus-metadata        |  90 +++++++
>  17 files changed, 629 insertions(+), 71 deletions(-)
>  create mode 100644 libavcodec/dynamic_hdr10_plus.c
>  rename libavutil/hdr_dynamic_metadata.h => libavcodec/dynamic_hdr10_plus.h (95%)

You cannot move a public header like that, it breaks API.
Also, the patch does way too many things at once, it should be split.
Mohammad Izadi Nov. 23, 2020, 9:27 p.m. UTC | #2
Thanks,
Mohammad


On Fri, Nov 20, 2020 at 5:44 AM Anton Khirnov <anton@khirnov.net> wrote:

> Quoting Mohammad Izadi (2020-11-20 04:57:11)
> > From: Mohammad Izadi <moh.izadi@gmail.com>
> >
> > HDR10+ is dynamic metadata (A/341 Amendment - SMPTE2094-40) that needs
> to be decoded from ITU-T T.35 in HEVC bitstream. The HDR10+ is transferred
> to side data packet to be used or passed through.
> > ---
> > The fate test file can be found here:
> https://drive.google.com/file/d/1vcT0ohzpoyVFcxQN32jKIhUrBaBwZweT/view?usp=sharing
> > The video file needs to be copied to fate-suite/mov/
>
> >  configure                                     |   1 +
> >  fftools/ffprobe.c                             | 106 +++++++++
> >  libavcodec/Makefile                           |   3 +
> >  libavcodec/avpacket.c                         |   1 +
> >  libavcodec/decode.c                           |   1 +
> >  libavcodec/dynamic_hdr10_plus.c               | 223 ++++++++++++++++++
> >  .../dynamic_hdr10_plus.h                      |  22 +-
> >  libavcodec/hevc_sei.c                         |  62 ++++-
> >  libavcodec/hevc_sei.h                         |   5 +
> >  libavcodec/hevcdec.c                          |  18 ++
> >  libavcodec/packet.h                           |   8 +
> >  libavcodec/version.h                          |   2 +-
> >  libavfilter/vf_showinfo.c                     | 106 ++++++++-
> >  libavutil/Makefile                            |   2 -
> >  libavutil/hdr_dynamic_metadata.c              |  47 ----
> >  tests/fate/mov.mak                            |   3 +
> >  tests/ref/fate/mov-hdr10-plus-metadata        |  90 +++++++
> >  17 files changed, 629 insertions(+), 71 deletions(-)
> >  create mode 100644 libavcodec/dynamic_hdr10_plus.c
> >  rename libavutil/hdr_dynamic_metadata.h =>
> libavcodec/dynamic_hdr10_plus.h (95%)
>
> You cannot move a public header like that, it breaks API.
>
Right reverted the change in the following patch.

> Also, the patch does way too many things at once, it should be split.

Some changes are asked to be added by reviewers just for being able to test
it. This CL has been hanging for a long time. I think Vittorio and Ian can
help on this to proceed with this CL.

>
> --
> Anton Khirnov
> _______________________________________________
> 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/configure b/configure
index 51e43fbf66..a9f12a421e 100755
--- a/configure
+++ b/configure
@@ -2360,6 +2360,7 @@  CONFIG_EXTRA="
     dirac_parse
     dnn
     dvprofile
+    dynamic_hdr10_plus
     exif
     faandct
     faanidct
diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 86bd23d36d..5187e03603 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -30,6 +30,7 @@ 
 
 #include "libavformat/avformat.h"
 #include "libavcodec/avcodec.h"
+#include "libavcodec/dynamic_hdr10_plus.h"
 #include "libavutil/avassert.h"
 #include "libavutil/avstring.h"
 #include "libavutil/bprint.h"
@@ -1860,6 +1861,105 @@  static inline int show_tags(WriterContext *w, AVDictionary *tags, int section_id
     return ret;
 }
 
+static void print_dynamic_hdr10_plus(WriterContext *w, AVDynamicHDRPlus *metadata)
+{
+    if (!metadata)
+        return;
+    print_int("application version", metadata->application_version);
+    print_int("num_windows", metadata->num_windows);
+    for (int n = 1; n < metadata->num_windows; n++) {
+        AVHDRPlusColorTransformParams *params = &metadata->params[n];
+        print_q("window_upper_left_corner_x",
+                params->window_upper_left_corner_x,'/');
+        print_q("window_upper_left_corner_y",
+                params->window_upper_left_corner_y,'/');
+        print_q("window_lower_right_corner_x",
+                params->window_lower_right_corner_x,'/');
+        print_q("window_lower_right_corner_y",
+                params->window_lower_right_corner_y,'/');
+        print_q("window_upper_left_corner_x",
+                params->window_upper_left_corner_x,'/');
+        print_q("window_upper_left_corner_y",
+                params->window_upper_left_corner_y,'/');
+        print_int("center_of_ellipse_x",
+                  params->center_of_ellipse_x ) ;
+        print_int("center_of_ellipse_y",
+                  params->center_of_ellipse_y );
+        print_int("rotation_angle",
+                  params->rotation_angle);
+        print_int("semimajor_axis_internal_ellipse",
+                  params->semimajor_axis_internal_ellipse);
+        print_int("semimajor_axis_external_ellipse",
+                  params->semimajor_axis_external_ellipse);
+        print_int("semiminor_axis_external_ellipse",
+                  params->semiminor_axis_external_ellipse);
+        print_int("overlap_process_option",
+                  params->overlap_process_option);
+    }
+    print_q("targeted_system_display_maximum_luminance",
+            metadata->targeted_system_display_maximum_luminance,'/');
+    if (metadata->targeted_system_display_actual_peak_luminance_flag) {
+        print_int("num_rows_targeted_system_display_actual_peak_luminance",
+                  metadata->num_rows_targeted_system_display_actual_peak_luminance);
+        print_int("num_cols_targeted_system_display_actual_peak_luminance",
+                  metadata->num_cols_targeted_system_display_actual_peak_luminance);
+        for (int i = 0; i < metadata->num_rows_targeted_system_display_actual_peak_luminance; i++) {
+            for (int j = 0; j < metadata->num_cols_targeted_system_display_actual_peak_luminance; j++) {
+                print_q("targeted_system_display_actual_peak_luminance",
+                        metadata->targeted_system_display_actual_peak_luminance[i][j],'/');
+            }
+        }
+    }
+    for (int n = 0; n < metadata->num_windows; n++) {
+        AVHDRPlusColorTransformParams *params = &metadata->params[n];
+        for (int i = 0; i < 3; i++) {
+            print_q("maxscl",params->maxscl[i],'/');
+        }
+        print_q("average_maxrgb",
+                params->average_maxrgb,'/');
+        print_int("num_distribution_maxrgb_percentiles",
+                  params->num_distribution_maxrgb_percentiles);
+        for (int i = 0; i < params->num_distribution_maxrgb_percentiles; i++) {
+            print_int("distribution_maxrgb_percentage",
+                      params->distribution_maxrgb[i].percentage);
+            print_q("distribution_maxrgb_percentile",
+                    params->distribution_maxrgb[i].percentile,'/');
+        }
+        print_q("fraction_bright_pixels",
+                params->fraction_bright_pixels,'/');
+    }
+    if (metadata->mastering_display_actual_peak_luminance_flag) {
+        print_int("num_rows_mastering_display_actual_peak_luminance",
+                  metadata->num_rows_mastering_display_actual_peak_luminance);
+        print_int("num_cols_mastering_display_actual_peak_luminance",
+                  metadata->num_cols_mastering_display_actual_peak_luminance);
+        for (int i = 0; i < metadata->num_rows_mastering_display_actual_peak_luminance; i++) {
+            for (int j = 0; j <  metadata->num_cols_mastering_display_actual_peak_luminance; j++) {
+                print_q("mastering_display_actual_peak_luminance",
+                        metadata->mastering_display_actual_peak_luminance[i][j],'/');
+            }
+        }
+    }
+
+    for (int n = 0; n < metadata->num_windows; n++) {
+        AVHDRPlusColorTransformParams *params = &metadata->params[n];
+        if (params->tone_mapping_flag) {
+            print_q("knee_point_x", params->knee_point_x,'/');
+            print_q("knee_point_y", params->knee_point_y,'/');
+            print_int("num_bezier_curve_anchors",
+                      params->num_bezier_curve_anchors );
+            for (int i = 0; i < params->num_bezier_curve_anchors; i++) {
+                print_q("bezier_curve_anchors",
+                        params->bezier_curve_anchors[i],'/');
+            }
+        }
+        if (params->color_saturation_mapping_flag) {
+            print_q("color_saturation_weight",
+                    params->color_saturation_weight,'/');
+        }
+    }
+}
+
 static void print_pkt_side_data(WriterContext *w,
                                 AVCodecParameters *par,
                                 const AVPacketSideData *side_data,
@@ -1925,6 +2025,9 @@  static void print_pkt_side_data(WriterContext *w,
                 print_q("min_luminance", metadata->min_luminance, '/');
                 print_q("max_luminance", metadata->max_luminance, '/');
             }
+        } else if (sd->type == AV_PKT_DATA_DYNAMIC_HDR_PLUS) {
+            AVDynamicHDRPlus *metadata = (AVDynamicHDRPlus *)sd->data;
+            print_dynamic_hdr10_plus(w, metadata);
         } else if (sd->type == AV_PKT_DATA_CONTENT_LIGHT_LEVEL) {
             AVContentLightMetadata *metadata = (AVContentLightMetadata *)sd->data;
             print_int("max_content", metadata->MaxCLL);
@@ -2250,6 +2353,9 @@  static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
                     print_q("min_luminance", metadata->min_luminance, '/');
                     print_q("max_luminance", metadata->max_luminance, '/');
                 }
+            } else if (sd->type == AV_FRAME_DATA_DYNAMIC_HDR_PLUS) {
+                AVDynamicHDRPlus *metadata = (AVDynamicHDRPlus *)sd->data;
+                print_dynamic_hdr10_plus(w, metadata);
             } else if (sd->type == AV_FRAME_DATA_CONTENT_LIGHT_LEVEL) {
                 AVContentLightMetadata *metadata = (AVContentLightMetadata *)sd->data;
                 print_int("max_content", metadata->MaxCLL);
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index b4777be4d4..57326c2bd6 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -15,6 +15,7 @@  HEADERS = ac3_parser.h                                                  \
           dirac.h                                                       \
           dv_profile.h                                                  \
           dxva2.h                                                       \
+          dynamic_hdr10_plus.h                                          \
           jni.h                                                         \
           mediacodec.h                                                  \
           packet.h                                                      \
@@ -43,6 +44,7 @@  OBJS = ac3_parser.o                                                     \
        dv_profile.o                                                     \
        encode.o                                                         \
        imgconvert.o                                                     \
+       dynamic_hdr10_plus.o                                             \
        jni.o                                                            \
        mathtables.o                                                     \
        mediacodec.o                                                     \
@@ -78,6 +80,7 @@  OBJS-$(CONFIG_CBS_MPEG2)               += cbs_mpeg2.o
 OBJS-$(CONFIG_CBS_VP9)                 += cbs_vp9.o
 OBJS-$(CONFIG_CRYSTALHD)               += crystalhd.o
 OBJS-$(CONFIG_DCT)                     += dct.o dct32_fixed.o dct32_float.o
+OBJS-$(CONFIG_DYNAMIC_HDR10_PLUS)      += dynamic_hdr10_plus.o
 OBJS-$(CONFIG_ERROR_RESILIENCE)        += error_resilience.o
 OBJS-$(CONFIG_EXIF)                    += exif.o tiff_common.o
 OBJS-$(CONFIG_FAANDCT)                 += faandct.o
diff --git a/libavcodec/avpacket.c b/libavcodec/avpacket.c
index e4ba403cf6..b18a7b0823 100644
--- a/libavcodec/avpacket.c
+++ b/libavcodec/avpacket.c
@@ -394,6 +394,7 @@  const char *av_packet_side_data_name(enum AVPacketSideDataType type)
     case AV_PKT_DATA_CONTENT_LIGHT_LEVEL:        return "Content light level metadata";
     case AV_PKT_DATA_SPHERICAL:                  return "Spherical Mapping";
     case AV_PKT_DATA_A53_CC:                     return "A53 Closed Captions";
+    case AV_PKT_DATA_DYNAMIC_HDR_PLUS:           return "HDR10+ Dynamic Metadata (SMPTE 2094-40)";
     case AV_PKT_DATA_ENCRYPTION_INIT_INFO:       return "Encryption initialization data";
     case AV_PKT_DATA_ENCRYPTION_INFO:            return "Encryption info";
     case AV_PKT_DATA_AFD:                        return "Active Format Description data";
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index 5a1849f944..6da22fa2b9 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -1717,6 +1717,7 @@  int ff_decode_frame_props(AVCodecContext *avctx, AVFrame *frame)
         { AV_PKT_DATA_MASTERING_DISPLAY_METADATA, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA },
         { AV_PKT_DATA_CONTENT_LIGHT_LEVEL,        AV_FRAME_DATA_CONTENT_LIGHT_LEVEL },
         { AV_PKT_DATA_A53_CC,                     AV_FRAME_DATA_A53_CC },
+        { AV_PKT_DATA_DYNAMIC_HDR_PLUS,           AV_FRAME_DATA_DYNAMIC_HDR_PLUS },
         { AV_PKT_DATA_ICC_PROFILE,                AV_FRAME_DATA_ICC_PROFILE },
         { AV_PKT_DATA_S12M_TIMECODE,              AV_FRAME_DATA_S12M_TIMECODE },
     };
diff --git a/libavcodec/dynamic_hdr10_plus.c b/libavcodec/dynamic_hdr10_plus.c
new file mode 100644
index 0000000000..f0252badda
--- /dev/null
+++ b/libavcodec/dynamic_hdr10_plus.c
@@ -0,0 +1,223 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "dynamic_hdr10_plus.h"
+
+#include "libavutil/mem.h"
+
+static const uint8_t usa_country_code = 0xB5;
+static const uint16_t smpte_provider_code = 0x003C;
+static const uint16_t smpte2094_40_provider_oriented_code = 0x0001;
+static const uint16_t smpte2094_40_application_identifier = 0x04;
+static const int64_t luminance_den = 1;
+static const int32_t peak_luminance_den = 15;
+static const int64_t rgb_den = 100000;
+static const int32_t fraction_pixel_den = 1000;
+static const int32_t knee_point_den = 4095;
+static const int32_t bezier_anchor_den = 1023;
+static const int32_t saturation_weight_den = 8;
+
+AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size)
+{
+    AVDynamicHDRPlus *hdr_plus = av_mallocz(sizeof(AVDynamicHDRPlus));
+    if (!hdr_plus)
+        return NULL;
+
+    if (size)
+        *size = sizeof(*hdr_plus);
+
+    return hdr_plus;
+}
+
+AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame)
+{
+    AVFrameSideData *side_data = av_frame_new_side_data(frame,
+                                                        AV_FRAME_DATA_DYNAMIC_HDR_PLUS,
+                                                        sizeof(AVDynamicHDRPlus));
+    if (!side_data)
+        return NULL;
+
+    memset(side_data->data, 0, sizeof(AVDynamicHDRPlus));
+
+    return (AVDynamicHDRPlus *)side_data->data;
+}
+
+int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(GetBitContext *gb, AVDynamicHDRPlus *s)
+{
+    uint8_t application_version  = get_bits(gb, 8);
+    if (!s)
+        return AVERROR(ENOMEM);
+
+    s->application_version = application_version;
+
+    if (get_bits_left(gb) < 2)
+        return AVERROR_INVALIDDATA;
+    s->num_windows = get_bits(gb, 2);
+
+    if (s->num_windows < 1 || s->num_windows > 3) {
+        return AVERROR_INVALIDDATA;
+    }
+
+    if (get_bits_left(gb) < ((19 * 8 + 1) * (s->num_windows - 1)))
+        return AVERROR_INVALIDDATA;
+
+    for (int w = 1; w < s->num_windows; w++) {
+        // The corners are set to absolute coordinates here. They should be
+        // converted to the relative coordinates (in [0, 1]) in the decoder.
+        AVHDRPlusColorTransformParams *params = &s->params[w];
+        params->window_upper_left_corner_x =
+            (AVRational){get_bits(gb, 16), 1};
+        params->window_upper_left_corner_y =
+            (AVRational){get_bits(gb, 16), 1};
+        params->window_lower_right_corner_x =
+            (AVRational){get_bits(gb, 16), 1};
+        params->window_lower_right_corner_y =
+            (AVRational){get_bits(gb, 16), 1};
+
+        params->center_of_ellipse_x = get_bits(gb, 16);
+        params->center_of_ellipse_y = get_bits(gb, 16);
+        params->rotation_angle = get_bits(gb, 8);
+        params->semimajor_axis_internal_ellipse = get_bits(gb, 16);
+        params->semimajor_axis_external_ellipse = get_bits(gb, 16);
+        params->semiminor_axis_external_ellipse = get_bits(gb, 16);
+        params->overlap_process_option = get_bits1(gb);
+    }
+
+    if (get_bits_left(gb) < 28)
+        return AVERROR(EINVAL);
+
+    s->targeted_system_display_maximum_luminance =
+        (AVRational){get_bits(gb, 27), luminance_den};
+    s->targeted_system_display_actual_peak_luminance_flag = get_bits1(gb);
+
+    if (s->targeted_system_display_actual_peak_luminance_flag) {
+        int rows, cols;
+        if (get_bits_left(gb) < 10)
+            return AVERROR(EINVAL);
+        rows = get_bits(gb, 5);
+        cols = get_bits(gb, 5);
+        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25))) {
+            return AVERROR_INVALIDDATA;
+        }
+        s->num_rows_targeted_system_display_actual_peak_luminance = rows;
+        s->num_cols_targeted_system_display_actual_peak_luminance = cols;
+
+        if (get_bits_left(gb) < (rows * cols * 4))
+            return AVERROR(EINVAL);
+
+        for (int i = 0; i < rows; i++) {
+            for (int j = 0; j < cols; j++) {
+                s->targeted_system_display_actual_peak_luminance[i][j] =
+                    (AVRational){get_bits(gb, 4), peak_luminance_den};
+            }
+        }
+    }
+    for (int w = 0; w < s->num_windows; w++) {
+        AVHDRPlusColorTransformParams *params = &s->params[w];
+        if (get_bits_left(gb) < (3 * 17 + 17 + 4))
+            return AVERROR(EINVAL);
+
+        for (int i = 0; i < 3; i++) {
+            params->maxscl[i] =
+                (AVRational){get_bits(gb, 17), rgb_den};
+        }
+        params->average_maxrgb =
+            (AVRational){get_bits(gb, 17), rgb_den};
+        params->num_distribution_maxrgb_percentiles = get_bits(gb, 4);
+
+        if (get_bits_left(gb) <
+            (params->num_distribution_maxrgb_percentiles * 24))
+            return AVERROR(EINVAL);
+
+        for (int i = 0; i < params->num_distribution_maxrgb_percentiles; i++) {
+            params->distribution_maxrgb[i].percentage = get_bits(gb, 7);
+            params->distribution_maxrgb[i].percentile =
+                (AVRational){get_bits(gb, 17), rgb_den};
+        }
+
+        if (get_bits_left(gb) < 10)
+            return AVERROR(EINVAL);
+
+        params->fraction_bright_pixels = (AVRational){get_bits(gb, 10), fraction_pixel_den};
+    }
+    if (get_bits_left(gb) < 1)
+        return AVERROR(EINVAL);
+    s->mastering_display_actual_peak_luminance_flag = get_bits1(gb);
+    if (s->mastering_display_actual_peak_luminance_flag) {
+        int rows, cols;
+        if (get_bits_left(gb) < 10)
+            return AVERROR(EINVAL);
+        rows = get_bits(gb, 5);
+        cols = get_bits(gb, 5);
+        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25))) {
+            return AVERROR_INVALIDDATA;
+        }
+        s->num_rows_mastering_display_actual_peak_luminance = rows;
+        s->num_cols_mastering_display_actual_peak_luminance = cols;
+
+        if (get_bits_left(gb) < (rows * cols * 4))
+            return AVERROR(EINVAL);
+
+        for (int i = 0; i < rows; i++) {
+            for (int j = 0; j < cols; j++) {
+                s->mastering_display_actual_peak_luminance[i][j] =
+                    (AVRational){get_bits(gb, 4), peak_luminance_den};
+            }
+        }
+    }
+
+    for (int w = 0; w < s->num_windows; w++) {
+        AVHDRPlusColorTransformParams *params = &s->params[w];
+        if (get_bits_left(gb) < 1)
+            return AVERROR(EINVAL);
+
+        params->tone_mapping_flag = get_bits1(gb);
+        if (params->tone_mapping_flag) {
+            if (get_bits_left(gb) < 28)
+                return AVERROR(EINVAL);
+
+            params->knee_point_x =
+                (AVRational){get_bits(gb, 12), knee_point_den};
+            params->knee_point_y =
+                (AVRational){get_bits(gb, 12), knee_point_den};
+            params->num_bezier_curve_anchors = get_bits(gb, 4);
+
+            if (get_bits_left(gb) < (params->num_bezier_curve_anchors * 10))
+                return AVERROR(EINVAL);
+
+            for (int i = 0; i < params->num_bezier_curve_anchors; i++) {
+                params->bezier_curve_anchors[i] =
+                    (AVRational){get_bits(gb, 10), bezier_anchor_den};
+            }
+        }
+
+        if (get_bits_left(gb) < 1)
+            return AVERROR(EINVAL);
+        params->color_saturation_mapping_flag = get_bits1(gb);
+        if (params->color_saturation_mapping_flag) {
+            if (get_bits_left(gb) < 6)
+                return AVERROR(EINVAL);
+            params->color_saturation_weight =
+                (AVRational){get_bits(gb, 6), saturation_weight_den};
+        }
+    }
+
+    skip_bits(gb, get_bits_left(gb));
+
+    return 0;
+}
diff --git a/libavutil/hdr_dynamic_metadata.h b/libavcodec/dynamic_hdr10_plus.h
similarity index 95%
rename from libavutil/hdr_dynamic_metadata.h
rename to libavcodec/dynamic_hdr10_plus.h
index 2d72de56ae..e861ba5790 100644
--- a/libavutil/hdr_dynamic_metadata.h
+++ b/libavcodec/dynamic_hdr10_plus.h
@@ -1,6 +1,4 @@ 
 /*
- * Copyright (c) 2018 Mohammad Izadi <moh.izadi at gmail.com>
- *
  * This file is part of FFmpeg.
  *
  * FFmpeg is free software; you can redistribute it and/or
@@ -18,11 +16,12 @@ 
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#ifndef AVUTIL_HDR_DYNAMIC_METADATA_H
-#define AVUTIL_HDR_DYNAMIC_METADATA_H
+#ifndef AVCODEC_DYNAMIC_HDR10_PLUS_H
+#define AVCODEC_DYNAMIC_HDR10_PLUS_H
 
-#include "frame.h"
-#include "rational.h"
+#include "get_bits.h"
+#include "libavutil/frame.h"
+#include "libavutil/rational.h"
 
 /**
  * Option for overlapping elliptical pixel selectors in an image.
@@ -340,4 +339,13 @@  AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size);
  */
 AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame);
 
-#endif /* AVUTIL_HDR_DYNAMIC_METADATA_H */
+/**
+ * Parse the user data registered ITU-T T.35 to AVbuffer (AVDynamicHDRPlus).
+ * @param gb The bit content to be decoded.
+ * @param s A pointer containing the decoded AVDynamicHDRPlus structure.
+ *
+ * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
+ */
+int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(GetBitContext *gb, AVDynamicHDRPlus *s);
+
+#endif /* AVCODEC_DYNAMIC_HDR10_PLUS_H */
diff --git a/libavcodec/hevc_sei.c b/libavcodec/hevc_sei.c
index 06e7a3a10c..211f3ab802 100644
--- a/libavcodec/hevc_sei.c
+++ b/libavcodec/hevc_sei.c
@@ -24,6 +24,7 @@ 
 
 #include "atsc_a53.h"
 #include "golomb.h"
+#include "dynamic_hdr10_plus.h"
 #include "hevc_ps.h"
 #include "hevc_sei.h"
 
@@ -206,11 +207,15 @@  static int decode_nal_sei_user_data_unregistered(HEVCSEIUnregistered *s, GetBitC
     return 0;
 }
 
-static int decode_nal_sei_user_data_registered_itu_t_t35(HEVCSEI *s, GetBitContext *gb,
+static int decode_nal_sei_user_data_registered_itu_t_t35(HEVCSEI *s,
+                                                         GetBitContext *gb,
                                                          int size)
 {
-    uint32_t country_code;
-    uint32_t user_identifier;
+    const uint8_t usa_country_code = 0xB5;
+    const uint16_t smpte_provider_code = 0x003C;
+
+    uint8_t country_code = 0;
+    uint16_t provider_code = 0;
 
     if (size < 7)
         return AVERROR(EINVAL);
@@ -222,18 +227,48 @@  static int decode_nal_sei_user_data_registered_itu_t_t35(HEVCSEI *s, GetBitConte
         size--;
     }
 
-    skip_bits(gb, 8);
-    skip_bits(gb, 8);
-
-    user_identifier = get_bits_long(gb, 32);
-
-    switch (user_identifier) {
-        case MKBETAG('G', 'A', '9', '4'):
+    provider_code = get_bits(gb, 16);
+
+    if (country_code == usa_country_code &&
+        provider_code == smpte_provider_code) {
+        // A/341 Amendment – 2094-40
+        const uint16_t smpte2094_40_provider_oriented_code = 0x0001;
+        const uint8_t smpte2094_40_application_identifier = 0x04;
+
+        uint16_t provider_oriented_code = get_bits(gb, 16);
+        uint8_t application_identifier = get_bits(gb, 8);
+
+        if (provider_oriented_code == smpte2094_40_provider_oriented_code &&
+            application_identifier == smpte2094_40_application_identifier) {
+            int err = 0;
+            size_t size = 0;
+            AVDynamicHDRPlus *metadata = av_dynamic_hdr_plus_alloc(&size);
+            if (!metadata)
+                return AVERROR(ENOMEM);
+
+            err = ff_parse_itu_t_t35_to_dynamic_hdr10_plus(gb, metadata);
+	    if (err < 0) {
+                av_freep(&metadata);
+                return err;
+	    }
+
+            if (s->dynamic_hdr_plus.info)
+                av_buffer_unref(&s->dynamic_hdr_plus.info);
+
+            s->dynamic_hdr_plus.info = av_buffer_create(
+                (uint8_t *)metadata, size, av_buffer_default_free, NULL, 0);
+            if (!s->dynamic_hdr_plus.info) {
+                av_freep(&s);
+                return AVERROR(ENOMEM);
+            }
+            return err;
+        }
+    } else {
+        uint32_t  user_identifier = get_bits_long(gb, 32);
+        if(user_identifier == MKBETAG('G', 'A', '9', '4'))
             return decode_registered_user_data_closed_caption(&s->a53_caption, gb, size);
-        default:
-            skip_bits_long(gb, size * 8);
-            break;
     }
+    skip_bits_long(gb, size * 8);
     return 0;
 }
 
@@ -420,4 +455,5 @@  void ff_hevc_reset_sei(HEVCSEI *s)
         av_buffer_unref(&s->unregistered.buf_ref[i]);
     s->unregistered.nb_buf_ref = 0;
     av_freep(&s->unregistered.buf_ref);
+    av_buffer_unref(&s->dynamic_hdr_plus.info);
 }
diff --git a/libavcodec/hevc_sei.h b/libavcodec/hevc_sei.h
index 5ee7a4796d..e9e2d46ed4 100644
--- a/libavcodec/hevc_sei.h
+++ b/libavcodec/hevc_sei.h
@@ -104,6 +104,10 @@  typedef struct HEVCSEIMasteringDisplay {
     uint32_t min_luminance;
 } HEVCSEIMasteringDisplay;
 
+typedef struct HEVCSEIDynamicHDRPlus {
+    AVBufferRef *info;
+} HEVCSEIDynamicHDRPlus;
+
 typedef struct HEVCSEIContentLight {
     int present;
     uint16_t max_content_light_level;
@@ -143,6 +147,7 @@  typedef struct HEVCSEI {
     HEVCSEIA53Caption a53_caption;
     HEVCSEIUnregistered unregistered;
     HEVCSEIMasteringDisplay mastering_display;
+    HEVCSEIDynamicHDRPlus dynamic_hdr_plus;
     HEVCSEIContentLight content_light;
     int active_seq_parameter_set_id;
     HEVCSEIAlternativeTransfer alternative_transfer;
diff --git a/libavcodec/hevcdec.c b/libavcodec/hevcdec.c
index 699c13bbcc..e98782b40e 100644
--- a/libavcodec/hevcdec.c
+++ b/libavcodec/hevcdec.c
@@ -2867,6 +2867,17 @@  static int set_side_data(HEVCContext *s)
         s->sei.timecode.num_clock_ts = 0;
     }
 
+    if (s->sei.dynamic_hdr_plus.info){
+        AVBufferRef *info_ref = av_buffer_ref(s->sei.dynamic_hdr_plus.info);
+        if (!info_ref)
+            return AVERROR(ENOMEM);
+
+        if(!av_frame_new_side_data_from_buf(out, AV_FRAME_DATA_DYNAMIC_HDR_PLUS, info_ref)){
+            av_buffer_unref(&info_ref);
+            return AVERROR(ENOMEM);
+        }
+    }
+
     return 0;
 }
 
@@ -3560,6 +3571,13 @@  static int hevc_update_thread_context(AVCodecContext *dst,
         }
     }
 
+    av_buffer_unref(&s->sei.dynamic_hdr_plus.info);
+    if (s0->sei.dynamic_hdr_plus.info) {
+        s->sei.dynamic_hdr_plus.info = av_buffer_ref(s0->sei.dynamic_hdr_plus.info);
+        if (!s->sei.dynamic_hdr_plus.info)
+            return AVERROR(ENOMEM);
+    }
+
     s->sei.frame_packing        = s0->sei.frame_packing;
     s->sei.display_orientation  = s0->sei.display_orientation;
     s->sei.mastering_display    = s0->sei.mastering_display;
diff --git a/libavcodec/packet.h b/libavcodec/packet.h
index b9d4c9c2c8..69316096f1 100644
--- a/libavcodec/packet.h
+++ b/libavcodec/packet.h
@@ -290,6 +290,14 @@  enum AVPacketSideDataType {
      */
     AV_PKT_DATA_S12M_TIMECODE,
 
+    /**
+     * HDR10+ dynamic metadata associated with a video frame. The metadata is in
+     * the form of the AVDynamicHDRPlus struct and contains
+     * information for color volume transform - application 4 of
+     * SPMTE 2094-40:2016 standard.
+     */
+    AV_PKT_DATA_DYNAMIC_HDR_PLUS,
+
     /**
      * The number of side data types.
      * This is not part of the public API/ABI in the sense that it may
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 1585d95777..cdd09096a1 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -28,7 +28,7 @@ 
 #include "libavutil/version.h"
 
 #define LIBAVCODEC_VERSION_MAJOR  58
-#define LIBAVCODEC_VERSION_MINOR 112
+#define LIBAVCODEC_VERSION_MINOR 113
 #define LIBAVCODEC_VERSION_MICRO 103
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
diff --git a/libavfilter/vf_showinfo.c b/libavfilter/vf_showinfo.c
index 66a30c7a32..f9ed54a70c 100644
--- a/libavfilter/vf_showinfo.c
+++ b/libavfilter/vf_showinfo.c
@@ -24,6 +24,7 @@ 
 
 #include <inttypes.h>
 
+#include "libavcodec/dynamic_hdr10_plus.h"
 #include "libavutil/bswap.h"
 #include "libavutil/adler32.h"
 #include "libavutil/display.h"
@@ -178,7 +179,107 @@  static void dump_mastering_display(AVFilterContext *ctx, const AVFrameSideData *
            av_q2d(mastering_display->min_luminance), av_q2d(mastering_display->max_luminance));
 }
 
-static void dump_content_light_metadata(AVFilterContext *ctx, const AVFrameSideData *sd)
+static void dump_dynamic_hdr_plus(AVFilterContext *ctx, AVFrameSideData *sd)
+{
+    AVDynamicHDRPlus *hdr_plus;
+
+    av_log(ctx, AV_LOG_INFO, "HDR10+ metadata: ");
+    if (sd->size < sizeof(*hdr_plus)) {
+        av_log(ctx, AV_LOG_ERROR, "invalid data\n");
+        return;
+    }
+
+    hdr_plus = (AVDynamicHDRPlus *)sd->data;
+    av_log(ctx, AV_LOG_INFO, "application version: %d, ", hdr_plus->application_version);
+    av_log(ctx, AV_LOG_INFO, "num_windows: %d, ", hdr_plus->num_windows);
+    for (int w = 1; w < hdr_plus->num_windows; w++) {
+        AVHDRPlusColorTransformParams *params = &hdr_plus->params[w];
+        av_log(ctx, AV_LOG_INFO, "window %d { ", w);
+        av_log(ctx, AV_LOG_INFO, "window_upper_left_corner: (%5.4f,%5.4f),",
+               av_q2d(params->window_upper_left_corner_x),
+               av_q2d(params->window_upper_left_corner_y));
+        av_log(ctx, AV_LOG_INFO, "window_lower_right_corner: (%5.4f,%5.4f), ",
+               av_q2d(params->window_lower_right_corner_x),
+               av_q2d(params->window_lower_right_corner_y));
+        av_log(ctx, AV_LOG_INFO, "window_upper_left_corner: (%5.4f, %5.4f), ",
+               av_q2d(params->window_upper_left_corner_x),
+               av_q2d(params->window_upper_left_corner_y));
+        av_log(ctx, AV_LOG_INFO, "center_of_ellipse_x: (%d,%d), ",
+               params->center_of_ellipse_x,
+               params->center_of_ellipse_y);
+        av_log(ctx, AV_LOG_INFO, "rotation_angle: %d, ",
+               params->rotation_angle);
+        av_log(ctx, AV_LOG_INFO, "semimajor_axis_internal_ellipse: %d, ",
+               params->semimajor_axis_internal_ellipse);
+        av_log(ctx, AV_LOG_INFO, "semimajor_axis_external_ellipse: %d, ",
+               params->semimajor_axis_external_ellipse);
+        av_log(ctx, AV_LOG_INFO, "semiminor_axis_external_ellipse: %d, ",
+               params->semiminor_axis_external_ellipse);
+        av_log(ctx, AV_LOG_INFO, "overlap_process_option: %d}, ",
+               params->overlap_process_option);
+    }
+    av_log(ctx, AV_LOG_INFO, "targeted_system_display_maximum_luminance: %9.4f, ",
+           av_q2d(hdr_plus->targeted_system_display_maximum_luminance));
+    if (hdr_plus->targeted_system_display_actual_peak_luminance_flag) {
+        av_log(ctx, AV_LOG_INFO, "targeted_system_display_actual_peak_luminance: {");
+        for (int i = 0; i < hdr_plus->num_rows_targeted_system_display_actual_peak_luminance; i++) {
+            av_log(ctx, AV_LOG_INFO, "(");
+            for (int j = 0; j < hdr_plus->num_cols_targeted_system_display_actual_peak_luminance; j++) {
+                av_log(ctx, AV_LOG_INFO, "%5.4f,",
+                       av_q2d(hdr_plus->targeted_system_display_actual_peak_luminance[i][j]));
+            }
+            av_log(ctx, AV_LOG_INFO, ")");
+        }
+        av_log(ctx, AV_LOG_INFO, "}, ");
+    }
+
+    for (int w = 0; w < hdr_plus->num_windows; w++) {
+        AVHDRPlusColorTransformParams *params = &hdr_plus->params[w];
+        av_log(ctx, AV_LOG_INFO, "window %d {maxscl: {", w);
+        for (int i = 0; i < 3; i++) {
+            av_log(ctx, AV_LOG_INFO, "%5.4f,",av_q2d(params->maxscl[i]));
+        }
+        av_log(ctx, AV_LOG_INFO, "} average_maxrgb: %5.4f, ",
+               av_q2d(params->average_maxrgb));
+        av_log(ctx, AV_LOG_INFO, "distribution_maxrgb: {");
+        for (int i = 0; i < params->num_distribution_maxrgb_percentiles; i++) {
+            av_log(ctx, AV_LOG_INFO, "(%d,%5.4f)",
+                   params->distribution_maxrgb[i].percentage,
+                   av_q2d(params->distribution_maxrgb[i].percentile));
+        }
+        av_log(ctx, AV_LOG_INFO, "} fraction_bright_pixels: %5.4f, ",
+               av_q2d(params->fraction_bright_pixels));
+        if (params->tone_mapping_flag) {
+            av_log(ctx, AV_LOG_INFO, "knee_point: (%5.4f,%5.4f), ", av_q2d(params->knee_point_x), av_q2d(params->knee_point_y));
+            av_log(ctx, AV_LOG_INFO, "bezier_curve_anchors: {");
+            for (int i = 0; i < params->num_bezier_curve_anchors; i++) {
+                av_log(ctx, AV_LOG_INFO, "%5.4f,",
+                       av_q2d(params->bezier_curve_anchors[i]));
+            }
+            av_log(ctx, AV_LOG_INFO, "} ");
+        }
+        if (params->color_saturation_mapping_flag) {
+            av_log(ctx, AV_LOG_INFO, "color_saturation_weight: %5.4f",
+                   av_q2d(params->color_saturation_weight));
+        }
+        av_log(ctx, AV_LOG_INFO, "} ");
+    }
+
+    if (hdr_plus->mastering_display_actual_peak_luminance_flag) {
+        av_log(ctx, AV_LOG_INFO, "mastering_display_actual_peak_luminance: {");
+        for (int i = 0; i < hdr_plus->num_rows_mastering_display_actual_peak_luminance; i++) {
+            av_log(ctx, AV_LOG_INFO, "(");
+            for (int j = 0; j <  hdr_plus->num_cols_mastering_display_actual_peak_luminance; j++) {
+                av_log(ctx, AV_LOG_INFO, " %5.4f,",
+                       av_q2d(hdr_plus->mastering_display_actual_peak_luminance[i][j]));
+            }
+            av_log(ctx, AV_LOG_INFO, ")");
+        }
+        av_log(ctx, AV_LOG_INFO, "} ");
+    }
+}
+
+static void dump_content_light_metadata(AVFilterContext *ctx, AVFrameSideData *sd)
 {
     const AVContentLightMetadata *metadata = (const AVContentLightMetadata *)sd->data;
 
@@ -396,6 +497,9 @@  static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
         case AV_FRAME_DATA_MASTERING_DISPLAY_METADATA:
             dump_mastering_display(ctx, sd);
             break;
+        case AV_FRAME_DATA_DYNAMIC_HDR_PLUS:
+            dump_dynamic_hdr_plus(ctx, sd);
+            break;
         case AV_FRAME_DATA_CONTENT_LIGHT_LEVEL:
             dump_content_light_metadata(ctx, sd);
             break;
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 9b08372eb2..60e41bfe25 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -32,7 +32,6 @@  HEADERS = adler32.h                                                     \
           file.h                                                        \
           frame.h                                                       \
           hash.h                                                        \
-          hdr_dynamic_metadata.h                                        \
           hmac.h                                                        \
           hwcontext.h                                                   \
           hwcontext_cuda.h                                              \
@@ -126,7 +125,6 @@  OBJS = adler32.o                                                        \
        fixed_dsp.o                                                      \
        frame.o                                                          \
        hash.o                                                           \
-       hdr_dynamic_metadata.o                                           \
        hmac.o                                                           \
        hwcontext.o                                                      \
        imgutils.o                                                       \
diff --git a/libavutil/hdr_dynamic_metadata.c b/libavutil/hdr_dynamic_metadata.c
deleted file mode 100644
index 0fa1ee82de..0000000000
--- a/libavutil/hdr_dynamic_metadata.c
+++ /dev/null
@@ -1,47 +0,0 @@ 
-/**
- * Copyright (c) 2018 Mohammad Izadi <moh.izadi at gmail.com>
- *
- * This file is part of FFmpeg.
- *
- * FFmpeg is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * FFmpeg is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with FFmpeg; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "hdr_dynamic_metadata.h"
-#include "mem.h"
-
-AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size)
-{
-    AVDynamicHDRPlus *hdr_plus = av_mallocz(sizeof(AVDynamicHDRPlus));
-    if (!hdr_plus)
-        return NULL;
-
-    if (size)
-        *size = sizeof(*hdr_plus);
-
-    return hdr_plus;
-}
-
-AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame)
-{
-    AVFrameSideData *side_data = av_frame_new_side_data(frame,
-                                                        AV_FRAME_DATA_DYNAMIC_HDR_PLUS,
-                                                        sizeof(AVDynamicHDRPlus));
-    if (!side_data)
-        return NULL;
-
-    memset(side_data->data, 0, sizeof(AVDynamicHDRPlus));
-
-    return (AVDynamicHDRPlus *)side_data->data;
-}
diff --git a/tests/fate/mov.mak b/tests/fate/mov.mak
index 0fd20fef96..55228f02fd 100644
--- a/tests/fate/mov.mak
+++ b/tests/fate/mov.mak
@@ -29,6 +29,7 @@  FATE_MOV_FFPROBE = fate-mov-neg-firstpts-discard \
                    fate-mov-guess-delay-2 \
                    fate-mov-guess-delay-3 \
                    fate-mov-mp4-with-mov-in24-ver \
+                   fate-mov-hdr10-plus-metadata \
 
 FATE_MOV_FASTSTART = fate-mov-faststart-4gb-overflow \
 
@@ -124,3 +125,5 @@  fate-mov-faststart-4gb-overflow: CMP = oneline
 fate-mov-faststart-4gb-overflow: REF = bc875921f151871e787c4b4023269b29
 
 fate-mov-mp4-with-mov-in24-ver: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -show_entries stream=codec_name -select_streams 1 $(TARGET_SAMPLES)/mov/mp4-with-mov-in24-ver.mp4
+
+fate-mov-hdr10-plus-metadata: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -show_frames -read_intervals 0%+0.01 -select_streams v -v 0 $(TARGET_SAMPLES)/mov/hdr10_plus_h265_sample.mp4
\ No newline at end of file
diff --git a/tests/ref/fate/mov-hdr10-plus-metadata b/tests/ref/fate/mov-hdr10-plus-metadata
new file mode 100644
index 0000000000..9f8f5e031b
--- /dev/null
+++ b/tests/ref/fate/mov-hdr10-plus-metadata
@@ -0,0 +1,90 @@ 
+[FRAME]
+media_type=video
+stream_index=0
+key_frame=1
+pkt_pts=0
+pkt_pts_time=0.000000
+pkt_dts=0
+pkt_dts_time=0.000000
+best_effort_timestamp=0
+best_effort_timestamp_time=0.000000
+pkt_duration=3540
+pkt_duration_time=0.039333
+pkt_pos=44
+pkt_size=77634
+width=1280
+height=720
+pix_fmt=yuv420p10le
+sample_aspect_ratio=1:1
+pict_type=I
+coded_picture_number=0
+display_picture_number=0
+interlaced_frame=0
+top_field_first=0
+repeat_pict=0
+color_range=tv
+color_space=bt2020nc
+color_primaries=bt2020
+color_transfer=smpte2084
+chroma_location=left
+[SIDE_DATA]
+side_data_type=Mastering display metadata
+red_x=13250/50000
+red_y=34500/50000
+green_x=7500/50000
+green_y=3000/50000
+blue_x=34000/50000
+blue_y=16000/50000
+white_point_x=15635/50000
+white_point_y=16450/50000
+min_luminance=50/10000
+max_luminance=10000000/10000
+[/SIDE_DATA]
+[SIDE_DATA]
+side_data_type=Content light level metadata
+max_content=1000
+max_average=200
+[/SIDE_DATA]
+[SIDE_DATA]
+side_data_type=HDR Dynamic Metadata SMPTE2094-40 (HDR10+)
+application version=1
+num_windows=1
+targeted_system_display_maximum_luminance=400/1
+maxscl=3340/100000
+maxscl=2870/100000
+maxscl=2720/100000
+average_maxrgb=510/100000
+num_distribution_maxrgb_percentiles=9
+distribution_maxrgb_percentage=1
+distribution_maxrgb_percentile=30/100000
+distribution_maxrgb_percentage=5
+distribution_maxrgb_percentile=2940/100000
+distribution_maxrgb_percentage=10
+distribution_maxrgb_percentile=255/100000
+distribution_maxrgb_percentage=25
+distribution_maxrgb_percentile=70/100000
+distribution_maxrgb_percentage=50
+distribution_maxrgb_percentile=1340/100000
+distribution_maxrgb_percentage=75
+distribution_maxrgb_percentile=1600/100000
+distribution_maxrgb_percentage=90
+distribution_maxrgb_percentile=1850/100000
+distribution_maxrgb_percentage=95
+distribution_maxrgb_percentile=1950/100000
+distribution_maxrgb_percentage=99
+distribution_maxrgb_percentile=2940/100000
+fraction_bright_pixels=1/1000
+knee_point_x=0/4095
+knee_point_y=0/4095
+num_bezier_curve_anchors=9
+bezier_curve_anchors=102/1023
+bezier_curve_anchors=205/1023
+bezier_curve_anchors=307/1023
+bezier_curve_anchors=410/1023
+bezier_curve_anchors=512/1023
+bezier_curve_anchors=614/1023
+bezier_curve_anchors=717/1023
+bezier_curve_anchors=819/1023
+bezier_curve_anchors=922/1023
+[/SIDE_DATA]
+[/FRAME]
\ No newline at end of file