libjxl supports animated encoding, so we add a wrapper to the library using the receive_packet callback method.
This code was based largely on a patch sent by Zsolt Vadász, although it was updated to use more recent coding practices and many of the leaks and issues were fixed. Co-authored-by: Zsolt Vadász <zsolt_vad...@protonmail.com> Signed-off-by: Leo Izen <leo.i...@gmail.com> --- configure | 2 + libavcodec/allcodecs.c | 2 + libavcodec/codec_desc.c | 8 + libavcodec/codec_id.h | 1 + libavcodec/jpegxl_parser.c | 2 +- libavcodec/libjxldec.c | 16 ++ libavcodec/libjxlenc.c | 365 +++++++++++++++++++++++++++---------- libavcodec/version.h | 2 +- 8 files changed, 303 insertions(+), 95 deletions(-) diff --git a/configure b/configure index d7b7b49f92..bda89c460f 100755 --- a/configure +++ b/configure @@ -3549,6 +3549,8 @@ libgsm_ms_decoder_deps="libgsm" libgsm_ms_encoder_deps="libgsm" libilbc_decoder_deps="libilbc" libilbc_encoder_deps="libilbc" +libjxl_anim_decoder_deps="libjxl libjxl_threads" +libjxl_anim_encoder_deps="libjxl libjxl_threads" libjxl_decoder_deps="libjxl libjxl_threads" libjxl_encoder_deps="libjxl libjxl_threads" libkvazaar_encoder_deps="libkvazaar" diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 0b559dfc58..4bc41239c7 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -777,6 +777,8 @@ extern const FFCodec ff_libgsm_ms_encoder; extern const FFCodec ff_libgsm_ms_decoder; extern const FFCodec ff_libilbc_encoder; extern const FFCodec ff_libilbc_decoder; +extern const FFCodec ff_libjxl_anim_decoder; +extern const FFCodec ff_libjxl_anim_encoder; extern const FFCodec ff_libjxl_decoder; extern const FFCodec ff_libjxl_encoder; extern const FFCodec ff_liblc3_encoder; diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index bc9163bf98..fc075ffd7f 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -1977,6 +1977,14 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("RealVideo 6.0"), .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER, }, + { + .id = AV_CODEC_ID_JPEGXL_ANIM, + .type = AVMEDIA_TYPE_VIDEO, + .name = "jpegxl_anim", + .long_name = NULL_IF_CONFIG_SMALL("JPEG XL animated"), + .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_LOSSLESS, + .mime_types= MT("image/jxl"), + }, /* various PCM "codecs" */ { diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h index 6bfaa02601..ba0480aa09 100644 --- a/libavcodec/codec_id.h +++ b/libavcodec/codec_id.h @@ -328,6 +328,7 @@ enum AVCodecID { AV_CODEC_ID_LEAD, AV_CODEC_ID_DNXUC, AV_CODEC_ID_RV60, + AV_CODEC_ID_JPEGXL_ANIM, /* various PCM "codecs" */ AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs diff --git a/libavcodec/jpegxl_parser.c b/libavcodec/jpegxl_parser.c index 76122af54a..a888e9ae6e 100644 --- a/libavcodec/jpegxl_parser.c +++ b/libavcodec/jpegxl_parser.c @@ -1534,7 +1534,7 @@ flush: } const AVCodecParser ff_jpegxl_parser = { - .codec_ids = { AV_CODEC_ID_JPEGXL }, + .codec_ids = { AV_CODEC_ID_JPEGXL, AV_CODEC_ID_JPEGXL_ANIM }, .priv_data_size = sizeof(JXLParseContext), .parser_parse = jpegxl_parse, .parser_close = ff_parse_close, diff --git a/libavcodec/libjxldec.c b/libavcodec/libjxldec.c index 9dfc261e3d..96c338d1b4 100644 --- a/libavcodec/libjxldec.c +++ b/libavcodec/libjxldec.c @@ -549,3 +549,19 @@ const FFCodec ff_libjxl_decoder = { FF_CODEC_CAP_ICC_PROFILES, .p.wrapper_name = "libjxl", }; + +const FFCodec ff_libjxl_anim_decoder = { + .p.name = "libjxl_anim", + CODEC_LONG_NAME("libjxl JPEG XL animated"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_JPEGXL_ANIM, + .priv_data_size = sizeof(LibJxlDecodeContext), + .init = libjxl_decode_init, + FF_CODEC_RECEIVE_FRAME_CB(libjxl_receive_frame), + .close = libjxl_decode_close, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_OTHER_THREADS, + .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | + FF_CODEC_CAP_AUTO_THREADS | FF_CODEC_CAP_INIT_CLEANUP | + FF_CODEC_CAP_ICC_PROFILES, + .p.wrapper_name = "libjxl", +}; diff --git a/libavcodec/libjxlenc.c b/libavcodec/libjxlenc.c index 8b0e60df6f..5da4d2baad 100644 --- a/libavcodec/libjxlenc.c +++ b/libavcodec/libjxlenc.c @@ -35,6 +35,7 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/pixfmt.h" +#include "libavutil/rational.h" #include "libavutil/version.h" #include "avcodec.h" @@ -56,6 +57,13 @@ typedef struct LibJxlEncodeContext { int xyb; uint8_t *buffer; size_t buffer_size; + JxlPixelFormat jxl_fmt; + const uint8_t *data; + + /* animation stuff */ + AVFrame *frame; + AVFrame *prev; + int64_t duration; } LibJxlEncodeContext; /** @@ -100,12 +108,6 @@ static int libjxl_init_jxl_encoder(AVCodecContext *avctx) /* 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"); - return AVERROR_EXTERNAL; - } - /* This needs to be set each time the encoder is reset */ if (JxlEncoderSetParallelRunner(ctx->encoder, JxlThreadParallelRunner, ctx->runner) != JXL_ENC_SUCCESS) { @@ -113,42 +115,9 @@ static int libjxl_init_jxl_encoder(AVCodecContext *avctx) return AVERROR_EXTERNAL; } - /* these shouldn't fail, libjxl bug notwithstanding */ - if (JxlEncoderFrameSettingsSetOption(ctx->options, JXL_ENC_FRAME_SETTING_EFFORT, ctx->effort) - != JXL_ENC_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "Failed to set effort to: %d\n", ctx->effort); - return AVERROR_EXTERNAL; - } - - /* check for negative, our default */ - if (ctx->distance < 0.0) { - /* use ffmpeg.c -q option if passed */ - if (avctx->flags & AV_CODEC_FLAG_QSCALE) - ctx->distance = quality_to_distance((float)avctx->global_quality / FF_QP2LAMBDA); - else - /* default 1.0 matches cjxl */ - ctx->distance = 1.0; - } - - /* - * 0.01 is the minimum distance accepted for lossy - * interpreting any positive value less than this as minimum - */ - if (ctx->distance > 0.0 && ctx->distance < 0.01) - ctx->distance = 0.01; - 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; - } - - /* - * In theory the library should automatically enable modular if necessary, - * but it appears it won't at the moment due to a bug. This will still - * work even if that is patched. - */ - if (JxlEncoderFrameSettingsSetOption(ctx->options, JXL_ENC_FRAME_SETTING_MODULAR, - ctx->modular || ctx->distance <= 0.0 ? 1 : -1) != JXL_ENC_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "Failed to set modular\n"); + ctx->options = JxlEncoderFrameSettingsCreate(ctx->encoder, NULL); + if (!ctx->options) { + av_log(avctx, AV_LOG_ERROR, "Failed to create JxlEncoderOptions\n"); return AVERROR_EXTERNAL; } @@ -185,6 +154,42 @@ static av_cold int libjxl_encode_init(AVCodecContext *avctx) return AVERROR(ENOMEM); } + /* check for negative, our default */ + if (ctx->distance < 0.0) { + /* use ffmpeg.c -q option if passed */ + if (avctx->flags & AV_CODEC_FLAG_QSCALE) + ctx->distance = quality_to_distance((float)avctx->global_quality / FF_QP2LAMBDA); + else + /* default 1.0 matches cjxl */ + ctx->distance = 1.0; + } + /* + * 0.01 is the minimum distance accepted for lossy + * interpreting any positive value less than this as minimum + */ + if (ctx->distance > 0.0 && ctx->distance < 0.01) + ctx->distance = 0.01; + + return 0; +} + +static av_cold int libjxl_anim_encode_init(AVCodecContext *avctx) +{ + int ret; + LibJxlEncodeContext *ctx = avctx->priv_data; + + ret = libjxl_encode_init(avctx); + if (ret < 0) + return ret; + + ret = libjxl_init_jxl_encoder(avctx); + if (ret < 0) + return ret; + + ctx->frame = av_frame_alloc(); + if (!ctx->frame) + return AVERROR(ENOMEM); + return 0; } @@ -239,55 +244,38 @@ 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_preprocess_stream(AVCodecContext *avctx, const AVFrame *frame, int animated) { LibJxlEncodeContext *ctx = avctx->priv_data; + int ret; AVFrameSideData *sd; const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(frame->format); JxlBasicInfo info; JxlColorEncoding jxl_color; - JxlPixelFormat jxl_fmt; + JxlPixelFormat *jxl_fmt = &ctx->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 uint8_t *data; - - ret = libjxl_init_jxl_encoder(avctx); - if (ret) { - av_log(avctx, AV_LOG_ERROR, "Error frame-initializing JxlEncoder\n"); - return ret; - } - - /* populate the basic info settings */ + /* 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) & 0x1; + 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) @@ -308,6 +296,15 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra info.uses_original_profile = ctx->distance == 0.0 || !ctx->xyb; info.orientation = frame->linesize[0] >= 0 ? JXL_ORIENT_IDENTITY : JXL_ORIENT_FLIP_VERTICAL; + if (animated) { + info.have_animation = 1; + info.animation.have_timecodes = 0; + info.animation.num_loops = 0; + /* avctx->timebase is in seconds per tick, so we take the reciprocol */ + info.animation.tps_numerator = avctx->time_base.den; + info.animation.tps_denominator = avctx->time_base.num; + } + if (JxlEncoderSetBasicInfo(ctx->encoder, &info) != JXL_ENC_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to set JxlBasicInfo\n"); return AVERROR_EXTERNAL; @@ -386,25 +383,55 @@ 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; - if (frame->linesize[0] >= 0) { - jxl_fmt.align = frame->linesize[0]; - data = frame->data[0]; - } else { - jxl_fmt.align = -frame->linesize[0]; - data = frame->data[0] + frame->linesize[0] * (info.ysize - 1); + return 0; +} + +static int libjxl_preprocess_frame(AVCodecContext *avctx, const AVFrame *frame) +{ + LibJxlEncodeContext *ctx = avctx->priv_data; + JxlPixelFormat *jxl_fmt = &ctx->jxl_fmt; + + /* these shouldn't fail, libjxl bug notwithstanding */ + if (JxlEncoderFrameSettingsSetOption(ctx->options, JXL_ENC_FRAME_SETTING_EFFORT, ctx->effort) + != JXL_ENC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to set effort to: %d\n", ctx->effort); + return AVERROR_EXTERNAL; } - if (JxlEncoderAddImageFrame(ctx->options, &jxl_fmt, data, jxl_fmt.align * info.ysize) != JXL_ENC_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "Failed to add Image Frame\n"); + 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; } /* - * Run this after the last frame in the image has been passed. - * TODO support animation + * In theory the library should automatically enable modular if necessary, + * but it appears it won't at the moment due to a bug. This will still + * work even if that is patched. */ - JxlEncoderCloseInput(ctx->encoder); + if (JxlEncoderFrameSettingsSetOption(ctx->options, JXL_ENC_FRAME_SETTING_MODULAR, + ctx->modular || ctx->distance <= 0.0 ? 1 : -1) != JXL_ENC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to set modular\n"); + return AVERROR_EXTERNAL; + } + + jxl_fmt->endianness = JXL_NATIVE_ENDIAN; + if (frame->linesize[0] >= 0) { + jxl_fmt->align = frame->linesize[0]; + ctx->data = frame->data[0]; + } else { + jxl_fmt->align = -frame->linesize[0]; + ctx->data = frame->data[0] + frame->linesize[0] * (frame->height - 1); + } + + return 0; +} + +static int libjxl_process_output(AVCodecContext *avctx, size_t *bytes_written) +{ + LibJxlEncodeContext *ctx = avctx->priv_data; + JxlEncoderStatus jret; + size_t available = ctx->buffer_size; + uint8_t *next_out = ctx->buffer; while (1) { jret = JxlEncoderProcessOutput(ctx->encoder, &next_out, &available); @@ -412,7 +439,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; @@ -429,14 +456,60 @@ 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. 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; + int ret; + size_t bytes_written = 0; + + ret = libjxl_init_jxl_encoder(avctx); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error frame-initializing JxlEncoder\n"); + return ret; + } + + ret = libjxl_preprocess_stream(avctx, frame, 0); + if (ret < 0) + return ret; + + ret = libjxl_preprocess_frame(avctx, frame); + if (ret < 0) + return ret; + + if (JxlEncoderAddImageFrame(ctx->options, &ctx->jxl_fmt, ctx->data, ctx->jxl_fmt.align * frame->height) + != JXL_ENC_SUCCESS) { + ctx->data = NULL; + av_log(avctx, AV_LOG_ERROR, "Failed to add Image Frame\n"); + return AVERROR_EXTERNAL; + } + + ctx->data = NULL; + + /* + * Run this after the last frame in the image has been passed. + */ + JxlEncoderCloseInput(ctx->encoder); + + ret = libjxl_process_output(avctx, &bytes_written); + if (ret < 0) + return ret; + ret = ff_get_encode_buffer(avctx, pkt, bytes_written, 0); if (ret < 0) return ret; @@ -447,6 +520,88 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra return 0; } +static int libjxl_anim_encode_frame(AVCodecContext *avctx, AVPacket *pkt) +{ + LibJxlEncodeContext *ctx = avctx->priv_data; + int ret = 0; + JxlFrameHeader frame_header; + size_t bytes_written = 0; + + if (!ctx->prev) { + ctx->prev = av_frame_alloc(); + if (!ctx->prev) { + ret = AVERROR(ENOMEM); + goto end; + } + ret = ff_encode_get_frame(avctx, ctx->prev); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) + goto end; + ret = libjxl_preprocess_stream(avctx, ctx->prev, 1); + if (ret < 0) + goto end; + } + + ret = ff_encode_get_frame(avctx, ctx->frame); + if (ret == AVERROR_EOF) { + av_frame_free(&ctx->frame); + ret = 0; + } + if (ret == AVERROR(EAGAIN)) + goto end; + + JxlEncoderInitFrameHeader(&frame_header); + + ctx->duration = ctx->prev->duration ? ctx->prev->duration : + ctx->frame ? ctx->frame->pts - ctx->prev->pts : + ctx->duration ? ctx->duration : + 1; + + frame_header.duration = ctx->duration; + pkt->duration = ctx->duration; + pkt->pts = ctx->prev->pts; + pkt->dts = pkt->pts; + + if (JxlEncoderSetFrameHeader(ctx->options, &frame_header) != JXL_ENC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to set JxlFrameHeader\n"); + return AVERROR_EXTERNAL; + } + + ret = libjxl_preprocess_frame(avctx, ctx->prev); + if (ret < 0) + return ret; + + if (JxlEncoderAddImageFrame(ctx->options, &ctx->jxl_fmt, ctx->data, ctx->jxl_fmt.align * ctx->prev->height) + != JXL_ENC_SUCCESS) { + ctx->data = NULL; + av_log(avctx, AV_LOG_ERROR, "Failed to add Image Frame\n"); + return AVERROR_EXTERNAL; + } + ctx->data = NULL; + + if (!ctx->frame) + JxlEncoderCloseInput(ctx->encoder); + + ret = libjxl_process_output(avctx, &bytes_written); + if (ret < 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); + + if (ctx->frame) { + av_frame_unref(ctx->prev); + av_frame_move_ref(ctx->prev, ctx->frame); + } else { + av_frame_free(&ctx->prev); + } + +end: + return ret; +} + static av_cold int libjxl_encode_close(AVCodecContext *avctx) { LibJxlEncodeContext *ctx = avctx->priv_data; @@ -464,6 +619,8 @@ static av_cold int libjxl_encode_close(AVCodecContext *avctx) ctx->encoder = NULL; av_freep(&ctx->buffer); + av_frame_free(&ctx->prev); + av_frame_free(&ctx->frame); return 0; } @@ -488,6 +645,16 @@ static const AVClass libjxl_encode_class = { .version = LIBAVUTIL_VERSION_INT, }; +static const enum AVPixelFormat libjxl_supported_pixfmts[] = { + AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, + AV_PIX_FMT_RGB48, AV_PIX_FMT_RGBA64, + AV_PIX_FMT_RGBF32, AV_PIX_FMT_RGBAF32, + 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"), @@ -503,15 +670,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_RGBF32, AV_PIX_FMT_RGBAF32, - 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_supported_pixfmts, + .p.priv_class = &libjxl_encode_class, + .p.wrapper_name = "libjxl", +}; + +const FFCodec ff_libjxl_anim_encoder = { + .p.name = "libjxl_anim", + CODEC_LONG_NAME("libjxl JPEG XL animated"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_JPEGXL_ANIM, + .priv_data_size = sizeof(LibJxlEncodeContext), + .init = libjxl_anim_encode_init, + FF_CODEC_RECEIVE_PACKET_CB(libjxl_anim_encode_frame), + .close = libjxl_encode_close, + .p.capabilities = AV_CODEC_CAP_OTHER_THREADS | + AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, + .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 = libjxl_supported_pixfmts, .p.priv_class = &libjxl_encode_class, .p.wrapper_name = "libjxl", }; diff --git a/libavcodec/version.h b/libavcodec/version.h index 26ee41eb1f..735c8b813c 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -29,7 +29,7 @@ #include "version_major.h" -#define LIBAVCODEC_VERSION_MINOR 26 +#define LIBAVCODEC_VERSION_MINOR 27 #define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ -- 2.47.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".