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