From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.ffmpeg.org (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id C6C394CEB6 for ; Mon, 1 Dec 2025 13:37:18 +0000 (UTC) Authentication-Results: ffbox; dkim=fail (body hash mismatch (got b'GChkc1oXPtbesrFi5W+Hyxf8y11Tg9e8WB6uguVShPg=', expected b'p/XoYGGiEsW4lLm4VVQ/G8PtHRKeYTHZBhXbPnXf2Qs=')) header.d=ffmpeg.org header.i=@ffmpeg.org header.a=rsa-sha256 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1764596218; h=mime-version : to : date : message-id : reply-to : subject : list-id : list-archive : list-archive : list-help : list-owner : list-post : list-subscribe : list-unsubscribe : from : cc : content-type : content-transfer-encoding : from; bh=GChkc1oXPtbesrFi5W+Hyxf8y11Tg9e8WB6uguVShPg=; b=P3sAvgUabXvHsTVhsLnlsA0qJ704O0Ts2IvftLpOBzWMPAEwuWUoG7MHyQ/kOA0UIApIe G1R6LMGsBBsli3l3w1geAaoVjw/3ljjR5zdOHUT9mtywI7OdCbbNfVED4DdgrV6eAwtT/ex vFvGK9CSiTbDS24ujo0Hx7I2k+Tk1NWpV1L/7Ep8A0JiiezU+RAmun11yEi1ysCUCeOA5T4 5naS+7vb4elGOGKoqRQntnjAG3Rz0bZpZOiI7pzbMND5BD1W3xmb5utzrHYfpATyYhsG+Xa cKtEi0BxAeHRvhBjqO5BgE7sHB+i9F5BKe/CqTvmBWa1FChH4MR5U7dgnktw== Received: from [172.19.0.3] (unknown [172.19.0.3]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id 572506903E5; Mon, 1 Dec 2025 15:36:58 +0200 (EET) ARC-Seal: i=1; cv=none; a=rsa-sha256; d=ffmpeg.org; s=arc; t=1764596201; b=Njl2guYOJV7r2BKIBJnvb2Ea9qLxzrXHFWXwMrDo98JkwXMvfizNzAVrFFyYHNCmZJFRR Lc96BIsCBwHxhpV6tUgT97s+PZhZqYiF1fP2BTwpllfsQQg+shH8awMkOXqDWGDecXOcJOH wzUTMkn7FV2Y952G4rpAvFZW3xz/crW4hKVwpsfEJ0ar7dr3qFJsFsLUu2i1Aznwa9liyJN 9Y4Zpjy2o52DVYMovpRP4otoJWpWekjag71TC460grK5v0uAEOnfOhiGvHfdDRxaR+E/X7d YW3Tll0xFnOyJvOWdyJiatGqDwCFQIlVLRta72ShlyT+hnrYKdpS3AMT3ZNA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=ffmpeg.org; s=arc; t=1764596201; h=from : sender : reply-to : subject : date : message-id : to : cc : mime-version : content-type : content-transfer-encoding : content-id : content-description : resent-date : resent-from : resent-sender : resent-to : resent-cc : resent-message-id : in-reply-to : references : list-id : list-help : list-unsubscribe : list-subscribe : list-post : list-owner : list-archive; bh=WjNTuPxh/HYWOxiM7V8bf6tp6BBG/9cweNKu6B4qQzE=; b=YMmAfBBO7Ro6Spn4oqx4eXpJD4rjRXBjT9T63wwvDgIikj0GOI4WlzeYDcj5AZ+Tr6Zvn iBRmLjABCwI0wdRCes/VSRk4vqgAcVX86miZPcEqO6RuB/Gqs4Ms0QnIUZuJPQz+hqHwh3K Phx99h3ipzYLYT2XdLMmilPQZTluq+hhGmM2pugMy2y85b5w3gy8KFwnoQU31nGlbLqyQr+ +o2XbTnABY8/fedPuHoi9VNjG+Brsij4VYDiGE6E7cHXVoq+sTtc+ytSJsrpSGEwjPd2sVJ 1eTazbA7wvUdF8nwZK9aIJxVAwfLrau8uV3kTHx4bL4Hh/IaGoxK40Fh4IbQ== ARC-Authentication-Results: i=1; ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none; dmarc=pass header.from=ffmpeg.org policy.dmarc=quarantine Authentication-Results: ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none (Message is not ARC signed); dmarc=pass (Used From Domain Record) header.from=ffmpeg.org policy.dmarc=quarantine DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1764596192; h=content-type : mime-version : content-transfer-encoding : from : to : reply-to : subject : date : from; bh=p/XoYGGiEsW4lLm4VVQ/G8PtHRKeYTHZBhXbPnXf2Qs=; b=1kaxXxQ8o5roUdURopyv9oflW53pz9FrUxPZxg6bQAem/6xxIVY0ca8n3ZeXNOfO4PLoa a8Qb67g5CXrVC6eBwPzO9QIWKoLurv5JXpmTbPPHrMnKH72L0CL5UiIlM2++uh5pJTuHehM 0Ke2CnHasBZqiPpbA59zogRUbz3gnt5DkgqfaG2EN0SRuyLZ93qO3pN2sbGPJfXEG/pDtYf pO3RLC8Iz2eAj6oeXpQ05wuSPP1isjh8WMsu7CLzSig3vBDW618UrDgVdnMeCfgtfRE32Tg LUviloMGdRgbtZeyruWE+K9GP6FAgKP/jOIEdKZ8U8LHFJ4ccnOrMzpLt59A== Received: from 55ca25703178 (code.ffmpeg.org [188.245.149.3]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTPS id D3A4A69036D for ; Mon, 1 Dec 2025 15:36:32 +0200 (EET) MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Date: Mon, 01 Dec 2025 13:36:32 -0000 Message-ID: <176459619306.39.7095376647679091870@2cb04c0e5124> Message-ID-Hash: REEBF2A7ESUCYLZVDA35A7E2ZC7VDFCE X-Message-ID-Hash: REEBF2A7ESUCYLZVDA35A7E2ZC7VDFCE X-MailFrom: code@ffmpeg.org X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-ffmpeg-devel.ffmpeg.org-0; header-match-ffmpeg-devel.ffmpeg.org-1; header-match-ffmpeg-devel.ffmpeg.org-2; header-match-ffmpeg-devel.ffmpeg.org-3; emergency; member-moderation X-Mailman-Version: 3.3.10 Precedence: list Reply-To: FFmpeg development discussions and patches Subject: [FFmpeg-devel] [PATCH] avcodec/libjxlenc: add EXIF box to output (PR #21070) List-Id: FFmpeg development discussions and patches Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Leo Izen via ffmpeg-devel Cc: Leo Izen Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Archived-At: List-Archive: List-Post: PR #21070 opened by Leo Izen (Traneptora) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21070 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21070.patch >>From 924a4592ade1ec883396956d89e0d0702ddfa42d Mon Sep 17 00:00:00 2001 From: Leo Izen Date: Mon, 1 Dec 2025 06:11:58 -0500 Subject: [PATCH 1/3] avcodec/libjxlenc: give display matrix sidedata priority Before this commit, we ignore the display matrix side data if any EXIF side data is present, even if that side data contains no orientation tag. This allows us to calculate the orientation from the display matrix sidedata first, if present. Ideally the decoder will have removed the orientation tag upon decoding and attached the data as display matrix side data instead, so this makes our orientation code respect this behavior. Signed-off-by: Leo Izen --- libavcodec/libjxlenc.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/libavcodec/libjxlenc.c b/libavcodec/libjxlenc.c index a2fec89560..8ddbfaa098 100644 --- a/libavcodec/libjxlenc.c +++ b/libavcodec/libjxlenc.c @@ -325,7 +325,7 @@ static int libjxl_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame, LibJxlEncodeContext *ctx = avctx->priv_data; AVFrameSideData *sd; int32_t *matrix = (int32_t[9]){ 0 }; - int ret = 0; + int ret = 0, have_matrix = 0; const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(frame->format); JxlBasicInfo info; JxlPixelFormat *jxl_fmt = &ctx->jxl_fmt; @@ -383,6 +383,11 @@ static int libjxl_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame, /* bitexact lossless requires there to be no XYB transform */ info.uses_original_profile = ctx->distance == 0.0 || !ctx->xyb; + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX); + if (sd) { + matrix = (int32_t *) sd->data; + have_matrix = 1; + } sd = av_frame_get_side_data(frame, AV_FRAME_DATA_EXIF); if (sd) { AVExifMetadata ifd = { 0 }; @@ -393,25 +398,26 @@ static int libjxl_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame, ret = ff_exif_sanitize_ifd(avctx, frame, &ifd); if (ret >= 0) ret = av_exif_get_entry(avctx, &ifd, tag, 0, &orient); - if (ret >= 0 && orient && orient->value.uint[0] >= 1 && orient->value.uint[0] <= 8) { - av_exif_orientation_to_matrix(matrix, orient->value.uint[0]); + if (ret >= 0 && orient) { + if (!have_matrix && orient->value.uint[0] >= 1 && orient->value.uint[0] <= 8) { + av_exif_orientation_to_matrix(matrix, orient->value.uint[0]); + have_matrix = 1; + } + /* pop the orientation tag anyway, because it only creates */ + /* ambiguity with the codestream orientation taking precdence */ ret = av_exif_remove_entry(avctx, &ifd, tag, 0); - } else { - av_exif_orientation_to_matrix(matrix, 1); } if (ret >= 0) ret = av_exif_write(avctx, &ifd, &exif_buffer, AV_EXIF_TIFF_HEADER); if (ret < 0) av_log(avctx, AV_LOG_WARNING, "unable to process EXIF frame data\n"); av_exif_free(&ifd); - } else { - sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX); - if (sd) - matrix = (int32_t *) sd->data; - else - av_exif_orientation_to_matrix(matrix, 1); } + /* use identity matrix as default */ + if (!have_matrix) + av_exif_orientation_to_matrix(matrix, 1); + /* av_display_matrix_flip is a right-multipilcation */ /* i.e. flip is applied before the previous matrix */ if (frame->linesize < 0) -- 2.49.1 >>From f1a7015a41a61d22a2ea0a006f68f6739f925e68 Mon Sep 17 00:00:00 2001 From: Leo Izen Date: Mon, 1 Dec 2025 06:20:58 -0500 Subject: [PATCH 2/3] avcodec/libjxlenc: avoid calling functions inside if statements It leads to messier, less readable code, and can also lead to bugs. I prefer this code style. Signed-off-by: Leo Izen --- libavcodec/libjxlenc.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/libavcodec/libjxlenc.c b/libavcodec/libjxlenc.c index 8ddbfaa098..67650da7e9 100644 --- a/libavcodec/libjxlenc.c +++ b/libavcodec/libjxlenc.c @@ -326,6 +326,7 @@ static int libjxl_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame, AVFrameSideData *sd; int32_t *matrix = (int32_t[9]){ 0 }; int ret = 0, have_matrix = 0; + JxlEncoderStatus jret = JXL_ENC_SUCCESS; const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(frame->format); JxlBasicInfo info; JxlPixelFormat *jxl_fmt = &ctx->jxl_fmt; @@ -445,7 +446,8 @@ static int libjxl_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame, info.animation.tps_denominator = avctx->time_base.num; } - if (JxlEncoderSetBasicInfo(ctx->encoder, &info) != JXL_ENC_SUCCESS) { + jret = JxlEncoderSetBasicInfo(ctx->encoder, &info); + if (jret != JXL_ENC_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to set JxlBasicInfo\n"); ret = AVERROR_EXTERNAL; goto end; @@ -457,36 +459,42 @@ static int libjxl_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame, extra_info.bits_per_sample = info.alpha_bits; extra_info.exponent_bits_per_sample = info.alpha_exponent_bits; extra_info.alpha_premultiplied = info.alpha_premultiplied; - - if (JxlEncoderSetExtraChannelInfo(ctx->encoder, 0, &extra_info) != JXL_ENC_SUCCESS) { + jret = JxlEncoderSetExtraChannelInfo(ctx->encoder, 0, &extra_info); + if (jret != JXL_ENC_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to set JxlExtraChannelInfo for alpha!\n"); - return AVERROR_EXTERNAL; + ret = AVERROR_EXTERNAL; + goto end; } } 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"); - sd = NULL; + if (sd && sd->size) { + jret = JxlEncoderSetICCProfile(ctx->encoder, sd->data, sd->size); + if (jret != JXL_ENC_SUCCESS) + av_log(avctx, AV_LOG_WARNING, "Could not set ICC Profile\n"); } - if (!sd || !sd->size) + /* jret != JXL_ENC_SUCCESS means fallthrough from above */ + if (!sd || !sd->size || jret != JXL_ENC_SUCCESS) libjxl_populate_colorspace(avctx, frame, pix_desc, &info); #if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 8, 0) - if (JxlEncoderSetFrameBitDepth(ctx->options, &jxl_bit_depth) != JXL_ENC_SUCCESS) + jret = JxlEncoderSetFrameBitDepth(ctx->options, &jxl_bit_depth); + if (jret != JXL_ENC_SUCCESS) av_log(avctx, AV_LOG_WARNING, "Failed to set JxlBitDepth\n"); #endif if (exif_buffer) { - if (JxlEncoderUseBoxes(ctx->encoder) != JXL_ENC_SUCCESS) + jret = JxlEncoderUseBoxes(ctx->encoder); + if (jret != JXL_ENC_SUCCESS) av_log(avctx, AV_LOG_WARNING, "Couldn't enable UseBoxes\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) + jret = JxlEncoderSetCodestreamLevel(ctx->encoder, 10); + if (jret != JXL_ENC_SUCCESS) av_log(avctx, AV_LOG_WARNING, "Could not increase codestream level\n"); } -- 2.49.1 >>From 2d6f067302dd5d60edd1a18844b8c2ab0dd6f95d Mon Sep 17 00:00:00 2001 From: Leo Izen Date: Mon, 1 Dec 2025 06:43:32 -0500 Subject: [PATCH 3/3] avcodec/libjxlenc: add EXIF box to output We already parse the EXIF side data to extract the orientation, so we should add it to the output file as an EXIF box. Signed-off-by: Leo Izen --- libavcodec/libjxlenc.c | 48 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/libavcodec/libjxlenc.c b/libavcodec/libjxlenc.c index 67650da7e9..ca46061545 100644 --- a/libavcodec/libjxlenc.c +++ b/libavcodec/libjxlenc.c @@ -59,6 +59,7 @@ typedef struct LibJxlEncodeContext { uint8_t *buffer; size_t buffer_size; JxlPixelFormat jxl_fmt; + AVBufferRef *exif_buffer; /* animation stuff */ AVFrame *frame; @@ -316,6 +317,40 @@ static int libjxl_populate_colorspace(AVCodecContext *avctx, const AVFrame *fram return 0; } +static int libjxl_add_boxes(AVCodecContext *avctx) +{ + LibJxlEncodeContext *ctx = avctx->priv_data; + JxlEncoderStatus jret = JXL_ENC_SUCCESS; + int ret = 0, opened = 0; + + /* no boxes need to be added */ + if (!ctx->exif_buffer) + goto end; + + jret = JxlEncoderUseBoxes(ctx->encoder); + if (jret != JXL_ENC_SUCCESS) { + av_log(avctx, AV_LOG_WARNING, "Could not enable UseBoxes\n"); + ret = AVERROR_EXTERNAL; + goto end; + } + opened = 1; + + jret = JxlEncoderAddBox(ctx->encoder, "Exif", ctx->exif_buffer->data, ctx->exif_buffer->size, JXL_TRUE); + if (jret != JXL_ENC_SUCCESS) + jret = JxlEncoderAddBox(ctx->encoder, "Exif", ctx->exif_buffer->data, ctx->exif_buffer->size, JXL_FALSE); + if (jret != JXL_ENC_SUCCESS) { + av_log(avctx, AV_LOG_WARNING, "Failed to add Exif box\n"); + ret = AVERROR_EXTERNAL; + goto end; + } + +end: + if (opened) + JxlEncoderCloseBoxes(ctx->encoder); + + return ret; +} + /** * Sends metadata to libjxl based on the first frame of the stream, such as pixel format, * orientation, bit depth, and that sort of thing. @@ -332,7 +367,6 @@ static int libjxl_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame, JxlPixelFormat *jxl_fmt = &ctx->jxl_fmt; int bits_per_sample; int orientation; - AVBufferRef *exif_buffer = NULL; #if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 8, 0) JxlBitDepth jxl_bit_depth; #endif @@ -409,7 +443,7 @@ static int libjxl_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame, ret = av_exif_remove_entry(avctx, &ifd, tag, 0); } if (ret >= 0) - ret = av_exif_write(avctx, &ifd, &exif_buffer, AV_EXIF_TIFF_HEADER); + ret = av_exif_write(avctx, &ifd, &ctx->exif_buffer, AV_EXIF_T_OFF); if (ret < 0) av_log(avctx, AV_LOG_WARNING, "unable to process EXIF frame data\n"); av_exif_free(&ifd); @@ -484,12 +518,6 @@ static int libjxl_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame, av_log(avctx, AV_LOG_WARNING, "Failed to set JxlBitDepth\n"); #endif - if (exif_buffer) { - jret = JxlEncoderUseBoxes(ctx->encoder); - if (jret != JXL_ENC_SUCCESS) - av_log(avctx, AV_LOG_WARNING, "Couldn't enable UseBoxes\n"); - } - /* depending on basic info, level 10 might * be required instead of level 5 */ if (JxlEncoderGetRequiredCodestreamLevel(ctx->encoder) > 5) { @@ -498,8 +526,10 @@ static int libjxl_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame, av_log(avctx, AV_LOG_WARNING, "Could not increase codestream level\n"); } + libjxl_add_boxes(avctx); + end: - av_buffer_unref(&exif_buffer); + av_buffer_unref(&ctx->exif_buffer); return ret; } -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org