This commit adds decoding support to libavcodec for Jpeg XL images via the external library libjxl.
Unified · Split
| @@ -194,6 +194,7 @@ Codecs: · 193 unmodified lines | |||
| 194 | libcodec2.c Tomas Härdin | 194 | libcodec2.c Tomas Härdin |
| 195 | libdirac* David Conrad | 195 | libdirac* David Conrad |
| 196 | libdavs2.c Huiwen Ren | 196 | libdavs2.c Huiwen Ren |
| 197 | libjxl*.c, libjxl.h Leo Izen | ||
| 197 | libgsm.c Michel Bardiaux | 198 | libgsm.c Michel Bardiaux |
| 198 | libkvazaar.c Arttu Ylä-Outinen | 199 | libkvazaar.c Arttu Ylä-Outinen |
| 199 | libopenh264enc.c Martin Storsjo, Linjie Fu | 200 | libopenh264enc.c Martin Storsjo, Linjie Fu |
| @@ -240,6 +240,7 @@ External library support: · 239 unmodified lines | |||
| 240 | --enable-libiec61883 enable iec61883 via libiec61883 [no] | 240 | --enable-libiec61883 enable iec61883 via libiec61883 [no] |
| 241 | --enable-libilbc enable iLBC de/encoding via libilbc [no] | 241 | --enable-libilbc enable iLBC de/encoding via libilbc [no] |
| 242 | --enable-libjack enable JACK audio sound server [no] | 242 | --enable-libjack enable JACK audio sound server [no] |
| 243 | --enable-libjxl enable JPEG XL decoding via libjxl [no] | ||
| 243 | --enable-libklvanc enable Kernel Labs VANC processing [no] | 244 | --enable-libklvanc enable Kernel Labs VANC processing [no] |
| 244 | --enable-libkvazaar enable HEVC encoding via libkvazaar [no] | 245 | --enable-libkvazaar enable HEVC encoding via libkvazaar [no] |
| 245 | --enable-liblensfun enable lensfun lens correction [no] | 246 | --enable-liblensfun enable lensfun lens correction [no] |
| @@ -1833,6 +1834,7 @@ EXTERNAL_LIBRARY_LIST=" · 1587 unmodified lines | |||
| 1833 | libiec61883 | 1834 | libiec61883 |
| 1834 | libilbc | 1835 | libilbc |
| 1835 | libjack | 1836 | libjack |
| 1837 | libjxl | ||
| 1836 | libklvanc | 1838 | libklvanc |
| 1837 | libkvazaar | 1839 | libkvazaar |
| 1838 | libmodplug | 1840 | libmodplug |
| @@ -3331,6 +3333,7 @@ libgsm_ms_decoder_deps="libgsm" · 1492 unmodified lines | |||
| 3331 | libgsm_ms_encoder_deps="libgsm" | 3333 | libgsm_ms_encoder_deps="libgsm" |
| 3332 | libilbc_decoder_deps="libilbc" | 3334 | libilbc_decoder_deps="libilbc" |
| 3333 | libilbc_encoder_deps="libilbc" | 3335 | libilbc_encoder_deps="libilbc" |
| 3336 | libjxl_decoder_deps="libjxl libjxl_threads" | ||
| 3334 | libkvazaar_encoder_deps="libkvazaar" | 3337 | libkvazaar_encoder_deps="libkvazaar" |
| 3335 | libmodplug_demuxer_deps="libmodplug" | 3338 | libmodplug_demuxer_deps="libmodplug" |
| 3336 | libmp3lame_encoder_deps="libmp3lame" | 3339 | libmp3lame_encoder_deps="libmp3lame" |
| @@ -6545,6 +6548,8 @@ enabled libgsm && { for gsm_hdr in "gsm.h" "gsm/gsm.h"; do · 3208 unmodified lines | |||
| 6545 | check_lib libgsm "${gsm_hdr}" gsm_create -lgsm && break; | 6548 | check_lib libgsm "${gsm_hdr}" gsm_create -lgsm && break; |
| 6546 | done || die "ERROR: libgsm not found"; } | 6549 | done || die "ERROR: libgsm not found"; } |
| 6547 | enabled libilbc && require libilbc ilbc.h WebRtcIlbcfix_InitDecode -lilbc $pthreads_extralibs | 6550 | enabled libilbc && require libilbc ilbc.h WebRtcIlbcfix_InitDecode -lilbc $pthreads_extralibs |
| 6551 | enabled libjxl && require_pkg_config libjxl "libjxl >= 0.7.0" jxl/decode.h JxlDecoderVersion && | ||
| 6552 | require_pkg_config libjxl_threads "libjxl_threads >= 0.7.0" jxl/thread_parallel_runner.h JxlThreadParallelRunner | ||
| 6548 | enabled libklvanc && require libklvanc libklvanc/vanc.h klvanc_context_create -lklvanc | 6553 | enabled libklvanc && require libklvanc libklvanc/vanc.h klvanc_context_create -lklvanc |
| 6549 | enabled libkvazaar && require_pkg_config libkvazaar "kvazaar >= 0.8.1" kvazaar.h kvz_api_get | 6554 | enabled libkvazaar && require_pkg_config libkvazaar "kvazaar >= 0.8.1" kvazaar.h kvz_api_get |
| 6550 | enabled liblensfun && require_pkg_config liblensfun lensfun lensfun.h lf_db_new | 6555 | enabled liblensfun && require_pkg_config liblensfun lensfun lensfun.h lf_db_new |
| @@ -171,6 +171,13 @@ Go to @url{https://github.com/TimothyGu/libilbc} and follow the instructions for · 170 unmodified lines | |||
| 171 | installing the library. Then pass @code{--enable-libilbc} to configure to | 171 | installing the library. Then pass @code{--enable-libilbc} to configure to |
| 172 | enable it. | 172 | enable it. |
| 173 | 173 | ||
| 174 | @section libjxl | ||
| 175 | |||
| 176 | JPEG XL is an image format intended to fully replace legacy JPEG for an extended | ||
| 177 | period of life. See @url{https://jpegxl.info/} for more information, and see | ||
| 178 | @url{https://github.com/libjxl/libjxl} for the library source. You can pass | ||
| 179 | @code{--enable-libjxl} to configure in order enable the libjxl wrapper. | ||
| 180 | |||
| 174 | @section libvpx | 181 | @section libvpx |
| 175 | 182 | ||
| 176 | FFmpeg can make use of the libvpx library for VP8/VP9 decoding and encoding. | 183 | FFmpeg can make use of the libvpx library for VP8/VP9 decoding and encoding. |
| @@ -1062,6 +1062,7 @@ OBJS-$(CONFIG_LIBGSM_MS_DECODER) += libgsmdec.o · 1061 unmodified lines | |||
| 1062 | OBJS-$(CONFIG_LIBGSM_MS_ENCODER) += libgsmenc.o | 1062 | OBJS-$(CONFIG_LIBGSM_MS_ENCODER) += libgsmenc.o |
| 1063 | OBJS-$(CONFIG_LIBILBC_DECODER) += libilbc.o | 1063 | OBJS-$(CONFIG_LIBILBC_DECODER) += libilbc.o |
| 1064 | OBJS-$(CONFIG_LIBILBC_ENCODER) += libilbc.o | 1064 | OBJS-$(CONFIG_LIBILBC_ENCODER) += libilbc.o |
| 1065 | OBJS-$(CONFIG_LIBJXL_DECODER) += libjxldec.o libjxl.o | ||
| 1065 | OBJS-$(CONFIG_LIBKVAZAAR_ENCODER) += libkvazaar.o | 1066 | OBJS-$(CONFIG_LIBKVAZAAR_ENCODER) += libkvazaar.o |
| 1066 | OBJS-$(CONFIG_LIBMP3LAME_ENCODER) += libmp3lame.o | 1067 | OBJS-$(CONFIG_LIBMP3LAME_ENCODER) += libmp3lame.o |
| 1067 | OBJS-$(CONFIG_LIBOPENCORE_AMRNB_DECODER) += libopencore-amr.o | 1068 | OBJS-$(CONFIG_LIBOPENCORE_AMRNB_DECODER) += libopencore-amr.o |
| @@ -751,6 +751,7 @@ extern const FFCodec ff_libgsm_ms_encoder; · 750 unmodified lines | |||
| 751 | extern const FFCodec ff_libgsm_ms_decoder; | 751 | extern const FFCodec ff_libgsm_ms_decoder; |
| 752 | extern const FFCodec ff_libilbc_encoder; | 752 | extern const FFCodec ff_libilbc_encoder; |
| 753 | extern const FFCodec ff_libilbc_decoder; | 753 | extern const FFCodec ff_libilbc_decoder; |
| 754 | extern const FFCodec ff_libjxl_decoder; | ||
| 754 | extern const FFCodec ff_libmp3lame_encoder; | 755 | extern const FFCodec ff_libmp3lame_encoder; |
| 755 | extern const FFCodec ff_libopencore_amrnb_encoder; | 756 | extern const FFCodec ff_libopencore_amrnb_encoder; |
| 756 | extern const FFCodec ff_libopencore_amrnb_decoder; | 757 | extern const FFCodec ff_libopencore_amrnb_decoder; |
| @@ -0,0 +1,70 @@ | |||
| 1 | /* | ||
| 2 | * JPEG XL de/encoding via libjxl, common support implementation | ||
| 3 | * Copyright (c) 2021 Leo Izen <leo.izen@gmail.com> | ||
| 4 | * | ||
| 5 | * This file is part of FFmpeg. | ||
| 6 | * | ||
| 7 | * FFmpeg is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with FFmpeg; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | /** | ||
| 23 | * @file | ||
| 24 | * JPEG XL via libjxl common support implementation | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include "libavutil/cpu.h" | ||
| 28 | #include "libavutil/mem.h" | ||
| 29 | |||
| 30 | #include <jxl/memory_manager.h> | ||
| 31 | #include "libjxl.h" | ||
| 32 | |||
| 33 | size_t ff_libjxl_get_threadcount(int threads) | ||
| 34 | { | ||
| 35 | if (threads <= 0) | ||
| 36 | return av_cpu_count(); | ||
| 37 | if (threads == 1) | ||
| 38 | return 0; | ||
| 39 | return threads; | ||
| 40 | } | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Wrapper around av_malloc used as a jpegxl_alloc_func. | ||
| 44 | * | ||
| 45 | * @param opaque opaque pointer for jpegxl_alloc_func, always ignored | ||
| 46 | * @param size Size in bytes for the memory block to be allocated | ||
| 47 | * @return Pointer to the allocated block, or `NULL` if it cannot be allocated | ||
| 48 | */ | ||
| 49 | static void *libjxl_av_malloc(void *opaque, size_t size) | ||
| 50 | { | ||
| 51 | return av_malloc(size); | ||
| 52 | } | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Wrapper around av_free used as a jpegxl_free_func. | ||
| 56 | * | ||
| 57 | * @param opaque opaque pointer for jpegxl_free_func, always ignored | ||
| 58 | * @param address Pointer to the allocated block, to free. `NULL` permitted as a no-op. | ||
| 59 | */ | ||
| 60 | static void libjxl_av_free(void *opaque, void *address) | ||
| 61 | { | ||
| 62 | av_free(address); | ||
| 63 | } | ||
| 64 | |||
| 65 | void ff_libjxl_init_memory_manager(JxlMemoryManager *manager) | ||
| 66 | { | ||
| 67 | manager->opaque = NULL; | ||
| 68 | manager->alloc = &libjxl_av_malloc; | ||
| 69 | manager->free = &libjxl_av_free; | ||
| 70 | } | ||
| @@ -0,0 +1,48 @@ | |||
| 1 | /* | ||
| 2 | * JPEG XL de/encoding via libjxl, common support header | ||
| 3 | * Copyright (c) 2021 Leo Izen <leo.izen@gmail.com> | ||
| 4 | * | ||
| 5 | * This file is part of FFmpeg. | ||
| 6 | * | ||
| 7 | * FFmpeg is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with FFmpeg; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | /** | ||
| 23 | * @file | ||
| 24 | * JPEG XL via libjxl common support header | ||
| 25 | */ | ||
| 26 | |||
| 27 | #ifndef AVCODEC_LIBJXL_H | ||
| 28 | #define AVCODEC_LIBJXL_H | ||
| 29 | |||
| 30 | #include <jxl/memory_manager.h> | ||
| 31 | |||
| 32 | /** | ||
| 33 | * Transform threadcount in ffmpeg to one used by libjxl. | ||
| 34 | * | ||
| 35 | * @param threads ffmpeg's threads AVOption | ||
| 36 | * @return thread count for libjxl's parallel runner | ||
| 37 | */ | ||
| 38 | size_t ff_libjxl_get_threadcount(int threads); | ||
| 39 | |||
| 40 | /** | ||
| 41 | * Initialize and populate a JxlMemoryManager | ||
| 42 | * with av_malloc() and av_free() so libjxl will use these | ||
| 43 | * functions. | ||
| 44 | * @param manager a pointer to a JxlMemoryManager struct | ||
| 45 | */ | ||
| 46 | void ff_libjxl_init_memory_manager(JxlMemoryManager *manager); | ||
| 47 | |||
| 48 | #endif /* AVCODEC_LIBJXL_H */ | ||
| @@ -0,0 +1,280 @@ | |||
| 1 | /* | ||
| 2 | * JPEG XL decoding support via libjxl | ||
| 3 | * Copyright (c) 2021 Leo Izen <leo.izen@gmail.com> | ||
| 4 | * | ||
| 5 | * This file is part of FFmpeg. | ||
| 6 | * | ||
| 7 | * FFmpeg is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with FFmpeg; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | /** | ||
| 23 | * @file | ||
| 24 | * JPEG XL decoder using libjxl | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include "libavutil/avassert.h" | ||
| 28 | #include "libavutil/buffer.h" | ||
| 29 | #include "libavutil/common.h" | ||
| 30 | #include "libavutil/error.h" | ||
| 31 | #include "libavutil/mem.h" | ||
| 32 | #include "libavutil/pixdesc.h" | ||
| 33 | #include "libavutil/pixfmt.h" | ||
| 34 | #include "libavutil/frame.h" | ||
| 35 | |||
| 36 | #include "avcodec.h" | ||
| 37 | #include "codec_internal.h" | ||
| 38 | #include "internal.h" | ||
| 39 | |||
| 40 | #include <jxl/decode.h> | ||
| 41 | #include <jxl/thread_parallel_runner.h> | ||
| 42 | #include "libjxl.h" | ||
| 43 | |||
| 44 | typedef struct LibJxlDecodeContext { | ||
| 45 | void *runner; | ||
| 46 | JxlDecoder *decoder; | ||
| 47 | JxlBasicInfo basic_info; | ||
| 48 | JxlPixelFormat jxl_pixfmt; | ||
| 49 | JxlDecoderStatus events; | ||
| 50 | AVBufferRef *iccp; | ||
| 51 | } LibJxlDecodeContext; | ||
| 52 | |||
| 53 | static int libjxl_init_jxl_decoder(AVCodecContext *avctx) | ||
| 54 | { | ||
| 55 | LibJxlDecodeContext *ctx = avctx->priv_data; | ||
| 56 | |||
| 57 | ctx->events = JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE | JXL_DEC_COLOR_ENCODING; | ||
| 58 | if (JxlDecoderSubscribeEvents(ctx->decoder, ctx->events) != JXL_DEC_SUCCESS) { | ||
| 59 | av_log(avctx, AV_LOG_ERROR, "Error subscribing to JXL events\n"); | ||
| 60 | return AVERROR_EXTERNAL; | ||
| 61 | } | ||
| 62 | |||
| 63 | if (JxlDecoderSetParallelRunner(ctx->decoder, JxlThreadParallelRunner, ctx->runner) != JXL_DEC_SUCCESS) { | ||
| 64 | av_log(avctx, AV_LOG_ERROR, "Failed to set JxlThreadParallelRunner\n"); | ||
| 65 | return AVERROR_EXTERNAL; | ||
| 66 | } | ||
| 67 | |||
| 68 | memset(&ctx->basic_info, 0, sizeof(JxlBasicInfo)); | ||
| 69 | memset(&ctx->jxl_pixfmt, 0, sizeof(JxlPixelFormat)); | ||
| 70 | |||
| 71 | return 0; | ||
| 72 | } | ||
| 73 | |||
| 74 | static av_cold int libjxl_decode_init(AVCodecContext *avctx) | ||
| 75 | { | ||
| 76 | LibJxlDecodeContext *ctx = avctx->priv_data; | ||
| 77 | JxlMemoryManager manager; | ||
| 78 | |||
| 79 | ff_libjxl_init_memory_manager(&manager); | ||
| 80 | ctx->decoder = JxlDecoderCreate(&manager); | ||
| 81 | if (!ctx->decoder) { | ||
| 82 | av_log(avctx, AV_LOG_ERROR, "Failed to create JxlDecoder\n"); | ||
| 83 | return AVERROR_EXTERNAL; | ||
| 84 | } | ||
| 85 | |||
| 86 | ctx->runner = JxlThreadParallelRunnerCreate(&manager, ff_libjxl_get_threadcount(avctx->thread_count)); | ||
| 87 | if (!ctx->runner) { | ||
| 88 | av_log(avctx, AV_LOG_ERROR, "Failed to create JxlThreadParallelRunner\n"); | ||
| 89 | return AVERROR_EXTERNAL; | ||
| 90 | } | ||
| 91 | |||
| 92 | return libjxl_init_jxl_decoder(avctx); | ||
| 93 | } | ||
| 94 | |||
| 95 | static enum AVPixelFormat libjxl_get_pix_fmt(AVCodecContext *avctx, JxlBasicInfo *basic_info, JxlPixelFormat *format) | ||
| 96 | { | ||
| 97 | format->endianness = JXL_NATIVE_ENDIAN; | ||
| 98 | format->num_channels = basic_info->num_color_channels + (basic_info->alpha_bits > 0); | ||
| 99 | /* Gray */ | ||
| 100 | if (basic_info->num_color_channels == 1) { | ||
| 101 | if (basic_info->bits_per_sample <= 8) { | ||
| 102 | format->data_type = JXL_TYPE_UINT8; | ||
| 103 | return basic_info->alpha_bits ? AV_PIX_FMT_YA8 : AV_PIX_FMT_GRAY8; | ||
| 104 | } | ||
| 105 | if (basic_info->exponent_bits_per_sample || basic_info->bits_per_sample > 16) { | ||
| 106 | if (basic_info->alpha_bits) | ||
| 107 | return AV_PIX_FMT_NONE; | ||
| 108 | format->data_type = JXL_TYPE_FLOAT; | ||
| 109 | return AV_PIX_FMT_GRAYF32; | ||
| 110 | } | ||
| 111 | format->data_type = JXL_TYPE_UINT16; | ||
| 112 | return basic_info->alpha_bits ? AV_PIX_FMT_YA16 : AV_PIX_FMT_GRAY16; | ||
| 113 | } | ||
| 114 | /* rgb only */ | ||
| 115 | /* libjxl only supports packed RGB and gray output at the moment */ | ||
| 116 | if (basic_info->num_color_channels == 3) { | ||
| 117 | if (basic_info->bits_per_sample <= 8) { | ||
| 118 | format->data_type = JXL_TYPE_UINT8; | ||
| 119 | return basic_info->alpha_bits ? AV_PIX_FMT_RGBA : AV_PIX_FMT_RGB24; | ||
| 120 | } | ||
| 121 | if (basic_info->bits_per_sample > 16) | ||
| 122 | av_log(avctx, AV_LOG_WARNING, "Downsampling larger integer to 16-bit via libjxl\n"); | ||
| 123 | if (basic_info->exponent_bits_per_sample) | ||
| 124 | av_log(avctx, AV_LOG_WARNING, "Downsampling float to 16-bit integer via libjxl\n"); | ||
| 125 | format->data_type = JXL_TYPE_UINT16; | ||
| 126 | return basic_info->alpha_bits ? AV_PIX_FMT_RGBA64 : AV_PIX_FMT_RGB48; | ||
| 127 | } | ||
| 128 | |||
| 129 | return AV_PIX_FMT_NONE; | ||
| 130 | } | ||
| 131 | |||
| 132 | static int libjxl_decode_frame(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *avpkt) | ||
| 133 | { | ||
| 134 | LibJxlDecodeContext *ctx = avctx->priv_data; | ||
| 135 | uint8_t *buf = avpkt->data; | ||
| 136 | size_t remaining = avpkt->size, iccp_len; | ||
| 137 | JxlDecoderStatus jret; | ||
| 138 | int ret; | ||
| 139 | *got_frame = 0; | ||
| 140 | |||
| 141 | while (1) { | ||
| 142 | |||
| 143 | jret = JxlDecoderSetInput(ctx->decoder, buf, remaining); | ||
| 144 | |||
| 145 | if (jret == JXL_DEC_ERROR) { | ||
| 146 | /* this should never happen here unless there's a bug in libjxl */ | ||
| 147 | av_log(avctx, AV_LOG_ERROR, "Unknown libjxl decode error\n"); | ||
| 148 | return AVERROR_EXTERNAL; | ||
| 149 | } | ||
| 150 | |||
| 151 | jret = JxlDecoderProcessInput(ctx->decoder); | ||
| 152 | /* | ||
| 153 | * JxlDecoderReleaseInput returns the number | ||
| 154 | * of bytes remaining to be read, rather than | ||
| 155 | * the number of bytes that it did read | ||
| 156 | */ | ||
| 157 | remaining = JxlDecoderReleaseInput(ctx->decoder); | ||
| 158 | buf = avpkt->data + avpkt->size - remaining; | ||
| 159 | |||
| 160 | switch(jret) { | ||
| 161 | case JXL_DEC_ERROR: | ||
| 162 | av_log(avctx, AV_LOG_ERROR, "Unknown libjxl decode error\n"); | ||
| 163 | return AVERROR_INVALIDDATA; | ||
| 164 | case JXL_DEC_NEED_MORE_INPUT: | ||
| 165 | if (remaining == 0) { | ||
| 166 | av_log(avctx, AV_LOG_ERROR, "Unexpected end of JXL codestream\n"); | ||
| 167 | return AVERROR_INVALIDDATA; | ||
| 168 | } | ||
| 169 | av_log(avctx, AV_LOG_DEBUG, "NEED_MORE_INPUT event emitted\n"); | ||
| 170 | continue; | ||
| 171 | case JXL_DEC_BASIC_INFO: | ||
| 172 | av_log(avctx, AV_LOG_DEBUG, "BASIC_INFO event emitted\n"); | ||
| 173 | if (JxlDecoderGetBasicInfo(ctx->decoder, &ctx->basic_info) != JXL_DEC_SUCCESS) { | ||
| 174 | /* | ||
| 175 | * this should never happen | ||
| 176 | * if it does it is likely a libjxl decoder bug | ||
| 177 | */ | ||
| 178 | av_log(avctx, AV_LOG_ERROR, "Bad libjxl basic info event\n"); | ||
| 179 | return AVERROR_EXTERNAL; | ||
| 180 | } | ||
| 181 | avctx->pix_fmt = libjxl_get_pix_fmt(avctx, &ctx->basic_info, &ctx->jxl_pixfmt); | ||
| 182 | if (avctx->pix_fmt == AV_PIX_FMT_NONE) { | ||
| 183 | av_log(avctx, AV_LOG_ERROR, "Bad libjxl pixel format\n"); | ||
| 184 | return AVERROR_EXTERNAL; | ||
| 185 | } | ||
| 186 | ret = ff_set_dimensions(avctx, ctx->basic_info.xsize, ctx->basic_info.ysize); | ||
| 187 | if (ret < 0) | ||
| 188 | return ret; | ||
| 189 | continue; | ||
| 190 | case JXL_DEC_COLOR_ENCODING: | ||
| 191 | av_log(avctx, AV_LOG_DEBUG, "COLOR_ENCODING event emitted\n"); | ||
| 192 | jret = JxlDecoderGetICCProfileSize(ctx->decoder, &ctx->jxl_pixfmt, JXL_COLOR_PROFILE_TARGET_ORIGINAL, &iccp_len); | ||
| 193 | if (jret == JXL_DEC_SUCCESS && iccp_len > 0) { | ||
| 194 | av_buffer_unref(&ctx->iccp); | ||
| 195 | ctx->iccp = av_buffer_alloc(iccp_len); | ||
| 196 | if (!ctx->iccp) | ||
| 197 | return AVERROR(ENOMEM); | ||
| 198 | jret = JxlDecoderGetColorAsICCProfile(ctx->decoder, &ctx->jxl_pixfmt, JXL_COLOR_PROFILE_TARGET_ORIGINAL, ctx->iccp->data, iccp_len); | ||
| 199 | if (jret != JXL_DEC_SUCCESS) | ||
| 200 | av_buffer_unref(&ctx->iccp); | ||
| 201 | } | ||
| 202 | continue; | ||
| 203 | case JXL_DEC_NEED_IMAGE_OUT_BUFFER: | ||
| 204 | av_log(avctx, AV_LOG_DEBUG, "NEED_IMAGE_OUT_BUFFER event emitted\n"); | ||
| 205 | ret = ff_get_buffer(avctx, frame, 0); | ||
| 206 | if (ret < 0) | ||
| 207 | return ret; | ||
| 208 | ctx->jxl_pixfmt.align = frame->linesize[0]; | ||
| 209 | if (JxlDecoderSetImageOutBuffer(ctx->decoder, &ctx->jxl_pixfmt, frame->data[0], frame->buf[0]->size) != JXL_DEC_SUCCESS) { | ||
| 210 | av_log(avctx, AV_LOG_ERROR, "Bad libjxl dec need image out buffer event\n"); | ||
| 211 | return AVERROR_EXTERNAL; | ||
| 212 | } | ||
| 213 | continue; | ||
| 214 | case JXL_DEC_FULL_IMAGE: | ||
| 215 | /* full image is one frame, even if animated */ | ||
| 216 | av_log(avctx, AV_LOG_DEBUG, "FULL_IMAGE event emitted\n"); | ||
| 217 | frame->pict_type = AV_PICTURE_TYPE_I; | ||
| 218 | frame->key_frame = 1; | ||
| 219 | if (ctx->iccp) { | ||
| 220 | AVFrameSideData *sd = av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_ICC_PROFILE, ctx->iccp); | ||
| 221 | if (!sd) | ||
| 222 | return AVERROR(ENOMEM); | ||
| 223 | /* ownership is transfered, and it is not ref-ed */ | ||
| 224 | ctx->iccp = NULL; | ||
| 225 | } | ||
| 226 | *got_frame = 1; | ||
| 227 | return avpkt->size - remaining; | ||
| 228 | case JXL_DEC_SUCCESS: | ||
| 229 | av_log(avctx, AV_LOG_DEBUG, "SUCCESS event emitted\n"); | ||
| 230 | /* | ||
| 231 | * The SUCCESS event isn't fired until after JXL_DEC_FULL_IMAGE. If this | ||
| 232 | * stream only contains one JXL image then JXL_DEC_SUCCESS will never fire. | ||
| 233 | * If the image2 sequence being decoded contains several JXL files, then | ||
| 234 | * libjxl will fire this event after the next AVPacket has been passed, | ||
| 235 | * which means the current packet is actually the next image in the sequence. | ||
| 236 | * This is why we reset the decoder and populate the packet data now, since | ||
| 237 | * this is the next packet and it has not been decoded yet. The decoder does | ||
| 238 | * have to be reset to allow us to use it for the next image, or libjxl | ||
| 239 | * will become very confused if the header information is not identical. | ||
| 240 | */ | ||
| 241 | JxlDecoderReset(ctx->decoder); | ||
| 242 | libjxl_init_jxl_decoder(avctx); | ||
| 243 | buf = avpkt->data; | ||
| 244 | remaining = avpkt->size; | ||
| 245 | continue; | ||
| 246 | default: | ||
| 247 | av_log(avctx, AV_LOG_ERROR, "Bad libjxl event: %d\n", jret); | ||
| 248 | return AVERROR_EXTERNAL; | ||
| 249 | } | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | static av_cold int libjxl_decode_close(AVCodecContext *avctx) | ||
| 254 | { | ||
| 255 | LibJxlDecodeContext *ctx = avctx->priv_data; | ||
| 256 | |||
| 257 | if (ctx->runner) | ||
| 258 | JxlThreadParallelRunnerDestroy(ctx->runner); | ||
| 259 | ctx->runner = NULL; | ||
| 260 | if (ctx->decoder) | ||
| 261 | JxlDecoderDestroy(ctx->decoder); | ||
| 262 | ctx->decoder = NULL; | ||
| 263 | av_buffer_unref(&ctx->iccp); | ||
| 264 | |||
| 265 | return 0; | ||
| 266 | } | ||
| 267 | |||
| 268 | const FFCodec ff_libjxl_decoder = { | ||
| 269 | .p.name = "libjxl", | ||
| 270 | .p.long_name = NULL_IF_CONFIG_SMALL("libjxl JPEG XL"), | ||
| 271 | .p.type = AVMEDIA_TYPE_VIDEO, | ||
| 272 | .p.id = AV_CODEC_ID_JPEGXL, | ||
| 273 | .priv_data_size = sizeof(LibJxlDecodeContext), | ||
| 274 | .init = libjxl_decode_init, | ||
| 275 | FF_CODEC_DECODE_CB(libjxl_decode_frame), | ||
| 276 | .close = libjxl_decode_close, | ||
| 277 | .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_OTHER_THREADS, | ||
| 278 | .caps_internal = FF_CODEC_CAP_AUTO_THREADS | FF_CODEC_CAP_INIT_CLEANUP, | ||
| 279 | .p.wrapper_name = "libjxl", | ||
| 280 | }; | ||