From patchwork Fri Jan 1 21:35:26 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Thompson X-Patchwork-Id: 24735 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 4C2C244BCD1 for ; Fri, 1 Jan 2021 23:38:08 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 2EE7B68A878; Fri, 1 Jan 2021 23:38:08 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wm1-f45.google.com (mail-wm1-f45.google.com [209.85.128.45]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 91D0468A86D for ; Fri, 1 Jan 2021 23:38:06 +0200 (EET) Received: by mail-wm1-f45.google.com with SMTP id a6so9864373wmc.2 for ; Fri, 01 Jan 2021 13:38:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jkqxz-net.20150623.gappssmtp.com; s=20150623; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=fxhiN29K2YVnDleYCGDtqcLccKs9B41X/oWpT7AWuNo=; b=Xe7dNvIoTyatkJ+ykK4yvxIJXZ/1iOq1fBxHado4tfxPm2wE6FwJsJ/5cxcp6RWB7x CVxNzbvanrXGXdL6wrFLFH/ZUPMCYW3K6frkNKTLyzHzamCcZwuao3UHyJwhmuApVFBD apKnuGryc2hmgKCDDKTMc2Sk9LNGT9VWxDqsOD61R7biLG836C/rcO4yaNlXMIb2RCjh UDY8HItRPRF2JPwvg1gPtpy10Q4RCYQ6/8g/BsudFBXSwm9TjRsJoc60oUGZTE90MPTm 0mpRsaSRC8JnopwJ6jvpmuwKunBYeYGM/BR9ciL2LQjr/SS67FcSpu5s3oF3U0nnfmGz XHZg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=fxhiN29K2YVnDleYCGDtqcLccKs9B41X/oWpT7AWuNo=; b=E4hJO/pxThaegW2Ujr2yEvIupNi03zkPj/VT4xjn41oJWfoSD/QKeX8IwUIshGrq/O 6SU3YobS74/o8v8wd3xWL+8jQ9g4CeUFPTEiGOaJmIsJ1NMbtYBQ0424OdJPYifIviIg cDOmasyFeA1QbqbwuLn4qTs4ayEqiPAsTeodferlDfmsAn5IeuDwpbzm5TuwQbZGG7gJ BB0YfUoNmkwrad+LhMp2UrJeDdr/ePTcbFKwQMht+tQNoWtxpN2ZEz7cd6HoPo7XX/iI 7XqkU2pBHxFHJqMOiWim97iXVgE2VgwaFg1FheX+BDiT+Ebufz39agtX2RDAwo35rv4l ozDA== X-Gm-Message-State: AOAM531MdSNbGv/81lUpT764vU1ab06lIbumeA9vwHRs+We+F6fYFPlf kudnv70HsbngMbBkDVS66/wbXNysbgagow== X-Google-Smtp-Source: ABdhPJw9NKsq7PJH8VgjvtcKS4yHwVFAVLadi7sPLGJRdSO7ywX3RCAj+LM1OLwljrkNGcLRIvvgxg== X-Received: by 2002:a1c:e083:: with SMTP id x125mr17583501wmg.0.1609537085863; Fri, 01 Jan 2021 13:38:05 -0800 (PST) Received: from localhost.localdomain (cpc91226-cmbg18-2-0-cust7.5-4.cable.virginm.net. [82.0.29.8]) by smtp.gmail.com with ESMTPSA id v20sm18784595wml.34.2021.01.01.13.38.04 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 01 Jan 2021 13:38:05 -0800 (PST) From: Mark Thompson To: ffmpeg-devel@ffmpeg.org Date: Fri, 1 Jan 2021 21:35:26 +0000 Message-Id: <20210101213537.169546-17-sw@jkqxz.net> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210101213537.169546-1-sw@jkqxz.net> References: <20210101213537.169546-1-sw@jkqxz.net> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 16/27] cbs_sei: Implement fill and extract for HDR SEI messages X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 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" Fill and extract both mastering display colour volume and content light level info messages. --- libavcodec/cbs_h2645.c | 130 +++++++++++++++++++++++++++++++++++++++++ libavcodec/cbs_sei.c | 4 ++ 2 files changed, 134 insertions(+) diff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c index 42b614034e..53d5b50b42 100644 --- a/libavcodec/cbs_h2645.c +++ b/libavcodec/cbs_h2645.c @@ -18,6 +18,7 @@ #include "libavutil/attributes.h" #include "libavutil/avassert.h" +#include "libavutil/mastering_display_metadata.h" #include "bytestream.h" #include "cbs.h" @@ -1504,6 +1505,133 @@ const CodedBitstreamType ff_cbs_type_h265 = { .extract_metadata = &ff_cbs_sei_extract_metadata, }; +static uint32_t rescale_clip(AVRational value, uint32_t scale, + uint32_t min, uint32_t max) +{ + int64_t scaled = av_rescale(scale, value.num, value.den); + return av_clip64(scaled, min, max); +} + +static void cbs_sei_fill_mastering_display_colour_volume + (SEIRawMasteringDisplayColourVolume *mdcv, + const AVMasteringDisplayMetadata *mdm) +{ + memset(mdcv, 0, sizeof(*mdcv)); + + if (mdm->has_primaries) { + // The values in the metadata structure are fractions between 0 and 1, + // while the SEI message contains fixed-point values with an increment + // of 0.00002. So, scale up by 50000 to convert between them and clip + // to the allowed range ([5, 37000] for x, [5, 42000] for y). + + for (int a = 0; a < 3; a++) { + // The metadata structure stores this in RGB order, but the SEI + // wants it in GBR order. + static const uint8_t mapping[] = { 1, 2, 0 }; + int b = mapping[a]; + mdcv->display_primaries_x[a] = + rescale_clip(mdm->display_primaries[b][0], 50000, 5, 37000); + mdcv->display_primaries_y[a] = + rescale_clip(mdm->display_primaries[b][1], 50000, 5, 42000); + } + + mdcv->white_point_x = + rescale_clip(mdm->white_point[0], 50000, 5, 37000); + mdcv->white_point_y = + rescale_clip(mdm->white_point[1], 50000, 5, 42000); + } + + if (mdm->has_luminance) { + // Metadata are rational values in candelas per square metre, SEI + // contains fixed point in units of 0.0001 candelas per square + // metre. So scale up by 10000 to convert between them, and clip to + // the allowed ranges. + + mdcv->max_display_mastering_luminance = + rescale_clip(mdm->max_luminance, 10000, 50000, 100000000); + mdcv->min_display_mastering_luminance = + rescale_clip(mdm->min_luminance, 10000, 1, 50000); + + // The spec requires that they are not equal when in the normal + // range. + if (mdcv->min_display_mastering_luminance >= + mdcv->max_display_mastering_luminance) { + mdcv->min_display_mastering_luminance = + mdcv->max_display_mastering_luminance - 1; + } + } else { + mdcv->max_display_mastering_luminance = 0; + mdcv->min_display_mastering_luminance = 0; + } +} + +static void cbs_sei_extract_mastering_display_colour_volume + (AVMasteringDisplayMetadata *mdm, + const SEIRawMasteringDisplayColourVolume *mdcv) +{ +#define IN_RANGE(v, min, max) ((v) >= (min) && (v) <= (max)) +#define IS_VALID_COORD(x, y) (IN_RANGE(x, 5, 37000) && IN_RANGE(y, 5, 42000)) + int valid_chromaticity = 1; + for (int a = 0; a < 3; a++) { + if (!IS_VALID_COORD(mdcv->display_primaries_x[a], + mdcv->display_primaries_y[a])) + valid_chromaticity = 0; + } + if (!IS_VALID_COORD(mdcv->white_point_x, mdcv->white_point_y)) + valid_chromaticity = 0; + + memset(mdm, 0, sizeof(*mdm)); + + if (valid_chromaticity) { + for (int a = 0; a < 3; a++) { + // SEI message in GBR order, but metadata structure in RGB order. + static const uint8_t mapping[] = { 2, 0, 1 }; + int b = mapping[a]; + + mdm->display_primaries[a][0] = + av_make_q(mdcv->display_primaries_x[b], 50000); + mdm->display_primaries[a][1] = + av_make_q(mdcv->display_primaries_y[b], 50000); + } + + mdm->white_point[0] = av_make_q(mdcv->white_point_x, 50000); + mdm->white_point[1] = av_make_q(mdcv->white_point_y, 50000); + + mdm->has_primaries = 1; + } + + if (IN_RANGE(mdcv->min_display_mastering_luminance, 1, 50000) && + IN_RANGE(mdcv->max_display_mastering_luminance, 50000, 100000000)) { + mdm->min_luminance = av_make_q(mdcv->min_display_mastering_luminance, 10000); + mdm->max_luminance = av_make_q(mdcv->max_display_mastering_luminance, 10000); + + mdm->has_luminance = 1; + } +#undef IN_RANGE +#undef IS_VALID_COORD +} + +static void cbs_sei_fill_content_light_level_info + (SEIRawContentLightLevelInfo *cll, const AVContentLightMetadata *clm) +{ + memset(cll, 0, sizeof(*cll)); + + // Both the metadata and the SEI are in units of candelas per square + // metre, so we only need to clip to ensure that they are in the valid + // range. + + cll->max_content_light_level = av_clip_uintp2(clm->MaxCLL, 16); + cll->max_pic_average_light_level = av_clip_uintp2(clm->MaxFALL, 16); +} + + +static void cbs_sei_extract_content_light_level_info + (AVContentLightMetadata *clm, const SEIRawContentLightLevelInfo *cll) +{ + clm->MaxCLL = cll->max_content_light_level; + clm->MaxFALL = cll->max_pic_average_light_level; +} + static const SEIMessageTypeDescriptor cbs_sei_common_types[] = { { SEI_TYPE_FILLER_PAYLOAD, @@ -1528,12 +1656,14 @@ static const SEIMessageTypeDescriptor cbs_sei_common_types[] = { 1, 0, sizeof(SEIRawMasteringDisplayColourVolume), SEI_MESSAGE_RW(sei, mastering_display_colour_volume), + SEI_MESSAGE_FE(sei, mastering_display_colour_volume), }, { SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO, 1, 0, sizeof(SEIRawContentLightLevelInfo), SEI_MESSAGE_RW(sei, content_light_level_info), + SEI_MESSAGE_FE(sei, content_light_level_info), }, { SEI_TYPE_ALTERNATIVE_TRANSFER_CHARACTERISTICS, diff --git a/libavcodec/cbs_sei.c b/libavcodec/cbs_sei.c index e5f9e3e403..0c05b2bdc9 100644 --- a/libavcodec/cbs_sei.c +++ b/libavcodec/cbs_sei.c @@ -375,6 +375,10 @@ typedef struct SEIMetadata { } SEIMetadata; static const SEIMetadata cbs_sei_metadata[] = { + { CBS_METADATA_MASTERING_DISPLAY, + SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME }, + { CBS_METADATA_CONTENT_LIGHT_LEVEL, + SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO }, }; static const SEIMessageTypeDescriptor *cbs_sei_find_type_from_metadata