From patchwork Wed May 4 12:40:46 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 35585 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:a885:b0:7f:4be2:bd17 with SMTP id ca5csp417026pzb; Wed, 4 May 2022 05:41:17 -0700 (PDT) X-Google-Smtp-Source: ABdhPJztmecJJ/bB0P+IGFb1vNQJ/u967xBeaZziTWzwnbgH6OfoeQNED5yIiXMxzpDPLy/1azxy X-Received: by 2002:a05:6402:5205:b0:427:b431:e0ed with SMTP id s5-20020a056402520500b00427b431e0edmr18988618edd.186.1651668077734; Wed, 04 May 2022 05:41:17 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1651668077; cv=none; d=google.com; s=arc-20160816; b=kCWGFRNgS8ql2I5gxjQGlJJzhZ8KQc2T3EHqb32U7v3Ijhzw1aLRUGIORf/wQHhpKp gfsOB0KHjJ3h1ThCh2C0bR+waeq/ICSzXcjOSrZ5wuUGbdEbD/mh3pdlNYgd0jfJ2agY DgR2zall07emcDrH4h485T9RyQwAylLMkRVZ8HnAr/L4V0+acDEZFvhqvGwt/jXN6LUP gE/LFedNvUffoW4UyYYysuX/baIWD5EUY5Aqa4YoD2SLDYwTMf5YWKBgGJZjVcr67g7u pNG7VOcEkzmEJOV84qNd34jT/xoxdGZ7b1yBbVQ/GMAsfPd8BNL2vXXaqLtW9hpuaPOg FsRQ== 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=A0j9TNOx4PJHHhvO/El+ra8M0InYWr2hlAYtp3BtOllv3Y+EN71lMV6qANOkvZK68J xUTjFnPnxW16EdWdf6mz+82W4raOKdQAEmEVHtR8aCk7uT5T8IX91PuT0X+Fwj5tYRwm TOHziHmN8B6ggOi5Dgqid/7sI4e47R7a3uHtItxBlgXpgxU5A5JjoIEdGVrKAVYRVIm7 k8KPsJZgL+HNwkucEzoHhnAmdc04V6M8ca1apzwQmDTfkm/z+zA557Uw8RCvZZfHbEPd INjtGn4zk9ZxX9iKtKYTKLwZr0fCpzwe5yaWjsJecbWx+ltT9gvxMpk1u7PLRHV9Y9M7 3bBQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=dlcBVe2k; 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 yz13-20020a170906dc4d00b006e89f35bd27si15764927ejb.874.2022.05.04.05.41.17; Wed, 04 May 2022 05:41:17 -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=dlcBVe2k; 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 37FD368B36E; Wed, 4 May 2022 15:40:59 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-io1-f52.google.com (mail-io1-f52.google.com [209.85.166.52]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id C9B8C68B28D for ; Wed, 4 May 2022 15:40:51 +0300 (EEST) Received: by mail-io1-f52.google.com with SMTP id c125so1319195iof.9 for ; Wed, 04 May 2022 05:40:51 -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=dlcBVe2kB48o7Ll8Jz+dxSC6eQknDUeFfgE4zAbEnNS/Pn1N6rarc6nOdkwFDmE8vV kPLPmEAcQidyT6GcNo9igWsXiKvCoE4d/yhI5/1TPJmfuzD7+FMXTTHwgQpv7ZvjkFoA S0j5pKOcILG7EvczP7iFOa7GMf/d7bW97wJu+PMrsGhheQ4TMhO7/azUb6bdq2+BoLdI WcHXtEti0ol7oBbv24zhVPnuPanoSL55ZEVq16xMVJfszh1/RfliQcijFTU/dciZVZ+f iIpL56+YKahwGtz9mJyh3PTPC3BSH1WOS5ROFTETinEgkSwVjTgmXlOizP2yqKsDw88R KEZA== 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=6Yws5WLKEV5mdCXSQ4kG+LsSDdmyEIY7jI1ShHS787UYp8+YtOG+pL0Xhfei4auj5n t30OlzVQEbuuGFj9VkCW09pjoZ0QXR2FBp60NQTQFUZVlSDLUAy2PG8sjjOWWvv4BGS7 apvMLn6c9rKuxNq6lES1dF5LiYC5tCGon5V2tzxhsC5eAnVbNo2VZ9lIlOjfb+q3Xngo 2XauT31d/P9aGW3qhMI7URKXb7LMTzHVleAtd0lFfIZiuiMPY+w1QQXHi39CXhR7RN8S kM+WcUUZNWglHHmfQmtmfYfnKVlxRssboC+TkW731y+jy2BTMpVEwiCKUYcVPgQIsWM3 S9Pw== X-Gm-Message-State: AOAM533SOnsJVl98b39bZVYJQtXxhpPNjWCgNR6GRg7dPaCQOXCZITmd WylEQNQbayppJR/XzECUKAmUXhWrNR4= X-Received: by 2002:a5e:dc06:0:b0:657:b4c5:5fe7 with SMTP id b6-20020a5edc06000000b00657b4c55fe7mr8222337iok.162.1651668050433; Wed, 04 May 2022 05:40:50 -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 j15-20020a92c20f000000b002cde6e352cbsm4114456ilo.21.2022.05.04.05.40.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 04 May 2022 05:40:50 -0700 (PDT) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Wed, 4 May 2022 08:40:46 -0400 Message-Id: <20220504124046.175331-3-leo.izen@gmail.com> X-Mailer: git-send-email 2.36.0 In-Reply-To: <20220504124046.175331-1-leo.izen@gmail.com> References: <20220504124046.175331-1-leo.izen@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v4 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: xDl3nIr1I2Jt 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; }