From patchwork Mon Dec 11 17:05:13 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Zsolt_Vad=C3=A1sz?= X-Patchwork-Id: 45068 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:1225:b0:181:818d:5e7f with SMTP id v37csp3060865pzf; Mon, 11 Dec 2023 09:05:56 -0800 (PST) X-Google-Smtp-Source: AGHT+IGuc0MOOSghjKMX+PGKjrKy6Sox+pA0ZnLBQ8D82ZqodPSAmKb4nTCrLEzahHg0Hc4JuZ1j X-Received: by 2002:a19:f818:0:b0:50b:f088:46b1 with SMTP id a24-20020a19f818000000b0050bf08846b1mr3576545lff.5.1702314356589; Mon, 11 Dec 2023 09:05:56 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1702314356; cv=none; d=google.com; s=arc-20160816; b=j5EEjgRb3AxzaLBEJgUPxvxXd2vxB1rhxDWAXTO/LYdq2seocoqStWagxWi9ZkMZjM jivpqYHAlay7+C8+8VZvicoiffMPM7jJKcmCVgZBMYfnBbxahcS2xTVPUei6xNUGeYhV vbrXybUyN03X8hQtHLiWTTziWtv2yT/+HbZgrKBHa7LVRtLVLnfpIQ+y1YlOkJVomVM3 jw2JOTbU/edds/9oa6KEKKZ7L5kaZc5ERMNYFsZ8cPtp/By7pT6Yzxz3mrKshFrR3dfG mmVVQ0EgU4ixk2PEnCrjepVca3vPdlDDwckU7/OrjzQ076S4GPSM2q5s7nPYfbB6Ar9B wrqw== 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:from :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:feedback-id:message-id:to :date:delivered-to; bh=IL/aBVN7vnsMjZe+fZChKX4fMbIKhKSQ0thyFFMhuoE=; fh=lvOXre93Kc+LM49rLl0yJcoSym9484cIB4O1jbJgoXk=; b=I9w6b+UgmFVxsxsgcSL2XuYsJWY0GYjFiOnTT6Zm8zNcnPwHRjxA6FB3JIU+1eHlX5 7GEeljKhMG0entL05GNnOaEzx257GxnpvvlADT+4fEm9UdjwjtfpaEhM8TW+IK81pk1S E6GadjPSdgrJSOJ1Ja5cCL0u9jep0VeuywM77mpuDj/yGhMHgdeaVf3OqZGCoqjp+WcC kwxx8gH9Bk9lXnmknWMHaONrMxrOvOKypB5KPOYtngW6elmv+Y60Pyq6f4TpuOcHH/E9 y0oFNBJ0mD8zNmIDn5eNV7IE4STv2VHHyWJx4IeFv+Ff5lbSxNUiOxNtXaT7whsIhXSc 75kQ== ARC-Authentication-Results: i=1; mx.google.com; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id i22-20020a170906251600b00a1a55cd350bsi3602502ejb.143.2023.12.11.09.05.48; Mon, 11 Dec 2023 09:05:56 -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; 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 Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id A2E7B68D1C8; Mon, 11 Dec 2023 19:05:38 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-40130.protonmail.ch (mail-40130.protonmail.ch [185.70.40.130]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id F2A1F68D1BC for ; Mon, 11 Dec 2023 19:05:31 +0200 (EET) Date: Mon, 11 Dec 2023 17:05:13 +0000 To: FFmpeg development discussions and patches Message-ID: Feedback-ID: 28710920:user:proton MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 1/3] avcodec/libjxlenc: Move JxlBasicInfo to LibJxlEncodeContext 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: , X-Patchwork-Original-From: =?utf-8?q?Zsolt_Vad=C3=A1sz_via_ffmpeg-devel?= From: =?utf-8?q?Zsolt_Vad=C3=A1sz?= Reply-To: FFmpeg development discussions and patches Cc: =?utf-8?q?Zsolt_Vad=C3=A1sz?= Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: BrU3eO5Xdrq5 --- libavcodec/libjxlenc.c | 45 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/libavcodec/libjxlenc.c b/libavcodec/libjxlenc.c index d707f3a61b..92a458d51a 100644 --- a/libavcodec/libjxlenc.c +++ b/libavcodec/libjxlenc.c @@ -49,6 +49,7 @@ typedef struct LibJxlEncodeContext { void *runner; JxlEncoder *encoder; JxlEncoderFrameSettings *options; + JxlBasicInfo info; int effort; float distance; int modular; @@ -95,9 +96,6 @@ static int libjxl_init_jxl_encoder(AVCodecContext *avctx) { LibJxlEncodeContext *ctx = avctx->priv_data; - /* reset the encoder every frame for image2 muxer */ - JxlEncoderReset(ctx->encoder); - ctx->options = JxlEncoderFrameSettingsCreate(ctx->encoder, NULL); if (!ctx->options) { av_log(avctx, AV_LOG_ERROR, "Failed to create JxlEncoderOptions\n"); @@ -247,7 +245,7 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra LibJxlEncodeContext *ctx = avctx->priv_data; AVFrameSideData *sd; const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(frame->format); - JxlBasicInfo info; + JxlBasicInfo *info = &ctx->info; JxlColorEncoding jxl_color; JxlPixelFormat jxl_fmt; int bits_per_sample; @@ -260,6 +258,9 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra size_t bytes_written = 0; uint8_t *next_out = ctx->buffer; + /* reset the encoder every frame for image2 muxer */ + JxlEncoderReset(ctx->encoder); + ret = libjxl_init_jxl_encoder(avctx); if (ret) { av_log(avctx, AV_LOG_ERROR, "Error frame-initializing JxlEncoder\n"); @@ -267,31 +268,31 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra } /* populate the basic info settings */ - JxlEncoderInitBasicInfo(&info); + JxlEncoderInitBasicInfo(info); jxl_fmt.num_channels = pix_desc->nb_components; - info.xsize = frame->width; - info.ysize = frame->height; - info.num_extra_channels = (jxl_fmt.num_channels + 1) % 2; - info.num_color_channels = jxl_fmt.num_channels - info.num_extra_channels; + info->xsize = frame->width; + info->ysize = frame->height; + info->num_extra_channels = (jxl_fmt.num_channels + 1) % 2; + info->num_color_channels = jxl_fmt.num_channels - info->num_extra_channels; bits_per_sample = av_get_bits_per_pixel(pix_desc) / jxl_fmt.num_channels; - info.bits_per_sample = avctx->bits_per_raw_sample > 0 && !(pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT) + info->bits_per_sample = avctx->bits_per_raw_sample > 0 && !(pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT) ? avctx->bits_per_raw_sample : bits_per_sample; - info.alpha_bits = (info.num_extra_channels > 0) * info.bits_per_sample; + 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; + 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; } 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; + 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; } #if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 8, 0) jxl_bit_depth.bits_per_sample = bits_per_sample; jxl_bit_depth.type = JXL_BIT_DEPTH_FROM_PIXEL_FORMAT; jxl_bit_depth.exponent_bits_per_sample = pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT ? - info.exponent_bits_per_sample : 0; + info->exponent_bits_per_sample : 0; #endif /* JPEG XL format itself does not support limited range */ @@ -302,9 +303,9 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra av_log(avctx, AV_LOG_WARNING, "Unknown color range, assuming full (pc)\n"); /* bitexact lossless requires there to be no XYB transform */ - info.uses_original_profile = ctx->distance == 0.0; + info->uses_original_profile = ctx->distance == 0.0; - if (JxlEncoderSetBasicInfo(ctx->encoder, &info) != JXL_ENC_SUCCESS) { + if (JxlEncoderSetBasicInfo(ctx->encoder, info) != JXL_ENC_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to set JxlBasicInfo\n"); return AVERROR_EXTERNAL; } @@ -353,7 +354,7 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra /* This should be implied to be honest * but a libjxl bug makes it fail otherwise */ - if (info.num_color_channels == 1) + if (info->num_color_channels == 1) jxl_color.color_space = JXL_COLOR_SPACE_GRAY; else jxl_color.color_space = JXL_COLOR_SPACE_RGB; @@ -385,7 +386,7 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra 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; } From patchwork Mon Dec 11 17:05:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Zsolt_Vad=C3=A1sz?= X-Patchwork-Id: 45067 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:1225:b0:181:818d:5e7f with SMTP id v37csp3060686pzf; Mon, 11 Dec 2023 09:05:45 -0800 (PST) X-Google-Smtp-Source: AGHT+IFaoVNDCFu3TVSKPct9Ds8nIpZDnXYErJK4iI4Q/1ErsYqdkfsB2Kno5PHo6JcPKtiqSLyw X-Received: by 2002:a17:907:c003:b0:a1d:6d:1392 with SMTP id ss3-20020a170907c00300b00a1d006d1392mr5084628ejc.1.1702314345488; Mon, 11 Dec 2023 09:05:45 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1702314345; cv=none; d=google.com; s=arc-20160816; b=mH0MNceK6WQQCjp+8OH8EWK6aMYIa3fPKM99ThBONaCp4yEkstL4/ZyXGk1pOvVJ+1 bZj7rFCSDMcA4mv5BOL9e9iji2V1QrdedDghYeKctbvGSMy5K47ZZEdya89NcS+jws32 OcJHaAxOl/xGwHs7r4kxPP/MlwG7K4IZU+90yDCNE3H77NXRSeDhncNNm+GKWChnp4Hc p6YCWDleEadp8+qP3UVd7+HLqXryxN04KLL0PBUxmDxr3ggJsp8pcj+0wRWH18RfkRxw N+/6Y6i15fCxSb0qAWeoVpwSLm1Vlqg8+HyI4NiZT7D7+6j7RBsNpOmTLtmr7pizLdez cgFQ== 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:from :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:feedback-id:message-id:to :date:delivered-to; bh=glRI3fXSg0v+GZOLuATkuXbJOlCnAVie0Bg2VU+ZXz4=; fh=lvOXre93Kc+LM49rLl0yJcoSym9484cIB4O1jbJgoXk=; b=lg5gMqDloebY6BMWNAA8zgujlRQcgfV+NxyZOIDr78aFUUFWWc3DpWbWv+KQ+R5Uvl 1qN4IPHTsww0geUv3aZKTocnfY7CEbGc1LdFgS++dedm06N4evSlcQzIbQvLyrHtR2V+ S3YOvLk2tOkKyrTiFRYmKcoKetRLZOCcDa5LNZTZFLt+WJP1BEcaP99tPedGYFaXanA9 p3Zl59MNjg20JS5o92s9xTIcdatuKtxUv6BjZE55rEMakhoL+AeHIaFUwPp1SVnPyna+ +9Rhw05hi3xPlUQnGyXR7Mpw93qBPZlHrwn8y5JmGtsIr20o/p7JWPOKa9oOgdUnmOkV v4gw== ARC-Authentication-Results: i=1; mx.google.com; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id va3-20020a17090711c300b00a1b855d8ca1si3901080ejb.513.2023.12.11.09.05.38; Mon, 11 Dec 2023 09:05:45 -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; 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 Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 8B01168D1B6; Mon, 11 Dec 2023 19:05:32 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-40130.protonmail.ch (mail-40130.protonmail.ch [185.70.40.130]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 4D20A68D1A6 for ; Mon, 11 Dec 2023 19:05:26 +0200 (EET) Date: Mon, 11 Dec 2023 17:05:19 +0000 To: FFmpeg development discussions and patches Message-ID: Feedback-ID: 28710920:user:proton MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 2/3] avcodec/libjxlenc: Move image initialization code into libjxl_encode_init_image 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: , X-Patchwork-Original-From: =?utf-8?q?Zsolt_Vad=C3=A1sz_via_ffmpeg-devel?= From: =?utf-8?q?Zsolt_Vad=C3=A1sz?= Reply-To: FFmpeg development discussions and patches Cc: =?utf-8?q?Zsolt_Vad=C3=A1sz?= Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: y3/wkh2apWcc --- libavcodec/libjxlenc.c | 69 +++++++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/libavcodec/libjxlenc.c b/libavcodec/libjxlenc.c index 92a458d51a..6110c42a7c 100644 --- a/libavcodec/libjxlenc.c +++ b/libavcodec/libjxlenc.c @@ -50,6 +50,9 @@ typedef struct LibJxlEncodeContext { JxlEncoder *encoder; JxlEncoderFrameSettings *options; JxlBasicInfo info; + JxlPixelFormat jxl_fmt; + int animated; + int first_frame; int effort; float distance; int modular; @@ -181,6 +184,9 @@ static av_cold int libjxl_encode_init(AVCodecContext *avctx) return AVERROR(ENOMEM); } + ctx->animated = 0; + ctx->first_frame = 1; + return 0; } @@ -235,28 +241,19 @@ static int libjxl_populate_primaries(void *avctx, JxlColorEncoding *jxl_color, e return 0; } -/** - * Encode an entire frame. Currently animation, is not supported by - * this encoder, so this will always reinitialize a new still image - * and encode a one-frame image (for image2 and image2pipe). - */ -static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet) +static int libjxl_encode_init_image(AVCodecContext *avctx, const AVFrame *frame) { LibJxlEncodeContext *ctx = avctx->priv_data; - AVFrameSideData *sd; - const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(frame->format); + int ret; JxlBasicInfo *info = &ctx->info; + JxlPixelFormat *jxl_fmt = &ctx->jxl_fmt; JxlColorEncoding jxl_color; - JxlPixelFormat jxl_fmt; int bits_per_sample; #if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 8, 0) JxlBitDepth jxl_bit_depth; #endif - JxlEncoderStatus jret; - int ret; - size_t available = ctx->buffer_size; - size_t bytes_written = 0; - uint8_t *next_out = ctx->buffer; + const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(frame->format); + AVFrameSideData *sd; /* reset the encoder every frame for image2 muxer */ JxlEncoderReset(ctx->encoder); @@ -269,23 +266,23 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra /* populate the basic info settings */ JxlEncoderInitBasicInfo(info); - jxl_fmt.num_channels = pix_desc->nb_components; + jxl_fmt->num_channels = pix_desc->nb_components; info->xsize = frame->width; info->ysize = frame->height; - info->num_extra_channels = (jxl_fmt.num_channels + 1) % 2; - info->num_color_channels = jxl_fmt.num_channels - info->num_extra_channels; - bits_per_sample = av_get_bits_per_pixel(pix_desc) / jxl_fmt.num_channels; + info->num_extra_channels = (jxl_fmt->num_channels + 1) % 2; + info->num_color_channels = jxl_fmt->num_channels - info->num_extra_channels; + bits_per_sample = av_get_bits_per_pixel(pix_desc) / jxl_fmt->num_channels; info->bits_per_sample = avctx->bits_per_raw_sample > 0 && !(pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT) ? avctx->bits_per_raw_sample : bits_per_sample; 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; + jxl_fmt->data_type = info->bits_per_sample > 16 ? JXL_TYPE_FLOAT : JXL_TYPE_FLOAT16; } 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; + jxl_fmt->data_type = info->bits_per_sample <= 8 ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16; } #if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 8, 0) @@ -383,17 +380,41 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra av_log(avctx, AV_LOG_WARNING, "Could not increase codestream level\n"); } - jxl_fmt.endianness = JXL_NATIVE_ENDIAN; - jxl_fmt.align = frame->linesize[0]; + jxl_fmt->endianness = JXL_NATIVE_ENDIAN; + jxl_fmt->align = frame->linesize[0]; + + return 0; +} + +/** + * Encode an entire frame. Currently animation, is not supported by + * this encoder, so this will always reinitialize a new still image + * and encode a one-frame image (for image2 and image2pipe). + */ +static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet) +{ + LibJxlEncodeContext *ctx = avctx->priv_data; + JxlEncoderStatus jret; + JxlBasicInfo *info = &ctx->info; + JxlPixelFormat *jxl_fmt = &ctx->jxl_fmt; + int ret; + size_t available = ctx->buffer_size; + size_t bytes_written = 0; + uint8_t *next_out = ctx->buffer; + + if(!ctx->animated || ctx->first_frame) { + if((ret = libjxl_encode_init_image(avctx, frame)) < 0) + return ret; + ctx->first_frame = 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; } /* * Run this after the last frame in the image has been passed. - * TODO support animation */ JxlEncoderCloseInput(ctx->encoder); From patchwork Mon Dec 11 17:05:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Zsolt_Vad=C3=A1sz?= X-Patchwork-Id: 45069 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:1225:b0:181:818d:5e7f with SMTP id v37csp3061030pzf; Mon, 11 Dec 2023 09:06:08 -0800 (PST) X-Google-Smtp-Source: AGHT+IETqJX6CT0oqEIfww/wMME3+PwSm6UEG1pQMGIoe3aKfWQaMS0HGKMwZN2ex3MAW4bh5VaO X-Received: by 2002:a17:906:7ac6:b0:a1f:6c5d:2ee8 with SMTP id k6-20020a1709067ac600b00a1f6c5d2ee8mr1408898ejo.140.1702314367693; Mon, 11 Dec 2023 09:06:07 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1702314367; cv=none; d=google.com; s=arc-20160816; b=Ud9pZpacUux59lKbsS2z32XF3eOhXWb1OZp6UBrplHyD1M7U5a//Bo/ieFixDucaNO 4eY7Ha0xGAcSLVRgDR6/zmrcHXHUQzKxL1W5CBfYHOQalbI1Z3eU4/iBobtnkzkUAhkX bXunlJNQWHQJnEChIOMH8Lg+q5Acb5YdzIM60wqRmuCh0fHlIk3A7X+dFkv90JC0tY+o 8mVAAj9Mlj554QitPwZvRhCGrhcRGaOszarbIf9AnRSFpsb9O+whJciSlJ/sQUdK+dAw NcPoOxirUqkT1fCpAVnxyUywW+oq6NR88+ttTOEFRDU9ngyW7nnyxDYt7/hrgDmhz8Pu ui6Q== 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:from :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:mime-version:feedback-id:message-id:to :date:delivered-to; bh=AQySE/R4SMyLawYWqDFvVa5uNHV2cT0LcP+I+Mn4b38=; fh=lvOXre93Kc+LM49rLl0yJcoSym9484cIB4O1jbJgoXk=; b=erjgmk+sx5XcwPRsld0bmwtdv2l4bM/zAhRFWRFYKcIXGuMQ1793Mkbra8oNgCe1Fc zK1JPslnjpUr5C3+zXcZtk/DHv7O3CUiqGnkNKF3XR7tDl7qbAP12ci71hIqoU+4utPz o79j8DYVYIWvkZMYe0oA8liEP05Bo/LeImGjrVmKl6BvhfVI9k+mUL+VKMCME09y+4mY IKGab2zK+06KYm6xQsoe/mM0nYfBMU2TXTM6+JtTANTRiq1wN74O/hmjWXD9A4SK2Rhv e/+/yvIaxoNvTmbMjFAv6u1iTabbv9oGFDhc4nWh9QnF5R8hKdAnuCM/zy0WA8GkyV1G Aeyg== ARC-Authentication-Results: i=1; mx.google.com; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id ov17-20020a170906fc1100b00a1cdeeaadb2si3804541ejb.257.2023.12.11.09.05.58; Mon, 11 Dec 2023 09:06:07 -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; 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 Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id C656168D1AE; Mon, 11 Dec 2023 19:05:47 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-40137.protonmail.ch (mail-40137.protonmail.ch [185.70.40.137]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 4D40E68D124 for ; Mon, 11 Dec 2023 19:05:41 +0200 (EET) Date: Mon, 11 Dec 2023 17:05:28 +0000 To: FFmpeg development discussions and patches Message-ID: Feedback-ID: 28710920:user:proton MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 3/3] avcodec/libjxlenc: Add libjxl_animated encoder 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: , X-Patchwork-Original-From: =?utf-8?q?Zsolt_Vad=C3=A1sz_via_ffmpeg-devel?= From: =?utf-8?q?Zsolt_Vad=C3=A1sz?= Reply-To: FFmpeg development discussions and patches Cc: =?utf-8?q?Zsolt_Vad=C3=A1sz?= Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: LF3oWgeCRglb --- libavcodec/allcodecs.c | 1 + libavcodec/libjxlenc.c | 197 ++++++++++++++++++++++++++++++++--------- 2 files changed, 157 insertions(+), 41 deletions(-) --- 2.43.0 diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index b0f004e15c..e6733b0d4f 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -784,6 +784,7 @@ extern const FFCodec ff_libilbc_encoder; extern const FFCodec ff_libilbc_decoder; extern const FFCodec ff_libjxl_decoder; extern const FFCodec ff_libjxl_encoder; +extern const FFCodec ff_libjxl_animated_encoder; extern const FFCodec ff_libmp3lame_encoder; extern const FFCodec ff_libopencore_amrnb_encoder; extern const FFCodec ff_libopencore_amrnb_decoder; diff --git a/libavcodec/libjxlenc.c b/libavcodec/libjxlenc.c index 6110c42a7c..5d437b2c05 100644 --- a/libavcodec/libjxlenc.c +++ b/libavcodec/libjxlenc.c @@ -31,14 +31,17 @@ #include "libavutil/error.h" #include "libavutil/frame.h" #include "libavutil/libm.h" +#include "libavutil/log.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/pixfmt.h" #include "libavutil/version.h" +#include "packet.h" #include "avcodec.h" #include "encode.h" #include "codec_internal.h" +#include "internal.h" #include #include @@ -51,13 +54,14 @@ typedef struct LibJxlEncodeContext { JxlEncoderFrameSettings *options; JxlBasicInfo info; JxlPixelFormat jxl_fmt; - int animated; - int first_frame; int effort; float distance; int modular; uint8_t *buffer; size_t buffer_size; + /* Only used by libjxl-animated */ + AVFrame *last; + int animated; } LibJxlEncodeContext; /** @@ -185,7 +189,6 @@ static av_cold int libjxl_encode_init(AVCodecContext *avctx) } ctx->animated = 0; - ctx->first_frame = 1; return 0; } @@ -284,6 +287,14 @@ static int libjxl_encode_init_image(AVCodecContext *avctx, const AVFrame *frame) info->alpha_exponent_bits = 0; jxl_fmt->data_type = info->bits_per_sample <= 8 ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16; } + if(ctx->animated) { + info->have_animation = 1; + info->animation.have_timecodes = 0; + info->animation.num_loops = 0; + info->animation.tps_numerator = frame->time_base.den; + info->animation.tps_denominator = frame->time_base.num; + avctx->time_base = frame->time_base; + } #if JPEGXL_NUMERIC_VERSION >= JPEGXL_COMPUTE_NUMERIC_VERSION(0, 8, 0) jxl_bit_depth.bits_per_sample = bits_per_sample; @@ -386,37 +397,12 @@ static int libjxl_encode_init_image(AVCodecContext *avctx, const AVFrame *frame) return 0; } -/** - * Encode an entire frame. Currently animation, is not supported by - * this encoder, so this will always reinitialize a new still image - * and encode a one-frame image (for image2 and image2pipe). - */ -static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet) +static int libjxl_encode_process_output(AVCodecContext *avctx, size_t *bytes_written) { LibJxlEncodeContext *ctx = avctx->priv_data; JxlEncoderStatus jret; - JxlBasicInfo *info = &ctx->info; - JxlPixelFormat *jxl_fmt = &ctx->jxl_fmt; - int ret; - size_t available = ctx->buffer_size; - size_t bytes_written = 0; uint8_t *next_out = ctx->buffer; - - if(!ctx->animated || ctx->first_frame) { - if((ret = libjxl_encode_init_image(avctx, frame)) < 0) - return ret; - ctx->first_frame = 0; - } - - 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; - } - - /* - * Run this after the last frame in the image has been passed. - */ - JxlEncoderCloseInput(ctx->encoder); + size_t available = ctx->buffer_size; while (1) { jret = JxlEncoderProcessOutput(ctx->encoder, &next_out, &available); @@ -424,7 +410,7 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra av_log(avctx, AV_LOG_ERROR, "Unspecified libjxl error occurred\n"); return AVERROR_EXTERNAL; } - bytes_written = ctx->buffer_size - available; + *bytes_written = ctx->buffer_size - available; /* all data passed has been encoded */ if (jret == JXL_ENC_SUCCESS) break; @@ -441,14 +427,46 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra return AVERROR(ENOMEM); ctx->buffer = temp; ctx->buffer_size = new_size; - next_out = ctx->buffer + bytes_written; - available = new_size - bytes_written; + next_out = ctx->buffer + *bytes_written; + available = new_size - *bytes_written; continue; } av_log(avctx, AV_LOG_ERROR, "Bad libjxl event: %d\n", jret); return AVERROR_EXTERNAL; } + return 0; +} + +/** + * Encode an entire frame. Currently animation, is not supported by + * this encoder, so this will always reinitialize a new still image + * and encode a one-frame image (for image2 and image2pipe). + */ +static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet) +{ + LibJxlEncodeContext *ctx = avctx->priv_data; + JxlBasicInfo *info = &ctx->info; + JxlPixelFormat *jxl_fmt = &ctx->jxl_fmt; + int ret; + size_t bytes_written = 0; + + if((ret = libjxl_encode_init_image(avctx, frame)) < 0) + return ret; + + 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; + } + + /* + * Run this after the last frame in the image has been passed. + */ + JxlEncoderCloseInput(ctx->encoder); + + if((ret = libjxl_encode_process_output(avctx, &bytes_written)) < 0) + return ret; + ret = ff_get_encode_buffer(avctx, pkt, bytes_written, 0); if (ret < 0) return ret; @@ -459,6 +477,70 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra return 0; } +static int libjxl_animated_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet) +{ + LibJxlEncodeContext *ctx = avctx->priv_data; + int ret = 0; + + if(avctx->frame_num == 0) + if((ret = libjxl_encode_init_image(avctx, frame)) < 0) + return ret; + + if(!ctx->last && !avctx->internal->draining) { + ctx->last = av_frame_clone(frame); + *got_packet = 0; + return AVERROR(EAGAIN); + } else if(!ctx->last && avctx->internal->draining && !frame) { + av_log(avctx, AV_LOG_WARNING, "Draining done\n"); + *got_packet = 0; + } else { + JxlBasicInfo *info = &ctx->info; + JxlPixelFormat *jxl_fmt = &ctx->jxl_fmt; + JxlFrameHeader frame_header; + size_t bytes_written = 0; + + JxlEncoderInitFrameHeader(&frame_header); + frame_header.duration = ctx->last->duration; + pkt->duration = ctx->last->duration; + pkt->pts = AV_NOPTS_VALUE; + pkt->dts = AV_NOPTS_VALUE; + + if(JxlEncoderSetFrameHeader(ctx->options, &frame_header) != JXL_ENC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to set frame header\n"); + return AVERROR_EXTERNAL; + } + if (JxlEncoderAddImageFrame(ctx->options, jxl_fmt, ctx->last->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; + } + + /* + * Run this after the last frame in the image has been passed. + */ + if(avctx->internal->draining) { + JxlEncoderCloseInput(ctx->encoder); + } + + if((ret = libjxl_encode_process_output(avctx, &bytes_written)) < 0) + return ret; + + ret = ff_get_encode_buffer(avctx, pkt, bytes_written, 0); + if (ret < 0) + return ret; + + memcpy(pkt->data, ctx->buffer, bytes_written); + *got_packet = 1; + + if(!avctx->internal->draining) { + av_frame_replace(ctx->last, frame); + } else { + av_frame_free(&ctx->last); + } + } + + return 0; +} + static av_cold int libjxl_encode_close(AVCodecContext *avctx) { LibJxlEncodeContext *ctx = avctx->priv_data; @@ -480,6 +562,17 @@ static av_cold int libjxl_encode_close(AVCodecContext *avctx) return 0; } +static av_cold int libjxl_animated_encode_init(AVCodecContext *avctx) +{ + int ret; + LibJxlEncodeContext *ctx = avctx->priv_data; + if((ret = libjxl_encode_init(avctx)) < 0) + return ret; + ctx->animated = 1; + ret = libjxl_init_jxl_encoder(avctx); + return ret; +} + #define OFFSET(x) offsetof(LibJxlEncodeContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM @@ -498,6 +591,15 @@ static const AVClass libjxl_encode_class = { .version = LIBAVUTIL_VERSION_INT, }; +static const enum AVPixelFormat libjxl_pix_fmts[] = { + AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, + AV_PIX_FMT_RGB48, AV_PIX_FMT_RGBA64, + AV_PIX_FMT_GRAY8, AV_PIX_FMT_YA8, + AV_PIX_FMT_GRAY16, AV_PIX_FMT_YA16, + AV_PIX_FMT_GRAYF32, + AV_PIX_FMT_NONE +}; + const FFCodec ff_libjxl_encoder = { .p.name = "libjxl", CODEC_LONG_NAME("libjxl JPEG XL"), @@ -512,14 +614,27 @@ const FFCodec ff_libjxl_encoder = { .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_AUTO_THREADS | FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_ICC_PROFILES, - .p.pix_fmts = (const enum AVPixelFormat[]) { - AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, - AV_PIX_FMT_RGB48, AV_PIX_FMT_RGBA64, - AV_PIX_FMT_GRAY8, AV_PIX_FMT_YA8, - AV_PIX_FMT_GRAY16, AV_PIX_FMT_YA16, - AV_PIX_FMT_GRAYF32, - AV_PIX_FMT_NONE - }, + .p.pix_fmts = libjxl_pix_fmts, + .p.priv_class = &libjxl_encode_class, + .p.wrapper_name = "libjxl", +}; + +const FFCodec ff_libjxl_animated_encoder = { + .p.name = "libjxl_animated", + CODEC_LONG_NAME("libjxl Animated JPEG XL"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_JPEGXL, + .priv_data_size = sizeof(LibJxlEncodeContext), + .init = libjxl_animated_encode_init, + FF_CODEC_ENCODE_CB(libjxl_animated_encode_frame), + .close = libjxl_encode_close, + .p.capabilities = AV_CODEC_CAP_OTHER_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE | + AV_CODEC_CAP_DELAY, + .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | + FF_CODEC_CAP_AUTO_THREADS | FF_CODEC_CAP_INIT_CLEANUP | + FF_CODEC_CAP_ICC_PROFILES | FF_CODEC_CAP_EOF_FLUSH, + .p.pix_fmts = libjxl_pix_fmts, .p.priv_class = &libjxl_encode_class, .p.wrapper_name = "libjxl", };