From patchwork Thu May 5 13:50:56 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 35606 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:a885:b0:7f:4be2:bd17 with SMTP id ca5csp1091168pzb; Thu, 5 May 2022 06:51:50 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzhWEN+GPwBByMs8pZv/Z06w7TMqTKgyLDX3A9L0PLLOT+1dWRm19lbi4wmIzgDbdJV6oqA X-Received: by 2002:a05:6402:2754:b0:426:2f8c:c39 with SMTP id z20-20020a056402275400b004262f8c0c39mr30189799edd.224.1651758710608; Thu, 05 May 2022 06:51:50 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1651758710; cv=none; d=google.com; s=arc-20160816; b=XdWRRgcM/3QiZxlmH6mzdbmMZvc61IHvrhvJG/X3cPFG/lx9jhI1b+ZFNUu08rWPK0 GYnlfmX2n7CYbc2HdUsQjrptgmgbXy2HEAv3U9cEiswWSjSPGt3LcBqo82+yvfQDwztu LC9m72lmIQXVi1Q1VpnQi9o0GgFgFqvrigqPI69epSel7UWWjyOVdKNad1lP1bVLY4PX NYiqm7PnFmzrWUeZfpIQi9UT4vGZZDWIjp+pg8sZN594pk3M7TJBGHmd2jx46DbRRL2n UsiG3mNXIN1JMGTXj9E/8sTMvhWXCwD/Lti6+6iFvdXf0FoFGO2IL+KO8IVaMTpVLMiC myFA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to; bh=6zvweKMHe5Rm90bEk80aC2wb0MY9XbAN6i7Ps9CIjvk=; b=GH/FEvUFWGx8HpoUCs+bUv9Agb8sOaqyNS/X3rADOdMC+o+gUBjZ4GXpvFm190We/U /gBm62sy7ssjTkGH3CO+YJID3jYqnRibnzzZzYQCS9DWag0HZX8GGOZSpqz6dPUxfWI3 sjPmzGBCanxDvmI0I67CjQ2kCKXRbyTw7zQu+9tQppFtOSTHCHtSoCvmDXD+QRIcjM4g igu1siYin5dSG0kw5Sukxu4PSKmL+ySbehiNna0XDVWds6ei4ID0RZ6gyn4gqcfxLez7 G7dMSC0w5buQ6WxvKalcM+0OpkJXDeN6O9F2sxl83kJQ2Maa3iiZP9eEmjsK88x2T35A +CPg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=aMZjNn5+; 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=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id s10-20020a170906284a00b006f4c130d9f4si2444120ejc.667.2022.05.05.06.51.50; Thu, 05 May 2022 06:51:50 -0700 (PDT) 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=@gmail.com header.s=20210112 header.b=aMZjNn5+; 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=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 1B1E868B3B5; Thu, 5 May 2022 16:51:34 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-io1-f51.google.com (mail-io1-f51.google.com [209.85.166.51]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 7CCE768B35B for ; Thu, 5 May 2022 16:51:25 +0300 (EEST) Received: by mail-io1-f51.google.com with SMTP id e3so4771023ios.6 for ; Thu, 05 May 2022 06:51:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=8E33JMBU1eOkKb+Y42kj1ut0ZPQjTrKnjLG0Vtvf2HI=; b=aMZjNn5+Hj84jkI6PrEkaw7Sqo6JqLACSObKC/9l8Z0rm+8uWC9b3eLQIYjVwclCNm mTwfgJHIyyZQ/JCR9GUqZxJ2bIcs4lUQZmNaLYeDE9YZB3vmpxCN4tSOEdvyP9VKwffo C9uaj3S3QFWrLCDNxc3LrRYewJBA+IowNuyHmFcDL15lnBQX8T4TdtDLwSeowmd++2Kf EGXDL2mV4QvGtdUw1t2QP2SRSRpMWmjXouIF1OAvxKXxtpWczfzP+KqUklS6FsKohonW Q6UlquzNhqfrY6LB8+WL80t6b37BLaqzVKDRoixbilfqqMrjtZpdsgtywxhEGmyKjN3N 3Ptg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=8E33JMBU1eOkKb+Y42kj1ut0ZPQjTrKnjLG0Vtvf2HI=; b=pFh2KaWsFOATZsEFvZsrdmKm0ncFKzJEfmD4b5YIcUIBvkSKZY592bAWoM0tJ0uM3K aV2zUrCaIZmcnWHfQCNeebMlWrbtGzPPGuM4AXfm+s5BLIgecenNo+zARHiPjT59p+jQ W86WWmhnJH3OYqPJ68Tn/zPN4XXA48/EIYPHAb90D+YowdPeM5AHTJ/FryVoX2KvAYSK X5h/z4E5/GZPiVSJI5spakLbRAX5Nu6RJ6PybeFtw9BT7/ILE3p5GNrGUKshRzOSyetH 1utk7E0jmEGG/dtLcYzc3FB7JExWYEGC6mjWmN0lYa1p1Mxv1ZAMTCBZv+aBSwq+yaJ/ bqWg== X-Gm-Message-State: AOAM532mBoH1caD207bZZj9IFhCqcBOhcBraQ2XktglA9VytOb8378Ig IldnbnjkDBml9DSFNJTFeq7ckwq8ALI= X-Received: by 2002:a02:6626:0:b0:32b:7881:c62f with SMTP id k38-20020a026626000000b0032b7881c62fmr6991067jac.251.1651758684160; Thu, 05 May 2022 06:51:24 -0700 (PDT) Received: from gauss.local (c-68-41-54-207.hsd1.mi.comcast.net. [68.41.54.207]) by smtp.gmail.com with ESMTPSA id h29-20020a022b1d000000b0032b3a78178csm502251jaa.80.2022.05.05.06.51.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 05 May 2022 06:51:23 -0700 (PDT) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Thu, 5 May 2022 09:50:56 -0400 Message-Id: <20220505135056.19268-3-leo.izen@gmail.com> X-Mailer: git-send-email 2.36.0 In-Reply-To: <20220505135056.19268-1-leo.izen@gmail.com> References: <20220505135056.19268-1-leo.izen@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v5 2/2] avcodec/libjxlenc: properly read input colorspace 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 Cc: Leo Izen Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: x9yVAwSF4TIc Whether an ICC profile is present or not, the libjxl encoder wrapper should now properly read colorspace tags and forward them to libjxl appropriately, rather than just assume sRGB as before. It will also print warnings when colorimetric assumptions are made about the input data. --- libavcodec/libjxlenc.c | 128 ++++++++++++++++++++++++++++++----------- 1 file changed, 96 insertions(+), 32 deletions(-) diff --git a/libavcodec/libjxlenc.c b/libavcodec/libjxlenc.c index 8bebec6aeb..43bb7299c6 100644 --- a/libavcodec/libjxlenc.c +++ b/libavcodec/libjxlenc.c @@ -117,7 +117,7 @@ static int libjxl_init_jxl_encoder(AVCodecContext *avctx) return AVERROR_EXTERNAL; } - /* check for negative zero, our default */ + /* check for negative, our default */ if (ctx->distance < 0.0) { /* use ffmpeg.c -q option if passed */ if (avctx->flags & AV_CODEC_FLAG_QSCALE) @@ -133,7 +133,8 @@ static int libjxl_init_jxl_encoder(AVCodecContext *avctx) */ if (ctx->distance > 0.0 && ctx->distance < 0.01) ctx->distance = 0.01; - if (JxlEncoderOptionsSetDistance(ctx->options, ctx->distance) != JXL_ENC_SUCCESS) { + + if (JxlEncoderSetFrameDistance(ctx->options, ctx->distance) != JXL_ENC_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to set distance: %f\n", ctx->distance); return AVERROR_EXTERNAL; } @@ -219,57 +220,120 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra info.num_color_channels = jxl_fmt.num_channels - info.num_extra_channels; info.bits_per_sample = av_get_bits_per_pixel(pix_desc) / jxl_fmt.num_channels; info.alpha_bits = (info.num_extra_channels > 0) * info.bits_per_sample; + if (pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT) { info.exponent_bits_per_sample = info.bits_per_sample > 16 ? 8 : 5; info.alpha_exponent_bits = info.alpha_bits ? info.exponent_bits_per_sample : 0; jxl_fmt.data_type = info.bits_per_sample > 16 ? JXL_TYPE_FLOAT : JXL_TYPE_FLOAT16; - JxlColorEncodingSetToLinearSRGB(&jxl_color, info.num_color_channels == 1); } else { info.exponent_bits_per_sample = 0; info.alpha_exponent_bits = 0; jxl_fmt.data_type = info.bits_per_sample <= 8 ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16; - JxlColorEncodingSetToSRGB(&jxl_color, info.num_color_channels == 1); } - if (info.bits_per_sample > 16 - || info.xsize > (1 << 18) || info.ysize > (1 << 18) - || (info.xsize << 4) * (info.ysize << 4) > (1 << 20)) { - /* - * must upgrade codestream to level 10, from level 5 - * the encoder will not do this automatically - */ - if (JxlEncoderSetCodestreamLevel(ctx->encoder, 10) != JXL_ENC_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "Could not upgrade JXL Codestream level.\n"); - return AVERROR_EXTERNAL; - } - } + /* JPEG XL format itself does not support partial range */ + if (avctx->color_range == AVCOL_RANGE_MPEG) + av_log(avctx, AV_LOG_ERROR, "This encoder does not support partial(tv) range, colors will be wrong!\n"); + else if (avctx->color_range != AVCOL_RANGE_JPEG) + av_log(avctx, AV_LOG_WARNING, "Unknown color range, assuming full\n"); - /* bitexact lossless requires there to be no XYB transform */ + /* bitexact lossless requires there to be no XYB transform */ info.uses_original_profile = ctx->distance == 0.0; - sd = av_frame_get_side_data(frame, AV_FRAME_DATA_ICC_PROFILE); - if (sd && sd->size && JxlEncoderSetICCProfile(ctx->encoder, sd->data, sd->size) != JXL_ENC_SUCCESS) { - av_log(avctx, AV_LOG_WARNING, "Could not set ICC Profile\n"); - } else if (info.uses_original_profile) { - /* - * the color encoding is not used if uses_original_profile is false - * this just works around a bug in libjxl 0.7.0 and lower - */ - if (JxlEncoderSetColorEncoding(ctx->encoder, &jxl_color) != JXL_ENC_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "Failed to set JxlColorEncoding\n"); - return AVERROR_EXTERNAL; - } - } - if (JxlEncoderSetBasicInfo(ctx->encoder, &info) != JXL_ENC_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to set JxlBasicInfo\n"); return AVERROR_EXTERNAL; } + /* rendering intent doesn't matter here + * but libjxl will whine if we don't set it */ + jxl_color.rendering_intent = JXL_RENDERING_INTENT_RELATIVE; + + switch (avctx->color_trc) { + case AVCOL_TRC_BT709: + jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_709; + break; + case AVCOL_TRC_LINEAR: + jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR; + break; + case AVCOL_TRC_IEC61966_2_1: + jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_SRGB; + break; + case AVCOL_TRC_SMPTE428: + jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_DCI; + break; + case AVCOL_TRC_SMPTE2084: + jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_PQ; + break; + case AVCOL_TRC_ARIB_STD_B67: + jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_HLG; + break; + case AVCOL_TRC_GAMMA22: + jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA; + jxl_color.gamma = 2.2; + break; + case AVCOL_TRC_GAMMA28: + jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA; + jxl_color.gamma = 2.8; + break; + default: + if (pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT) { + av_log(avctx, AV_LOG_WARNING, "Unknown transfer function, assuming Linear Light\n"); + jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR; + } else { + av_log(avctx, AV_LOG_WARNING, "Unknown transfer function, assuming IEC61966-2-1/sRGB\n"); + jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_SRGB; + } + } + + /* This should be implied to be honest + * but a libjxl but makes it fail otherwise */ + if (info.num_color_channels == 1) + jxl_color.color_space = JXL_COLOR_SPACE_GRAY; + else + jxl_color.color_space = JXL_COLOR_SPACE_RGB; + + switch (avctx->color_primaries) { + case AVCOL_PRI_BT709: + jxl_color.primaries = JXL_PRIMARIES_SRGB; + jxl_color.white_point = JXL_WHITE_POINT_D65; + break; + case AVCOL_PRI_BT2020: + jxl_color.primaries = JXL_PRIMARIES_2100; + jxl_color.white_point = JXL_WHITE_POINT_D65; + break; + case AVCOL_PRI_SMPTE431: + jxl_color.primaries = JXL_PRIMARIES_P3; + jxl_color.primaries = JXL_WHITE_POINT_DCI; + break; + case AVCOL_PRI_SMPTE432: + jxl_color.primaries = JXL_PRIMARIES_P3; + jxl_color.primaries = JXL_WHITE_POINT_D65; + break; + default: + av_log(avctx, AV_LOG_WARNING, "Unknown color primaries, assuming BT.709/sRGB+D65\n"); + jxl_color.primaries = JXL_PRIMARIES_SRGB; + jxl_color.white_point = JXL_WHITE_POINT_D65; + } + + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_ICC_PROFILE); + if (sd && sd->size && JxlEncoderSetICCProfile(ctx->encoder, sd->data, sd->size) != JXL_ENC_SUCCESS) + av_log(avctx, AV_LOG_WARNING, "Could not set ICC Profile\n"); + if (JxlEncoderSetColorEncoding(ctx->encoder, &jxl_color) != JXL_ENC_SUCCESS) + av_log(avctx, AV_LOG_WARNING, "Failed to set JxlColorEncoding\n"); + + /* depending on basic info, level 10 might + * be required instead of level 5 */ + if (JxlEncoderGetRequiredCodestreamLevel(ctx->encoder) > 5) { + if (JxlEncoderSetCodestreamLevel(ctx->encoder, 10) != JXL_ENC_SUCCESS) + av_log(avctx, AV_LOG_WARNING, "Could not increase codestream level\n"); + } + jxl_fmt.endianness = JXL_NATIVE_ENDIAN; jxl_fmt.align = frame->linesize[0]; - if (JxlEncoderAddImageFrame(ctx->options, &jxl_fmt, frame->data[0], jxl_fmt.align * info.ysize) != JXL_ENC_SUCCESS) { + if (JxlEncoderAddImageFrame(ctx->options, &jxl_fmt, frame->data[0], + jxl_fmt.align * info.ysize) != JXL_ENC_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to add Image Frame\n"); return AVERROR_EXTERNAL; }