diff mbox series

[FFmpeg-devel] Update HDR10+ metadata structure.

Message ID 20200127190832.224284-1-moh.izadi@gmail.com
State Superseded
Headers show
Series [FFmpeg-devel] Update HDR10+ metadata structure. | expand

Checks

Context Check Description
andriy/ffmpeg-patchwork success Make fate finished

Commit Message

Mohammad Izadi Jan. 27, 2020, 7:08 p.m. UTC
From: Mohammad Izadi <izadi@google.com>

Trying to read HDR10+ metadata from HEVC/SEI and pass it to packet side data in the follow-up CLs.
---
 libavutil/hdr_dynamic_metadata.c | 386 +++++++++++++++++++++++++++++++
 libavutil/hdr_dynamic_metadata.h |  51 +++-
 libavutil/version.h              |   2 +-
 3 files changed, 431 insertions(+), 8 deletions(-)

Comments

Carl Eugen Hoyos Jan. 27, 2020, 7:43 p.m. UTC | #1
Am Mo., 27. Jan. 2020 um 20:34 Uhr schrieb Mohammad Izadi <moh.izadi@gmail.com>:

>  /**
>   * Allocate an AVDynamicHDRPlus structure and set its fields to
>   * default values. The resulting struct can be freed using av_freep().
> + * @param gb The input bit stream.
> + * @param size The size of allocated memory for the returned
> + *        AVDynamicHDRPlus structure.
>   *
>   * @return An AVDynamicHDRPlus filled with default values or NULL
>   *         on failure.
>   */
> -AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size);
> +AVDynamicHDRPlus *av_dynamic_hdr_plus(GetBitContext *gb, size_t *size);

This is a public function in an installed header: You cannot simply change
or remove it.

Carl Eugen
James Almer Jan. 27, 2020, 7:49 p.m. UTC | #2
On 1/27/2020 4:08 PM, Mohammad Izadi wrote:
> From: Mohammad Izadi <izadi@google.com>
> 
> Trying to read HDR10+ metadata from HEVC/SEI and pass it to packet side data in the follow-up CLs.
> ---
>  libavutil/hdr_dynamic_metadata.c | 386 +++++++++++++++++++++++++++++++
>  libavutil/hdr_dynamic_metadata.h |  51 +++-
>  libavutil/version.h              |   2 +-
>  3 files changed, 431 insertions(+), 8 deletions(-)
> 
> diff --git a/libavutil/hdr_dynamic_metadata.c b/libavutil/hdr_dynamic_metadata.c
> index 0fa1ee82de..e321a22af8 100644
> --- a/libavutil/hdr_dynamic_metadata.c
> +++ b/libavutil/hdr_dynamic_metadata.c
> @@ -21,6 +21,19 @@
>  #include "hdr_dynamic_metadata.h"
>  #include "mem.h"
>  
> +static const uint8_t usa_country_code = 0xB5;
> +static const uint16_t smpte_provider_code = 0x003C;
> +static const uint16_t smpte2094_40_provider_oriented_code = 0x0001;
> +static const uint16_t smpte2094_40_application_identifier = 0x04;
> +
> +static const int64_t luminance_den = 1;
> +static const int32_t peak_luminance_den = 15;
> +static const int64_t rgb_den = 100000;
> +static const int32_t fraction_pixel_den = 1000;
> +static const int32_t knee_point_den = 4095;
> +static const int32_t bezier_anchor_den = 1023;
> +static const int32_t saturation_weight_den = 8;
> +
>  AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size)
>  {
>      AVDynamicHDRPlus *hdr_plus = av_mallocz(sizeof(AVDynamicHDRPlus));
> @@ -33,6 +46,48 @@ AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size)
>      return hdr_plus;
>  }
>  
> +AVDynamicHDRPlus *av_dynamic_hdr_plus(GetBitContext *gb, size_t *size)
> +{
> +    uint8_t country_code;
> +    uint16_t provider_code;
> +    uint16_t provider_oriented_code;
> +    uint8_t application_identifier;
> +    uint8_t application_version;
> +    AVDynamicHDRPlus *hdr_plus;
> +    int err;
> +    
> +    if (get_bits_left(gb) < 7)
> +        return NULL;
> +
> +    country_code = get_bits(gb, 8);
> +    provider_code = get_bits(gb, 16);
> +
> +    if (country_code != usa_country_code ||
> +        provider_code != smpte_provider_code)
> +        return NULL;
> +
> +    // A/341 Amendment – 2094-40
> +    provider_oriented_code = get_bits(gb, 16);
> +    application_identifier = get_bits(gb, 8);
> +    application_version  = get_bits(gb, 8);
> +    if (provider_oriented_code != smpte2094_40_provider_oriented_code ||
> +        application_identifier != smpte2094_40_application_identifier)
> +        return NULL;
> +
> +    hdr_plus = av_dynamic_hdr_plus_alloc(size);
> +    if (!hdr_plus)
> +        return NULL;
> +
> +    hdr_plus->application_version = application_version;
> +    err = decode_itu_t_t35_to_dynamic_hdr_plus(gb, hdr_plus);
> +    if (err < 0) {
> +        av_freep(&hdr_plus);
> +        return NULL;
> +    }
> +
> +    return hdr_plus;
> +}
> +
>  AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame)
>  {
>      AVFrameSideData *side_data = av_frame_new_side_data(frame,
> @@ -43,5 +98,336 @@ AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame)
>  
>      memset(side_data->data, 0, sizeof(AVDynamicHDRPlus));
>  
> +
>      return (AVDynamicHDRPlus *)side_data->data;
>  }
> +
> +int decode_itu_t_t35_to_dynamic_hdr_plus(GetBitContext *gb, AVDynamicHDRPlus *s)
> +{
> +    int w, i, j;
> +
> +    if (get_bits_left(gb) < 2)
> +        return AVERROR_INVALIDDATA;
> +    s->num_windows = get_bits(gb, 2);
> +
> +    if (s->num_windows < 1 || s->num_windows > 3) {
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    if (get_bits_left(gb) < ((19 * 8 + 1) * (s->num_windows - 1)))
> +        return AVERROR_INVALIDDATA;
> +    for (w = 1; w < s->num_windows; w++) {
> +        s->params[w].window_upper_left_corner_x.num = get_bits(gb, 16);
> +        s->params[w].window_upper_left_corner_y.num = get_bits(gb, 16);
> +        s->params[w].window_lower_right_corner_x.num = get_bits(gb, 16);
> +        s->params[w].window_lower_right_corner_y.num = get_bits(gb, 16);
> +        // The corners are set to absolute coordinates here. They should be
> +        // converted to the relative coordinates (in [0, 1]) in the decoder.
> +        s->params[w].window_upper_left_corner_x.den = 1;
> +        s->params[w].window_upper_left_corner_y.den = 1;
> +        s->params[w].window_lower_right_corner_x.den = 1;
> +        s->params[w].window_lower_right_corner_y.den = 1;
> +
> +        s->params[w].center_of_ellipse_x = get_bits(gb, 16);
> +        s->params[w].center_of_ellipse_y = get_bits(gb, 16);
> +        s->params[w].rotation_angle = get_bits(gb, 8);
> +        s->params[w].semimajor_axis_internal_ellipse = get_bits(gb, 16);
> +        s->params[w].semimajor_axis_external_ellipse = get_bits(gb, 16);
> +        s->params[w].semiminor_axis_external_ellipse = get_bits(gb, 16);
> +        s->params[w].overlap_process_option = get_bits1(gb);
> +    }
> +
> +    if (get_bits_left(gb) < 28)
> +        return AVERROR(EINVAL);
> +    s->targeted_system_display_maximum_luminance.num = get_bits(gb, 27);
> +    s->targeted_system_display_maximum_luminance.den = luminance_den;
> +    s->targeted_system_display_actual_peak_luminance_flag = get_bits1(gb);
> +
> +    if (s->targeted_system_display_actual_peak_luminance_flag) {
> +        int rows, cols;
> +        if (get_bits_left(gb) < 10)
> +            return AVERROR(EINVAL);
> +        rows = get_bits(gb, 5);
> +        cols = get_bits(gb, 5);
> +        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25))) {
> +            return AVERROR_INVALIDDATA;
> +        }
> +        s->num_rows_targeted_system_display_actual_peak_luminance = rows;
> +        s->num_cols_targeted_system_display_actual_peak_luminance = cols;
> +
> +        if (get_bits_left(gb) < (rows * cols * 4))
> +            return AVERROR(EINVAL);
> +
> +        for (i = 0; i < rows; i++) {
> +            for (j = 0; j < cols; j++) {
> +                s->targeted_system_display_actual_peak_luminance[i][j].num = get_bits(gb, 4);
> +                s->targeted_system_display_actual_peak_luminance[i][j].den = peak_luminance_den;
> +            }
> +        }
> +    }
> +    for (w = 0; w < s->num_windows; w++) {
> +        if (get_bits_left(gb) < (3 * 17 + 17 + 4))
> +            return AVERROR(EINVAL);
> +        for (i = 0; i < 3; i++) {
> +            s->params[w].maxscl[i].num = get_bits(gb, 17);
> +            s->params[w].maxscl[i].den = rgb_den;
> +        }
> +        s->params[w].average_maxrgb.num = get_bits(gb, 17);
> +        s->params[w].average_maxrgb.den = rgb_den;
> +        s->params[w].num_distribution_maxrgb_percentiles = get_bits(gb, 4);
> +
> +        if (get_bits_left(gb) <
> +            (s->params[w].num_distribution_maxrgb_percentiles * 24))
> +            return AVERROR(EINVAL);
> +        for (i = 0; i < s->params[w].num_distribution_maxrgb_percentiles; i++) {
> +            s->params[w].distribution_maxrgb[i].percentage = get_bits(gb, 7);
> +            s->params[w].distribution_maxrgb[i].percentile.num = get_bits(gb, 17);
> +            s->params[w].distribution_maxrgb[i].percentile.den = rgb_den;
> +        }
> +
> +        if (get_bits_left(gb) < 10)
> +            return AVERROR(EINVAL);
> +        s->params[w].fraction_bright_pixels.num = get_bits(gb, 10);
> +        s->params[w].fraction_bright_pixels.den = fraction_pixel_den;
> +    }
> +    if (get_bits_left(gb) < 1)
> +        return AVERROR(EINVAL);
> +    s->mastering_display_actual_peak_luminance_flag = get_bits1(gb);
> +    if (s->mastering_display_actual_peak_luminance_flag) {
> +        int rows, cols;
> +        if (get_bits_left(gb) < 10)
> +            return AVERROR(EINVAL);
> +        rows = get_bits(gb, 5);
> +        cols = get_bits(gb, 5);
> +        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25))) {
> +            return AVERROR_INVALIDDATA;
> +        }
> +        s->num_rows_mastering_display_actual_peak_luminance = rows;
> +        s->num_cols_mastering_display_actual_peak_luminance = cols;
> +
> +        if (get_bits_left(gb) < (rows * cols * 4))
> +            return AVERROR(EINVAL);
> +
> +        for (i = 0; i < rows; i++) {
> +            for (j = 0; j < cols; j++) {
> +                s->mastering_display_actual_peak_luminance[i][j].num = get_bits(gb, 4);
> +                s->mastering_display_actual_peak_luminance[i][j].den = peak_luminance_den;
> +            }
> +        }
> +    }
> +
> +    for (w = 0; w < s->num_windows; w++) {
> +        if (get_bits_left(gb) < 1)
> +            return AVERROR(EINVAL);
> +        s->params[w].tone_mapping_flag = get_bits1(gb);
> +        if (s->params[w].tone_mapping_flag) {
> +            if (get_bits_left(gb) < 28)
> +                return AVERROR(EINVAL);
> +            s->params[w].knee_point_x.num = get_bits(gb, 12);
> +            s->params[w].knee_point_x.den = knee_point_den;
> +            s->params[w].knee_point_y.num = get_bits(gb, 12);
> +            s->params[w].knee_point_y.den = knee_point_den;
> +            s->params[w].num_bezier_curve_anchors = get_bits(gb, 4);
> +
> +            if (get_bits_left(gb) < (s->params[w].num_bezier_curve_anchors * 10))
> +                return AVERROR(EINVAL);
> +            for (i = 0; i < s->params[w].num_bezier_curve_anchors; i++) {
> +                s->params[w].bezier_curve_anchors[i].num = get_bits(gb, 10);
> +                s->params[w].bezier_curve_anchors[i].den = bezier_anchor_den;
> +            }
> +        }
> +
> +        if (get_bits_left(gb) < 1)
> +            return AVERROR(EINVAL);
> +        s->params[w].color_saturation_mapping_flag = get_bits1(gb);
> +        if (s->params[w].color_saturation_mapping_flag) {
> +            if (get_bits_left(gb) < 6)
> +                return AVERROR(EINVAL);
> +            s->params[w].color_saturation_weight.num = get_bits(gb, 6);
> +            s->params[w].color_saturation_weight.den = saturation_weight_den;
> +        }
> +    }
> +
> +    skip_bits(gb, get_bits_left(gb));
> +
> +    return 0;
> +}
> +
> +int get_encoded_dynamic_hdr_plus_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_rows_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 encode_dynamic_hdr_plus_to_itu_t_t35(const AVDynamicHDRPlus *s, PutBitContext *pb)
> +{
> +    int w, i, j;
> +    if (!s)
> +        return AVERROR(EINVAL);
> +
> +    if (put_bits_left(pb) < get_encoded_dynamic_hdr_plus_buffer_size(s))
> +        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/libavutil/hdr_dynamic_metadata.h b/libavutil/hdr_dynamic_metadata.h
> index 2d72de56ae..4e075f2f58 100644
> --- a/libavutil/hdr_dynamic_metadata.h
> +++ b/libavutil/hdr_dynamic_metadata.h
> @@ -23,6 +23,8 @@
>  
>  #include "frame.h"
>  #include "rational.h"
> +#include "libavcodec/get_bits.h"
> +#include "libavcodec/put_bits.h"
>  
>  /**
>   * Option for overlapping elliptical pixel selectors in an image.
> @@ -241,11 +243,6 @@ typedef struct AVHDRPlusColorTransformParams {
>   * the public ABI.
>   */
>  typedef struct AVDynamicHDRPlus {
> -    /**
> -     * Country code by Rec. ITU-T T.35 Annex A. The value shall be 0xB5.
> -     */
> -    uint8_t itu_t_t35_country_code;

This is an API break.

> -
>      /**
>       * Application version in the application defining document in ST-2094
>       * suite. The value shall be set to 0.
> @@ -265,7 +262,7 @@ typedef struct AVDynamicHDRPlus {
>  
>      /**
>       * The nominal maximum display luminance of the targeted system display,
> -     * in units of 0.0001 candelas per square metre. The value shall be in
> +     * in units of 1 candelas per square metre. The value shall be in
>       * the range of 0 to 10000, inclusive.
>       */
>      AVRational targeted_system_display_maximum_luminance;
> @@ -322,14 +319,27 @@ typedef struct AVDynamicHDRPlus {
>      AVRational mastering_display_actual_peak_luminance[25][25];
>  } AVDynamicHDRPlus;
>  
> +/**
> + * Allocate an AVDynamicHDRPlus structure and set its fields to
> + * the values decoded from the user data registered ITU-T T.35.
> + * The resulting struct can be freed using av_freep().
> + *
> + * @return An AVDynamicHDRPlus filled with decoded values or NULL
> + *         on failure.
> + */
> +AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size);
> +
>  /**
>   * Allocate an AVDynamicHDRPlus structure and set its fields to
>   * default values. The resulting struct can be freed using av_freep().
> + * @param gb The input bit stream.
> + * @param size The size of allocated memory for the returned
> + *        AVDynamicHDRPlus structure.
>   *
>   * @return An AVDynamicHDRPlus filled with default values or NULL
>   *         on failure.
>   */
> -AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size);
> +AVDynamicHDRPlus *av_dynamic_hdr_plus(GetBitContext *gb, size_t *size);

As is this. Also, GetBitContext and PutBitContext are internal structs
to libavcodec, and can't be used in public headers.

>  
>  /**
>   * Allocate a complete AVDynamicHDRPlus and add it to the frame.
> @@ -340,4 +350,31 @@ AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size);
>   */
>  AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame);
>  
> +/**
> + * Decode the user data registered ITU-T T.35 to AVDynamicHDRPlus.
> + * @param gb The bit content to be decoded.
> + * @param s The decoded AVDynamicHDRPlus structure.
> + *
> + * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
> + */
> +int decode_itu_t_t35_to_dynamic_hdr_plus(GetBitContext *gb, AVDynamicHDRPlus *s);
> +
> +/**
> + * 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 get_encoded_dynamic_hdr_plus_buffer_size(const AVDynamicHDRPlus *s);
> +
> +/**
> + * Encode AVDynamicHDRPlus to the user data registered ITU-T T.35.
> + * @param s The AVDynamicHDRPlus structure to be encoded.
> + * @param pb The encoded bit stream.
> + *
> + * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
> + */
> +int encode_dynamic_hdr_plus_to_itu_t_t35(const AVDynamicHDRPlus *s, PutBitContext *pb);
> +
>  #endif /* AVUTIL_HDR_DYNAMIC_METADATA_H */
> diff --git a/libavutil/version.h b/libavutil/version.h
> index af8f614aff..2bc1b98615 100644
> --- a/libavutil/version.h
> +++ b/libavutil/version.h
> @@ -79,7 +79,7 @@
>   */
>  
>  #define LIBAVUTIL_VERSION_MAJOR  56
> -#define LIBAVUTIL_VERSION_MINOR  38
> +#define LIBAVUTIL_VERSION_MINOR  39
>  #define LIBAVUTIL_VERSION_MICRO 100
>  
>  #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
>
Mohammad Izadi Jan. 27, 2020, 7:54 p.m. UTC | #3
Hey Carl,

I created this metadata 6 months ago to use it at YouTube. The metadata is
incomplete here. I already implemented HDR10+ for our local ffmpeg and
trying to push them to the head.
Please note that the current code of HDR10+ is not working.

On Mon, Jan 27, 2020, 11:44 AM Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:

> Am Mo., 27. Jan. 2020 um 20:34 Uhr schrieb Mohammad Izadi <
> moh.izadi@gmail.com>:
>
> >  /**
> >   * Allocate an AVDynamicHDRPlus structure and set its fields to
> >   * default values. The resulting struct can be freed using av_freep().
> > + * @param gb The input bit stream.
> > + * @param size The size of allocated memory for the returned
> > + *        AVDynamicHDRPlus structure.
> >   *
> >   * @return An AVDynamicHDRPlus filled with default values or NULL
> >   *         on failure.
> >   */
> > -AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size);
> > +AVDynamicHDRPlus *av_dynamic_hdr_plus(GetBitContext *gb, size_t *size);
>
> This is a public function in an installed header: You cannot simply change
> or remove it.
>
> Carl Eugen
> _______________________________________________
> 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".
Carl Eugen Hoyos Jan. 27, 2020, 8:42 p.m. UTC | #4
Am Mo., 27. Jan. 2020 um 21:02 Uhr schrieb Mohammad Izadi <moh.izadi@gmail.com>:

> I created this metadata 6 months ago to use it at YouTube. The metadata is
> incomplete here. I already implemented HDR10+ for our local ffmpeg and
> trying to push them to the head.
> Please note that the current code of HDR10+ is not working.

Please find out what top-posting means and avoid it here.

The fact that you (!) provided code that you say does not work is sad
but cannot be changed.
As it is sad that apparently untested code was pushed.

Deprecate the existing code and provide new code that works, do not
break api or abi.

Carl Eugen
Mohammad Izadi Feb. 4, 2020, 2:01 a.m. UTC | #5
James, I am making another CL to comply with API.
I have a question about using GetBitContext and PutBitContext. Is there any
alternative to use in libavutil?
--
Best,
Mohammad


On Mon, Jan 27, 2020 at 11:50 AM James Almer <jamrial@gmail.com> wrote:

> On 1/27/2020 4:08 PM, Mohammad Izadi wrote:
> > From: Mohammad Izadi <izadi@google.com>
> >
> > Trying to read HDR10+ metadata from HEVC/SEI and pass it to packet side
> data in the follow-up CLs.
> > ---
> >  libavutil/hdr_dynamic_metadata.c | 386 +++++++++++++++++++++++++++++++
> >  libavutil/hdr_dynamic_metadata.h |  51 +++-
> >  libavutil/version.h              |   2 +-
> >  3 files changed, 431 insertions(+), 8 deletions(-)
> >
> > diff --git a/libavutil/hdr_dynamic_metadata.c
> b/libavutil/hdr_dynamic_metadata.c
> > index 0fa1ee82de..e321a22af8 100644
> > --- a/libavutil/hdr_dynamic_metadata.c
> > +++ b/libavutil/hdr_dynamic_metadata.c
> > @@ -21,6 +21,19 @@
> >  #include "hdr_dynamic_metadata.h"
> >  #include "mem.h"
> >
> > +static const uint8_t usa_country_code = 0xB5;
> > +static const uint16_t smpte_provider_code = 0x003C;
> > +static const uint16_t smpte2094_40_provider_oriented_code = 0x0001;
> > +static const uint16_t smpte2094_40_application_identifier = 0x04;
> > +
> > +static const int64_t luminance_den = 1;
> > +static const int32_t peak_luminance_den = 15;
> > +static const int64_t rgb_den = 100000;
> > +static const int32_t fraction_pixel_den = 1000;
> > +static const int32_t knee_point_den = 4095;
> > +static const int32_t bezier_anchor_den = 1023;
> > +static const int32_t saturation_weight_den = 8;
> > +
> >  AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size)
> >  {
> >      AVDynamicHDRPlus *hdr_plus = av_mallocz(sizeof(AVDynamicHDRPlus));
> > @@ -33,6 +46,48 @@ AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t
> *size)
> >      return hdr_plus;
> >  }
> >
> > +AVDynamicHDRPlus *av_dynamic_hdr_plus(GetBitContext *gb, size_t *size)
> > +{
> > +    uint8_t country_code;
> > +    uint16_t provider_code;
> > +    uint16_t provider_oriented_code;
> > +    uint8_t application_identifier;
> > +    uint8_t application_version;
> > +    AVDynamicHDRPlus *hdr_plus;
> > +    int err;
> > +
> > +    if (get_bits_left(gb) < 7)
> > +        return NULL;
> > +
> > +    country_code = get_bits(gb, 8);
> > +    provider_code = get_bits(gb, 16);
> > +
> > +    if (country_code != usa_country_code ||
> > +        provider_code != smpte_provider_code)
> > +        return NULL;
> > +
> > +    // A/341 Amendment – 2094-40
> > +    provider_oriented_code = get_bits(gb, 16);
> > +    application_identifier = get_bits(gb, 8);
> > +    application_version  = get_bits(gb, 8);
> > +    if (provider_oriented_code != smpte2094_40_provider_oriented_code ||
> > +        application_identifier != smpte2094_40_application_identifier)
> > +        return NULL;
> > +
> > +    hdr_plus = av_dynamic_hdr_plus_alloc(size);
> > +    if (!hdr_plus)
> > +        return NULL;
> > +
> > +    hdr_plus->application_version = application_version;
> > +    err = decode_itu_t_t35_to_dynamic_hdr_plus(gb, hdr_plus);
> > +    if (err < 0) {
> > +        av_freep(&hdr_plus);
> > +        return NULL;
> > +    }
> > +
> > +    return hdr_plus;
> > +}
> > +
> >  AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame)
> >  {
> >      AVFrameSideData *side_data = av_frame_new_side_data(frame,
> > @@ -43,5 +98,336 @@ AVDynamicHDRPlus
> *av_dynamic_hdr_plus_create_side_data(AVFrame *frame)
> >
> >      memset(side_data->data, 0, sizeof(AVDynamicHDRPlus));
> >
> > +
> >      return (AVDynamicHDRPlus *)side_data->data;
> >  }
> > +
> > +int decode_itu_t_t35_to_dynamic_hdr_plus(GetBitContext *gb,
> AVDynamicHDRPlus *s)
> > +{
> > +    int w, i, j;
> > +
> > +    if (get_bits_left(gb) < 2)
> > +        return AVERROR_INVALIDDATA;
> > +    s->num_windows = get_bits(gb, 2);
> > +
> > +    if (s->num_windows < 1 || s->num_windows > 3) {
> > +        return AVERROR_INVALIDDATA;
> > +    }
> > +
> > +    if (get_bits_left(gb) < ((19 * 8 + 1) * (s->num_windows - 1)))
> > +        return AVERROR_INVALIDDATA;
> > +    for (w = 1; w < s->num_windows; w++) {
> > +        s->params[w].window_upper_left_corner_x.num = get_bits(gb, 16);
> > +        s->params[w].window_upper_left_corner_y.num = get_bits(gb, 16);
> > +        s->params[w].window_lower_right_corner_x.num = get_bits(gb, 16);
> > +        s->params[w].window_lower_right_corner_y.num = get_bits(gb, 16);
> > +        // The corners are set to absolute coordinates here. They
> should be
> > +        // converted to the relative coordinates (in [0, 1]) in the
> decoder.
> > +        s->params[w].window_upper_left_corner_x.den = 1;
> > +        s->params[w].window_upper_left_corner_y.den = 1;
> > +        s->params[w].window_lower_right_corner_x.den = 1;
> > +        s->params[w].window_lower_right_corner_y.den = 1;
> > +
> > +        s->params[w].center_of_ellipse_x = get_bits(gb, 16);
> > +        s->params[w].center_of_ellipse_y = get_bits(gb, 16);
> > +        s->params[w].rotation_angle = get_bits(gb, 8);
> > +        s->params[w].semimajor_axis_internal_ellipse = get_bits(gb, 16);
> > +        s->params[w].semimajor_axis_external_ellipse = get_bits(gb, 16);
> > +        s->params[w].semiminor_axis_external_ellipse = get_bits(gb, 16);
> > +        s->params[w].overlap_process_option = get_bits1(gb);
> > +    }
> > +
> > +    if (get_bits_left(gb) < 28)
> > +        return AVERROR(EINVAL);
> > +    s->targeted_system_display_maximum_luminance.num = get_bits(gb, 27);
> > +    s->targeted_system_display_maximum_luminance.den = luminance_den;
> > +    s->targeted_system_display_actual_peak_luminance_flag =
> get_bits1(gb);
> > +
> > +    if (s->targeted_system_display_actual_peak_luminance_flag) {
> > +        int rows, cols;
> > +        if (get_bits_left(gb) < 10)
> > +            return AVERROR(EINVAL);
> > +        rows = get_bits(gb, 5);
> > +        cols = get_bits(gb, 5);
> > +        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25)))
> {
> > +            return AVERROR_INVALIDDATA;
> > +        }
> > +        s->num_rows_targeted_system_display_actual_peak_luminance =
> rows;
> > +        s->num_cols_targeted_system_display_actual_peak_luminance =
> cols;
> > +
> > +        if (get_bits_left(gb) < (rows * cols * 4))
> > +            return AVERROR(EINVAL);
> > +
> > +        for (i = 0; i < rows; i++) {
> > +            for (j = 0; j < cols; j++) {
> > +
> s->targeted_system_display_actual_peak_luminance[i][j].num = get_bits(gb,
> 4);
> > +
> s->targeted_system_display_actual_peak_luminance[i][j].den =
> peak_luminance_den;
> > +            }
> > +        }
> > +    }
> > +    for (w = 0; w < s->num_windows; w++) {
> > +        if (get_bits_left(gb) < (3 * 17 + 17 + 4))
> > +            return AVERROR(EINVAL);
> > +        for (i = 0; i < 3; i++) {
> > +            s->params[w].maxscl[i].num = get_bits(gb, 17);
> > +            s->params[w].maxscl[i].den = rgb_den;
> > +        }
> > +        s->params[w].average_maxrgb.num = get_bits(gb, 17);
> > +        s->params[w].average_maxrgb.den = rgb_den;
> > +        s->params[w].num_distribution_maxrgb_percentiles = get_bits(gb,
> 4);
> > +
> > +        if (get_bits_left(gb) <
> > +            (s->params[w].num_distribution_maxrgb_percentiles * 24))
> > +            return AVERROR(EINVAL);
> > +        for (i = 0; i <
> s->params[w].num_distribution_maxrgb_percentiles; i++) {
> > +            s->params[w].distribution_maxrgb[i].percentage =
> get_bits(gb, 7);
> > +            s->params[w].distribution_maxrgb[i].percentile.num =
> get_bits(gb, 17);
> > +            s->params[w].distribution_maxrgb[i].percentile.den =
> rgb_den;
> > +        }
> > +
> > +        if (get_bits_left(gb) < 10)
> > +            return AVERROR(EINVAL);
> > +        s->params[w].fraction_bright_pixels.num = get_bits(gb, 10);
> > +        s->params[w].fraction_bright_pixels.den = fraction_pixel_den;
> > +    }
> > +    if (get_bits_left(gb) < 1)
> > +        return AVERROR(EINVAL);
> > +    s->mastering_display_actual_peak_luminance_flag = get_bits1(gb);
> > +    if (s->mastering_display_actual_peak_luminance_flag) {
> > +        int rows, cols;
> > +        if (get_bits_left(gb) < 10)
> > +            return AVERROR(EINVAL);
> > +        rows = get_bits(gb, 5);
> > +        cols = get_bits(gb, 5);
> > +        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25)))
> {
> > +            return AVERROR_INVALIDDATA;
> > +        }
> > +        s->num_rows_mastering_display_actual_peak_luminance = rows;
> > +        s->num_cols_mastering_display_actual_peak_luminance = cols;
> > +
> > +        if (get_bits_left(gb) < (rows * cols * 4))
> > +            return AVERROR(EINVAL);
> > +
> > +        for (i = 0; i < rows; i++) {
> > +            for (j = 0; j < cols; j++) {
> > +                s->mastering_display_actual_peak_luminance[i][j].num =
> get_bits(gb, 4);
> > +                s->mastering_display_actual_peak_luminance[i][j].den =
> peak_luminance_den;
> > +            }
> > +        }
> > +    }
> > +
> > +    for (w = 0; w < s->num_windows; w++) {
> > +        if (get_bits_left(gb) < 1)
> > +            return AVERROR(EINVAL);
> > +        s->params[w].tone_mapping_flag = get_bits1(gb);
> > +        if (s->params[w].tone_mapping_flag) {
> > +            if (get_bits_left(gb) < 28)
> > +                return AVERROR(EINVAL);
> > +            s->params[w].knee_point_x.num = get_bits(gb, 12);
> > +            s->params[w].knee_point_x.den = knee_point_den;
> > +            s->params[w].knee_point_y.num = get_bits(gb, 12);
> > +            s->params[w].knee_point_y.den = knee_point_den;
> > +            s->params[w].num_bezier_curve_anchors = get_bits(gb, 4);
> > +
> > +            if (get_bits_left(gb) <
> (s->params[w].num_bezier_curve_anchors * 10))
> > +                return AVERROR(EINVAL);
> > +            for (i = 0; i < s->params[w].num_bezier_curve_anchors; i++)
> {
> > +                s->params[w].bezier_curve_anchors[i].num = get_bits(gb,
> 10);
> > +                s->params[w].bezier_curve_anchors[i].den =
> bezier_anchor_den;
> > +            }
> > +        }
> > +
> > +        if (get_bits_left(gb) < 1)
> > +            return AVERROR(EINVAL);
> > +        s->params[w].color_saturation_mapping_flag = get_bits1(gb);
> > +        if (s->params[w].color_saturation_mapping_flag) {
> > +            if (get_bits_left(gb) < 6)
> > +                return AVERROR(EINVAL);
> > +            s->params[w].color_saturation_weight.num = get_bits(gb, 6);
> > +            s->params[w].color_saturation_weight.den =
> saturation_weight_den;
> > +        }
> > +    }
> > +
> > +    skip_bits(gb, get_bits_left(gb));
> > +
> > +    return 0;
> > +}
> > +
> > +int get_encoded_dynamic_hdr_plus_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_rows_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 encode_dynamic_hdr_plus_to_itu_t_t35(const AVDynamicHDRPlus *s,
> PutBitContext *pb)
> > +{
> > +    int w, i, j;
> > +    if (!s)
> > +        return AVERROR(EINVAL);
> > +
> > +    if (put_bits_left(pb) < get_encoded_dynamic_hdr_plus_buffer_size(s))
> > +        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/libavutil/hdr_dynamic_metadata.h
> b/libavutil/hdr_dynamic_metadata.h
> > index 2d72de56ae..4e075f2f58 100644
> > --- a/libavutil/hdr_dynamic_metadata.h
> > +++ b/libavutil/hdr_dynamic_metadata.h
> > @@ -23,6 +23,8 @@
> >
> >  #include "frame.h"
> >  #include "rational.h"
> > +#include "libavcodec/get_bits.h"
> > +#include "libavcodec/put_bits.h"
> >
> >  /**
> >   * Option for overlapping elliptical pixel selectors in an image.
> > @@ -241,11 +243,6 @@ typedef struct AVHDRPlusColorTransformParams {
> >   * the public ABI.
> >   */
> >  typedef struct AVDynamicHDRPlus {
> > -    /**
> > -     * Country code by Rec. ITU-T T.35 Annex A. The value shall be 0xB5.
> > -     */
> > -    uint8_t itu_t_t35_country_code;
>
> This is an API break.
>
> > -
> >      /**
> >       * Application version in the application defining document in
> ST-2094
> >       * suite. The value shall be set to 0.
> > @@ -265,7 +262,7 @@ typedef struct AVDynamicHDRPlus {
> >
> >      /**
> >       * The nominal maximum display luminance of the targeted system
> display,
> > -     * in units of 0.0001 candelas per square metre. The value shall be
> in
> > +     * in units of 1 candelas per square metre. The value shall be in
> >       * the range of 0 to 10000, inclusive.
> >       */
> >      AVRational targeted_system_display_maximum_luminance;
> > @@ -322,14 +319,27 @@ typedef struct AVDynamicHDRPlus {
> >      AVRational mastering_display_actual_peak_luminance[25][25];
> >  } AVDynamicHDRPlus;
> >
> > +/**
> > + * Allocate an AVDynamicHDRPlus structure and set its fields to
> > + * the values decoded from the user data registered ITU-T T.35.
> > + * The resulting struct can be freed using av_freep().
> > + *
> > + * @return An AVDynamicHDRPlus filled with decoded values or NULL
> > + *         on failure.
> > + */
> > +AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size);
> > +
> >  /**
> >   * Allocate an AVDynamicHDRPlus structure and set its fields to
> >   * default values. The resulting struct can be freed using av_freep().
> > + * @param gb The input bit stream.
> > + * @param size The size of allocated memory for the returned
> > + *        AVDynamicHDRPlus structure.
> >   *
> >   * @return An AVDynamicHDRPlus filled with default values or NULL
> >   *         on failure.
> >   */
> > -AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size);
> > +AVDynamicHDRPlus *av_dynamic_hdr_plus(GetBitContext *gb, size_t *size);
>
> As is this. Also, GetBitContext and PutBitContext are internal structs
> to libavcodec, and can't be used in public headers.
>
> >
> >  /**
> >   * Allocate a complete AVDynamicHDRPlus and add it to the frame.
> > @@ -340,4 +350,31 @@ AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t
> *size);
> >   */
> >  AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame);
> >
> > +/**
> > + * Decode the user data registered ITU-T T.35 to AVDynamicHDRPlus.
> > + * @param gb The bit content to be decoded.
> > + * @param s The decoded AVDynamicHDRPlus structure.
> > + *
> > + * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
> > + */
> > +int decode_itu_t_t35_to_dynamic_hdr_plus(GetBitContext *gb,
> AVDynamicHDRPlus *s);
> > +
> > +/**
> > + * 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 get_encoded_dynamic_hdr_plus_buffer_size(const AVDynamicHDRPlus *s);
> > +
> > +/**
> > + * Encode AVDynamicHDRPlus to the user data registered ITU-T T.35.
> > + * @param s The AVDynamicHDRPlus structure to be encoded.
> > + * @param pb The encoded bit stream.
> > + *
> > + * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
> > + */
> > +int encode_dynamic_hdr_plus_to_itu_t_t35(const AVDynamicHDRPlus *s,
> PutBitContext *pb);
> > +
> >  #endif /* AVUTIL_HDR_DYNAMIC_METADATA_H */
> > diff --git a/libavutil/version.h b/libavutil/version.h
> > index af8f614aff..2bc1b98615 100644
> > --- a/libavutil/version.h
> > +++ b/libavutil/version.h
> > @@ -79,7 +79,7 @@
> >   */
> >
> >  #define LIBAVUTIL_VERSION_MAJOR  56
> > -#define LIBAVUTIL_VERSION_MINOR  38
> > +#define LIBAVUTIL_VERSION_MINOR  39
> >  #define LIBAVUTIL_VERSION_MICRO 100
> >
> >  #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR,
> \
> >
>
> _______________________________________________
> 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".
James Almer Feb. 4, 2020, 2:06 a.m. UTC | #6
On 2/3/2020 11:01 PM, Mohammad Izadi wrote:
> James, I am making another CL to comply with API.
> I have a question about using GetBitContext and PutBitContext. Is there any
> alternative to use in libavutil?

You can use it in libavutil as long as it's not part of the public API.
So you should make the new public functions take size_t and uint8_t*
parameters, then use get/putbits internally in the implementation if needed.

Also, all public functions must use an av_ prefix.

> --
> Best,
> Mohammad
> 
> 
> On Mon, Jan 27, 2020 at 11:50 AM James Almer <jamrial@gmail.com> wrote:
> 
>> On 1/27/2020 4:08 PM, Mohammad Izadi wrote:
>>> From: Mohammad Izadi <izadi@google.com>
>>>
>>> Trying to read HDR10+ metadata from HEVC/SEI and pass it to packet side
>> data in the follow-up CLs.
>>> ---
>>>  libavutil/hdr_dynamic_metadata.c | 386 +++++++++++++++++++++++++++++++
>>>  libavutil/hdr_dynamic_metadata.h |  51 +++-
>>>  libavutil/version.h              |   2 +-
>>>  3 files changed, 431 insertions(+), 8 deletions(-)
>>>
>>> diff --git a/libavutil/hdr_dynamic_metadata.c
>> b/libavutil/hdr_dynamic_metadata.c
>>> index 0fa1ee82de..e321a22af8 100644
>>> --- a/libavutil/hdr_dynamic_metadata.c
>>> +++ b/libavutil/hdr_dynamic_metadata.c
>>> @@ -21,6 +21,19 @@
>>>  #include "hdr_dynamic_metadata.h"
>>>  #include "mem.h"
>>>
>>> +static const uint8_t usa_country_code = 0xB5;
>>> +static const uint16_t smpte_provider_code = 0x003C;
>>> +static const uint16_t smpte2094_40_provider_oriented_code = 0x0001;
>>> +static const uint16_t smpte2094_40_application_identifier = 0x04;
>>> +
>>> +static const int64_t luminance_den = 1;
>>> +static const int32_t peak_luminance_den = 15;
>>> +static const int64_t rgb_den = 100000;
>>> +static const int32_t fraction_pixel_den = 1000;
>>> +static const int32_t knee_point_den = 4095;
>>> +static const int32_t bezier_anchor_den = 1023;
>>> +static const int32_t saturation_weight_den = 8;
>>> +
>>>  AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size)
>>>  {
>>>      AVDynamicHDRPlus *hdr_plus = av_mallocz(sizeof(AVDynamicHDRPlus));
>>> @@ -33,6 +46,48 @@ AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t
>> *size)
>>>      return hdr_plus;
>>>  }
>>>
>>> +AVDynamicHDRPlus *av_dynamic_hdr_plus(GetBitContext *gb, size_t *size)
>>> +{
>>> +    uint8_t country_code;
>>> +    uint16_t provider_code;
>>> +    uint16_t provider_oriented_code;
>>> +    uint8_t application_identifier;
>>> +    uint8_t application_version;
>>> +    AVDynamicHDRPlus *hdr_plus;
>>> +    int err;
>>> +
>>> +    if (get_bits_left(gb) < 7)
>>> +        return NULL;
>>> +
>>> +    country_code = get_bits(gb, 8);
>>> +    provider_code = get_bits(gb, 16);
>>> +
>>> +    if (country_code != usa_country_code ||
>>> +        provider_code != smpte_provider_code)
>>> +        return NULL;
>>> +
>>> +    // A/341 Amendment – 2094-40
>>> +    provider_oriented_code = get_bits(gb, 16);
>>> +    application_identifier = get_bits(gb, 8);
>>> +    application_version  = get_bits(gb, 8);
>>> +    if (provider_oriented_code != smpte2094_40_provider_oriented_code ||
>>> +        application_identifier != smpte2094_40_application_identifier)
>>> +        return NULL;
>>> +
>>> +    hdr_plus = av_dynamic_hdr_plus_alloc(size);
>>> +    if (!hdr_plus)
>>> +        return NULL;
>>> +
>>> +    hdr_plus->application_version = application_version;
>>> +    err = decode_itu_t_t35_to_dynamic_hdr_plus(gb, hdr_plus);
>>> +    if (err < 0) {
>>> +        av_freep(&hdr_plus);
>>> +        return NULL;
>>> +    }
>>> +
>>> +    return hdr_plus;
>>> +}
>>> +
>>>  AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame)
>>>  {
>>>      AVFrameSideData *side_data = av_frame_new_side_data(frame,
>>> @@ -43,5 +98,336 @@ AVDynamicHDRPlus
>> *av_dynamic_hdr_plus_create_side_data(AVFrame *frame)
>>>
>>>      memset(side_data->data, 0, sizeof(AVDynamicHDRPlus));
>>>
>>> +
>>>      return (AVDynamicHDRPlus *)side_data->data;
>>>  }
>>> +
>>> +int decode_itu_t_t35_to_dynamic_hdr_plus(GetBitContext *gb,
>> AVDynamicHDRPlus *s)
>>> +{
>>> +    int w, i, j;
>>> +
>>> +    if (get_bits_left(gb) < 2)
>>> +        return AVERROR_INVALIDDATA;
>>> +    s->num_windows = get_bits(gb, 2);
>>> +
>>> +    if (s->num_windows < 1 || s->num_windows > 3) {
>>> +        return AVERROR_INVALIDDATA;
>>> +    }
>>> +
>>> +    if (get_bits_left(gb) < ((19 * 8 + 1) * (s->num_windows - 1)))
>>> +        return AVERROR_INVALIDDATA;
>>> +    for (w = 1; w < s->num_windows; w++) {
>>> +        s->params[w].window_upper_left_corner_x.num = get_bits(gb, 16);
>>> +        s->params[w].window_upper_left_corner_y.num = get_bits(gb, 16);
>>> +        s->params[w].window_lower_right_corner_x.num = get_bits(gb, 16);
>>> +        s->params[w].window_lower_right_corner_y.num = get_bits(gb, 16);
>>> +        // The corners are set to absolute coordinates here. They
>> should be
>>> +        // converted to the relative coordinates (in [0, 1]) in the
>> decoder.
>>> +        s->params[w].window_upper_left_corner_x.den = 1;
>>> +        s->params[w].window_upper_left_corner_y.den = 1;
>>> +        s->params[w].window_lower_right_corner_x.den = 1;
>>> +        s->params[w].window_lower_right_corner_y.den = 1;
>>> +
>>> +        s->params[w].center_of_ellipse_x = get_bits(gb, 16);
>>> +        s->params[w].center_of_ellipse_y = get_bits(gb, 16);
>>> +        s->params[w].rotation_angle = get_bits(gb, 8);
>>> +        s->params[w].semimajor_axis_internal_ellipse = get_bits(gb, 16);
>>> +        s->params[w].semimajor_axis_external_ellipse = get_bits(gb, 16);
>>> +        s->params[w].semiminor_axis_external_ellipse = get_bits(gb, 16);
>>> +        s->params[w].overlap_process_option = get_bits1(gb);
>>> +    }
>>> +
>>> +    if (get_bits_left(gb) < 28)
>>> +        return AVERROR(EINVAL);
>>> +    s->targeted_system_display_maximum_luminance.num = get_bits(gb, 27);
>>> +    s->targeted_system_display_maximum_luminance.den = luminance_den;
>>> +    s->targeted_system_display_actual_peak_luminance_flag =
>> get_bits1(gb);
>>> +
>>> +    if (s->targeted_system_display_actual_peak_luminance_flag) {
>>> +        int rows, cols;
>>> +        if (get_bits_left(gb) < 10)
>>> +            return AVERROR(EINVAL);
>>> +        rows = get_bits(gb, 5);
>>> +        cols = get_bits(gb, 5);
>>> +        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25)))
>> {
>>> +            return AVERROR_INVALIDDATA;
>>> +        }
>>> +        s->num_rows_targeted_system_display_actual_peak_luminance =
>> rows;
>>> +        s->num_cols_targeted_system_display_actual_peak_luminance =
>> cols;
>>> +
>>> +        if (get_bits_left(gb) < (rows * cols * 4))
>>> +            return AVERROR(EINVAL);
>>> +
>>> +        for (i = 0; i < rows; i++) {
>>> +            for (j = 0; j < cols; j++) {
>>> +
>> s->targeted_system_display_actual_peak_luminance[i][j].num = get_bits(gb,
>> 4);
>>> +
>> s->targeted_system_display_actual_peak_luminance[i][j].den =
>> peak_luminance_den;
>>> +            }
>>> +        }
>>> +    }
>>> +    for (w = 0; w < s->num_windows; w++) {
>>> +        if (get_bits_left(gb) < (3 * 17 + 17 + 4))
>>> +            return AVERROR(EINVAL);
>>> +        for (i = 0; i < 3; i++) {
>>> +            s->params[w].maxscl[i].num = get_bits(gb, 17);
>>> +            s->params[w].maxscl[i].den = rgb_den;
>>> +        }
>>> +        s->params[w].average_maxrgb.num = get_bits(gb, 17);
>>> +        s->params[w].average_maxrgb.den = rgb_den;
>>> +        s->params[w].num_distribution_maxrgb_percentiles = get_bits(gb,
>> 4);
>>> +
>>> +        if (get_bits_left(gb) <
>>> +            (s->params[w].num_distribution_maxrgb_percentiles * 24))
>>> +            return AVERROR(EINVAL);
>>> +        for (i = 0; i <
>> s->params[w].num_distribution_maxrgb_percentiles; i++) {
>>> +            s->params[w].distribution_maxrgb[i].percentage =
>> get_bits(gb, 7);
>>> +            s->params[w].distribution_maxrgb[i].percentile.num =
>> get_bits(gb, 17);
>>> +            s->params[w].distribution_maxrgb[i].percentile.den =
>> rgb_den;
>>> +        }
>>> +
>>> +        if (get_bits_left(gb) < 10)
>>> +            return AVERROR(EINVAL);
>>> +        s->params[w].fraction_bright_pixels.num = get_bits(gb, 10);
>>> +        s->params[w].fraction_bright_pixels.den = fraction_pixel_den;
>>> +    }
>>> +    if (get_bits_left(gb) < 1)
>>> +        return AVERROR(EINVAL);
>>> +    s->mastering_display_actual_peak_luminance_flag = get_bits1(gb);
>>> +    if (s->mastering_display_actual_peak_luminance_flag) {
>>> +        int rows, cols;
>>> +        if (get_bits_left(gb) < 10)
>>> +            return AVERROR(EINVAL);
>>> +        rows = get_bits(gb, 5);
>>> +        cols = get_bits(gb, 5);
>>> +        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25)))
>> {
>>> +            return AVERROR_INVALIDDATA;
>>> +        }
>>> +        s->num_rows_mastering_display_actual_peak_luminance = rows;
>>> +        s->num_cols_mastering_display_actual_peak_luminance = cols;
>>> +
>>> +        if (get_bits_left(gb) < (rows * cols * 4))
>>> +            return AVERROR(EINVAL);
>>> +
>>> +        for (i = 0; i < rows; i++) {
>>> +            for (j = 0; j < cols; j++) {
>>> +                s->mastering_display_actual_peak_luminance[i][j].num =
>> get_bits(gb, 4);
>>> +                s->mastering_display_actual_peak_luminance[i][j].den =
>> peak_luminance_den;
>>> +            }
>>> +        }
>>> +    }
>>> +
>>> +    for (w = 0; w < s->num_windows; w++) {
>>> +        if (get_bits_left(gb) < 1)
>>> +            return AVERROR(EINVAL);
>>> +        s->params[w].tone_mapping_flag = get_bits1(gb);
>>> +        if (s->params[w].tone_mapping_flag) {
>>> +            if (get_bits_left(gb) < 28)
>>> +                return AVERROR(EINVAL);
>>> +            s->params[w].knee_point_x.num = get_bits(gb, 12);
>>> +            s->params[w].knee_point_x.den = knee_point_den;
>>> +            s->params[w].knee_point_y.num = get_bits(gb, 12);
>>> +            s->params[w].knee_point_y.den = knee_point_den;
>>> +            s->params[w].num_bezier_curve_anchors = get_bits(gb, 4);
>>> +
>>> +            if (get_bits_left(gb) <
>> (s->params[w].num_bezier_curve_anchors * 10))
>>> +                return AVERROR(EINVAL);
>>> +            for (i = 0; i < s->params[w].num_bezier_curve_anchors; i++)
>> {
>>> +                s->params[w].bezier_curve_anchors[i].num = get_bits(gb,
>> 10);
>>> +                s->params[w].bezier_curve_anchors[i].den =
>> bezier_anchor_den;
>>> +            }
>>> +        }
>>> +
>>> +        if (get_bits_left(gb) < 1)
>>> +            return AVERROR(EINVAL);
>>> +        s->params[w].color_saturation_mapping_flag = get_bits1(gb);
>>> +        if (s->params[w].color_saturation_mapping_flag) {
>>> +            if (get_bits_left(gb) < 6)
>>> +                return AVERROR(EINVAL);
>>> +            s->params[w].color_saturation_weight.num = get_bits(gb, 6);
>>> +            s->params[w].color_saturation_weight.den =
>> saturation_weight_den;
>>> +        }
>>> +    }
>>> +
>>> +    skip_bits(gb, get_bits_left(gb));
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +int get_encoded_dynamic_hdr_plus_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_rows_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 encode_dynamic_hdr_plus_to_itu_t_t35(const AVDynamicHDRPlus *s,
>> PutBitContext *pb)
>>> +{
>>> +    int w, i, j;
>>> +    if (!s)
>>> +        return AVERROR(EINVAL);
>>> +
>>> +    if (put_bits_left(pb) < get_encoded_dynamic_hdr_plus_buffer_size(s))
>>> +        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/libavutil/hdr_dynamic_metadata.h
>> b/libavutil/hdr_dynamic_metadata.h
>>> index 2d72de56ae..4e075f2f58 100644
>>> --- a/libavutil/hdr_dynamic_metadata.h
>>> +++ b/libavutil/hdr_dynamic_metadata.h
>>> @@ -23,6 +23,8 @@
>>>
>>>  #include "frame.h"
>>>  #include "rational.h"
>>> +#include "libavcodec/get_bits.h"
>>> +#include "libavcodec/put_bits.h"
>>>
>>>  /**
>>>   * Option for overlapping elliptical pixel selectors in an image.
>>> @@ -241,11 +243,6 @@ typedef struct AVHDRPlusColorTransformParams {
>>>   * the public ABI.
>>>   */
>>>  typedef struct AVDynamicHDRPlus {
>>> -    /**
>>> -     * Country code by Rec. ITU-T T.35 Annex A. The value shall be 0xB5.
>>> -     */
>>> -    uint8_t itu_t_t35_country_code;
>>
>> This is an API break.
>>
>>> -
>>>      /**
>>>       * Application version in the application defining document in
>> ST-2094
>>>       * suite. The value shall be set to 0.
>>> @@ -265,7 +262,7 @@ typedef struct AVDynamicHDRPlus {
>>>
>>>      /**
>>>       * The nominal maximum display luminance of the targeted system
>> display,
>>> -     * in units of 0.0001 candelas per square metre. The value shall be
>> in
>>> +     * in units of 1 candelas per square metre. The value shall be in
>>>       * the range of 0 to 10000, inclusive.
>>>       */
>>>      AVRational targeted_system_display_maximum_luminance;
>>> @@ -322,14 +319,27 @@ typedef struct AVDynamicHDRPlus {
>>>      AVRational mastering_display_actual_peak_luminance[25][25];
>>>  } AVDynamicHDRPlus;
>>>
>>> +/**
>>> + * Allocate an AVDynamicHDRPlus structure and set its fields to
>>> + * the values decoded from the user data registered ITU-T T.35.
>>> + * The resulting struct can be freed using av_freep().
>>> + *
>>> + * @return An AVDynamicHDRPlus filled with decoded values or NULL
>>> + *         on failure.
>>> + */
>>> +AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size);
>>> +
>>>  /**
>>>   * Allocate an AVDynamicHDRPlus structure and set its fields to
>>>   * default values. The resulting struct can be freed using av_freep().
>>> + * @param gb The input bit stream.
>>> + * @param size The size of allocated memory for the returned
>>> + *        AVDynamicHDRPlus structure.
>>>   *
>>>   * @return An AVDynamicHDRPlus filled with default values or NULL
>>>   *         on failure.
>>>   */
>>> -AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size);
>>> +AVDynamicHDRPlus *av_dynamic_hdr_plus(GetBitContext *gb, size_t *size);
>>
>> As is this. Also, GetBitContext and PutBitContext are internal structs
>> to libavcodec, and can't be used in public headers.
>>
>>>
>>>  /**
>>>   * Allocate a complete AVDynamicHDRPlus and add it to the frame.
>>> @@ -340,4 +350,31 @@ AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t
>> *size);
>>>   */
>>>  AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame);
>>>
>>> +/**
>>> + * Decode the user data registered ITU-T T.35 to AVDynamicHDRPlus.
>>> + * @param gb The bit content to be decoded.
>>> + * @param s The decoded AVDynamicHDRPlus structure.
>>> + *
>>> + * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
>>> + */
>>> +int decode_itu_t_t35_to_dynamic_hdr_plus(GetBitContext *gb,
>> AVDynamicHDRPlus *s);
>>> +
>>> +/**
>>> + * 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 get_encoded_dynamic_hdr_plus_buffer_size(const AVDynamicHDRPlus *s);
>>> +
>>> +/**
>>> + * Encode AVDynamicHDRPlus to the user data registered ITU-T T.35.
>>> + * @param s The AVDynamicHDRPlus structure to be encoded.
>>> + * @param pb The encoded bit stream.
>>> + *
>>> + * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
>>> + */
>>> +int encode_dynamic_hdr_plus_to_itu_t_t35(const AVDynamicHDRPlus *s,
>> PutBitContext *pb);
>>> +
>>>  #endif /* AVUTIL_HDR_DYNAMIC_METADATA_H */
>>> diff --git a/libavutil/version.h b/libavutil/version.h
>>> index af8f614aff..2bc1b98615 100644
>>> --- a/libavutil/version.h
>>> +++ b/libavutil/version.h
>>> @@ -79,7 +79,7 @@
>>>   */
>>>
>>>  #define LIBAVUTIL_VERSION_MAJOR  56
>>> -#define LIBAVUTIL_VERSION_MINOR  38
>>> +#define LIBAVUTIL_VERSION_MINOR  39
>>>  #define LIBAVUTIL_VERSION_MICRO 100
>>>
>>>  #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR,
>> \
>>>
>>
>> _______________________________________________
>> 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".
> _______________________________________________
> 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".
>
Mohammad Izadi Feb. 4, 2020, 2:41 a.m. UTC | #7
Great! Thanks!

On Mon, Feb 3, 2020, 6:37 PM James Almer <jamrial@gmail.com> wrote:

> On 2/3/2020 11:01 PM, Mohammad Izadi wrote:
> > James, I am making another CL to comply with API.
> > I have a question about using GetBitContext and PutBitContext. Is there
> any
> > alternative to use in libavutil?
>
> You can use it in libavutil as long as it's not part of the public API.
> So you should make the new public functions take size_t and uint8_t*
> parameters, then use get/putbits internally in the implementation if
> needed.
>
> Also, all public functions must use an av_ prefix.
>
> > --
> > Best,
> > Mohammad
> >
> >
> > On Mon, Jan 27, 2020 at 11:50 AM James Almer <jamrial@gmail.com> wrote:
> >
> >> On 1/27/2020 4:08 PM, Mohammad Izadi wrote:
> >>> From: Mohammad Izadi <izadi@google.com>
> >>>
> >>> Trying to read HDR10+ metadata from HEVC/SEI and pass it to packet side
> >> data in the follow-up CLs.
> >>> ---
> >>>  libavutil/hdr_dynamic_metadata.c | 386 +++++++++++++++++++++++++++++++
> >>>  libavutil/hdr_dynamic_metadata.h |  51 +++-
> >>>  libavutil/version.h              |   2 +-
> >>>  3 files changed, 431 insertions(+), 8 deletions(-)
> >>>
> >>> diff --git a/libavutil/hdr_dynamic_metadata.c
> >> b/libavutil/hdr_dynamic_metadata.c
> >>> index 0fa1ee82de..e321a22af8 100644
> >>> --- a/libavutil/hdr_dynamic_metadata.c
> >>> +++ b/libavutil/hdr_dynamic_metadata.c
> >>> @@ -21,6 +21,19 @@
> >>>  #include "hdr_dynamic_metadata.h"
> >>>  #include "mem.h"
> >>>
> >>> +static const uint8_t usa_country_code = 0xB5;
> >>> +static const uint16_t smpte_provider_code = 0x003C;
> >>> +static const uint16_t smpte2094_40_provider_oriented_code = 0x0001;
> >>> +static const uint16_t smpte2094_40_application_identifier = 0x04;
> >>> +
> >>> +static const int64_t luminance_den = 1;
> >>> +static const int32_t peak_luminance_den = 15;
> >>> +static const int64_t rgb_den = 100000;
> >>> +static const int32_t fraction_pixel_den = 1000;
> >>> +static const int32_t knee_point_den = 4095;
> >>> +static const int32_t bezier_anchor_den = 1023;
> >>> +static const int32_t saturation_weight_den = 8;
> >>> +
> >>>  AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size)
> >>>  {
> >>>      AVDynamicHDRPlus *hdr_plus = av_mallocz(sizeof(AVDynamicHDRPlus));
> >>> @@ -33,6 +46,48 @@ AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t
> >> *size)
> >>>      return hdr_plus;
> >>>  }
> >>>
> >>> +AVDynamicHDRPlus *av_dynamic_hdr_plus(GetBitContext *gb, size_t *size)
> >>> +{
> >>> +    uint8_t country_code;
> >>> +    uint16_t provider_code;
> >>> +    uint16_t provider_oriented_code;
> >>> +    uint8_t application_identifier;
> >>> +    uint8_t application_version;
> >>> +    AVDynamicHDRPlus *hdr_plus;
> >>> +    int err;
> >>> +
> >>> +    if (get_bits_left(gb) < 7)
> >>> +        return NULL;
> >>> +
> >>> +    country_code = get_bits(gb, 8);
> >>> +    provider_code = get_bits(gb, 16);
> >>> +
> >>> +    if (country_code != usa_country_code ||
> >>> +        provider_code != smpte_provider_code)
> >>> +        return NULL;
> >>> +
> >>> +    // A/341 Amendment – 2094-40
> >>> +    provider_oriented_code = get_bits(gb, 16);
> >>> +    application_identifier = get_bits(gb, 8);
> >>> +    application_version  = get_bits(gb, 8);
> >>> +    if (provider_oriented_code != smpte2094_40_provider_oriented_code
> ||
> >>> +        application_identifier != smpte2094_40_application_identifier)
> >>> +        return NULL;
> >>> +
> >>> +    hdr_plus = av_dynamic_hdr_plus_alloc(size);
> >>> +    if (!hdr_plus)
> >>> +        return NULL;
> >>> +
> >>> +    hdr_plus->application_version = application_version;
> >>> +    err = decode_itu_t_t35_to_dynamic_hdr_plus(gb, hdr_plus);
> >>> +    if (err < 0) {
> >>> +        av_freep(&hdr_plus);
> >>> +        return NULL;
> >>> +    }
> >>> +
> >>> +    return hdr_plus;
> >>> +}
> >>> +
> >>>  AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame)
> >>>  {
> >>>      AVFrameSideData *side_data = av_frame_new_side_data(frame,
> >>> @@ -43,5 +98,336 @@ AVDynamicHDRPlus
> >> *av_dynamic_hdr_plus_create_side_data(AVFrame *frame)
> >>>
> >>>      memset(side_data->data, 0, sizeof(AVDynamicHDRPlus));
> >>>
> >>> +
> >>>      return (AVDynamicHDRPlus *)side_data->data;
> >>>  }
> >>> +
> >>> +int decode_itu_t_t35_to_dynamic_hdr_plus(GetBitContext *gb,
> >> AVDynamicHDRPlus *s)
> >>> +{
> >>> +    int w, i, j;
> >>> +
> >>> +    if (get_bits_left(gb) < 2)
> >>> +        return AVERROR_INVALIDDATA;
> >>> +    s->num_windows = get_bits(gb, 2);
> >>> +
> >>> +    if (s->num_windows < 1 || s->num_windows > 3) {
> >>> +        return AVERROR_INVALIDDATA;
> >>> +    }
> >>> +
> >>> +    if (get_bits_left(gb) < ((19 * 8 + 1) * (s->num_windows - 1)))
> >>> +        return AVERROR_INVALIDDATA;
> >>> +    for (w = 1; w < s->num_windows; w++) {
> >>> +        s->params[w].window_upper_left_corner_x.num = get_bits(gb,
> 16);
> >>> +        s->params[w].window_upper_left_corner_y.num = get_bits(gb,
> 16);
> >>> +        s->params[w].window_lower_right_corner_x.num = get_bits(gb,
> 16);
> >>> +        s->params[w].window_lower_right_corner_y.num = get_bits(gb,
> 16);
> >>> +        // The corners are set to absolute coordinates here. They
> >> should be
> >>> +        // converted to the relative coordinates (in [0, 1]) in the
> >> decoder.
> >>> +        s->params[w].window_upper_left_corner_x.den = 1;
> >>> +        s->params[w].window_upper_left_corner_y.den = 1;
> >>> +        s->params[w].window_lower_right_corner_x.den = 1;
> >>> +        s->params[w].window_lower_right_corner_y.den = 1;
> >>> +
> >>> +        s->params[w].center_of_ellipse_x = get_bits(gb, 16);
> >>> +        s->params[w].center_of_ellipse_y = get_bits(gb, 16);
> >>> +        s->params[w].rotation_angle = get_bits(gb, 8);
> >>> +        s->params[w].semimajor_axis_internal_ellipse = get_bits(gb,
> 16);
> >>> +        s->params[w].semimajor_axis_external_ellipse = get_bits(gb,
> 16);
> >>> +        s->params[w].semiminor_axis_external_ellipse = get_bits(gb,
> 16);
> >>> +        s->params[w].overlap_process_option = get_bits1(gb);
> >>> +    }
> >>> +
> >>> +    if (get_bits_left(gb) < 28)
> >>> +        return AVERROR(EINVAL);
> >>> +    s->targeted_system_display_maximum_luminance.num = get_bits(gb,
> 27);
> >>> +    s->targeted_system_display_maximum_luminance.den = luminance_den;
> >>> +    s->targeted_system_display_actual_peak_luminance_flag =
> >> get_bits1(gb);
> >>> +
> >>> +    if (s->targeted_system_display_actual_peak_luminance_flag) {
> >>> +        int rows, cols;
> >>> +        if (get_bits_left(gb) < 10)
> >>> +            return AVERROR(EINVAL);
> >>> +        rows = get_bits(gb, 5);
> >>> +        cols = get_bits(gb, 5);
> >>> +        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols >
> 25)))
> >> {
> >>> +            return AVERROR_INVALIDDATA;
> >>> +        }
> >>> +        s->num_rows_targeted_system_display_actual_peak_luminance =
> >> rows;
> >>> +        s->num_cols_targeted_system_display_actual_peak_luminance =
> >> cols;
> >>> +
> >>> +        if (get_bits_left(gb) < (rows * cols * 4))
> >>> +            return AVERROR(EINVAL);
> >>> +
> >>> +        for (i = 0; i < rows; i++) {
> >>> +            for (j = 0; j < cols; j++) {
> >>> +
> >> s->targeted_system_display_actual_peak_luminance[i][j].num =
> get_bits(gb,
> >> 4);
> >>> +
> >> s->targeted_system_display_actual_peak_luminance[i][j].den =
> >> peak_luminance_den;
> >>> +            }
> >>> +        }
> >>> +    }
> >>> +    for (w = 0; w < s->num_windows; w++) {
> >>> +        if (get_bits_left(gb) < (3 * 17 + 17 + 4))
> >>> +            return AVERROR(EINVAL);
> >>> +        for (i = 0; i < 3; i++) {
> >>> +            s->params[w].maxscl[i].num = get_bits(gb, 17);
> >>> +            s->params[w].maxscl[i].den = rgb_den;
> >>> +        }
> >>> +        s->params[w].average_maxrgb.num = get_bits(gb, 17);
> >>> +        s->params[w].average_maxrgb.den = rgb_den;
> >>> +        s->params[w].num_distribution_maxrgb_percentiles =
> get_bits(gb,
> >> 4);
> >>> +
> >>> +        if (get_bits_left(gb) <
> >>> +            (s->params[w].num_distribution_maxrgb_percentiles * 24))
> >>> +            return AVERROR(EINVAL);
> >>> +        for (i = 0; i <
> >> s->params[w].num_distribution_maxrgb_percentiles; i++) {
> >>> +            s->params[w].distribution_maxrgb[i].percentage =
> >> get_bits(gb, 7);
> >>> +            s->params[w].distribution_maxrgb[i].percentile.num =
> >> get_bits(gb, 17);
> >>> +            s->params[w].distribution_maxrgb[i].percentile.den =
> >> rgb_den;
> >>> +        }
> >>> +
> >>> +        if (get_bits_left(gb) < 10)
> >>> +            return AVERROR(EINVAL);
> >>> +        s->params[w].fraction_bright_pixels.num = get_bits(gb, 10);
> >>> +        s->params[w].fraction_bright_pixels.den = fraction_pixel_den;
> >>> +    }
> >>> +    if (get_bits_left(gb) < 1)
> >>> +        return AVERROR(EINVAL);
> >>> +    s->mastering_display_actual_peak_luminance_flag = get_bits1(gb);
> >>> +    if (s->mastering_display_actual_peak_luminance_flag) {
> >>> +        int rows, cols;
> >>> +        if (get_bits_left(gb) < 10)
> >>> +            return AVERROR(EINVAL);
> >>> +        rows = get_bits(gb, 5);
> >>> +        cols = get_bits(gb, 5);
> >>> +        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols >
> 25)))
> >> {
> >>> +            return AVERROR_INVALIDDATA;
> >>> +        }
> >>> +        s->num_rows_mastering_display_actual_peak_luminance = rows;
> >>> +        s->num_cols_mastering_display_actual_peak_luminance = cols;
> >>> +
> >>> +        if (get_bits_left(gb) < (rows * cols * 4))
> >>> +            return AVERROR(EINVAL);
> >>> +
> >>> +        for (i = 0; i < rows; i++) {
> >>> +            for (j = 0; j < cols; j++) {
> >>> +                s->mastering_display_actual_peak_luminance[i][j].num =
> >> get_bits(gb, 4);
> >>> +                s->mastering_display_actual_peak_luminance[i][j].den =
> >> peak_luminance_den;
> >>> +            }
> >>> +        }
> >>> +    }
> >>> +
> >>> +    for (w = 0; w < s->num_windows; w++) {
> >>> +        if (get_bits_left(gb) < 1)
> >>> +            return AVERROR(EINVAL);
> >>> +        s->params[w].tone_mapping_flag = get_bits1(gb);
> >>> +        if (s->params[w].tone_mapping_flag) {
> >>> +            if (get_bits_left(gb) < 28)
> >>> +                return AVERROR(EINVAL);
> >>> +            s->params[w].knee_point_x.num = get_bits(gb, 12);
> >>> +            s->params[w].knee_point_x.den = knee_point_den;
> >>> +            s->params[w].knee_point_y.num = get_bits(gb, 12);
> >>> +            s->params[w].knee_point_y.den = knee_point_den;
> >>> +            s->params[w].num_bezier_curve_anchors = get_bits(gb, 4);
> >>> +
> >>> +            if (get_bits_left(gb) <
> >> (s->params[w].num_bezier_curve_anchors * 10))
> >>> +                return AVERROR(EINVAL);
> >>> +            for (i = 0; i < s->params[w].num_bezier_curve_anchors;
> i++)
> >> {
> >>> +                s->params[w].bezier_curve_anchors[i].num =
> get_bits(gb,
> >> 10);
> >>> +                s->params[w].bezier_curve_anchors[i].den =
> >> bezier_anchor_den;
> >>> +            }
> >>> +        }
> >>> +
> >>> +        if (get_bits_left(gb) < 1)
> >>> +            return AVERROR(EINVAL);
> >>> +        s->params[w].color_saturation_mapping_flag = get_bits1(gb);
> >>> +        if (s->params[w].color_saturation_mapping_flag) {
> >>> +            if (get_bits_left(gb) < 6)
> >>> +                return AVERROR(EINVAL);
> >>> +            s->params[w].color_saturation_weight.num = get_bits(gb,
> 6);
> >>> +            s->params[w].color_saturation_weight.den =
> >> saturation_weight_den;
> >>> +        }
> >>> +    }
> >>> +
> >>> +    skip_bits(gb, get_bits_left(gb));
> >>> +
> >>> +    return 0;
> >>> +}
> >>> +
> >>> +int get_encoded_dynamic_hdr_plus_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_rows_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 encode_dynamic_hdr_plus_to_itu_t_t35(const AVDynamicHDRPlus *s,
> >> PutBitContext *pb)
> >>> +{
> >>> +    int w, i, j;
> >>> +    if (!s)
> >>> +        return AVERROR(EINVAL);
> >>> +
> >>> +    if (put_bits_left(pb) <
> get_encoded_dynamic_hdr_plus_buffer_size(s))
> >>> +        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/libavutil/hdr_dynamic_metadata.h
> >> b/libavutil/hdr_dynamic_metadata.h
> >>> index 2d72de56ae..4e075f2f58 100644
> >>> --- a/libavutil/hdr_dynamic_metadata.h
> >>> +++ b/libavutil/hdr_dynamic_metadata.h
> >>> @@ -23,6 +23,8 @@
> >>>
> >>>  #include "frame.h"
> >>>  #include "rational.h"
> >>> +#include "libavcodec/get_bits.h"
> >>> +#include "libavcodec/put_bits.h"
> >>>
> >>>  /**
> >>>   * Option for overlapping elliptical pixel selectors in an image.
> >>> @@ -241,11 +243,6 @@ typedef struct AVHDRPlusColorTransformParams {
> >>>   * the public ABI.
> >>>   */
> >>>  typedef struct AVDynamicHDRPlus {
> >>> -    /**
> >>> -     * Country code by Rec. ITU-T T.35 Annex A. The value shall be
> 0xB5.
> >>> -     */
> >>> -    uint8_t itu_t_t35_country_code;
> >>
> >> This is an API break.
> >>
> >>> -
> >>>      /**
> >>>       * Application version in the application defining document in
> >> ST-2094
> >>>       * suite. The value shall be set to 0.
> >>> @@ -265,7 +262,7 @@ typedef struct AVDynamicHDRPlus {
> >>>
> >>>      /**
> >>>       * The nominal maximum display luminance of the targeted system
> >> display,
> >>> -     * in units of 0.0001 candelas per square metre. The value shall
> be
> >> in
> >>> +     * in units of 1 candelas per square metre. The value shall be in
> >>>       * the range of 0 to 10000, inclusive.
> >>>       */
> >>>      AVRational targeted_system_display_maximum_luminance;
> >>> @@ -322,14 +319,27 @@ typedef struct AVDynamicHDRPlus {
> >>>      AVRational mastering_display_actual_peak_luminance[25][25];
> >>>  } AVDynamicHDRPlus;
> >>>
> >>> +/**
> >>> + * Allocate an AVDynamicHDRPlus structure and set its fields to
> >>> + * the values decoded from the user data registered ITU-T T.35.
> >>> + * The resulting struct can be freed using av_freep().
> >>> + *
> >>> + * @return An AVDynamicHDRPlus filled with decoded values or NULL
> >>> + *         on failure.
> >>> + */
> >>> +AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size);
> >>> +
> >>>  /**
> >>>   * Allocate an AVDynamicHDRPlus structure and set its fields to
> >>>   * default values. The resulting struct can be freed using av_freep().
> >>> + * @param gb The input bit stream.
> >>> + * @param size The size of allocated memory for the returned
> >>> + *        AVDynamicHDRPlus structure.
> >>>   *
> >>>   * @return An AVDynamicHDRPlus filled with default values or NULL
> >>>   *         on failure.
> >>>   */
> >>> -AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size);
> >>> +AVDynamicHDRPlus *av_dynamic_hdr_plus(GetBitContext *gb, size_t
> *size);
> >>
> >> As is this. Also, GetBitContext and PutBitContext are internal structs
> >> to libavcodec, and can't be used in public headers.
> >>
> >>>
> >>>  /**
> >>>   * Allocate a complete AVDynamicHDRPlus and add it to the frame.
> >>> @@ -340,4 +350,31 @@ AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t
> >> *size);
> >>>   */
> >>>  AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame
> *frame);
> >>>
> >>> +/**
> >>> + * Decode the user data registered ITU-T T.35 to AVDynamicHDRPlus.
> >>> + * @param gb The bit content to be decoded.
> >>> + * @param s The decoded AVDynamicHDRPlus structure.
> >>> + *
> >>> + * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
> >>> + */
> >>> +int decode_itu_t_t35_to_dynamic_hdr_plus(GetBitContext *gb,
> >> AVDynamicHDRPlus *s);
> >>> +
> >>> +/**
> >>> + * 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 get_encoded_dynamic_hdr_plus_buffer_size(const AVDynamicHDRPlus
> *s);
> >>> +
> >>> +/**
> >>> + * Encode AVDynamicHDRPlus to the user data registered ITU-T T.35.
> >>> + * @param s The AVDynamicHDRPlus structure to be encoded.
> >>> + * @param pb The encoded bit stream.
> >>> + *
> >>> + * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
> >>> + */
> >>> +int encode_dynamic_hdr_plus_to_itu_t_t35(const AVDynamicHDRPlus *s,
> >> PutBitContext *pb);
> >>> +
> >>>  #endif /* AVUTIL_HDR_DYNAMIC_METADATA_H */
> >>> diff --git a/libavutil/version.h b/libavutil/version.h
> >>> index af8f614aff..2bc1b98615 100644
> >>> --- a/libavutil/version.h
> >>> +++ b/libavutil/version.h
> >>> @@ -79,7 +79,7 @@
> >>>   */
> >>>
> >>>  #define LIBAVUTIL_VERSION_MAJOR  56
> >>> -#define LIBAVUTIL_VERSION_MINOR  38
> >>> +#define LIBAVUTIL_VERSION_MINOR  39
> >>>  #define LIBAVUTIL_VERSION_MICRO 100
> >>>
> >>>  #define LIBAVUTIL_VERSION_INT
>  AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR,
> >> \
> >>>
> >>
> >> _______________________________________________
> >> 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".
> > _______________________________________________
> > 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".
> >
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
diff mbox series

Patch

diff --git a/libavutil/hdr_dynamic_metadata.c b/libavutil/hdr_dynamic_metadata.c
index 0fa1ee82de..e321a22af8 100644
--- a/libavutil/hdr_dynamic_metadata.c
+++ b/libavutil/hdr_dynamic_metadata.c
@@ -21,6 +21,19 @@ 
 #include "hdr_dynamic_metadata.h"
 #include "mem.h"
 
+static const uint8_t usa_country_code = 0xB5;
+static const uint16_t smpte_provider_code = 0x003C;
+static const uint16_t smpte2094_40_provider_oriented_code = 0x0001;
+static const uint16_t smpte2094_40_application_identifier = 0x04;
+
+static const int64_t luminance_den = 1;
+static const int32_t peak_luminance_den = 15;
+static const int64_t rgb_den = 100000;
+static const int32_t fraction_pixel_den = 1000;
+static const int32_t knee_point_den = 4095;
+static const int32_t bezier_anchor_den = 1023;
+static const int32_t saturation_weight_den = 8;
+
 AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size)
 {
     AVDynamicHDRPlus *hdr_plus = av_mallocz(sizeof(AVDynamicHDRPlus));
@@ -33,6 +46,48 @@  AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size)
     return hdr_plus;
 }
 
+AVDynamicHDRPlus *av_dynamic_hdr_plus(GetBitContext *gb, size_t *size)
+{
+    uint8_t country_code;
+    uint16_t provider_code;
+    uint16_t provider_oriented_code;
+    uint8_t application_identifier;
+    uint8_t application_version;
+    AVDynamicHDRPlus *hdr_plus;
+    int err;
+    
+    if (get_bits_left(gb) < 7)
+        return NULL;
+
+    country_code = get_bits(gb, 8);
+    provider_code = get_bits(gb, 16);
+
+    if (country_code != usa_country_code ||
+        provider_code != smpte_provider_code)
+        return NULL;
+
+    // A/341 Amendment – 2094-40
+    provider_oriented_code = get_bits(gb, 16);
+    application_identifier = get_bits(gb, 8);
+    application_version  = get_bits(gb, 8);
+    if (provider_oriented_code != smpte2094_40_provider_oriented_code ||
+        application_identifier != smpte2094_40_application_identifier)
+        return NULL;
+
+    hdr_plus = av_dynamic_hdr_plus_alloc(size);
+    if (!hdr_plus)
+        return NULL;
+
+    hdr_plus->application_version = application_version;
+    err = decode_itu_t_t35_to_dynamic_hdr_plus(gb, hdr_plus);
+    if (err < 0) {
+        av_freep(&hdr_plus);
+        return NULL;
+    }
+
+    return hdr_plus;
+}
+
 AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame)
 {
     AVFrameSideData *side_data = av_frame_new_side_data(frame,
@@ -43,5 +98,336 @@  AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame)
 
     memset(side_data->data, 0, sizeof(AVDynamicHDRPlus));
 
+
     return (AVDynamicHDRPlus *)side_data->data;
 }
+
+int decode_itu_t_t35_to_dynamic_hdr_plus(GetBitContext *gb, AVDynamicHDRPlus *s)
+{
+    int w, i, j;
+
+    if (get_bits_left(gb) < 2)
+        return AVERROR_INVALIDDATA;
+    s->num_windows = get_bits(gb, 2);
+
+    if (s->num_windows < 1 || s->num_windows > 3) {
+        return AVERROR_INVALIDDATA;
+    }
+
+    if (get_bits_left(gb) < ((19 * 8 + 1) * (s->num_windows - 1)))
+        return AVERROR_INVALIDDATA;
+    for (w = 1; w < s->num_windows; w++) {
+        s->params[w].window_upper_left_corner_x.num = get_bits(gb, 16);
+        s->params[w].window_upper_left_corner_y.num = get_bits(gb, 16);
+        s->params[w].window_lower_right_corner_x.num = get_bits(gb, 16);
+        s->params[w].window_lower_right_corner_y.num = get_bits(gb, 16);
+        // The corners are set to absolute coordinates here. They should be
+        // converted to the relative coordinates (in [0, 1]) in the decoder.
+        s->params[w].window_upper_left_corner_x.den = 1;
+        s->params[w].window_upper_left_corner_y.den = 1;
+        s->params[w].window_lower_right_corner_x.den = 1;
+        s->params[w].window_lower_right_corner_y.den = 1;
+
+        s->params[w].center_of_ellipse_x = get_bits(gb, 16);
+        s->params[w].center_of_ellipse_y = get_bits(gb, 16);
+        s->params[w].rotation_angle = get_bits(gb, 8);
+        s->params[w].semimajor_axis_internal_ellipse = get_bits(gb, 16);
+        s->params[w].semimajor_axis_external_ellipse = get_bits(gb, 16);
+        s->params[w].semiminor_axis_external_ellipse = get_bits(gb, 16);
+        s->params[w].overlap_process_option = get_bits1(gb);
+    }
+
+    if (get_bits_left(gb) < 28)
+        return AVERROR(EINVAL);
+    s->targeted_system_display_maximum_luminance.num = get_bits(gb, 27);
+    s->targeted_system_display_maximum_luminance.den = luminance_den;
+    s->targeted_system_display_actual_peak_luminance_flag = get_bits1(gb);
+
+    if (s->targeted_system_display_actual_peak_luminance_flag) {
+        int rows, cols;
+        if (get_bits_left(gb) < 10)
+            return AVERROR(EINVAL);
+        rows = get_bits(gb, 5);
+        cols = get_bits(gb, 5);
+        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25))) {
+            return AVERROR_INVALIDDATA;
+        }
+        s->num_rows_targeted_system_display_actual_peak_luminance = rows;
+        s->num_cols_targeted_system_display_actual_peak_luminance = cols;
+
+        if (get_bits_left(gb) < (rows * cols * 4))
+            return AVERROR(EINVAL);
+
+        for (i = 0; i < rows; i++) {
+            for (j = 0; j < cols; j++) {
+                s->targeted_system_display_actual_peak_luminance[i][j].num = get_bits(gb, 4);
+                s->targeted_system_display_actual_peak_luminance[i][j].den = peak_luminance_den;
+            }
+        }
+    }
+    for (w = 0; w < s->num_windows; w++) {
+        if (get_bits_left(gb) < (3 * 17 + 17 + 4))
+            return AVERROR(EINVAL);
+        for (i = 0; i < 3; i++) {
+            s->params[w].maxscl[i].num = get_bits(gb, 17);
+            s->params[w].maxscl[i].den = rgb_den;
+        }
+        s->params[w].average_maxrgb.num = get_bits(gb, 17);
+        s->params[w].average_maxrgb.den = rgb_den;
+        s->params[w].num_distribution_maxrgb_percentiles = get_bits(gb, 4);
+
+        if (get_bits_left(gb) <
+            (s->params[w].num_distribution_maxrgb_percentiles * 24))
+            return AVERROR(EINVAL);
+        for (i = 0; i < s->params[w].num_distribution_maxrgb_percentiles; i++) {
+            s->params[w].distribution_maxrgb[i].percentage = get_bits(gb, 7);
+            s->params[w].distribution_maxrgb[i].percentile.num = get_bits(gb, 17);
+            s->params[w].distribution_maxrgb[i].percentile.den = rgb_den;
+        }
+
+        if (get_bits_left(gb) < 10)
+            return AVERROR(EINVAL);
+        s->params[w].fraction_bright_pixels.num = get_bits(gb, 10);
+        s->params[w].fraction_bright_pixels.den = fraction_pixel_den;
+    }
+    if (get_bits_left(gb) < 1)
+        return AVERROR(EINVAL);
+    s->mastering_display_actual_peak_luminance_flag = get_bits1(gb);
+    if (s->mastering_display_actual_peak_luminance_flag) {
+        int rows, cols;
+        if (get_bits_left(gb) < 10)
+            return AVERROR(EINVAL);
+        rows = get_bits(gb, 5);
+        cols = get_bits(gb, 5);
+        if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25))) {
+            return AVERROR_INVALIDDATA;
+        }
+        s->num_rows_mastering_display_actual_peak_luminance = rows;
+        s->num_cols_mastering_display_actual_peak_luminance = cols;
+
+        if (get_bits_left(gb) < (rows * cols * 4))
+            return AVERROR(EINVAL);
+
+        for (i = 0; i < rows; i++) {
+            for (j = 0; j < cols; j++) {
+                s->mastering_display_actual_peak_luminance[i][j].num = get_bits(gb, 4);
+                s->mastering_display_actual_peak_luminance[i][j].den = peak_luminance_den;
+            }
+        }
+    }
+
+    for (w = 0; w < s->num_windows; w++) {
+        if (get_bits_left(gb) < 1)
+            return AVERROR(EINVAL);
+        s->params[w].tone_mapping_flag = get_bits1(gb);
+        if (s->params[w].tone_mapping_flag) {
+            if (get_bits_left(gb) < 28)
+                return AVERROR(EINVAL);
+            s->params[w].knee_point_x.num = get_bits(gb, 12);
+            s->params[w].knee_point_x.den = knee_point_den;
+            s->params[w].knee_point_y.num = get_bits(gb, 12);
+            s->params[w].knee_point_y.den = knee_point_den;
+            s->params[w].num_bezier_curve_anchors = get_bits(gb, 4);
+
+            if (get_bits_left(gb) < (s->params[w].num_bezier_curve_anchors * 10))
+                return AVERROR(EINVAL);
+            for (i = 0; i < s->params[w].num_bezier_curve_anchors; i++) {
+                s->params[w].bezier_curve_anchors[i].num = get_bits(gb, 10);
+                s->params[w].bezier_curve_anchors[i].den = bezier_anchor_den;
+            }
+        }
+
+        if (get_bits_left(gb) < 1)
+            return AVERROR(EINVAL);
+        s->params[w].color_saturation_mapping_flag = get_bits1(gb);
+        if (s->params[w].color_saturation_mapping_flag) {
+            if (get_bits_left(gb) < 6)
+                return AVERROR(EINVAL);
+            s->params[w].color_saturation_weight.num = get_bits(gb, 6);
+            s->params[w].color_saturation_weight.den = saturation_weight_den;
+        }
+    }
+
+    skip_bits(gb, get_bits_left(gb));
+
+    return 0;
+}
+
+int get_encoded_dynamic_hdr_plus_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_rows_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 encode_dynamic_hdr_plus_to_itu_t_t35(const AVDynamicHDRPlus *s, PutBitContext *pb)
+{
+    int w, i, j;
+    if (!s)
+        return AVERROR(EINVAL);
+
+    if (put_bits_left(pb) < get_encoded_dynamic_hdr_plus_buffer_size(s))
+        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/libavutil/hdr_dynamic_metadata.h b/libavutil/hdr_dynamic_metadata.h
index 2d72de56ae..4e075f2f58 100644
--- a/libavutil/hdr_dynamic_metadata.h
+++ b/libavutil/hdr_dynamic_metadata.h
@@ -23,6 +23,8 @@ 
 
 #include "frame.h"
 #include "rational.h"
+#include "libavcodec/get_bits.h"
+#include "libavcodec/put_bits.h"
 
 /**
  * Option for overlapping elliptical pixel selectors in an image.
@@ -241,11 +243,6 @@  typedef struct AVHDRPlusColorTransformParams {
  * the public ABI.
  */
 typedef struct AVDynamicHDRPlus {
-    /**
-     * Country code by Rec. ITU-T T.35 Annex A. The value shall be 0xB5.
-     */
-    uint8_t itu_t_t35_country_code;
-
     /**
      * Application version in the application defining document in ST-2094
      * suite. The value shall be set to 0.
@@ -265,7 +262,7 @@  typedef struct AVDynamicHDRPlus {
 
     /**
      * The nominal maximum display luminance of the targeted system display,
-     * in units of 0.0001 candelas per square metre. The value shall be in
+     * in units of 1 candelas per square metre. The value shall be in
      * the range of 0 to 10000, inclusive.
      */
     AVRational targeted_system_display_maximum_luminance;
@@ -322,14 +319,27 @@  typedef struct AVDynamicHDRPlus {
     AVRational mastering_display_actual_peak_luminance[25][25];
 } AVDynamicHDRPlus;
 
+/**
+ * Allocate an AVDynamicHDRPlus structure and set its fields to
+ * the values decoded from the user data registered ITU-T T.35.
+ * The resulting struct can be freed using av_freep().
+ *
+ * @return An AVDynamicHDRPlus filled with decoded values or NULL
+ *         on failure.
+ */
+AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size);
+
 /**
  * Allocate an AVDynamicHDRPlus structure and set its fields to
  * default values. The resulting struct can be freed using av_freep().
+ * @param gb The input bit stream.
+ * @param size The size of allocated memory for the returned
+ *        AVDynamicHDRPlus structure.
  *
  * @return An AVDynamicHDRPlus filled with default values or NULL
  *         on failure.
  */
-AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size);
+AVDynamicHDRPlus *av_dynamic_hdr_plus(GetBitContext *gb, size_t *size);
 
 /**
  * Allocate a complete AVDynamicHDRPlus and add it to the frame.
@@ -340,4 +350,31 @@  AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size);
  */
 AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame);
 
+/**
+ * Decode the user data registered ITU-T T.35 to AVDynamicHDRPlus.
+ * @param gb The bit content to be decoded.
+ * @param s The decoded AVDynamicHDRPlus structure.
+ *
+ * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
+ */
+int decode_itu_t_t35_to_dynamic_hdr_plus(GetBitContext *gb, AVDynamicHDRPlus *s);
+
+/**
+ * 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 get_encoded_dynamic_hdr_plus_buffer_size(const AVDynamicHDRPlus *s);
+
+/**
+ * Encode AVDynamicHDRPlus to the user data registered ITU-T T.35.
+ * @param s The AVDynamicHDRPlus structure to be encoded.
+ * @param pb The encoded bit stream.
+ *
+ * @return 0 if succeed. Otherwise, returns the appropriate AVERROR.
+ */
+int encode_dynamic_hdr_plus_to_itu_t_t35(const AVDynamicHDRPlus *s, PutBitContext *pb);
+
 #endif /* AVUTIL_HDR_DYNAMIC_METADATA_H */
diff --git a/libavutil/version.h b/libavutil/version.h
index af8f614aff..2bc1b98615 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,7 +79,7 @@ 
  */
 
 #define LIBAVUTIL_VERSION_MAJOR  56
-#define LIBAVUTIL_VERSION_MINOR  38
+#define LIBAVUTIL_VERSION_MINOR  39
 #define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \