diff mbox series

[FFmpeg-devel] avformat/matroska: Support HDR10+ metadata in Matroska.

Message ID 20210817224028.2867940-1-izadi@google.com
State New
Headers show
Series [FFmpeg-devel] avformat/matroska: Support HDR10+ metadata in Matroska. | expand

Checks

Context Check Description
andriy/make_x86 success Make finished
andriy/make_fate_x86 fail Make fate failed

Commit Message

Mohammad Izadi Aug. 17, 2021, 10:40 p.m. UTC
From: Gyan Doshi <ffmpeg@gyani.pro>

The fate test file can be found here: https://drive.google.com/file/d/1jGW3f94rglLfr5WGmMQe3SEnp1YkbMRy/view?usp=drivesdk
The video file needs to be copied to fate-suite/mkv/
---
 libavcodec/dynamic_hdr10_plus.c             | 273 +++++++++++++++++---
 libavcodec/dynamic_hdr10_plus.h             |  35 ++-
 libavformat/matroska.h                      |   5 +
 libavformat/matroskadec.c                   |  30 ++-
 libavformat/matroskaenc.c                   |  47 ++--
 tests/fate/matroska.mak                     |   6 +
 tests/ref/fate/matroska-hdr10-plus-metadata | 150 +++++++++++
 7 files changed, 484 insertions(+), 62 deletions(-)
 create mode 100644 tests/ref/fate/matroska-hdr10-plus-metadata

Comments

Gyan Doshi Aug. 18, 2021, 4:40 a.m. UTC | #1
On 2021-08-18 04:10 am, Mohammad Izadi wrote:
> From: Gyan Doshi <ffmpeg@gyani.pro>

Can you refresh my memory on how I'm involved?

>
> The fate test file can be found here: https://drive.google.com/file/d/1jGW3f94rglLfr5WGmMQe3SEnp1YkbMRy/view?usp=drivesdk
> The video file needs to be copied to fate-suite/mkv/
> ---
>   libavcodec/dynamic_hdr10_plus.c             | 273 +++++++++++++++++---
>   libavcodec/dynamic_hdr10_plus.h             |  35 ++-
>   libavformat/matroska.h                      |   5 +
>   libavformat/matroskadec.c                   |  30 ++-
>   libavformat/matroskaenc.c                   |  47 ++--
>   tests/fate/matroska.mak                     |   6 +
>   tests/ref/fate/matroska-hdr10-plus-metadata | 150 +++++++++++
>   7 files changed, 484 insertions(+), 62 deletions(-)
>   create mode 100644 tests/ref/fate/matroska-hdr10-plus-metadata
>
> diff --git a/libavcodec/dynamic_hdr10_plus.c b/libavcodec/dynamic_hdr10_plus.c
> index a602e606ed..df7828a476 100644
> --- a/libavcodec/dynamic_hdr10_plus.c
> +++ b/libavcodec/dynamic_hdr10_plus.c
> @@ -18,6 +18,12 @@
>   
>   #include "dynamic_hdr10_plus.h"
>   #include "get_bits.h"
> +#include "put_bits.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;
> @@ -27,8 +33,8 @@ static const int32_t knee_point_den = 4095;
>   static const int32_t bezier_anchor_den = 1023;
>   static const int32_t saturation_weight_den = 8;
>   
> -int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t *data,
> -                                             int size)
> +int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus* s, const uint8_t* data,
> +					     int size)
>   {
>       GetBitContext gbc, *gb = &gbc;
>       int ret;
> @@ -40,7 +46,9 @@ int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
>       if (ret < 0)
>           return ret;
>   
> -    s->application_version = get_bits(gb, 8);
> +    if (get_bits_left(gb) < 8)
> +        return AVERROR_INVALIDDATA;
> +     s->application_version = get_bits(gb, 8);
>   
>       if (get_bits_left(gb) < 2)
>           return AVERROR_INVALIDDATA;
> @@ -56,15 +64,11 @@ int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
>       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};
> +        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);
> @@ -78,8 +82,7 @@ int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
>       if (get_bits_left(gb) < 28)
>           return AVERROR(EINVAL);
>   
> -    s->targeted_system_display_maximum_luminance =
> -        (AVRational){get_bits_long(gb, 27), luminance_den};
> +    s->targeted_system_display_maximum_luminance = (AVRational) { get_bits_long(gb, 27), luminance_den };
>       s->targeted_system_display_actual_peak_luminance_flag = get_bits1(gb);
>   
>       if (s->targeted_system_display_actual_peak_luminance_flag) {
> @@ -99,38 +102,33 @@ int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
>   
>           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};
> +                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];
> +        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->maxscl[i] = (AVRational) { get_bits(gb, 17), rgb_den };
>           }
> -        params->average_maxrgb =
> -            (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))
> +        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};
> +            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};
> +        params->fraction_bright_pixels = (AVRational) { get_bits(gb, 10), fraction_pixel_den };
>       }
>       if (get_bits_left(gb) < 1)
>           return AVERROR(EINVAL);
> @@ -152,14 +150,13 @@ int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
>   
>           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};
> +                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];
> +        AVHDRPlusColorTransformParams* params = &s->params[w];
>           if (get_bits_left(gb) < 1)
>               return AVERROR(EINVAL);
>   
> @@ -168,18 +165,15 @@ int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
>               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->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};
> +                params->bezier_curve_anchors[i] = (AVRational) { get_bits(gb, 10), bezier_anchor_den };
>               }
>           }
>   
> @@ -189,10 +183,215 @@ int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
>           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};
> +            params->color_saturation_weight = (AVRational) { get_bits(gb, 6), saturation_weight_den };
>           }
>       }
>   
>       return 0;
>   }
> +
> +int ff_parse_full_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus* s, const uint8_t* data,
> +					     int size)
> +{
> +    uint8_t country_code;
> +    uint16_t provider_code;
> +    uint16_t provider_oriented_code;
> +    uint8_t application_identifier;
> +    GetBitContext gbc, *gb = &gbc;
> +    int ret, offset;
> +
> +    if (!s)
> +        return AVERROR(ENOMEM);
> +
> +    if (size < 7)
> +        return AVERROR_INVALIDDATA;
> +
> +    ret = init_get_bits8(gb, data, size);
> +    if (ret < 0)
> +        return ret;
> +
> +    country_code = get_bits(gb, 8);
> +    provider_code = get_bits(gb, 16);
> +
> +    if (country_code != usa_country_code ||
> +        provider_code != smpte_provider_code)
> +        return AVERROR_INVALIDDATA;
> +
> +    // A/341 Amendment – 2094-40
> +    provider_oriented_code = get_bits(gb, 16);
> +    application_identifier = get_bits(gb, 8);
> +    if (provider_oriented_code != smpte2094_40_provider_oriented_code ||
> +        application_identifier != smpte2094_40_application_identifier)
> +        return AVERROR_INVALIDDATA;
> +
> +    offset = get_bits_count(gb) / 8;
> +
> +    return ff_parse_itu_t_t35_to_dynamic_hdr10_plus(s, gb->buffer + offset, size - offset);
> +}
> +
> +int ff_itu_t_t35_buffer_size(const AVDynamicHDRPlus* s)
> +{
> +    int bit_count = 0;
> +    int w, size;
> +
> +    if (!s)
> +        return 0;
> +
> +    // 7 bytes for country code, provider code, and user identifier.
> +    bit_count += 56;
> +
> +    if (s->num_windows < 1 || s->num_windows > 3)
> +        return 0;
> +    // Count bits for window params.
> +    bit_count += 2 + ((19 * 8 + 1) * (s->num_windows - 1));
> +
> +    bit_count += 28;
> +    if (s->targeted_system_display_actual_peak_luminance_flag) {
> +        int rows, cols;
> +        rows = s->num_rows_targeted_system_display_actual_peak_luminance;
> +        cols = s->num_cols_targeted_system_display_actual_peak_luminance;
> +        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25)))
> +            return 0;
> +
> +        bit_count += (10 + rows * cols * 4);
> +    }
> +    for (w = 0; w < s->num_windows; w++) {
> +        bit_count += (3 * 17 + 17 + 4 + 10) + (s->params[w].num_distribution_maxrgb_percentiles * 24);
> +    }
> +    bit_count++;
> +
> +    if (s->mastering_display_actual_peak_luminance_flag) {
> +        int rows, cols;
> +        rows = s->num_rows_mastering_display_actual_peak_luminance;
> +        cols = s->num_cols_mastering_display_actual_peak_luminance;
> +        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25)))
> +            return 0;
> +
> +        bit_count += (10 + rows * cols * 4);
> +    }
> +
> +    for (w = 0; w < s->num_windows; w++) {
> +        bit_count++;
> +        if (s->params[w].tone_mapping_flag)
> +            bit_count += (28 + s->params[w].num_bezier_curve_anchors * 10);
> +
> +        bit_count++;
> +        if (s->params[w].color_saturation_mapping_flag)
> +            bit_count += 6;
> +    }
> +    size = bit_count / 8;
> +    if (bit_count % 8 != 0)
> +        size++;
> +    return size;
> +}
> +
> +int ff_write_dynamic_hdr10_plus_to_full_itu_t_t35(const AVDynamicHDRPlus* s, uint8_t** data, size_t* size)
> +{
> +    int w, i, j;
> +    PutBitContext pbc, *pb = &pbc;
> +
> +    if (!s || !size)
> +        return AVERROR(EINVAL);
> +
> +    *size = ff_itu_t_t35_buffer_size(s);
> +    if (*size <= 0)
> +        return AVERROR(EINVAL);
> +    *data = av_mallocz(*size);
> +    init_put_bits(pb, *data, *size);
> +    if (put_bits_left(pb) < *size) {
> +        av_freep(data);
> +        return AVERROR(EINVAL);
> +    }
> +    put_bits(pb, 8, usa_country_code);
> +
> +    put_bits(pb, 16, smpte_provider_code);
> +    put_bits(pb, 16, smpte2094_40_provider_oriented_code);
> +    put_bits(pb, 8, smpte2094_40_application_identifier);
> +    put_bits(pb, 8, s->application_version);
> +
> +    put_bits(pb, 2, s->num_windows);
> +
> +    for (w = 1; w < s->num_windows; w++) {
> +        put_bits(pb, 16, s->params[w].window_upper_left_corner_x.num / s->params[w].window_upper_left_corner_x.den);
> +        put_bits(pb, 16, s->params[w].window_upper_left_corner_y.num / s->params[w].window_upper_left_corner_y.den);
> +        put_bits(pb, 16, s->params[w].window_lower_right_corner_x.num / s->params[w].window_lower_right_corner_x.den);
> +        put_bits(pb, 16, s->params[w].window_lower_right_corner_y.num / s->params[w].window_lower_right_corner_y.den);
> +        put_bits(pb, 16, s->params[w].center_of_ellipse_x);
> +        put_bits(pb, 16, s->params[w].center_of_ellipse_y);
> +        put_bits(pb, 8, s->params[w].rotation_angle);
> +        put_bits(pb, 16, s->params[w].semimajor_axis_internal_ellipse);
> +        put_bits(pb, 16, s->params[w].semimajor_axis_external_ellipse);
> +        put_bits(pb, 16, s->params[w].semiminor_axis_external_ellipse);
> +        put_bits(pb, 1, s->params[w].overlap_process_option);
> +    }
> +    put_bits(pb, 27,
> +             s->targeted_system_display_maximum_luminance.num * luminance_den / s->targeted_system_display_maximum_luminance.den);
> +    put_bits(pb, 1, s->targeted_system_display_actual_peak_luminance_flag);
> +    if (s->targeted_system_display_actual_peak_luminance_flag) {
> +        int rows, cols;
> +        rows = s->num_rows_targeted_system_display_actual_peak_luminance;
> +        cols = s->num_cols_targeted_system_display_actual_peak_luminance;
> +        put_bits(pb, 5, rows);
> +        put_bits(pb, 5, cols);
> +        for (i = 0; i < rows; i++) {
> +            for (j = 0; j < cols; j++) {
> +                put_bits(
> +                    pb, 4,
> +                    s->targeted_system_display_actual_peak_luminance[i][j].num * peak_luminance_den / s->targeted_system_display_actual_peak_luminance[i][j].den);
> +            }
> +        }
> +    }
> +    for (w = 0; w < s->num_windows; w++) {
> +        for (i = 0; i < 3; i++) {
> +            put_bits(pb, 17,
> +                     s->params[w].maxscl[i].num * rgb_den / s->params[w].maxscl[i].den);
> +        }
> +        put_bits(pb, 17,
> +                 s->params[w].average_maxrgb.num * rgb_den / s->params[w].average_maxrgb.den);
> +        put_bits(pb, 4, s->params[w].num_distribution_maxrgb_percentiles);
> +
> +        for (i = 0; i < s->params[w].num_distribution_maxrgb_percentiles; i++) {
> +            put_bits(pb, 7, s->params[w].distribution_maxrgb[i].percentage);
> +            put_bits(pb, 17,
> +                     s->params[w].distribution_maxrgb[i].percentile.num * rgb_den / s->params[w].distribution_maxrgb[i].percentile.den);
> +        }
> +        put_bits(pb, 10,
> +                 s->params[w].fraction_bright_pixels.num * fraction_pixel_den / s->params[w].fraction_bright_pixels.den);
> +    }
> +    put_bits(pb, 1, s->mastering_display_actual_peak_luminance_flag);
> +    if (s->mastering_display_actual_peak_luminance_flag) {
> +        int rows, cols;
> +        rows = s->num_rows_mastering_display_actual_peak_luminance;
> +        cols = s->num_cols_mastering_display_actual_peak_luminance;
> +        put_bits(pb, 5, rows);
> +        put_bits(pb, 5, cols);
> +        for (i = 0; i < rows; i++) {
> +            for (j = 0; j < cols; j++) {
> +                put_bits(
> +                    pb, 4,
> +                    s->mastering_display_actual_peak_luminance[i][j].num * peak_luminance_den / s->mastering_display_actual_peak_luminance[i][j].den);
> +            }
> +        }
> +    }
> +
> +    for (w = 0; w < s->num_windows; w++) {
> +        put_bits(pb, 1, s->params[w].tone_mapping_flag);
> +        if (s->params[w].tone_mapping_flag) {
> +            put_bits(pb, 12,
> +                     s->params[w].knee_point_x.num * knee_point_den / s->params[w].knee_point_x.den);
> +            put_bits(pb, 12,
> +                     s->params[w].knee_point_y.num * knee_point_den / s->params[w].knee_point_y.den);
> +            put_bits(pb, 4, s->params[w].num_bezier_curve_anchors);
> +            for (i = 0; i < s->params[w].num_bezier_curve_anchors; i++) {
> +                put_bits(pb, 10,
> +                         s->params[w].bezier_curve_anchors[i].num * bezier_anchor_den / s->params[w].bezier_curve_anchors[i].den);
> +            }
> +        }
> +        put_bits(pb, 1, s->params[w].color_saturation_mapping_flag);
> +        if (s->params[w].color_saturation_mapping_flag)
> +            put_bits(pb, 6,
> +                     s->params[w].color_saturation_weight.num * saturation_weight_den / s->params[w].color_saturation_weight.den);
> +    }
> +    flush_put_bits(pb);
> +    return 0;
> +}
> diff --git a/libavcodec/dynamic_hdr10_plus.h b/libavcodec/dynamic_hdr10_plus.h
> index cd7acf0432..dafe548d2d 100644
> --- a/libavcodec/dynamic_hdr10_plus.h
> +++ b/libavcodec/dynamic_hdr10_plus.h
> @@ -29,7 +29,38 @@
>    *
>    * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
>    */
> -int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t *data,
> -                                             int size);
> +int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus* s, const uint8_t* data,
> +					     int size);
> +
> +/**
> + * Parse the user data registered ITU-T T.35 with header to AVDynamicHDRPlus. At first check
> + * the header if the provider code is SMPTE-2094-40. Then will parse the data to AVDynamicHDRPlus.
> + * @param s A pointer containing the decoded AVDynamicHDRPlus structure.
> + * @param data The byte array containing the raw ITU-T T.35 data with header.
> + * @param size Size of the data array in bytes.
> + *
> + * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
> + */
> +int ff_parse_full_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus* s, const uint8_t* data,
> +					     int size);
> +
> +/**
> + * Get the size of buffer required to save the encoded bit stream of
> + * AVDynamicHDRPlus in the form of the user data registered ITU-T T.35.
> + * @param s The AVDynamicHDRPlus structure.
> + *
> + * @return The size of bit stream required for encoding. 0 if the data is invalid.
> + */
> +int ff_itu_t_t35_buffer_size(const AVDynamicHDRPlus* s);
> +
> +/**
> + * Encode and write AVDynamicHDRPlus to the user data registered ITU-T T.3 with header (containing the provider code).
> + * @param s A pointer containing the AVDynamicHDRPlus structure.
> + * @param data The byte array containing the raw ITU-T T.35 data with header.
> + * @param size The size of the raw ITU-T T.35 data.
> + *
> + * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
> + */
> +int ff_write_dynamic_hdr10_plus_to_full_itu_t_t35(const AVDynamicHDRPlus* s, uint8_t** data, size_t* size);
>   
>   #endif /* AVCODEC_DYNAMIC_HDR10_PLUS_H */
> diff --git a/libavformat/matroska.h b/libavformat/matroska.h
> index 2d04a6838b..37c60cccf7 100644
> --- a/libavformat/matroska.h
> +++ b/libavformat/matroska.h
> @@ -351,6 +351,11 @@ typedef enum {
>     MATROSKA_VIDEO_PROJECTION_TYPE_MESH               = 3,
>   } MatroskaVideoProjectionType;
>   
> +typedef enum {
> +  MATROSKA_BLOCK_ADD_ID_DEFAULT                     = 0,
> +  MATROSKA_BLOCK_ADD_ID_DYNAMIC_HDR10_PLUS          = 4,
> +} MatroskaBlockAddID;
> +
>   /*
>    * Matroska Codec IDs, strings
>    */
> diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
> index fdfcc86aeb..e8860cc214 100644
> --- a/libavformat/matroskadec.c
> +++ b/libavformat/matroskadec.c
> @@ -46,6 +46,7 @@
>   #include "libavutil/spherical.h"
>   
>   #include "libavcodec/bytestream.h"
> +#include "libavcodec/dynamic_hdr10_plus.h"
>   #include "libavcodec/flac.h"
>   #include "libavcodec/mpeg4audio.h"
>   #include "libavcodec/packet_internal.h"
> @@ -3528,15 +3529,28 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska,
>       pkt->stream_index = st->index;
>   
>       if (additional_size > 0) {
> -        uint8_t *side_data = av_packet_new_side_data(pkt,
> -                                                     AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
> -                                                     additional_size + 8);
> -        if (!side_data) {
> -            av_packet_unref(pkt);
> -            return AVERROR(ENOMEM);
> +        if (additional_id == MATROSKA_BLOCK_ADD_ID_DYNAMIC_HDR10_PLUS) {
> +            AVDynamicHDRPlus hdr10_plus;
> +            if (!ff_parse_full_itu_t_t35_to_dynamic_hdr10_plus(&hdr10_plus, additional, additional_size)) {
> +                uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_DYNAMIC_HDR10_PLUS, sizeof(hdr10_plus));
> +                if (!side_data) {
> +                    av_packet_unref(pkt);
> +                    av_free(pkt);
> +                    return AVERROR(ENOMEM);
> +                }
> +                memcpy(side_data, (uint8_t*)(&hdr10_plus), sizeof(hdr10_plus));
> +                }
> +	} else {
> +	    uint8_t *side_data = av_packet_new_side_data(pkt,
> +                                                         AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
> +                                                         additional_size + 8);
> +            if (!side_data) {
> +                av_packet_unref(pkt);
> +                return AVERROR(ENOMEM);
> +            }
> +            AV_WB64(side_data, additional_id);
> +            memcpy(side_data + 8, additional, additional_size);
>           }
> -        AV_WB64(side_data, additional_id);
> -        memcpy(side_data + 8, additional, additional_size);
>       }
>   
>       if (discard_padding) {
> diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
> index 899a3388cd..832d837ed7 100644
> --- a/libavformat/matroskaenc.c
> +++ b/libavformat/matroskaenc.c
> @@ -51,6 +51,7 @@
>   #include "libavutil/samplefmt.h"
>   #include "libavutil/stereo3d.h"
>   
> +#include "libavcodec/dynamic_hdr10_plus.h"
>   #include "libavcodec/xiph.h"
>   #include "libavcodec/mpeg4audio.h"
>   
> @@ -2029,14 +2030,15 @@ static int mkv_write_block(AVFormatContext *s, AVIOContext *pb,
>       MatroskaMuxContext *mkv = s->priv_data;
>       AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar;
>       mkv_track *track = &mkv->tracks[pkt->stream_index];
> -    uint8_t *data = NULL, *side_data = NULL;
> -    size_t side_data_size;
> +    uint8_t *data = NULL, *side_data = NULL, *hdr10_plus_itu_t_t35 = NULL;
> +    size_t side_data_size, hdr10_plus_itu_t_t35_size = 0;
>       int err = 0, offset = 0, size = pkt->size;
>       int64_t ts = track->write_dts ? pkt->dts : pkt->pts;
>       uint64_t additional_id;
>       int64_t discard_padding = 0;
>       unsigned track_number = track->track_num;
>       ebml_master block_group, block_additions, block_more;
> +    int use_blockgroup = 0;
>   
>       ts += track->ts_offset;
>   
> @@ -2084,13 +2086,18 @@ static int mkv_write_block(AVFormatContext *s, AVIOContext *pb,
>                                          (AVRational){1, par->sample_rate},
>                                          (AVRational){1, 1000000000});
>       }
> -
>       side_data = av_packet_get_side_data(pkt,
> +                                        AV_PKT_DATA_DYNAMIC_HDR10_PLUS,
> +                                        &side_data_size);
> +    if (side_data && side_data_size > 0)
> +        ff_write_dynamic_hdr10_plus_to_full_itu_t_t35((AVDynamicHDRPlus*)side_data, &hdr10_plus_itu_t_t35, &hdr10_plus_itu_t_t35_size);
> +
> +  side_data = av_packet_get_side_data(pkt,
>                                           AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
>                                           &side_data_size);
>       if (side_data) {
> -        // Only the Codec-specific BlockMore (id == 1) is currently supported.
> -        if (side_data_size < 8 || (additional_id = AV_RB64(side_data)) != 1) {
> +        // Only the Codec-specific BlockMore (id == 1) and HDR10+ BlockMore (id == 4) are currently supported.
> +        if (side_data_size < 8 || (additional_id = AV_RB64(side_data)) != 1 || !hdr10_plus_itu_t_t35_size) {
>               side_data_size = 0;
>           } else {
>               side_data      += 8;
> @@ -2098,7 +2105,8 @@ static int mkv_write_block(AVFormatContext *s, AVIOContext *pb,
>           }
>       }
>   
> -    if (side_data_size || discard_padding) {
> +    use_blockgroup = hdr10_plus_itu_t_t35_size || side_data_size || discard_padding;
> +    if (use_blockgroup) {
>           block_group = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP, 0);
>           blockid = MATROSKA_ID_BLOCK;
>       }
> @@ -2119,18 +2127,27 @@ static int mkv_write_block(AVFormatContext *s, AVIOContext *pb,
>       if (discard_padding)
>           put_ebml_sint(pb, MATROSKA_ID_DISCARDPADDING, discard_padding);
>   
> -    if (side_data_size) {
> +    if (side_data_size || hdr10_plus_itu_t_t35_size) {
>           block_additions = start_ebml_master(pb, MATROSKA_ID_BLOCKADDITIONS, 0);
> -        block_more = start_ebml_master(pb, MATROSKA_ID_BLOCKMORE, 0);
> -        /* Until dbc50f8a our demuxer used a wrong default value
> -         * of BlockAddID, so we write it unconditionally. */
> -        put_ebml_uint  (pb, MATROSKA_ID_BLOCKADDID, additional_id);
> -        put_ebml_binary(pb, MATROSKA_ID_BLOCKADDITIONAL,
> -                        side_data, side_data_size);
> -        end_ebml_master(pb, block_more);
> +        if (side_data_size) {
> +            block_more = start_ebml_master(pb, MATROSKA_ID_BLOCKMORE, 0);
> +            /* Until dbc50f8a our demuxer used a wrong default value
> +             * of BlockAddID, so we write it unconditionally. */
> +            put_ebml_uint  (pb, MATROSKA_ID_BLOCKADDID, additional_id);
> +            put_ebml_binary(pb, MATROSKA_ID_BLOCKADDITIONAL,
> +                            side_data, side_data_size);
> +            end_ebml_master(pb, block_more);
> +        }
> +        if(hdr10_plus_itu_t_t35_size) {
> +            block_more = start_ebml_master(pb, MATROSKA_ID_BLOCKMORE, 0);
> +            put_ebml_uint(pb, MATROSKA_ID_BLOCKADDID, MATROSKA_BLOCK_ADD_ID_DYNAMIC_HDR10_PLUS);
> +            put_ebml_binary(pb, MATROSKA_ID_BLOCKADDITIONAL,
> +                            hdr10_plus_itu_t_t35, hdr10_plus_itu_t_t35_size);
> +            end_ebml_master(pb, block_more);
> +        }
>           end_ebml_master(pb, block_additions);
>       }
> -    if (side_data_size || discard_padding)
> +    if (use_blockgroup)
>           end_ebml_master(pb, block_group);
>   
>       return 0;
> diff --git a/tests/fate/matroska.mak b/tests/fate/matroska.mak
> index b57765280a..f27b731fa1 100644
> --- a/tests/fate/matroska.mak
> +++ b/tests/fate/matroska.mak
> @@ -75,6 +75,12 @@ FATE_MATROSKA_FFMPEG_FFPROBE-$(call ALLYES, MATROSKA_DEMUXER MATROSKA_MUXER \
>                                  += fate-matroska-spherical-mono-remux
>   fate-matroska-spherical-mono-remux: CMD = transcode matroska $(TARGET_SAMPLES)/mkv/spherical.mkv matroska "-map 0 -map 0 -c copy -disposition:0 -default+forced -disposition:1 -default -default_mode passthrough -color_primaries:1 bt709 -color_trc:1 smpte170m -colorspace:1 bt2020c -color_range:1 pc"  "-map 0 -c copy -t 0" "" "-show_entries stream_side_data_list:stream_disposition=default,forced:stream=color_range,color_space,color_primaries,color_transfer"
>   
> +# The input file of the following test contains HDR10+ metadata and so this
> +# test tests correct encoding and decoding HDR10+ for VP9/MKV.
> +FATE_MATROSKA_FFMPEG_FFPROBE-$(call ALLYES, MATROSKA_DEMUXER MATROSKA_MUXER) \
> +                               += fate-matroska-hdr10-plus-metadata
> +fate-matroska-hdr10-plus-metadata: CMD = transcode matroska $(TARGET_SAMPLES)/mkv/hdr10_plus_vp9_sample.mkv matroska "-map 0 -map 0 -c copy -pixel_format yuv420p10le" "" "" "-show_frames"
> +
>   # The input file of the following test contains Content Light Level as well as
>   # Mastering Display Metadata and so this test tests correct muxing and demuxing
>   # of these. It furthermore also tests that this data is correctly propagated
> diff --git a/tests/ref/fate/matroska-hdr10-plus-metadata b/tests/ref/fate/matroska-hdr10-plus-metadata
> new file mode 100644
> index 0000000000..cbcfc0f41f
> --- /dev/null
> +++ b/tests/ref/fate/matroska-hdr10-plus-metadata
> @@ -0,0 +1,150 @@
> +#tb 0: 1/25
> +#media_type 0: video
> +#codec_id 0: rawvideo
> +#dimensions 0: 1280x720
> +#sar 0: 1/1
> +0,          0,          0,        1,  2764800, 0xf2617bf2
> +[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=40
> +pkt_duration_time=0.040000
> +pkt_pos=582
> +pkt_size=13350
> +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=unknown
> +color_primaries=unknown
> +color_transfer=unknown
> +chroma_location=unspecified
> +[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]
> +[FRAME]
> +media_type=video
> +stream_index=1
> +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=40
> +pkt_duration_time=0.040000
> +pkt_pos=14051
> +pkt_size=13350
> +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=unknown
> +color_primaries=unknown
> +color_transfer=unknown
> +chroma_location=unspecified
> +[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]
Michael Niedermayer Aug. 18, 2021, 8:55 p.m. UTC | #2
On Tue, Aug 17, 2021 at 03:40:28PM -0700, Mohammad Izadi wrote:
> From: Gyan Doshi <ffmpeg@gyani.pro>
> 
> The fate test file can be found here: https://drive.google.com/file/d/1jGW3f94rglLfr5WGmMQe3SEnp1YkbMRy/view?usp=drivesdk
> The video file needs to be copied to fate-suite/mkv/
> ---
>  libavcodec/dynamic_hdr10_plus.c             | 273 +++++++++++++++++---
>  libavcodec/dynamic_hdr10_plus.h             |  35 ++-
>  libavformat/matroska.h                      |   5 +
>  libavformat/matroskadec.c                   |  30 ++-
>  libavformat/matroskaenc.c                   |  47 ++--
>  tests/fate/matroska.mak                     |   6 +
>  tests/ref/fate/matroska-hdr10-plus-metadata | 150 +++++++++++
>  7 files changed, 484 insertions(+), 62 deletions(-)
>  create mode 100644 tests/ref/fate/matroska-hdr10-plus-metadata
> 
> diff --git a/libavcodec/dynamic_hdr10_plus.c b/libavcodec/dynamic_hdr10_plus.c
> index a602e606ed..df7828a476 100644
> --- a/libavcodec/dynamic_hdr10_plus.c
> +++ b/libavcodec/dynamic_hdr10_plus.c
[...]
> +int ff_write_dynamic_hdr10_plus_to_full_itu_t_t35(const AVDynamicHDRPlus* s, uint8_t** data, size_t* size)
[...]
> diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
> index 899a3388cd..832d837ed7 100644
> --- a/libavformat/matroskaenc.c
> +++ b/libavformat/matroskaenc.c
[...]
> @@ -2084,13 +2086,18 @@ static int mkv_write_block(AVFormatContext *s, AVIOContext *pb,
>                                         (AVRational){1, par->sample_rate},
>                                         (AVRational){1, 1000000000});
>      }
> -
>      side_data = av_packet_get_side_data(pkt,
> +                                        AV_PKT_DATA_DYNAMIC_HDR10_PLUS,
> +                                        &side_data_size);
> +    if (side_data && side_data_size > 0)
> +        ff_write_dynamic_hdr10_plus_to_full_itu_t_t35((AVDynamicHDRPlus*)side_data, &hdr10_plus_itu_t_t35, &hdr10_plus_itu_t_t35_size);

This will fail to link with shared libs

[...]
Mohammad Izadi Aug. 18, 2021, 11:23 p.m. UTC | #3
On Tue, Aug 17, 2021 at 9:41 PM Gyan Doshi <ffmpeg@gyani.pro> wrote:

>
>
> On 2021-08-18 04:10 am, Mohammad Izadi wrote:
> > From: Gyan Doshi <ffmpeg@gyani.pro>
>
> Can you refresh my memory on how I'm involved?
>
Don't know. Sent to ffmpeg-devel@ffmpeg.org.

>
> >
> > The fate test file can be found here:
> https://drive.google.com/file/d/1jGW3f94rglLfr5WGmMQe3SEnp1YkbMRy/view?usp=drivesdk
> > The video file needs to be copied to fate-suite/mkv/
> > ---
> >   libavcodec/dynamic_hdr10_plus.c             | 273 +++++++++++++++++---
> >   libavcodec/dynamic_hdr10_plus.h             |  35 ++-
> >   libavformat/matroska.h                      |   5 +
> >   libavformat/matroskadec.c                   |  30 ++-
> >   libavformat/matroskaenc.c                   |  47 ++--
> >   tests/fate/matroska.mak                     |   6 +
> >   tests/ref/fate/matroska-hdr10-plus-metadata | 150 +++++++++++
> >   7 files changed, 484 insertions(+), 62 deletions(-)
> >   create mode 100644 tests/ref/fate/matroska-hdr10-plus-metadata
> >
> > diff --git a/libavcodec/dynamic_hdr10_plus.c
> b/libavcodec/dynamic_hdr10_plus.c
> > index a602e606ed..df7828a476 100644
> > --- a/libavcodec/dynamic_hdr10_plus.c
> > +++ b/libavcodec/dynamic_hdr10_plus.c
> > @@ -18,6 +18,12 @@
> >
> >   #include "dynamic_hdr10_plus.h"
> >   #include "get_bits.h"
> > +#include "put_bits.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;
> > @@ -27,8 +33,8 @@ static const int32_t knee_point_den = 4095;
> >   static const int32_t bezier_anchor_den = 1023;
> >   static const int32_t saturation_weight_den = 8;
> >
> > -int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const
> uint8_t *data,
> > -                                             int size)
> > +int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus* s, const
> uint8_t* data,
> > +                                          int size)
> >   {
> >       GetBitContext gbc, *gb = &gbc;
> >       int ret;
> > @@ -40,7 +46,9 @@ int
> ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
> >       if (ret < 0)
> >           return ret;
> >
> > -    s->application_version = get_bits(gb, 8);
> > +    if (get_bits_left(gb) < 8)
> > +        return AVERROR_INVALIDDATA;
> > +     s->application_version = get_bits(gb, 8);
> >
> >       if (get_bits_left(gb) < 2)
> >           return AVERROR_INVALIDDATA;
> > @@ -56,15 +64,11 @@ int
> ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
> >       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};
> > +        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);
> > @@ -78,8 +82,7 @@ int
> ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
> >       if (get_bits_left(gb) < 28)
> >           return AVERROR(EINVAL);
> >
> > -    s->targeted_system_display_maximum_luminance =
> > -        (AVRational){get_bits_long(gb, 27), luminance_den};
> > +    s->targeted_system_display_maximum_luminance = (AVRational) {
> get_bits_long(gb, 27), luminance_den };
> >       s->targeted_system_display_actual_peak_luminance_flag =
> get_bits1(gb);
> >
> >       if (s->targeted_system_display_actual_peak_luminance_flag) {
> > @@ -99,38 +102,33 @@ int
> ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
> >
> >           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};
> > +                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];
> > +        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->maxscl[i] = (AVRational) { get_bits(gb, 17),
> rgb_den };
> >           }
> > -        params->average_maxrgb =
> > -            (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))
> > +        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};
> > +            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};
> > +        params->fraction_bright_pixels = (AVRational) { get_bits(gb,
> 10), fraction_pixel_den };
> >       }
> >       if (get_bits_left(gb) < 1)
> >           return AVERROR(EINVAL);
> > @@ -152,14 +150,13 @@ int
> ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
> >
> >           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};
> > +                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];
> > +        AVHDRPlusColorTransformParams* params = &s->params[w];
> >           if (get_bits_left(gb) < 1)
> >               return AVERROR(EINVAL);
> >
> > @@ -168,18 +165,15 @@ int
> ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
> >               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->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};
> > +                params->bezier_curve_anchors[i] = (AVRational) {
> get_bits(gb, 10), bezier_anchor_den };
> >               }
> >           }
> >
> > @@ -189,10 +183,215 @@ int
> ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
> >           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};
> > +            params->color_saturation_weight = (AVRational) {
> get_bits(gb, 6), saturation_weight_den };
> >           }
> >       }
> >
> >       return 0;
> >   }
> > +
> > +int ff_parse_full_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus* s,
> const uint8_t* data,
> > +                                          int size)
> > +{
> > +    uint8_t country_code;
> > +    uint16_t provider_code;
> > +    uint16_t provider_oriented_code;
> > +    uint8_t application_identifier;
> > +    GetBitContext gbc, *gb = &gbc;
> > +    int ret, offset;
> > +
> > +    if (!s)
> > +        return AVERROR(ENOMEM);
> > +
> > +    if (size < 7)
> > +        return AVERROR_INVALIDDATA;
> > +
> > +    ret = init_get_bits8(gb, data, size);
> > +    if (ret < 0)
> > +        return ret;
> > +
> > +    country_code = get_bits(gb, 8);
> > +    provider_code = get_bits(gb, 16);
> > +
> > +    if (country_code != usa_country_code ||
> > +        provider_code != smpte_provider_code)
> > +        return AVERROR_INVALIDDATA;
> > +
> > +    // A/341 Amendment – 2094-40
> > +    provider_oriented_code = get_bits(gb, 16);
> > +    application_identifier = get_bits(gb, 8);
> > +    if (provider_oriented_code != smpte2094_40_provider_oriented_code ||
> > +        application_identifier != smpte2094_40_application_identifier)
> > +        return AVERROR_INVALIDDATA;
> > +
> > +    offset = get_bits_count(gb) / 8;
> > +
> > +    return ff_parse_itu_t_t35_to_dynamic_hdr10_plus(s, gb->buffer +
> offset, size - offset);
> > +}
> > +
> > +int ff_itu_t_t35_buffer_size(const AVDynamicHDRPlus* s)
> > +{
> > +    int bit_count = 0;
> > +    int w, size;
> > +
> > +    if (!s)
> > +        return 0;
> > +
> > +    // 7 bytes for country code, provider code, and user identifier.
> > +    bit_count += 56;
> > +
> > +    if (s->num_windows < 1 || s->num_windows > 3)
> > +        return 0;
> > +    // Count bits for window params.
> > +    bit_count += 2 + ((19 * 8 + 1) * (s->num_windows - 1));
> > +
> > +    bit_count += 28;
> > +    if (s->targeted_system_display_actual_peak_luminance_flag) {
> > +        int rows, cols;
> > +        rows =
> s->num_rows_targeted_system_display_actual_peak_luminance;
> > +        cols =
> s->num_cols_targeted_system_display_actual_peak_luminance;
> > +        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25)))
> > +            return 0;
> > +
> > +        bit_count += (10 + rows * cols * 4);
> > +    }
> > +    for (w = 0; w < s->num_windows; w++) {
> > +        bit_count += (3 * 17 + 17 + 4 + 10) +
> (s->params[w].num_distribution_maxrgb_percentiles * 24);
> > +    }
> > +    bit_count++;
> > +
> > +    if (s->mastering_display_actual_peak_luminance_flag) {
> > +        int rows, cols;
> > +        rows = s->num_rows_mastering_display_actual_peak_luminance;
> > +        cols = s->num_cols_mastering_display_actual_peak_luminance;
> > +        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25)))
> > +            return 0;
> > +
> > +        bit_count += (10 + rows * cols * 4);
> > +    }
> > +
> > +    for (w = 0; w < s->num_windows; w++) {
> > +        bit_count++;
> > +        if (s->params[w].tone_mapping_flag)
> > +            bit_count += (28 + s->params[w].num_bezier_curve_anchors *
> 10);
> > +
> > +        bit_count++;
> > +        if (s->params[w].color_saturation_mapping_flag)
> > +            bit_count += 6;
> > +    }
> > +    size = bit_count / 8;
> > +    if (bit_count % 8 != 0)
> > +        size++;
> > +    return size;
> > +}
> > +
> > +int ff_write_dynamic_hdr10_plus_to_full_itu_t_t35(const
> AVDynamicHDRPlus* s, uint8_t** data, size_t* size)
> > +{
> > +    int w, i, j;
> > +    PutBitContext pbc, *pb = &pbc;
> > +
> > +    if (!s || !size)
> > +        return AVERROR(EINVAL);
> > +
> > +    *size = ff_itu_t_t35_buffer_size(s);
> > +    if (*size <= 0)
> > +        return AVERROR(EINVAL);
> > +    *data = av_mallocz(*size);
> > +    init_put_bits(pb, *data, *size);
> > +    if (put_bits_left(pb) < *size) {
> > +        av_freep(data);
> > +        return AVERROR(EINVAL);
> > +    }
> > +    put_bits(pb, 8, usa_country_code);
> > +
> > +    put_bits(pb, 16, smpte_provider_code);
> > +    put_bits(pb, 16, smpte2094_40_provider_oriented_code);
> > +    put_bits(pb, 8, smpte2094_40_application_identifier);
> > +    put_bits(pb, 8, s->application_version);
> > +
> > +    put_bits(pb, 2, s->num_windows);
> > +
> > +    for (w = 1; w < s->num_windows; w++) {
> > +        put_bits(pb, 16, s->params[w].window_upper_left_corner_x.num /
> s->params[w].window_upper_left_corner_x.den);
> > +        put_bits(pb, 16, s->params[w].window_upper_left_corner_y.num /
> s->params[w].window_upper_left_corner_y.den);
> > +        put_bits(pb, 16, s->params[w].window_lower_right_corner_x.num /
> s->params[w].window_lower_right_corner_x.den);
> > +        put_bits(pb, 16, s->params[w].window_lower_right_corner_y.num /
> s->params[w].window_lower_right_corner_y.den);
> > +        put_bits(pb, 16, s->params[w].center_of_ellipse_x);
> > +        put_bits(pb, 16, s->params[w].center_of_ellipse_y);
> > +        put_bits(pb, 8, s->params[w].rotation_angle);
> > +        put_bits(pb, 16, s->params[w].semimajor_axis_internal_ellipse);
> > +        put_bits(pb, 16, s->params[w].semimajor_axis_external_ellipse);
> > +        put_bits(pb, 16, s->params[w].semiminor_axis_external_ellipse);
> > +        put_bits(pb, 1, s->params[w].overlap_process_option);
> > +    }
> > +    put_bits(pb, 27,
> > +             s->targeted_system_display_maximum_luminance.num *
> luminance_den / s->targeted_system_display_maximum_luminance.den);
> > +    put_bits(pb, 1,
> s->targeted_system_display_actual_peak_luminance_flag);
> > +    if (s->targeted_system_display_actual_peak_luminance_flag) {
> > +        int rows, cols;
> > +        rows =
> s->num_rows_targeted_system_display_actual_peak_luminance;
> > +        cols =
> s->num_cols_targeted_system_display_actual_peak_luminance;
> > +        put_bits(pb, 5, rows);
> > +        put_bits(pb, 5, cols);
> > +        for (i = 0; i < rows; i++) {
> > +            for (j = 0; j < cols; j++) {
> > +                put_bits(
> > +                    pb, 4,
> > +
> s->targeted_system_display_actual_peak_luminance[i][j].num *
> peak_luminance_den /
> s->targeted_system_display_actual_peak_luminance[i][j].den);
> > +            }
> > +        }
> > +    }
> > +    for (w = 0; w < s->num_windows; w++) {
> > +        for (i = 0; i < 3; i++) {
> > +            put_bits(pb, 17,
> > +                     s->params[w].maxscl[i].num * rgb_den /
> s->params[w].maxscl[i].den);
> > +        }
> > +        put_bits(pb, 17,
> > +                 s->params[w].average_maxrgb.num * rgb_den /
> s->params[w].average_maxrgb.den);
> > +        put_bits(pb, 4,
> s->params[w].num_distribution_maxrgb_percentiles);
> > +
> > +        for (i = 0; i <
> s->params[w].num_distribution_maxrgb_percentiles; i++) {
> > +            put_bits(pb, 7,
> s->params[w].distribution_maxrgb[i].percentage);
> > +            put_bits(pb, 17,
> > +                     s->params[w].distribution_maxrgb[i].percentile.num
> * rgb_den / s->params[w].distribution_maxrgb[i].percentile.den);
> > +        }
> > +        put_bits(pb, 10,
> > +                 s->params[w].fraction_bright_pixels.num *
> fraction_pixel_den / s->params[w].fraction_bright_pixels.den);
> > +    }
> > +    put_bits(pb, 1, s->mastering_display_actual_peak_luminance_flag);
> > +    if (s->mastering_display_actual_peak_luminance_flag) {
> > +        int rows, cols;
> > +        rows = s->num_rows_mastering_display_actual_peak_luminance;
> > +        cols = s->num_cols_mastering_display_actual_peak_luminance;
> > +        put_bits(pb, 5, rows);
> > +        put_bits(pb, 5, cols);
> > +        for (i = 0; i < rows; i++) {
> > +            for (j = 0; j < cols; j++) {
> > +                put_bits(
> > +                    pb, 4,
> > +
> s->mastering_display_actual_peak_luminance[i][j].num * peak_luminance_den /
> s->mastering_display_actual_peak_luminance[i][j].den);
> > +            }
> > +        }
> > +    }
> > +
> > +    for (w = 0; w < s->num_windows; w++) {
> > +        put_bits(pb, 1, s->params[w].tone_mapping_flag);
> > +        if (s->params[w].tone_mapping_flag) {
> > +            put_bits(pb, 12,
> > +                     s->params[w].knee_point_x.num * knee_point_den /
> s->params[w].knee_point_x.den);
> > +            put_bits(pb, 12,
> > +                     s->params[w].knee_point_y.num * knee_point_den /
> s->params[w].knee_point_y.den);
> > +            put_bits(pb, 4, s->params[w].num_bezier_curve_anchors);
> > +            for (i = 0; i < s->params[w].num_bezier_curve_anchors; i++)
> {
> > +                put_bits(pb, 10,
> > +                         s->params[w].bezier_curve_anchors[i].num *
> bezier_anchor_den / s->params[w].bezier_curve_anchors[i].den);
> > +            }
> > +        }
> > +        put_bits(pb, 1, s->params[w].color_saturation_mapping_flag);
> > +        if (s->params[w].color_saturation_mapping_flag)
> > +            put_bits(pb, 6,
> > +                     s->params[w].color_saturation_weight.num *
> saturation_weight_den / s->params[w].color_saturation_weight.den);
> > +    }
> > +    flush_put_bits(pb);
> > +    return 0;
> > +}
> > diff --git a/libavcodec/dynamic_hdr10_plus.h
> b/libavcodec/dynamic_hdr10_plus.h
> > index cd7acf0432..dafe548d2d 100644
> > --- a/libavcodec/dynamic_hdr10_plus.h
> > +++ b/libavcodec/dynamic_hdr10_plus.h
> > @@ -29,7 +29,38 @@
> >    *
> >    * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
> >    */
> > -int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const
> uint8_t *data,
> > -                                             int size);
> > +int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus* s, const
> uint8_t* data,
> > +                                          int size);
> > +
> > +/**
> > + * Parse the user data registered ITU-T T.35 with header to
> AVDynamicHDRPlus. At first check
> > + * the header if the provider code is SMPTE-2094-40. Then will parse
> the data to AVDynamicHDRPlus.
> > + * @param s A pointer containing the decoded AVDynamicHDRPlus structure.
> > + * @param data The byte array containing the raw ITU-T T.35 data with
> header.
> > + * @param size Size of the data array in bytes.
> > + *
> > + * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
> > + */
> > +int ff_parse_full_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus* s,
> const uint8_t* data,
> > +                                          int size);
> > +
> > +/**
> > + * Get the size of buffer required to save the encoded bit stream of
> > + * AVDynamicHDRPlus in the form of the user data registered ITU-T T.35.
> > + * @param s The AVDynamicHDRPlus structure.
> > + *
> > + * @return The size of bit stream required for encoding. 0 if the data
> is invalid.
> > + */
> > +int ff_itu_t_t35_buffer_size(const AVDynamicHDRPlus* s);
> > +
> > +/**
> > + * Encode and write AVDynamicHDRPlus to the user data registered ITU-T
> T.3 with header (containing the provider code).
> > + * @param s A pointer containing the AVDynamicHDRPlus structure.
> > + * @param data The byte array containing the raw ITU-T T.35 data with
> header.
> > + * @param size The size of the raw ITU-T T.35 data.
> > + *
> > + * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
> > + */
> > +int ff_write_dynamic_hdr10_plus_to_full_itu_t_t35(const
> AVDynamicHDRPlus* s, uint8_t** data, size_t* size);
> >
> >   #endif /* AVCODEC_DYNAMIC_HDR10_PLUS_H */
> > diff --git a/libavformat/matroska.h b/libavformat/matroska.h
> > index 2d04a6838b..37c60cccf7 100644
> > --- a/libavformat/matroska.h
> > +++ b/libavformat/matroska.h
> > @@ -351,6 +351,11 @@ typedef enum {
> >     MATROSKA_VIDEO_PROJECTION_TYPE_MESH               = 3,
> >   } MatroskaVideoProjectionType;
> >
> > +typedef enum {
> > +  MATROSKA_BLOCK_ADD_ID_DEFAULT                     = 0,
> > +  MATROSKA_BLOCK_ADD_ID_DYNAMIC_HDR10_PLUS          = 4,
> > +} MatroskaBlockAddID;
> > +
> >   /*
> >    * Matroska Codec IDs, strings
> >    */
> > diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
> > index fdfcc86aeb..e8860cc214 100644
> > --- a/libavformat/matroskadec.c
> > +++ b/libavformat/matroskadec.c
> > @@ -46,6 +46,7 @@
> >   #include "libavutil/spherical.h"
> >
> >   #include "libavcodec/bytestream.h"
> > +#include "libavcodec/dynamic_hdr10_plus.h"
> >   #include "libavcodec/flac.h"
> >   #include "libavcodec/mpeg4audio.h"
> >   #include "libavcodec/packet_internal.h"
> > @@ -3528,15 +3529,28 @@ static int
> matroska_parse_frame(MatroskaDemuxContext *matroska,
> >       pkt->stream_index = st->index;
> >
> >       if (additional_size > 0) {
> > -        uint8_t *side_data = av_packet_new_side_data(pkt,
> > -
>  AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
> > -                                                     additional_size +
> 8);
> > -        if (!side_data) {
> > -            av_packet_unref(pkt);
> > -            return AVERROR(ENOMEM);
> > +        if (additional_id == MATROSKA_BLOCK_ADD_ID_DYNAMIC_HDR10_PLUS) {
> > +            AVDynamicHDRPlus hdr10_plus;
> > +            if
> (!ff_parse_full_itu_t_t35_to_dynamic_hdr10_plus(&hdr10_plus, additional,
> additional_size)) {
> > +                uint8_t *side_data = av_packet_new_side_data(pkt,
> AV_PKT_DATA_DYNAMIC_HDR10_PLUS, sizeof(hdr10_plus));
> > +                if (!side_data) {
> > +                    av_packet_unref(pkt);
> > +                    av_free(pkt);
> > +                    return AVERROR(ENOMEM);
> > +                }
> > +                memcpy(side_data, (uint8_t*)(&hdr10_plus),
> sizeof(hdr10_plus));
> > +                }
> > +     } else {
> > +         uint8_t *side_data = av_packet_new_side_data(pkt,
> > +
>  AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
> > +
>  additional_size + 8);
> > +            if (!side_data) {
> > +                av_packet_unref(pkt);
> > +                return AVERROR(ENOMEM);
> > +            }
> > +            AV_WB64(side_data, additional_id);
> > +            memcpy(side_data + 8, additional, additional_size);
> >           }
> > -        AV_WB64(side_data, additional_id);
> > -        memcpy(side_data + 8, additional, additional_size);
> >       }
> >
> >       if (discard_padding) {
> > diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
> > index 899a3388cd..832d837ed7 100644
> > --- a/libavformat/matroskaenc.c
> > +++ b/libavformat/matroskaenc.c
> > @@ -51,6 +51,7 @@
> >   #include "libavutil/samplefmt.h"
> >   #include "libavutil/stereo3d.h"
> >
> > +#include "libavcodec/dynamic_hdr10_plus.h"
> >   #include "libavcodec/xiph.h"
> >   #include "libavcodec/mpeg4audio.h"
> >
> > @@ -2029,14 +2030,15 @@ static int mkv_write_block(AVFormatContext *s,
> AVIOContext *pb,
> >       MatroskaMuxContext *mkv = s->priv_data;
> >       AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar;
> >       mkv_track *track = &mkv->tracks[pkt->stream_index];
> > -    uint8_t *data = NULL, *side_data = NULL;
> > -    size_t side_data_size;
> > +    uint8_t *data = NULL, *side_data = NULL, *hdr10_plus_itu_t_t35 =
> NULL;
> > +    size_t side_data_size, hdr10_plus_itu_t_t35_size = 0;
> >       int err = 0, offset = 0, size = pkt->size;
> >       int64_t ts = track->write_dts ? pkt->dts : pkt->pts;
> >       uint64_t additional_id;
> >       int64_t discard_padding = 0;
> >       unsigned track_number = track->track_num;
> >       ebml_master block_group, block_additions, block_more;
> > +    int use_blockgroup = 0;
> >
> >       ts += track->ts_offset;
> >
> > @@ -2084,13 +2086,18 @@ static int mkv_write_block(AVFormatContext *s,
> AVIOContext *pb,
> >                                          (AVRational){1,
> par->sample_rate},
> >                                          (AVRational){1, 1000000000});
> >       }
> > -
> >       side_data = av_packet_get_side_data(pkt,
> > +                                        AV_PKT_DATA_DYNAMIC_HDR10_PLUS,
> > +                                        &side_data_size);
> > +    if (side_data && side_data_size > 0)
> > +
> ff_write_dynamic_hdr10_plus_to_full_itu_t_t35((AVDynamicHDRPlus*)side_data,
> &hdr10_plus_itu_t_t35, &hdr10_plus_itu_t_t35_size);
> > +
> > +  side_data = av_packet_get_side_data(pkt,
> >
>  AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
> >                                           &side_data_size);
> >       if (side_data) {
> > -        // Only the Codec-specific BlockMore (id == 1) is currently
> supported.
> > -        if (side_data_size < 8 || (additional_id = AV_RB64(side_data))
> != 1) {
> > +        // Only the Codec-specific BlockMore (id == 1) and HDR10+
> BlockMore (id == 4) are currently supported.
> > +        if (side_data_size < 8 || (additional_id = AV_RB64(side_data))
> != 1 || !hdr10_plus_itu_t_t35_size) {
> >               side_data_size = 0;
> >           } else {
> >               side_data      += 8;
> > @@ -2098,7 +2105,8 @@ static int mkv_write_block(AVFormatContext *s,
> AVIOContext *pb,
> >           }
> >       }
> >
> > -    if (side_data_size || discard_padding) {
> > +    use_blockgroup = hdr10_plus_itu_t_t35_size || side_data_size ||
> discard_padding;
> > +    if (use_blockgroup) {
> >           block_group = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP, 0);
> >           blockid = MATROSKA_ID_BLOCK;
> >       }
> > @@ -2119,18 +2127,27 @@ static int mkv_write_block(AVFormatContext *s,
> AVIOContext *pb,
> >       if (discard_padding)
> >           put_ebml_sint(pb, MATROSKA_ID_DISCARDPADDING, discard_padding);
> >
> > -    if (side_data_size) {
> > +    if (side_data_size || hdr10_plus_itu_t_t35_size) {
> >           block_additions = start_ebml_master(pb,
> MATROSKA_ID_BLOCKADDITIONS, 0);
> > -        block_more = start_ebml_master(pb, MATROSKA_ID_BLOCKMORE, 0);
> > -        /* Until dbc50f8a our demuxer used a wrong default value
> > -         * of BlockAddID, so we write it unconditionally. */
> > -        put_ebml_uint  (pb, MATROSKA_ID_BLOCKADDID, additional_id);
> > -        put_ebml_binary(pb, MATROSKA_ID_BLOCKADDITIONAL,
> > -                        side_data, side_data_size);
> > -        end_ebml_master(pb, block_more);
> > +        if (side_data_size) {
> > +            block_more = start_ebml_master(pb, MATROSKA_ID_BLOCKMORE,
> 0);
> > +            /* Until dbc50f8a our demuxer used a wrong default value
> > +             * of BlockAddID, so we write it unconditionally. */
> > +            put_ebml_uint  (pb, MATROSKA_ID_BLOCKADDID, additional_id);
> > +            put_ebml_binary(pb, MATROSKA_ID_BLOCKADDITIONAL,
> > +                            side_data, side_data_size);
> > +            end_ebml_master(pb, block_more);
> > +        }
> > +        if(hdr10_plus_itu_t_t35_size) {
> > +            block_more = start_ebml_master(pb, MATROSKA_ID_BLOCKMORE,
> 0);
> > +            put_ebml_uint(pb, MATROSKA_ID_BLOCKADDID,
> MATROSKA_BLOCK_ADD_ID_DYNAMIC_HDR10_PLUS);
> > +            put_ebml_binary(pb, MATROSKA_ID_BLOCKADDITIONAL,
> > +                            hdr10_plus_itu_t_t35,
> hdr10_plus_itu_t_t35_size);
> > +            end_ebml_master(pb, block_more);
> > +        }
> >           end_ebml_master(pb, block_additions);
> >       }
> > -    if (side_data_size || discard_padding)
> > +    if (use_blockgroup)
> >           end_ebml_master(pb, block_group);
> >
> >       return 0;
> > diff --git a/tests/fate/matroska.mak b/tests/fate/matroska.mak
> > index b57765280a..f27b731fa1 100644
> > --- a/tests/fate/matroska.mak
> > +++ b/tests/fate/matroska.mak
> > @@ -75,6 +75,12 @@ FATE_MATROSKA_FFMPEG_FFPROBE-$(call ALLYES,
> MATROSKA_DEMUXER MATROSKA_MUXER \
> >                                  += fate-matroska-spherical-mono-remux
> >   fate-matroska-spherical-mono-remux: CMD = transcode matroska
> $(TARGET_SAMPLES)/mkv/spherical.mkv matroska "-map 0 -map 0 -c copy
> -disposition:0 -default+forced -disposition:1 -default -default_mode
> passthrough -color_primaries:1 bt709 -color_trc:1 smpte170m -colorspace:1
> bt2020c -color_range:1 pc"  "-map 0 -c copy -t 0" "" "-show_entries
> stream_side_data_list:stream_disposition=default,forced:stream=color_range,color_space,color_primaries,color_transfer"
> >
> > +# The input file of the following test contains HDR10+ metadata and so
> this
> > +# test tests correct encoding and decoding HDR10+ for VP9/MKV.
> > +FATE_MATROSKA_FFMPEG_FFPROBE-$(call ALLYES, MATROSKA_DEMUXER
> MATROSKA_MUXER) \
> > +                               += fate-matroska-hdr10-plus-metadata
> > +fate-matroska-hdr10-plus-metadata: CMD = transcode matroska
> $(TARGET_SAMPLES)/mkv/hdr10_plus_vp9_sample.mkv matroska "-map 0 -map 0 -c
> copy -pixel_format yuv420p10le" "" "" "-show_frames"
> > +
> >   # The input file of the following test contains Content Light Level as
> well as
> >   # Mastering Display Metadata and so this test tests correct muxing and
> demuxing
> >   # of these. It furthermore also tests that this data is correctly
> propagated
> > diff --git a/tests/ref/fate/matroska-hdr10-plus-metadata
> b/tests/ref/fate/matroska-hdr10-plus-metadata
> > new file mode 100644
> > index 0000000000..cbcfc0f41f
> > --- /dev/null
> > +++ b/tests/ref/fate/matroska-hdr10-plus-metadata
> > @@ -0,0 +1,150 @@
> > +#tb 0: 1/25
> > +#media_type 0: video
> > +#codec_id 0: rawvideo
> > +#dimensions 0: 1280x720
> > +#sar 0: 1/1
> > +0,          0,          0,        1,  2764800, 0xf2617bf2
> > +[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=40
> > +pkt_duration_time=0.040000
> > +pkt_pos=582
> > +pkt_size=13350
> > +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=unknown
> > +color_primaries=unknown
> > +color_transfer=unknown
> > +chroma_location=unspecified
> > +[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]
> > +[FRAME]
> > +media_type=video
> > +stream_index=1
> > +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=40
> > +pkt_duration_time=0.040000
> > +pkt_pos=14051
> > +pkt_size=13350
> > +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=unknown
> > +color_primaries=unknown
> > +color_transfer=unknown
> > +chroma_location=unspecified
> > +[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]
>
> _______________________________________________
> 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".
>
Gyan Doshi Aug. 19, 2021, 4:04 a.m. UTC | #4
On 2021-08-19 04:53 am, Mohammad Izadi wrote:
> On Tue, Aug 17, 2021 at 9:41 PM Gyan Doshi <ffmpeg@gyani.pro> wrote:
>
>>
>> On 2021-08-18 04:10 am, Mohammad Izadi wrote:
>>> From: Gyan Doshi <ffmpeg@gyani.pro>
>> Can you refresh my memory on how I'm involved?
>>
> Don't know. Sent to ffmpeg-devel@ffmpeg.org.

Then remove it from the commit msg.

Thanks,
Gyan
diff mbox series

Patch

diff --git a/libavcodec/dynamic_hdr10_plus.c b/libavcodec/dynamic_hdr10_plus.c
index a602e606ed..df7828a476 100644
--- a/libavcodec/dynamic_hdr10_plus.c
+++ b/libavcodec/dynamic_hdr10_plus.c
@@ -18,6 +18,12 @@ 
 
 #include "dynamic_hdr10_plus.h"
 #include "get_bits.h"
+#include "put_bits.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;
@@ -27,8 +33,8 @@  static const int32_t knee_point_den = 4095;
 static const int32_t bezier_anchor_den = 1023;
 static const int32_t saturation_weight_den = 8;
 
-int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t *data,
-                                             int size)
+int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus* s, const uint8_t* data,
+					     int size)
 {
     GetBitContext gbc, *gb = &gbc;
     int ret;
@@ -40,7 +46,9 @@  int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
     if (ret < 0)
         return ret;
 
-    s->application_version = get_bits(gb, 8);
+    if (get_bits_left(gb) < 8)
+        return AVERROR_INVALIDDATA;
+     s->application_version = get_bits(gb, 8);
 
     if (get_bits_left(gb) < 2)
         return AVERROR_INVALIDDATA;
@@ -56,15 +64,11 @@  int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
     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};
+        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);
@@ -78,8 +82,7 @@  int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
     if (get_bits_left(gb) < 28)
         return AVERROR(EINVAL);
 
-    s->targeted_system_display_maximum_luminance =
-        (AVRational){get_bits_long(gb, 27), luminance_den};
+    s->targeted_system_display_maximum_luminance = (AVRational) { get_bits_long(gb, 27), luminance_den };
     s->targeted_system_display_actual_peak_luminance_flag = get_bits1(gb);
 
     if (s->targeted_system_display_actual_peak_luminance_flag) {
@@ -99,38 +102,33 @@  int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
 
         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};
+                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];
+        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->maxscl[i] = (AVRational) { get_bits(gb, 17), rgb_den };
         }
-        params->average_maxrgb =
-            (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))
+        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};
+            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};
+        params->fraction_bright_pixels = (AVRational) { get_bits(gb, 10), fraction_pixel_den };
     }
     if (get_bits_left(gb) < 1)
         return AVERROR(EINVAL);
@@ -152,14 +150,13 @@  int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
 
         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};
+                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];
+        AVHDRPlusColorTransformParams* params = &s->params[w];
         if (get_bits_left(gb) < 1)
             return AVERROR(EINVAL);
 
@@ -168,18 +165,15 @@  int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
             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->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};
+                params->bezier_curve_anchors[i] = (AVRational) { get_bits(gb, 10), bezier_anchor_den };
             }
         }
 
@@ -189,10 +183,215 @@  int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t
         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};
+            params->color_saturation_weight = (AVRational) { get_bits(gb, 6), saturation_weight_den };
         }
     }
 
     return 0;
 }
+
+int ff_parse_full_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus* s, const uint8_t* data,
+					     int size)
+{
+    uint8_t country_code;
+    uint16_t provider_code;
+    uint16_t provider_oriented_code;
+    uint8_t application_identifier;
+    GetBitContext gbc, *gb = &gbc;
+    int ret, offset;
+
+    if (!s)
+        return AVERROR(ENOMEM);
+
+    if (size < 7)
+        return AVERROR_INVALIDDATA;
+
+    ret = init_get_bits8(gb, data, size);
+    if (ret < 0)
+        return ret;
+
+    country_code = get_bits(gb, 8);
+    provider_code = get_bits(gb, 16);
+
+    if (country_code != usa_country_code ||
+        provider_code != smpte_provider_code)
+        return AVERROR_INVALIDDATA;
+
+    // A/341 Amendment – 2094-40
+    provider_oriented_code = get_bits(gb, 16);
+    application_identifier = get_bits(gb, 8);
+    if (provider_oriented_code != smpte2094_40_provider_oriented_code ||
+        application_identifier != smpte2094_40_application_identifier)
+        return AVERROR_INVALIDDATA;
+
+    offset = get_bits_count(gb) / 8;
+
+    return ff_parse_itu_t_t35_to_dynamic_hdr10_plus(s, gb->buffer + offset, size - offset);
+}
+
+int ff_itu_t_t35_buffer_size(const AVDynamicHDRPlus* s)
+{
+    int bit_count = 0;
+    int w, size;
+
+    if (!s)
+        return 0;
+
+    // 7 bytes for country code, provider code, and user identifier.
+    bit_count += 56;
+
+    if (s->num_windows < 1 || s->num_windows > 3)
+        return 0;
+    // Count bits for window params.
+    bit_count += 2 + ((19 * 8 + 1) * (s->num_windows - 1));
+
+    bit_count += 28;
+    if (s->targeted_system_display_actual_peak_luminance_flag) {
+        int rows, cols;
+        rows = s->num_rows_targeted_system_display_actual_peak_luminance;
+        cols = s->num_cols_targeted_system_display_actual_peak_luminance;
+        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25)))
+            return 0;
+
+        bit_count += (10 + rows * cols * 4);
+    }
+    for (w = 0; w < s->num_windows; w++) {
+        bit_count += (3 * 17 + 17 + 4 + 10) + (s->params[w].num_distribution_maxrgb_percentiles * 24);
+    }
+    bit_count++;
+
+    if (s->mastering_display_actual_peak_luminance_flag) {
+        int rows, cols;
+        rows = s->num_rows_mastering_display_actual_peak_luminance;
+        cols = s->num_cols_mastering_display_actual_peak_luminance;
+        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25)))
+            return 0;
+
+        bit_count += (10 + rows * cols * 4);
+    }
+
+    for (w = 0; w < s->num_windows; w++) {
+        bit_count++;
+        if (s->params[w].tone_mapping_flag)
+            bit_count += (28 + s->params[w].num_bezier_curve_anchors * 10);
+
+        bit_count++;
+        if (s->params[w].color_saturation_mapping_flag)
+            bit_count += 6;
+    }
+    size = bit_count / 8;
+    if (bit_count % 8 != 0)
+        size++;
+    return size;
+}
+
+int ff_write_dynamic_hdr10_plus_to_full_itu_t_t35(const AVDynamicHDRPlus* s, uint8_t** data, size_t* size)
+{
+    int w, i, j;
+    PutBitContext pbc, *pb = &pbc;
+
+    if (!s || !size)
+        return AVERROR(EINVAL);
+
+    *size = ff_itu_t_t35_buffer_size(s);
+    if (*size <= 0)
+        return AVERROR(EINVAL);
+    *data = av_mallocz(*size);
+    init_put_bits(pb, *data, *size);
+    if (put_bits_left(pb) < *size) {
+        av_freep(data);
+        return AVERROR(EINVAL);
+    }
+    put_bits(pb, 8, usa_country_code);
+
+    put_bits(pb, 16, smpte_provider_code);
+    put_bits(pb, 16, smpte2094_40_provider_oriented_code);
+    put_bits(pb, 8, smpte2094_40_application_identifier);
+    put_bits(pb, 8, s->application_version);
+
+    put_bits(pb, 2, s->num_windows);
+
+    for (w = 1; w < s->num_windows; w++) {
+        put_bits(pb, 16, s->params[w].window_upper_left_corner_x.num / s->params[w].window_upper_left_corner_x.den);
+        put_bits(pb, 16, s->params[w].window_upper_left_corner_y.num / s->params[w].window_upper_left_corner_y.den);
+        put_bits(pb, 16, s->params[w].window_lower_right_corner_x.num / s->params[w].window_lower_right_corner_x.den);
+        put_bits(pb, 16, s->params[w].window_lower_right_corner_y.num / s->params[w].window_lower_right_corner_y.den);
+        put_bits(pb, 16, s->params[w].center_of_ellipse_x);
+        put_bits(pb, 16, s->params[w].center_of_ellipse_y);
+        put_bits(pb, 8, s->params[w].rotation_angle);
+        put_bits(pb, 16, s->params[w].semimajor_axis_internal_ellipse);
+        put_bits(pb, 16, s->params[w].semimajor_axis_external_ellipse);
+        put_bits(pb, 16, s->params[w].semiminor_axis_external_ellipse);
+        put_bits(pb, 1, s->params[w].overlap_process_option);
+    }
+    put_bits(pb, 27,
+             s->targeted_system_display_maximum_luminance.num * luminance_den / s->targeted_system_display_maximum_luminance.den);
+    put_bits(pb, 1, s->targeted_system_display_actual_peak_luminance_flag);
+    if (s->targeted_system_display_actual_peak_luminance_flag) {
+        int rows, cols;
+        rows = s->num_rows_targeted_system_display_actual_peak_luminance;
+        cols = s->num_cols_targeted_system_display_actual_peak_luminance;
+        put_bits(pb, 5, rows);
+        put_bits(pb, 5, cols);
+        for (i = 0; i < rows; i++) {
+            for (j = 0; j < cols; j++) {
+                put_bits(
+                    pb, 4,
+                    s->targeted_system_display_actual_peak_luminance[i][j].num * peak_luminance_den / s->targeted_system_display_actual_peak_luminance[i][j].den);
+            }
+        }
+    }
+    for (w = 0; w < s->num_windows; w++) {
+        for (i = 0; i < 3; i++) {
+            put_bits(pb, 17,
+                     s->params[w].maxscl[i].num * rgb_den / s->params[w].maxscl[i].den);
+        }
+        put_bits(pb, 17,
+                 s->params[w].average_maxrgb.num * rgb_den / s->params[w].average_maxrgb.den);
+        put_bits(pb, 4, s->params[w].num_distribution_maxrgb_percentiles);
+
+        for (i = 0; i < s->params[w].num_distribution_maxrgb_percentiles; i++) {
+            put_bits(pb, 7, s->params[w].distribution_maxrgb[i].percentage);
+            put_bits(pb, 17,
+                     s->params[w].distribution_maxrgb[i].percentile.num * rgb_den / s->params[w].distribution_maxrgb[i].percentile.den);
+        }
+        put_bits(pb, 10,
+                 s->params[w].fraction_bright_pixels.num * fraction_pixel_den / s->params[w].fraction_bright_pixels.den);
+    }
+    put_bits(pb, 1, s->mastering_display_actual_peak_luminance_flag);
+    if (s->mastering_display_actual_peak_luminance_flag) {
+        int rows, cols;
+        rows = s->num_rows_mastering_display_actual_peak_luminance;
+        cols = s->num_cols_mastering_display_actual_peak_luminance;
+        put_bits(pb, 5, rows);
+        put_bits(pb, 5, cols);
+        for (i = 0; i < rows; i++) {
+            for (j = 0; j < cols; j++) {
+                put_bits(
+                    pb, 4,
+                    s->mastering_display_actual_peak_luminance[i][j].num * peak_luminance_den / s->mastering_display_actual_peak_luminance[i][j].den);
+            }
+        }
+    }
+
+    for (w = 0; w < s->num_windows; w++) {
+        put_bits(pb, 1, s->params[w].tone_mapping_flag);
+        if (s->params[w].tone_mapping_flag) {
+            put_bits(pb, 12,
+                     s->params[w].knee_point_x.num * knee_point_den / s->params[w].knee_point_x.den);
+            put_bits(pb, 12,
+                     s->params[w].knee_point_y.num * knee_point_den / s->params[w].knee_point_y.den);
+            put_bits(pb, 4, s->params[w].num_bezier_curve_anchors);
+            for (i = 0; i < s->params[w].num_bezier_curve_anchors; i++) {
+                put_bits(pb, 10,
+                         s->params[w].bezier_curve_anchors[i].num * bezier_anchor_den / s->params[w].bezier_curve_anchors[i].den);
+            }
+        }
+        put_bits(pb, 1, s->params[w].color_saturation_mapping_flag);
+        if (s->params[w].color_saturation_mapping_flag)
+            put_bits(pb, 6,
+                     s->params[w].color_saturation_weight.num * saturation_weight_den / s->params[w].color_saturation_weight.den);
+    }
+    flush_put_bits(pb);
+    return 0;
+}
diff --git a/libavcodec/dynamic_hdr10_plus.h b/libavcodec/dynamic_hdr10_plus.h
index cd7acf0432..dafe548d2d 100644
--- a/libavcodec/dynamic_hdr10_plus.h
+++ b/libavcodec/dynamic_hdr10_plus.h
@@ -29,7 +29,38 @@ 
  *
  * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
  */
-int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t *data,
-                                             int size);
+int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus* s, const uint8_t* data,
+					     int size);
+
+/**
+ * Parse the user data registered ITU-T T.35 with header to AVDynamicHDRPlus. At first check
+ * the header if the provider code is SMPTE-2094-40. Then will parse the data to AVDynamicHDRPlus.
+ * @param s A pointer containing the decoded AVDynamicHDRPlus structure.
+ * @param data The byte array containing the raw ITU-T T.35 data with header.
+ * @param size Size of the data array in bytes.
+ *
+ * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
+ */
+int ff_parse_full_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus* s, const uint8_t* data,
+					     int size);
+
+/**
+ * Get the size of buffer required to save the encoded bit stream of
+ * AVDynamicHDRPlus in the form of the user data registered ITU-T T.35.
+ * @param s The AVDynamicHDRPlus structure.
+ *
+ * @return The size of bit stream required for encoding. 0 if the data is invalid.
+ */
+int ff_itu_t_t35_buffer_size(const AVDynamicHDRPlus* s);
+
+/**
+ * Encode and write AVDynamicHDRPlus to the user data registered ITU-T T.3 with header (containing the provider code).
+ * @param s A pointer containing the AVDynamicHDRPlus structure.
+ * @param data The byte array containing the raw ITU-T T.35 data with header.
+ * @param size The size of the raw ITU-T T.35 data.
+ *
+ * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
+ */
+int ff_write_dynamic_hdr10_plus_to_full_itu_t_t35(const AVDynamicHDRPlus* s, uint8_t** data, size_t* size);
 
 #endif /* AVCODEC_DYNAMIC_HDR10_PLUS_H */
diff --git a/libavformat/matroska.h b/libavformat/matroska.h
index 2d04a6838b..37c60cccf7 100644
--- a/libavformat/matroska.h
+++ b/libavformat/matroska.h
@@ -351,6 +351,11 @@  typedef enum {
   MATROSKA_VIDEO_PROJECTION_TYPE_MESH               = 3,
 } MatroskaVideoProjectionType;
 
+typedef enum {
+  MATROSKA_BLOCK_ADD_ID_DEFAULT                     = 0,
+  MATROSKA_BLOCK_ADD_ID_DYNAMIC_HDR10_PLUS          = 4,
+} MatroskaBlockAddID;
+
 /*
  * Matroska Codec IDs, strings
  */
diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c
index fdfcc86aeb..e8860cc214 100644
--- a/libavformat/matroskadec.c
+++ b/libavformat/matroskadec.c
@@ -46,6 +46,7 @@ 
 #include "libavutil/spherical.h"
 
 #include "libavcodec/bytestream.h"
+#include "libavcodec/dynamic_hdr10_plus.h"
 #include "libavcodec/flac.h"
 #include "libavcodec/mpeg4audio.h"
 #include "libavcodec/packet_internal.h"
@@ -3528,15 +3529,28 @@  static int matroska_parse_frame(MatroskaDemuxContext *matroska,
     pkt->stream_index = st->index;
 
     if (additional_size > 0) {
-        uint8_t *side_data = av_packet_new_side_data(pkt,
-                                                     AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
-                                                     additional_size + 8);
-        if (!side_data) {
-            av_packet_unref(pkt);
-            return AVERROR(ENOMEM);
+        if (additional_id == MATROSKA_BLOCK_ADD_ID_DYNAMIC_HDR10_PLUS) {
+            AVDynamicHDRPlus hdr10_plus;
+            if (!ff_parse_full_itu_t_t35_to_dynamic_hdr10_plus(&hdr10_plus, additional, additional_size)) {
+                uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_DYNAMIC_HDR10_PLUS, sizeof(hdr10_plus));
+                if (!side_data) {
+                    av_packet_unref(pkt);
+                    av_free(pkt);
+                    return AVERROR(ENOMEM);
+                }
+                memcpy(side_data, (uint8_t*)(&hdr10_plus), sizeof(hdr10_plus));
+                }
+	} else {
+	    uint8_t *side_data = av_packet_new_side_data(pkt,
+                                                         AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
+                                                         additional_size + 8);
+            if (!side_data) {
+                av_packet_unref(pkt);
+                return AVERROR(ENOMEM);
+            }
+            AV_WB64(side_data, additional_id);
+            memcpy(side_data + 8, additional, additional_size);
         }
-        AV_WB64(side_data, additional_id);
-        memcpy(side_data + 8, additional, additional_size);
     }
 
     if (discard_padding) {
diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index 899a3388cd..832d837ed7 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -51,6 +51,7 @@ 
 #include "libavutil/samplefmt.h"
 #include "libavutil/stereo3d.h"
 
+#include "libavcodec/dynamic_hdr10_plus.h"
 #include "libavcodec/xiph.h"
 #include "libavcodec/mpeg4audio.h"
 
@@ -2029,14 +2030,15 @@  static int mkv_write_block(AVFormatContext *s, AVIOContext *pb,
     MatroskaMuxContext *mkv = s->priv_data;
     AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar;
     mkv_track *track = &mkv->tracks[pkt->stream_index];
-    uint8_t *data = NULL, *side_data = NULL;
-    size_t side_data_size;
+    uint8_t *data = NULL, *side_data = NULL, *hdr10_plus_itu_t_t35 = NULL;
+    size_t side_data_size, hdr10_plus_itu_t_t35_size = 0;
     int err = 0, offset = 0, size = pkt->size;
     int64_t ts = track->write_dts ? pkt->dts : pkt->pts;
     uint64_t additional_id;
     int64_t discard_padding = 0;
     unsigned track_number = track->track_num;
     ebml_master block_group, block_additions, block_more;
+    int use_blockgroup = 0;
 
     ts += track->ts_offset;
 
@@ -2084,13 +2086,18 @@  static int mkv_write_block(AVFormatContext *s, AVIOContext *pb,
                                        (AVRational){1, par->sample_rate},
                                        (AVRational){1, 1000000000});
     }
-
     side_data = av_packet_get_side_data(pkt,
+                                        AV_PKT_DATA_DYNAMIC_HDR10_PLUS,
+                                        &side_data_size);
+    if (side_data && side_data_size > 0)
+        ff_write_dynamic_hdr10_plus_to_full_itu_t_t35((AVDynamicHDRPlus*)side_data, &hdr10_plus_itu_t_t35, &hdr10_plus_itu_t_t35_size);
+
+  side_data = av_packet_get_side_data(pkt,
                                         AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
                                         &side_data_size);
     if (side_data) {
-        // Only the Codec-specific BlockMore (id == 1) is currently supported.
-        if (side_data_size < 8 || (additional_id = AV_RB64(side_data)) != 1) {
+        // Only the Codec-specific BlockMore (id == 1) and HDR10+ BlockMore (id == 4) are currently supported.
+        if (side_data_size < 8 || (additional_id = AV_RB64(side_data)) != 1 || !hdr10_plus_itu_t_t35_size) {
             side_data_size = 0;
         } else {
             side_data      += 8;
@@ -2098,7 +2105,8 @@  static int mkv_write_block(AVFormatContext *s, AVIOContext *pb,
         }
     }
 
-    if (side_data_size || discard_padding) {
+    use_blockgroup = hdr10_plus_itu_t_t35_size || side_data_size || discard_padding;
+    if (use_blockgroup) {
         block_group = start_ebml_master(pb, MATROSKA_ID_BLOCKGROUP, 0);
         blockid = MATROSKA_ID_BLOCK;
     }
@@ -2119,18 +2127,27 @@  static int mkv_write_block(AVFormatContext *s, AVIOContext *pb,
     if (discard_padding)
         put_ebml_sint(pb, MATROSKA_ID_DISCARDPADDING, discard_padding);
 
-    if (side_data_size) {
+    if (side_data_size || hdr10_plus_itu_t_t35_size) {
         block_additions = start_ebml_master(pb, MATROSKA_ID_BLOCKADDITIONS, 0);
-        block_more = start_ebml_master(pb, MATROSKA_ID_BLOCKMORE, 0);
-        /* Until dbc50f8a our demuxer used a wrong default value
-         * of BlockAddID, so we write it unconditionally. */
-        put_ebml_uint  (pb, MATROSKA_ID_BLOCKADDID, additional_id);
-        put_ebml_binary(pb, MATROSKA_ID_BLOCKADDITIONAL,
-                        side_data, side_data_size);
-        end_ebml_master(pb, block_more);
+        if (side_data_size) {
+            block_more = start_ebml_master(pb, MATROSKA_ID_BLOCKMORE, 0);
+            /* Until dbc50f8a our demuxer used a wrong default value
+             * of BlockAddID, so we write it unconditionally. */
+            put_ebml_uint  (pb, MATROSKA_ID_BLOCKADDID, additional_id);
+            put_ebml_binary(pb, MATROSKA_ID_BLOCKADDITIONAL,
+                            side_data, side_data_size);
+            end_ebml_master(pb, block_more);
+        }
+        if(hdr10_plus_itu_t_t35_size) {
+            block_more = start_ebml_master(pb, MATROSKA_ID_BLOCKMORE, 0);
+            put_ebml_uint(pb, MATROSKA_ID_BLOCKADDID, MATROSKA_BLOCK_ADD_ID_DYNAMIC_HDR10_PLUS);
+            put_ebml_binary(pb, MATROSKA_ID_BLOCKADDITIONAL,
+                            hdr10_plus_itu_t_t35, hdr10_plus_itu_t_t35_size);
+            end_ebml_master(pb, block_more);
+        }
         end_ebml_master(pb, block_additions);
     }
-    if (side_data_size || discard_padding)
+    if (use_blockgroup)
         end_ebml_master(pb, block_group);
 
     return 0;
diff --git a/tests/fate/matroska.mak b/tests/fate/matroska.mak
index b57765280a..f27b731fa1 100644
--- a/tests/fate/matroska.mak
+++ b/tests/fate/matroska.mak
@@ -75,6 +75,12 @@  FATE_MATROSKA_FFMPEG_FFPROBE-$(call ALLYES, MATROSKA_DEMUXER MATROSKA_MUXER \
                                += fate-matroska-spherical-mono-remux
 fate-matroska-spherical-mono-remux: CMD = transcode matroska $(TARGET_SAMPLES)/mkv/spherical.mkv matroska "-map 0 -map 0 -c copy -disposition:0 -default+forced -disposition:1 -default -default_mode passthrough -color_primaries:1 bt709 -color_trc:1 smpte170m -colorspace:1 bt2020c -color_range:1 pc"  "-map 0 -c copy -t 0" "" "-show_entries stream_side_data_list:stream_disposition=default,forced:stream=color_range,color_space,color_primaries,color_transfer"
 
+# The input file of the following test contains HDR10+ metadata and so this
+# test tests correct encoding and decoding HDR10+ for VP9/MKV.
+FATE_MATROSKA_FFMPEG_FFPROBE-$(call ALLYES, MATROSKA_DEMUXER MATROSKA_MUXER) \
+                               += fate-matroska-hdr10-plus-metadata
+fate-matroska-hdr10-plus-metadata: CMD = transcode matroska $(TARGET_SAMPLES)/mkv/hdr10_plus_vp9_sample.mkv matroska "-map 0 -map 0 -c copy -pixel_format yuv420p10le" "" "" "-show_frames"
+
 # The input file of the following test contains Content Light Level as well as
 # Mastering Display Metadata and so this test tests correct muxing and demuxing
 # of these. It furthermore also tests that this data is correctly propagated
diff --git a/tests/ref/fate/matroska-hdr10-plus-metadata b/tests/ref/fate/matroska-hdr10-plus-metadata
new file mode 100644
index 0000000000..cbcfc0f41f
--- /dev/null
+++ b/tests/ref/fate/matroska-hdr10-plus-metadata
@@ -0,0 +1,150 @@ 
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 1280x720
+#sar 0: 1/1
+0,          0,          0,        1,  2764800, 0xf2617bf2
+[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=40
+pkt_duration_time=0.040000
+pkt_pos=582
+pkt_size=13350
+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=unknown
+color_primaries=unknown
+color_transfer=unknown
+chroma_location=unspecified
+[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]
+[FRAME]
+media_type=video
+stream_index=1
+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=40
+pkt_duration_time=0.040000
+pkt_pos=14051
+pkt_size=13350
+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=unknown
+color_primaries=unknown
+color_transfer=unknown
+chroma_location=unspecified
+[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]