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