From patchwork Mon Feb 27 17:33:42 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Rapha=C3=ABl_Zumer?= X-Patchwork-Id: 40532 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:d046:b0:cd:afd7:272c with SMTP id hv6csp201990pzb; Mon, 27 Feb 2023 09:33:57 -0800 (PST) X-Google-Smtp-Source: AK7set/NGwG8ms+aG5f9CpNxO1CcUqHFhl1Ayp5JKHXp7ozLU7JkGxKyVkr8FDPBZ9qSIjHHs1DR X-Received: by 2002:a17:906:4d8c:b0:8ae:f73e:233f with SMTP id s12-20020a1709064d8c00b008aef73e233fmr37663395eju.32.1677519237068; Mon, 27 Feb 2023 09:33:57 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1677519237; cv=none; d=google.com; s=arc-20160816; b=rU74CZg0I1vAZZ05IUtWUG7SXKLBiB9D/gPTGPkwZ5zCUhFprrZEaSNtv/H8fsCrcy 1o/XlzxnkOX8UXwMkzoopL+29axthHkc3qGkzWCJSQj9ac4LDU/UM0FbTCDuuLjmbdhI BbkKVDSJg4SiukfAlWxgprfDZlFAlmnG0FEjJzuLhJDwGI7bigght1wRrPP/b58QGdWC W3exBTqVKx1pubhe4TvjlTODa3NhiiRKxcwqUOtBMaU2AXtmlNkMLHbipoVypztwcgq/ GaXOfVj9X+fKpQfhi42zuQZ+Xw6jTzQTx8O/9OB+glp5nkdiIuw0Y7/cwkVQXX9Haz/K ghpQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:content-language:to:from:user-agent:mime-version :date:message-id:dkim-signature:delivered-to; bh=UWyIJLSxkensvE+uHnGwznXqhUllhQE+NFiMqSRlB7Q=; b=ACVaQfaCHJ0dBVOdPvcHPghMDr87zZtFlDmFyGNn5RPXSqD1Gb9POP2jIshpz9ysab a2eiU8FTroxaoq74hMg0zepRkR39BbQm++m4QI17TnnkbDYnsCWdpRsm2wwDIhiKB+59 UzMFGCHqTyWMIqbp/2SDupZ688bXh03/OwB9/q5dzec4LXWs+0xpn0tICDbeB7NkeZOD PeF6zBcFyWj5CSIxhLHMeGcJz7PJ6zyhR2vBbKtjrE6oD0StLTg/5O6F/cpESJ0SkGBE 1neYqTOyXZizij2aV1XzGyRxm8A7zJaaq6Nha0gZHqxPuIbe+O/6wYAsIz9E1sNDDaO/ BV6w== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@vimeo.com header.s=google header.b=J085HUph; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=vimeo.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id o22-20020a1709061b1600b008b2a99b17fbsi8277688ejg.205.2023.02.27.09.33.56; Mon, 27 Feb 2023 09:33:57 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@vimeo.com header.s=google header.b=J085HUph; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=vimeo.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 3BF1A689E37; Mon, 27 Feb 2023 19:33:52 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qt1-f171.google.com (mail-qt1-f171.google.com [209.85.160.171]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 67DFB6809C7 for ; Mon, 27 Feb 2023 19:33:46 +0200 (EET) Received: by mail-qt1-f171.google.com with SMTP id r5so7520253qtp.4 for ; Mon, 27 Feb 2023 09:33:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=vimeo.com; s=google; h=content-transfer-encoding:content-language:to:subject:from :user-agent:mime-version:date:message-id:from:to:cc:subject:date :message-id:reply-to; bh=j7h71zIwYzaw9MiwPYlQEVgPVHvY9qX8pHLfnkeRFyU=; b=J085HUphaSLxLMJsGsVSVaxQgWmpshTK9EBVz63CINIttwY+oEfSZ61G2MRzEnqDqk Gd6f7AXe7c8UATX9tfQhDzt9R5dGXojWY87HFxk0ph61okhLANeocVry34ka7hgaGHll J+lNgte/6StifM0mbn3ZLtUMf6gHFl8TKUGEQ= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:content-language:to:subject:from :user-agent:mime-version:date:message-id:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=j7h71zIwYzaw9MiwPYlQEVgPVHvY9qX8pHLfnkeRFyU=; b=uAKh30Xem8zIk90Z7m7Dtqa8ZbIz8Au0xQedHGek8MwenGOALomkffddXaWg+Ws2FI xJyFq48JyJ0XYE1eJt48SmD5Ip5PpsWC5nAOwryZDwblwzBAoHITX5bZcpgsjeU0p9Yy 52NSt9jkR021V8sI96LFJwFVbbHNbNzZ4y6vLGI2yARYGUDsCKPOP6TqSbz04vTtllW/ Yd722r0UEXkcxnOwQSAqG05ejTgdc4raEjo5r0fPLMrPm2kggjaos82CGO3qiesYKMqP 6kme+NPfDS4jRLX6s8q1hsPJ6WrYKOsb5e6XBQTzP5xDYyCuf9+Ccjc57lk6ZlMZgNdo OX0Q== X-Gm-Message-State: AO0yUKVJQAGfNhD2UQIgCuy5LQfx1ruKYm+bGY6MD7kIuK8iqneFrZwi Kj+qEVQ7v3KY4DKa9YEhpWz+4m93+S/we6Mljpc= X-Received: by 2002:ac8:5f10:0:b0:3bf:da89:1946 with SMTP id x16-20020ac85f10000000b003bfda891946mr244724qta.1.1677519223904; Mon, 27 Feb 2023 09:33:43 -0800 (PST) Received: from ?IPV6:2600:4041:6c:7000:1f01:20c:8ba9:815b? ([2600:4041:6c:7000:1f01:20c:8ba9:815b]) by smtp.gmail.com with ESMTPSA id j3-20020a378703000000b00742bc037f29sm1195231qkd.120.2023.02.27.09.33.43 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 27 Feb 2023 09:33:43 -0800 (PST) Message-ID: <9078bd19-6b49-3cad-adb1-9f7a67a3e598@vimeo.com> Date: Mon, 27 Feb 2023 12:33:42 -0500 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.2.2 From: =?utf-8?q?Rapha=C3=ABl_Zumer?= To: ffmpeg-devel@ffmpeg.org Content-Language: en-US, fr-FR Subject: [FFmpeg-devel] [PATCH 1/2] avcodec/avutil: move dynamic HDR metadata parsing to libavutil X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: i0Niz6SqqUbw Resending this patch set due to my mail client messing with the line wrapping in the messages I sent earlier today. Below is a copy of the initial explanation. This patch set implements serialization for HDR10+ dynamic metadata (AVDynamicHDRPlus), which is the inverse operation of the existing ff_parse_itu_t_t35_to_dynamic_hdr10_plus() function. It also moves both functions from libavcodec to libavutil and makes them public. For consistency, the equivalent vivid HDR parsing function is also migrated, but I did not implement serialization for it. Finally, the patch renames those functions to av_dynamic_hdr_plus_from_t35() (for parsing) and av_dynamic_hdr_plus_to_t35 (for serialization), with the equivalent change being made for vivid as well. The motivation for this change is to allow users to easily convert HDR10+ side data (which is parsed into AVDynamicHDRPlus) to a standard ITU-T T.35 payload that can be passed directly to applications that expect HDR10+ dynamic metadata in that format (e.g. x265 and rav1e encoders). The return value of the serialization function is AVBufferRef*, which I expect to be contentious. Payload size is not embedded in the T.35 data, so it must be calculated, used to allocate a buffer, and returned along with that buffer to the user. As far as I'm aware, AVBufferRef is the simplest way to do that, but I will be happy to consider alternative solutions. Please let me know if it is preferred to bump libavutil with the first commit, or with both of them, considering there are public API changes associated with each one. Raphaël Zumer Signed-off-by: Raphaël Zumer --- libavcodec/Makefile | 3 +- libavcodec/dynamic_hdr10_plus.c | 198 ------------------------- libavcodec/dynamic_hdr10_plus.h | 35 ----- libavcodec/dynamic_hdr_vivid.c | 139 ----------------- libavcodec/dynamic_hdr_vivid.h | 35 ----- libavcodec/h2645_sei.c | 12 +- libavutil/hdr_dynamic_metadata.c | 180 ++++++++++++++++++++++ libavutil/hdr_dynamic_metadata.h | 11 ++ libavutil/hdr_dynamic_vivid_metadata.c | 120 +++++++++++++++ libavutil/hdr_dynamic_vivid_metadata.h | 11 ++ 10 files changed, 329 insertions(+), 415 deletions(-) delete mode 100644 libavcodec/dynamic_hdr10_plus.c delete mode 100644 libavcodec/dynamic_hdr10_plus.h delete mode 100644 libavcodec/dynamic_hdr_vivid.c delete mode 100644 libavcodec/dynamic_hdr_vivid.h diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 389253f5d0..4bdfcbab12 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -103,8 +103,7 @@ OBJS-$(CONFIG_H264QPEL) += h264qpel.o OBJS-$(CONFIG_H264_SEI) += h264_sei.o h2645_sei.o OBJS-$(CONFIG_HEVCPARSE) += hevc_parse.o hevc_ps.o hevc_data.o \ h2645data.o h2645_parse.o h2645_vui.o -OBJS-$(CONFIG_HEVC_SEI) += hevc_sei.o h2645_sei.o \ - dynamic_hdr10_plus.o dynamic_hdr_vivid.o +OBJS-$(CONFIG_HEVC_SEI) += hevc_sei.o h2645_sei.o OBJS-$(CONFIG_HPELDSP) += hpeldsp.o OBJS-$(CONFIG_HUFFMAN) += huffman.o OBJS-$(CONFIG_HUFFYUVDSP) += huffyuvdsp.o diff --git a/libavcodec/dynamic_hdr10_plus.c b/libavcodec/dynamic_hdr10_plus.c deleted file mode 100644 index 34a44aac65..0000000000 --- a/libavcodec/dynamic_hdr10_plus.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * This file is part of FFmpeg. - * - * FFmpeg is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * FFmpeg is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with FFmpeg; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "dynamic_hdr10_plus.h" -#include "get_bits.h" - -static const int64_t luminance_den = 1; -static const int32_t peak_luminance_den = 15; -static const int64_t rgb_den = 100000; -static const int32_t fraction_pixel_den = 1000; -static const int32_t knee_point_den = 4095; -static const int32_t bezier_anchor_den = 1023; -static const int32_t saturation_weight_den = 8; - -int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t *data, - int size) -{ - GetBitContext gbc, *gb = &gbc; - int ret; - - if (!s) - return AVERROR(ENOMEM); - - ret = init_get_bits8(gb, data, size); - if (ret < 0) - return ret; - - if (get_bits_left(gb) < 10) - return AVERROR_INVALIDDATA; - - s->application_version = get_bits(gb, 8); - s->num_windows = get_bits(gb, 2); - - if (s->num_windows < 1 || s->num_windows > 3) { - return AVERROR_INVALIDDATA; - } - - if (get_bits_left(gb) < ((19 * 8 + 1) * (s->num_windows - 1))) - return AVERROR_INVALIDDATA; - - for (int w = 1; w < s->num_windows; w++) { - // The corners are set to absolute coordinates here. They should be - // converted to the relative coordinates (in [0, 1]) in the decoder. - AVHDRPlusColorTransformParams *params = &s->params[w]; - params->window_upper_left_corner_x = - (AVRational){get_bits(gb, 16), 1}; - params->window_upper_left_corner_y = - (AVRational){get_bits(gb, 16), 1}; - params->window_lower_right_corner_x = - (AVRational){get_bits(gb, 16), 1}; - params->window_lower_right_corner_y = - (AVRational){get_bits(gb, 16), 1}; - - params->center_of_ellipse_x = get_bits(gb, 16); - params->center_of_ellipse_y = get_bits(gb, 16); - params->rotation_angle = get_bits(gb, 8); - params->semimajor_axis_internal_ellipse = get_bits(gb, 16); - params->semimajor_axis_external_ellipse = get_bits(gb, 16); - params->semiminor_axis_external_ellipse = get_bits(gb, 16); - params->overlap_process_option = get_bits1(gb); - } - - if (get_bits_left(gb) < 28) - return AVERROR_INVALIDDATA; - - 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) { - int rows, cols; - if (get_bits_left(gb) < 10) - return AVERROR_INVALIDDATA; - rows = get_bits(gb, 5); - cols = get_bits(gb, 5); - if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25))) { - return AVERROR_INVALIDDATA; - } - s->num_rows_targeted_system_display_actual_peak_luminance = rows; - s->num_cols_targeted_system_display_actual_peak_luminance = cols; - - if (get_bits_left(gb) < (rows * cols * 4)) - return AVERROR_INVALIDDATA; - - for (int i = 0; i < rows; i++) { - for (int j = 0; j < cols; j++) { - s->targeted_system_display_actual_peak_luminance[i][j] = - (AVRational){get_bits(gb, 4), peak_luminance_den}; - } - } - } - for (int w = 0; w < s->num_windows; w++) { - AVHDRPlusColorTransformParams *params = &s->params[w]; - if (get_bits_left(gb) < (3 * 17 + 17 + 4)) - return AVERROR_INVALIDDATA; - - for (int i = 0; i < 3; i++) { - params->maxscl[i] = - (AVRational){get_bits(gb, 17), rgb_den}; - } - params->average_maxrgb = - (AVRational){get_bits(gb, 17), rgb_den}; - params->num_distribution_maxrgb_percentiles = get_bits(gb, 4); - - if (get_bits_left(gb) < - (params->num_distribution_maxrgb_percentiles * 24)) - return AVERROR_INVALIDDATA; - - for (int i = 0; i < params->num_distribution_maxrgb_percentiles; i++) { - params->distribution_maxrgb[i].percentage = get_bits(gb, 7); - params->distribution_maxrgb[i].percentile = - (AVRational){get_bits(gb, 17), rgb_den}; - } - - if (get_bits_left(gb) < 10) - return AVERROR_INVALIDDATA; - - params->fraction_bright_pixels = (AVRational){get_bits(gb, 10), fraction_pixel_den}; - } - if (get_bits_left(gb) < 1) - return AVERROR_INVALIDDATA; - s->mastering_display_actual_peak_luminance_flag = get_bits1(gb); - if (s->mastering_display_actual_peak_luminance_flag) { - int rows, cols; - if (get_bits_left(gb) < 10) - return AVERROR_INVALIDDATA; - rows = get_bits(gb, 5); - cols = get_bits(gb, 5); - if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25))) { - return AVERROR_INVALIDDATA; - } - s->num_rows_mastering_display_actual_peak_luminance = rows; - s->num_cols_mastering_display_actual_peak_luminance = cols; - - if (get_bits_left(gb) < (rows * cols * 4)) - return AVERROR_INVALIDDATA; - - for (int i = 0; i < rows; i++) { - for (int j = 0; j < cols; j++) { - s->mastering_display_actual_peak_luminance[i][j] = - (AVRational){get_bits(gb, 4), peak_luminance_den}; - } - } - } - - for (int w = 0; w < s->num_windows; w++) { - AVHDRPlusColorTransformParams *params = &s->params[w]; - if (get_bits_left(gb) < 1) - return AVERROR_INVALIDDATA; - - params->tone_mapping_flag = get_bits1(gb); - if (params->tone_mapping_flag) { - if (get_bits_left(gb) < 28) - return AVERROR_INVALIDDATA; - - 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_INVALIDDATA; - - for (int i = 0; i < params->num_bezier_curve_anchors; i++) { - params->bezier_curve_anchors[i] = - (AVRational){get_bits(gb, 10), bezier_anchor_den}; - } - } - - if (get_bits_left(gb) < 1) - return AVERROR_INVALIDDATA; - params->color_saturation_mapping_flag = get_bits1(gb); - if (params->color_saturation_mapping_flag) { - if (get_bits_left(gb) < 6) - return AVERROR_INVALIDDATA; - params->color_saturation_weight = - (AVRational){get_bits(gb, 6), saturation_weight_den}; - } - } - - return 0; -} diff --git a/libavcodec/dynamic_hdr10_plus.h b/libavcodec/dynamic_hdr10_plus.h deleted file mode 100644 index cd7acf0432..0000000000 --- a/libavcodec/dynamic_hdr10_plus.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of FFmpeg. - * - * FFmpeg is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * FFmpeg is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with FFmpeg; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef AVCODEC_DYNAMIC_HDR10_PLUS_H -#define AVCODEC_DYNAMIC_HDR10_PLUS_H - -#include "libavutil/hdr_dynamic_metadata.h" - -/** - * Parse the user data registered ITU-T T.35 to AVbuffer (AVDynamicHDRPlus). - * @param s A pointer containing the decoded AVDynamicHDRPlus structure. - * @param data The byte array containing the raw ITU-T T.35 data. - * @param size Size of the data array in bytes. - * - * @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); - -#endif /* AVCODEC_DYNAMIC_HDR10_PLUS_H */ diff --git a/libavcodec/dynamic_hdr_vivid.c b/libavcodec/dynamic_hdr_vivid.c deleted file mode 100644 index d689669dec..0000000000 --- a/libavcodec/dynamic_hdr_vivid.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * This file is part of FFmpeg. - * - * FFmpeg is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * FFmpeg is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with FFmpeg; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "dynamic_hdr_vivid.h" -#include "get_bits.h" - -static const int32_t maxrgb_den = 4095; -static const int32_t color_saturation_gain_den = 128; -static const int32_t maximum_luminance_den = 4095; -static const int32_t base_param_m_p_den = 16383; -static const int32_t base_param_m_m_den = 10; -static const int32_t base_param_m_a_den = 1023; -static const int32_t base_param_m_b_den = 1023; -static const int32_t base_param_m_n_den = 10; -static const int32_t base_param_Delta_den = 127; - -int ff_parse_itu_t_t35_to_dynamic_hdr_vivid(AVDynamicHDRVivid *s, const uint8_t *data, - int size) -{ - GetBitContext gbc, *gb = &gbc; - int ret; - - if (!s) - return AVERROR(ENOMEM); - - ret = init_get_bits8(gb, data, size); - if (ret < 0) - return ret; - - if (get_bits_left(gb) < 8) - return AVERROR_INVALIDDATA; - - s->system_start_code = get_bits(gb, 8); - if (s->system_start_code == 0x01) { - s->num_windows = 1; - - if (get_bits_left(gb) < 12 * 4 * s->num_windows) - return AVERROR_INVALIDDATA; - for (int w = 0; w < s->num_windows; w++) { - AVHDRVividColorTransformParams *params = &s->params[w]; - - params->minimum_maxrgb = (AVRational){get_bits(gb, 12), maxrgb_den}; - params->average_maxrgb = (AVRational){get_bits(gb, 12), maxrgb_den}; - params->variance_maxrgb = (AVRational){get_bits(gb, 12), maxrgb_den}; - params->maximum_maxrgb = (AVRational){get_bits(gb, 12), maxrgb_den}; - } - - if (get_bits_left(gb) < 2 * s->num_windows) - return AVERROR_INVALIDDATA; - for (int w = 0; w < s->num_windows; w++) { - AVHDRVividColorTransformParams *params = &s->params[w]; - - params->tone_mapping_mode_flag = get_bits(gb, 1); - if (params->tone_mapping_mode_flag) { - if (get_bits_left(gb) < 1 ) - return AVERROR_INVALIDDATA; - params->tone_mapping_param_num = get_bits(gb, 1) + 1; - for (int i = 0; i < params->tone_mapping_param_num; i++) { - AVHDRVividColorToneMappingParams *tm_params = ¶ms->tm_params[i]; - - if (get_bits_left(gb) < 13) - return AVERROR_INVALIDDATA; - tm_params->targeted_system_display_maximum_luminance = (AVRational){get_bits(gb, 12), maximum_luminance_den}; - tm_params->base_enable_flag = get_bits(gb, 1); - if (tm_params->base_enable_flag) { - if (get_bits_left(gb) < (14 + 6 + 10 + 10 + 6 + 8 + 10)) - return AVERROR_INVALIDDATA; - tm_params->base_param_m_p = (AVRational){get_bits(gb, 14), base_param_m_p_den}; - tm_params->base_param_m_m = (AVRational){get_bits(gb, 6), base_param_m_m_den}; - tm_params->base_param_m_a = (AVRational){get_bits(gb, 10), base_param_m_a_den}; - tm_params->base_param_m_b = (AVRational){get_bits(gb, 10), base_param_m_b_den}; - tm_params->base_param_m_n = (AVRational){get_bits(gb, 6), base_param_m_n_den}; - tm_params->base_param_k1 = get_bits(gb, 2); - tm_params->base_param_k2 = get_bits(gb, 2); - tm_params->base_param_k3 = get_bits(gb, 4); - tm_params->base_param_Delta_enable_mode = get_bits(gb, 3); - if (tm_params->base_param_Delta_enable_mode == 2 || tm_params->base_param_Delta_enable_mode == 6) - tm_params->base_param_Delta = (AVRational){get_bits(gb, 7) * -1, base_param_Delta_den}; - else - tm_params->base_param_Delta = (AVRational){get_bits(gb, 7), base_param_Delta_den}; - - if (get_bits_left(gb) < 1) - return AVERROR_INVALIDDATA; - tm_params->three_Spline_enable_flag = get_bits(gb, 1); - if (tm_params->three_Spline_enable_flag) { - if (get_bits_left(gb) < 1 + tm_params->three_Spline_num * (2 + 12 + 28 + 1)) - return AVERROR_INVALIDDATA; - tm_params->three_Spline_num = get_bits(gb, 1) + 1; - for (int j = 0; j < tm_params->three_Spline_num; j++) { - tm_params->three_Spline_TH_mode = get_bits(gb, 2); - if (tm_params->three_Spline_TH_mode == 0 || tm_params->three_Spline_TH_mode == 2) { - if (get_bits_left(gb) < 8) - return AVERROR_INVALIDDATA; - tm_params->three_Spline_TH_enable_MB = (AVRational){get_bits(gb, 8), 255}; - } - tm_params->three_Spline_TH_enable = (AVRational){get_bits(gb, 12), 4095}; - tm_params->three_Spline_TH_Delta1 = (AVRational){get_bits(gb, 10), 1023}; - tm_params->three_Spline_TH_Delta2 = (AVRational){get_bits(gb, 10), 1023}; - tm_params->three_Spline_enable_Strength = (AVRational){get_bits(gb, 8), 255}; - } - } else { - tm_params->three_Spline_num = 1; - tm_params->three_Spline_TH_mode = 0; - } - - } - } - } - - params->color_saturation_mapping_flag = get_bits(gb, 1); - if (params->color_saturation_mapping_flag) { - if (get_bits_left(gb) < 3 + params->color_saturation_num * 8) - return AVERROR_INVALIDDATA; - - params->color_saturation_num = get_bits(gb, 3); - for (int i = 0; i < params->color_saturation_num; i++) { - params->color_saturation_gain[i] = (AVRational){get_bits(gb, 8), color_saturation_gain_den}; - } - } - } - } - - return 0; -} diff --git a/libavcodec/dynamic_hdr_vivid.h b/libavcodec/dynamic_hdr_vivid.h deleted file mode 100644 index d521b3d263..0000000000 --- a/libavcodec/dynamic_hdr_vivid.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of FFmpeg. - * - * FFmpeg is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * FFmpeg is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with FFmpeg; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef AVCODEC_DYNAMIC_HDR_VIVID_H -#define AVCODEC_DYNAMIC_HDR_VIVID_H - -#include "libavutil/hdr_dynamic_vivid_metadata.h" - -/** - * Parse the user data registered ITU-T T.35 to AVbuffer (AVDynamicHDRVivid). - * @param s A pointer containing the decoded AVDynamicHDRVivid structure. - * @param data The byte array containing the raw ITU-T T.35 data. - * @param size Size of the data array in bytes. - * - * @return 0 if succeed. Otherwise, returns the appropriate AVERROR. - */ -int ff_parse_itu_t_t35_to_dynamic_hdr_vivid(AVDynamicHDRVivid *s, const uint8_t *data, - int size); - -#endif /* AVCODEC_DYNAMIC_HDR_VIVID_H */ diff --git a/libavcodec/h2645_sei.c b/libavcodec/h2645_sei.c index 6e4a9a1af2..7a96bc6056 100644 --- a/libavcodec/h2645_sei.c +++ b/libavcodec/h2645_sei.c @@ -27,14 +27,14 @@ #include "libavutil/ambient_viewing_environment.h" #include "libavutil/display.h" +#include "libavutil/hdr_dynamic_metadata.h" +#include "libavutil/hdr_dynamic_vivid_metadata.h" #include "libavutil/film_grain_params.h" #include "libavutil/pixdesc.h" #include "libavutil/stereo3d.h" #include "atsc_a53.h" #include "avcodec.h" -#include "dynamic_hdr10_plus.h" -#include "dynamic_hdr_vivid.h" #include "get_bits.h" #include "golomb.h" #include "h2645_sei.h" @@ -52,8 +52,8 @@ static int decode_registered_user_data_dynamic_hdr_plus(HEVCSEIDynamicHDRPlus *s if (!metadata) return AVERROR(ENOMEM); - err = ff_parse_itu_t_t35_to_dynamic_hdr10_plus(metadata, gb->buffer, - bytestream2_get_bytes_left(gb)); + err = av_dynamic_hdr_plus_from_t35(metadata, gb->buffer, + bytestream2_get_bytes_left(gb)); if (err < 0) { av_free(metadata); return err; @@ -78,8 +78,8 @@ static int decode_registered_user_data_dynamic_hdr_vivid(HEVCSEIDynamicHDRVivid if (!metadata) return AVERROR(ENOMEM); - err = ff_parse_itu_t_t35_to_dynamic_hdr_vivid(metadata, - gb->buffer, bytestream2_get_bytes_left(gb)); + err = av_dynamic_hdr_vivid_from_t35(metadata, + gb->buffer, bytestream2_get_bytes_left(gb)); if (err < 0) { av_free(metadata); return err; diff --git a/libavutil/hdr_dynamic_metadata.c b/libavutil/hdr_dynamic_metadata.c index 0fa1ee82de..98f399b032 100644 --- a/libavutil/hdr_dynamic_metadata.c +++ b/libavutil/hdr_dynamic_metadata.c @@ -20,6 +20,16 @@ #include "hdr_dynamic_metadata.h" #include "mem.h" +#include "libavcodec/get_bits.h" +#include "libavcodec/put_bits.h" + +static const int64_t luminance_den = 1; +static const int32_t peak_luminance_den = 15; +static const int64_t rgb_den = 100000; +static const int32_t fraction_pixel_den = 1000; +static const int32_t knee_point_den = 4095; +static const int32_t bezier_anchor_den = 1023; +static const int32_t saturation_weight_den = 8; AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size) { @@ -45,3 +55,173 @@ AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame) return (AVDynamicHDRPlus *)side_data->data; } + +int av_dynamic_hdr_plus_from_t35(AVDynamicHDRPlus *s, const uint8_t *data, + int size) +{ + GetBitContext gbc, *gb = &gbc; + int ret; + + if (!s) + return AVERROR(ENOMEM); + + ret = init_get_bits8(gb, data, size); + if (ret < 0) + return ret; + + if (get_bits_left(gb) < 10) + return AVERROR_INVALIDDATA; + + s->application_version = get_bits(gb, 8); + s->num_windows = get_bits(gb, 2); + + if (s->num_windows < 1 || s->num_windows > 3) { + return AVERROR_INVALIDDATA; + } + + if (get_bits_left(gb) < ((19 * 8 + 1) * (s->num_windows - 1))) + return AVERROR_INVALIDDATA; + + for (int w = 1; w < s->num_windows; w++) { + // The corners are set to absolute coordinates here. They should be + // converted to the relative coordinates (in [0, 1]) in the decoder. + AVHDRPlusColorTransformParams *params = &s->params[w]; + params->window_upper_left_corner_x = + (AVRational){get_bits(gb, 16), 1}; + params->window_upper_left_corner_y = + (AVRational){get_bits(gb, 16), 1}; + params->window_lower_right_corner_x = + (AVRational){get_bits(gb, 16), 1}; + params->window_lower_right_corner_y = + (AVRational){get_bits(gb, 16), 1}; + + params->center_of_ellipse_x = get_bits(gb, 16); + params->center_of_ellipse_y = get_bits(gb, 16); + params->rotation_angle = get_bits(gb, 8); + params->semimajor_axis_internal_ellipse = get_bits(gb, 16); + params->semimajor_axis_external_ellipse = get_bits(gb, 16); + params->semiminor_axis_external_ellipse = get_bits(gb, 16); + params->overlap_process_option = get_bits1(gb); + } + + if (get_bits_left(gb) < 28) + return AVERROR_INVALIDDATA; + + 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) { + int rows, cols; + if (get_bits_left(gb) < 10) + return AVERROR_INVALIDDATA; + rows = get_bits(gb, 5); + cols = get_bits(gb, 5); + if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25))) { + return AVERROR_INVALIDDATA; + } + s->num_rows_targeted_system_display_actual_peak_luminance = rows; + s->num_cols_targeted_system_display_actual_peak_luminance = cols; + + if (get_bits_left(gb) < (rows * cols * 4)) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + s->targeted_system_display_actual_peak_luminance[i][j] = + (AVRational){get_bits(gb, 4), peak_luminance_den}; + } + } + } + for (int w = 0; w < s->num_windows; w++) { + AVHDRPlusColorTransformParams *params = &s->params[w]; + if (get_bits_left(gb) < (3 * 17 + 17 + 4)) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < 3; i++) { + params->maxscl[i] = + (AVRational){get_bits(gb, 17), rgb_den}; + } + params->average_maxrgb = + (AVRational){get_bits(gb, 17), rgb_den}; + params->num_distribution_maxrgb_percentiles = get_bits(gb, 4); + + if (get_bits_left(gb) < + (params->num_distribution_maxrgb_percentiles * 24)) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < params->num_distribution_maxrgb_percentiles; i++) { + params->distribution_maxrgb[i].percentage = get_bits(gb, 7); + params->distribution_maxrgb[i].percentile = + (AVRational){get_bits(gb, 17), rgb_den}; + } + + if (get_bits_left(gb) < 10) + return AVERROR_INVALIDDATA; + + params->fraction_bright_pixels = (AVRational){get_bits(gb, 10), fraction_pixel_den}; + } + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; + s->mastering_display_actual_peak_luminance_flag = get_bits1(gb); + if (s->mastering_display_actual_peak_luminance_flag) { + int rows, cols; + if (get_bits_left(gb) < 10) + return AVERROR_INVALIDDATA; + rows = get_bits(gb, 5); + cols = get_bits(gb, 5); + if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25))) { + return AVERROR_INVALIDDATA; + } + s->num_rows_mastering_display_actual_peak_luminance = rows; + s->num_cols_mastering_display_actual_peak_luminance = cols; + + if (get_bits_left(gb) < (rows * cols * 4)) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + s->mastering_display_actual_peak_luminance[i][j] = + (AVRational){get_bits(gb, 4), peak_luminance_den}; + } + } + } + + for (int w = 0; w < s->num_windows; w++) { + AVHDRPlusColorTransformParams *params = &s->params[w]; + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; + + params->tone_mapping_flag = get_bits1(gb); + if (params->tone_mapping_flag) { + if (get_bits_left(gb) < 28) + return AVERROR_INVALIDDATA; + + 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_INVALIDDATA; + + for (int i = 0; i < params->num_bezier_curve_anchors; i++) { + params->bezier_curve_anchors[i] = + (AVRational){get_bits(gb, 10), bezier_anchor_den}; + } + } + + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; + params->color_saturation_mapping_flag = get_bits1(gb); + if (params->color_saturation_mapping_flag) { + if (get_bits_left(gb) < 6) + return AVERROR_INVALIDDATA; + params->color_saturation_weight = + (AVRational){get_bits(gb, 6), saturation_weight_den}; + } + } + + return 0; +} diff --git a/libavutil/hdr_dynamic_metadata.h b/libavutil/hdr_dynamic_metadata.h index 2d72de56ae..1f953ef1f5 100644 --- a/libavutil/hdr_dynamic_metadata.h +++ b/libavutil/hdr_dynamic_metadata.h @@ -340,4 +340,15 @@ AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size); */ AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame); +/** + * Parse the user data registered ITU-T T.35 to AVbuffer (AVDynamicHDRPlus). + * @param s A pointer containing the decoded AVDynamicHDRPlus structure. + * @param data The byte array containing the raw ITU-T T.35 data. + * @param size Size of the data array in bytes. + * + * @return 0 if succeed. Otherwise, returns the appropriate AVERROR. + */ +int av_dynamic_hdr_plus_from_t35(AVDynamicHDRPlus *s, const uint8_t *data, + int size); + #endif /* AVUTIL_HDR_DYNAMIC_METADATA_H */ diff --git a/libavutil/hdr_dynamic_vivid_metadata.c b/libavutil/hdr_dynamic_vivid_metadata.c index 32da01f587..1218644123 100644 --- a/libavutil/hdr_dynamic_vivid_metadata.c +++ b/libavutil/hdr_dynamic_vivid_metadata.c @@ -20,6 +20,17 @@ #include "hdr_dynamic_vivid_metadata.h" #include "mem.h" +#include "libavcodec/get_bits.h" + +static const int32_t maxrgb_den = 4095; +static const int32_t color_saturation_gain_den = 128; +static const int32_t maximum_luminance_den = 4095; +static const int32_t base_param_m_p_den = 16383; +static const int32_t base_param_m_m_den = 10; +static const int32_t base_param_m_a_den = 1023; +static const int32_t base_param_m_b_den = 1023; +static const int32_t base_param_m_n_den = 10; +static const int32_t base_param_Delta_den = 127; AVDynamicHDRVivid *av_dynamic_hdr_vivid_alloc(size_t *size) { @@ -45,3 +56,112 @@ AVDynamicHDRVivid *av_dynamic_hdr_vivid_create_side_data(AVFrame *frame) return (AVDynamicHDRVivid *)side_data->data; } + +int av_dynamic_hdr_vivid_from_t35(AVDynamicHDRVivid *s, const uint8_t *data, + int size) +{ + GetBitContext gbc, *gb = &gbc; + int ret; + + if (!s) + return AVERROR(ENOMEM); + + ret = init_get_bits8(gb, data, size); + if (ret < 0) + return ret; + + if (get_bits_left(gb) < 8) + return AVERROR_INVALIDDATA; + + s->system_start_code = get_bits(gb, 8); + if (s->system_start_code == 0x01) { + s->num_windows = 1; + + if (get_bits_left(gb) < 12 * 4 * s->num_windows) + return AVERROR_INVALIDDATA; + for (int w = 0; w < s->num_windows; w++) { + AVHDRVividColorTransformParams *params = &s->params[w]; + + params->minimum_maxrgb = (AVRational){get_bits(gb, 12), maxrgb_den}; + params->average_maxrgb = (AVRational){get_bits(gb, 12), maxrgb_den}; + params->variance_maxrgb = (AVRational){get_bits(gb, 12), maxrgb_den}; + params->maximum_maxrgb = (AVRational){get_bits(gb, 12), maxrgb_den}; + } + + if (get_bits_left(gb) < 2 * s->num_windows) + return AVERROR_INVALIDDATA; + for (int w = 0; w < s->num_windows; w++) { + AVHDRVividColorTransformParams *params = &s->params[w]; + + params->tone_mapping_mode_flag = get_bits(gb, 1); + if (params->tone_mapping_mode_flag) { + if (get_bits_left(gb) < 1 ) + return AVERROR_INVALIDDATA; + params->tone_mapping_param_num = get_bits(gb, 1) + 1; + for (int i = 0; i < params->tone_mapping_param_num; i++) { + AVHDRVividColorToneMappingParams *tm_params = ¶ms->tm_params[i]; + + if (get_bits_left(gb) < 13) + return AVERROR_INVALIDDATA; + tm_params->targeted_system_display_maximum_luminance = (AVRational){get_bits(gb, 12), maximum_luminance_den}; + tm_params->base_enable_flag = get_bits(gb, 1); + if (tm_params->base_enable_flag) { + if (get_bits_left(gb) < (14 + 6 + 10 + 10 + 6 + 8 + 10)) + return AVERROR_INVALIDDATA; + tm_params->base_param_m_p = (AVRational){get_bits(gb, 14), base_param_m_p_den}; + tm_params->base_param_m_m = (AVRational){get_bits(gb, 6), base_param_m_m_den}; + tm_params->base_param_m_a = (AVRational){get_bits(gb, 10), base_param_m_a_den}; + tm_params->base_param_m_b = (AVRational){get_bits(gb, 10), base_param_m_b_den}; + tm_params->base_param_m_n = (AVRational){get_bits(gb, 6), base_param_m_n_den}; + tm_params->base_param_k1 = get_bits(gb, 2); + tm_params->base_param_k2 = get_bits(gb, 2); + tm_params->base_param_k3 = get_bits(gb, 4); + tm_params->base_param_Delta_enable_mode = get_bits(gb, 3); + if (tm_params->base_param_Delta_enable_mode == 2 || tm_params->base_param_Delta_enable_mode == 6) + tm_params->base_param_Delta = (AVRational){get_bits(gb, 7) * -1, base_param_Delta_den}; + else + tm_params->base_param_Delta = (AVRational){get_bits(gb, 7), base_param_Delta_den}; + + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; + tm_params->three_Spline_enable_flag = get_bits(gb, 1); + if (tm_params->three_Spline_enable_flag) { + if (get_bits_left(gb) < 1 + tm_params->three_Spline_num * (2 + 12 + 28 + 1)) + return AVERROR_INVALIDDATA; + tm_params->three_Spline_num = get_bits(gb, 1) + 1; + for (int j = 0; j < tm_params->three_Spline_num; j++) { + tm_params->three_Spline_TH_mode = get_bits(gb, 2); + if (tm_params->three_Spline_TH_mode == 0 || tm_params->three_Spline_TH_mode == 2) { + if (get_bits_left(gb) < 8) + return AVERROR_INVALIDDATA; + tm_params->three_Spline_TH_enable_MB = (AVRational){get_bits(gb, 8), 255}; + } + tm_params->three_Spline_TH_enable = (AVRational){get_bits(gb, 12), 4095}; + tm_params->three_Spline_TH_Delta1 = (AVRational){get_bits(gb, 10), 1023}; + tm_params->three_Spline_TH_Delta2 = (AVRational){get_bits(gb, 10), 1023}; + tm_params->three_Spline_enable_Strength = (AVRational){get_bits(gb, 8), 255}; + } + } else { + tm_params->three_Spline_num = 1; + tm_params->three_Spline_TH_mode = 0; + } + + } + } + } + + params->color_saturation_mapping_flag = get_bits(gb, 1); + if (params->color_saturation_mapping_flag) { + if (get_bits_left(gb) < 3 + params->color_saturation_num * 8) + return AVERROR_INVALIDDATA; + + params->color_saturation_num = get_bits(gb, 3); + for (int i = 0; i < params->color_saturation_num; i++) { + params->color_saturation_gain[i] = (AVRational){get_bits(gb, 8), color_saturation_gain_den}; + } + } + } + } + + return 0; +} diff --git a/libavutil/hdr_dynamic_vivid_metadata.h b/libavutil/hdr_dynamic_vivid_metadata.h index a34f83072c..16a84ce343 100644 --- a/libavutil/hdr_dynamic_vivid_metadata.h +++ b/libavutil/hdr_dynamic_vivid_metadata.h @@ -282,4 +282,15 @@ AVDynamicHDRVivid *av_dynamic_hdr_vivid_alloc(size_t *size); */ AVDynamicHDRVivid *av_dynamic_hdr_vivid_create_side_data(AVFrame *frame); +/** + * Parse the user data registered ITU-T T.35 to AVbuffer (AVDynamicHDRVivid). + * @param s A pointer containing the decoded AVDynamicHDRVivid structure. + * @param data The byte array containing the raw ITU-T T.35 data. + * @param size Size of the data array in bytes. + * + * @return 0 if succeed. Otherwise, returns the appropriate AVERROR. + */ +int av_dynamic_hdr_vivid_from_t35(AVDynamicHDRVivid *s, const uint8_t *data, + int size); + #endif /* AVUTIL_HDR_DYNAMIC_VIVID_METADATA_H */ From patchwork Mon Feb 27 17:34:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Rapha=C3=ABl_Zumer?= X-Patchwork-Id: 40533 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:d046:b0:cd:afd7:272c with SMTP id hv6csp203171pzb; Mon, 27 Feb 2023 09:35:11 -0800 (PST) X-Google-Smtp-Source: AK7set9MVzeD+PSCq6tOUfnD7JSZDJn3hsfPd36hZfsH07iOOx2jIMolvMVbM6LjiNMWSK3bxOwT X-Received: by 2002:a17:906:a2d0:b0:8e4:96c4:8a4 with SMTP id by16-20020a170906a2d000b008e496c408a4mr20668518ejb.62.1677519311011; Mon, 27 Feb 2023 09:35:11 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1677519310; cv=none; d=google.com; s=arc-20160816; b=dWlCLQ8Nd8siGebdbvrGcYnhlv49hBGZ+0NmX+Y+6Ur0OSE3fpF487lX2EK/boCY7L Mre//f/Pk7yavAxuXcbl02FgNlh1gbHOrBoV0pX0X+31/oHtRSWqDLrrVwhreXB21fI+ EFTn11WVL1aGU19P+AKByIRgCGZcK9v80Rp9CSVqLqL3GiQBjBrqLka37+5TFCJebXz6 jjl/jmt+QF8La17iGDqx60CPHMbvn/M4TlN9UiG+SDMx0NRYb3pZoT0bcxymnlaGh6Pb I/NQryGmKbazknsss92QV7DGbqQxuQKJTijaEMTUlM1g5KPOveXUkS7Vuk6GAeuWsSgE XD8g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:content-language:to:from:user-agent:mime-version :date:message-id:dkim-signature:delivered-to; bh=F6uH4VibqdEDwoBIr1TwwdpVzs/2GPNN9z65soMV8jQ=; b=kOWnViTYNO96KXCoQHZtB5t0PSfmTFXLwbezt3mMCd61pNwRAqtmIDkKEVnypt3Xmj BOHWW60vwqYK/QUy0pH3HTlJpufTFgCqXicSd2f4ETwyJAd1KIjpAMOJwljMb9na97Ch kixS63v1pPmjHG2E2tanJuVdo3asYj5NGRljoLiE7gBHOUJErzAbFTItqC0C7Mw3TFAp 4fDBawfEfpeIY8Cd3Y50rz/sUxSpppLXTsSNixRVinLALWQL09b6ARA9dhyMwyeNZtUf 0zGGhQ9WWDLhm/wjifji/5kdvtb3CJpy1/NARtoSouIlEPBSLeFqVkzY8VC2d18fd0tG vrfQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@vimeo.com header.s=google header.b="A2/ZkHmH"; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=vimeo.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id rp5-20020a170906d96500b008b23ed7e385si9294343ejb.91.2023.02.27.09.35.10; Mon, 27 Feb 2023 09:35:10 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@vimeo.com header.s=google header.b="A2/ZkHmH"; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=vimeo.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 6E42D689E9B; Mon, 27 Feb 2023 19:35:08 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qt1-f174.google.com (mail-qt1-f174.google.com [209.85.160.174]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id C6AA4689900 for ; Mon, 27 Feb 2023 19:35:01 +0200 (EET) Received: by mail-qt1-f174.google.com with SMTP id c19so7441101qtn.13 for ; Mon, 27 Feb 2023 09:35:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=vimeo.com; s=google; h=content-transfer-encoding:content-language:to:subject:from :user-agent:mime-version:date:message-id:from:to:cc:subject:date :message-id:reply-to; bh=Z0NycDzRuVbaanhO/zwe56IGuQlNlXUIRcwd0LScmyI=; b=A2/ZkHmHsfo5fALO5VEjYrLmClqD4FpbzmZ5RuIcVzUMtxmYp2G4xm9fEU2SQBUGO6 VOjRonEj1aO0Qo/BXI/M7Dnewj2qvMEy1S7F5I7OkHWa5NCrKJI66C/lOy1zXstAaVNi pZ1xv4WPHAvIUtqu3CE6iPA65SGiiVOYzJbO8= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:content-language:to:subject:from :user-agent:mime-version:date:message-id:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=Z0NycDzRuVbaanhO/zwe56IGuQlNlXUIRcwd0LScmyI=; b=oA6Q7Pt2HdYi11sJA52yrFHrCLiLuNcNltWaULXkL8D8GmFRfd4gkcBr+bBtlsdZud rNpgWVmgoPBe41rYFST+7a3D2tqib3tfEjSCGRLNV0vsiU3q051FNR33Jrrx+bP2OWuD FannFZOfmhfT1b0VAox918uJXh5xXIpB6/lo9KWZiF4U+R3kjB/Jld0y8ijiNtnMo+Up PiOONdaVkMavIFPXFERK9EUrgVcaShpLHGT4E3PgIM4GCSJKKKegiGK/AYOyo00IqQNv F20l88dKc5TGI3w5l6RNjkUEhaGOGqdF5HWBLRQfNj4EgdG8DL3ZSSeR5XKfLZoCvjs/ NpgQ== X-Gm-Message-State: AO0yUKXxQoAgKHwtbhK4B+n4mpQ3VBw52OmQvBApD2WFlSaM3GfvU1OV nLcBpi75NtY4HriKLTna4BGYx54SvSNdkbKg++w= X-Received: by 2002:ac8:4e4d:0:b0:3bf:a8cf:3096 with SMTP id e13-20020ac84e4d000000b003bfa8cf3096mr39037qtw.33.1677519300082; Mon, 27 Feb 2023 09:35:00 -0800 (PST) Received: from ?IPV6:2600:4041:6c:7000:1f01:20c:8ba9:815b? ([2600:4041:6c:7000:1f01:20c:8ba9:815b]) by smtp.gmail.com with ESMTPSA id x2-20020ac84a02000000b003b9a426d626sm4923004qtq.22.2023.02.27.09.34.59 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 27 Feb 2023 09:34:59 -0800 (PST) Message-ID: <725ad573-5e22-84a3-b12f-e68baa2b3980@vimeo.com> Date: Mon, 27 Feb 2023 12:34:58 -0500 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.2.2 From: =?utf-8?q?Rapha=C3=ABl_Zumer?= To: ffmpeg-devel@ffmpeg.org Content-Language: en-US, fr-FR Subject: [FFmpeg-devel] [PATCH 2/2] avutil: add HDR10+ dynamic metadata serialization function X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: vaIv8aY3aygG Resent due to my mail client incorrectly re-wrapping lines in the version I sent earlier. See the previous patch for context. Signed-off-by: Raphaël Zumer --- libavutil/hdr_dynamic_metadata.c | 147 +++++++++++++++++++++++++++++++ libavutil/hdr_dynamic_metadata.h | 10 +++ libavutil/version.h | 2 +- 3 files changed, 158 insertions(+), 1 deletion(-) diff --git a/libavutil/hdr_dynamic_metadata.c b/libavutil/hdr_dynamic_metadata.c index 98f399b032..72d310e633 100644 --- a/libavutil/hdr_dynamic_metadata.c +++ b/libavutil/hdr_dynamic_metadata.c @@ -225,3 +225,150 @@ int av_dynamic_hdr_plus_from_t35(AVDynamicHDRPlus *s, const uint8_t *data, return 0; } + +AVBufferRef *av_dynamic_hdr_plus_to_t35(AVDynamicHDRPlus *s) +{ + AVBufferRef *buf; + size_t size_bits, size_bytes; + PutBitContext pbc, *pb = &pbc; + + if (!s) + return NULL; + + // Buffer size per CTA-861-H p.253-254: + size_bits = + // 56 bits for the header + 58 + + // 2 bits for num_windows + 2 + + // 937 bits for window geometry for each window above 1 + FFMAX((s->num_windows - 1), 0) * 937 + + // 27 bits for targeted_system_display_maximum_luminance + 27 + + // 1-3855 bits for targeted system display peak luminance information + 1 + (s->targeted_system_display_actual_peak_luminance_flag ? 10 + + s->num_rows_targeted_system_display_actual_peak_luminance * + s->num_cols_targeted_system_display_actual_peak_luminance * 4 : 0) + + // 0-442 bits for intra-window pixel distribution information + s->num_windows * 82; + for (int w = 0; w < s->num_windows; w++) { + size_bits += s->params[w].num_distribution_maxrgb_percentiles * 24; + } + // 1-3855 bits for mastering display peak luminance information + size_bits += 1 + (s->mastering_display_actual_peak_luminance_flag ? 10 + + s->num_rows_mastering_display_actual_peak_luminance * + s->num_cols_mastering_display_actual_peak_luminance * 4 : 0) + + // 0-537 bits for per-window tonemapping information + s->num_windows * 1; + for (int w = 0; w < s->num_windows; w++) { + if (s->params[w].tone_mapping_flag) { + size_bits += 28 + s->params[w].num_bezier_curve_anchors * 10; + } + } + // 0-21 bits for per-window color saturation mapping information + size_bits += s->num_windows * 1; + for (int w = 0; w < s->num_windows; w++) { + if (s->params[w].color_saturation_mapping_flag) { + size_bits += 6; + } + } + + size_bytes = (size_bits + 7) / 8; + + buf = av_buffer_alloc(size_bytes); + if (!buf) { + return NULL; + } + + init_put_bits(pb, buf->data, size_bytes); + + // itu_t_t35_country_code shall be 0xB5 (USA) + put_bits(pb, 8, 0xB5); + // itu_t_t35_terminal_provider_code shall be 0x003C + put_bits(pb, 16, 0x003C); + // itu_t_t35_terminal_provider_oriented_code is set to ST 2094-40 + put_bits(pb, 16, 0x0001); + // application_identifier shall be set to 4 + put_bits(pb, 8, 4); + // application_mode is set to Application Version 1 + put_bits(pb, 8, 1); + + // Payload as per CTA-861-H p.253-254 + put_bits(pb, 2, s->num_windows); + + for (int 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) { + put_bits(pb, 5, s->num_rows_targeted_system_display_actual_peak_luminance); + put_bits(pb, 5, s->num_cols_targeted_system_display_actual_peak_luminance); + for (int i = 0; i < s->num_rows_targeted_system_display_actual_peak_luminance; i++) { + for (int j = 0; j < s->num_cols_targeted_system_display_actual_peak_luminance; 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 (int w = 0; w < s->num_windows; w++) { + for (int 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 (int 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) { + put_bits(pb, 5, s->num_rows_mastering_display_actual_peak_luminance); + put_bits(pb, 5, s->num_cols_mastering_display_actual_peak_luminance); + for (int i = 0; i < s->num_rows_mastering_display_actual_peak_luminance; i++) { + for (int j = 0; j < s->num_cols_mastering_display_actual_peak_luminance; 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 (int 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 (int 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 buf; +} diff --git a/libavutil/hdr_dynamic_metadata.h b/libavutil/hdr_dynamic_metadata.h index 1f953ef1f5..f75d3e0739 100644 --- a/libavutil/hdr_dynamic_metadata.h +++ b/libavutil/hdr_dynamic_metadata.h @@ -21,6 +21,7 @@ #ifndef AVUTIL_HDR_DYNAMIC_METADATA_H #define AVUTIL_HDR_DYNAMIC_METADATA_H +#include "buffer.h" #include "frame.h" #include "rational.h" @@ -351,4 +352,13 @@ AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame); int av_dynamic_hdr_plus_from_t35(AVDynamicHDRPlus *s, const uint8_t *data, int size); +/** + * Serialize dynamic HDR10+ metadata to a user data registered ITU-T T.35 buffer. + * @param s A pointer containing the decoded AVDynamicHDRPlus structure. + * + * @return Pointer to an AVBufferRef containing the raw ITU-T T.35 representation + * of the HDR10+ metadata if succeed, or NULL if buffer allocation fails. + */ +AVBufferRef *av_dynamic_hdr_plus_to_t35(AVDynamicHDRPlus *s); + #endif /* AVUTIL_HDR_DYNAMIC_METADATA_H */ diff --git a/libavutil/version.h b/libavutil/version.h index 900b798971..7635672985 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -79,7 +79,7 @@ */ #define LIBAVUTIL_VERSION_MAJOR 58 -#define LIBAVUTIL_VERSION_MINOR 3 +#define LIBAVUTIL_VERSION_MINOR 4 #define LIBAVUTIL_VERSION_MICRO 100 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \