PR #21084 opened by James Almer (jamrial) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21084 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21084.patch
Starting with a native parser (simple header parsing to get dimensions and pixel format), a decoder and encoder using an external library, and a demuxer for raw bitstreams. >From 6c5c23d3c75fa16b4d46eebf21aa44cfe2be8f59 Mon Sep 17 00:00:00 2001 From: Tomasz Szumski <[email protected]> Date: Tue, 9 Jul 2024 10:04:16 +0200 Subject: [PATCH 1/4] avcodec/codec_id: add JPEG-XS Signed-off-by: James Almer <[email protected]> --- libavcodec/codec_desc.c | 9 +++++++++ libavcodec/codec_id.h | 1 + 2 files changed, 10 insertions(+) diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index c72271bfad..b3f4e73e1d 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -2000,6 +2000,15 @@ static const AVCodecDescriptor codec_descriptors[] = { .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, .profiles = NULL_IF_CONFIG_SMALL(ff_prores_raw_profiles), }, + { + .id = AV_CODEC_ID_JPEGXS, + .type = AVMEDIA_TYPE_VIDEO, + .name = "jpegxs", + .long_name = NULL_IF_CONFIG_SMALL("JPEG XS"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY | + AV_CODEC_PROP_LOSSLESS, + .mime_types= MT("image/jxs"), + }, /* various PCM "codecs" */ { diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h index 8c98ac6335..6529f0a6bc 100644 --- a/libavcodec/codec_id.h +++ b/libavcodec/codec_id.h @@ -331,6 +331,7 @@ enum AVCodecID { AV_CODEC_ID_JPEGXL_ANIM, AV_CODEC_ID_APV, AV_CODEC_ID_PRORES_RAW, + AV_CODEC_ID_JPEGXS, /* various PCM "codecs" */ AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs -- 2.49.1 >From 5a92be5fe64dbb7e086e66f12670127e406c2b93 Mon Sep 17 00:00:00 2001 From: James Almer <[email protected]> Date: Mon, 1 Dec 2025 22:59:01 -0300 Subject: [PATCH 2/4] avcodec: add a JPEG-XS parser Signed-off-by: James Almer <[email protected]> --- Changelog | 1 + libavcodec/Makefile | 1 + libavcodec/jpegxs.h | 37 +++++++ libavcodec/jpegxs_parser.c | 207 +++++++++++++++++++++++++++++++++++++ libavcodec/parsers.c | 1 + 5 files changed, 247 insertions(+) create mode 100644 libavcodec/jpegxs.h create mode 100644 libavcodec/jpegxs_parser.c diff --git a/Changelog b/Changelog index cda59ebc90..663c232e70 100644 --- a/Changelog +++ b/Changelog @@ -14,6 +14,7 @@ version <next>: - ProRes Vulkan hwaccel - DPX Vulkan hwaccel - Rockchip H.264/HEVC hardware encoder +- JPEG-XS parser version 8.0: diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 40e68116e8..5ed8a27b76 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1272,6 +1272,7 @@ OBJS-$(CONFIG_HDR_PARSER) += hdr_parser.o OBJS-$(CONFIG_IPU_PARSER) += ipu_parser.o OBJS-$(CONFIG_JPEG2000_PARSER) += jpeg2000_parser.o OBJS-$(CONFIG_JPEGXL_PARSER) += jpegxl_parser.o jpegxl_parse.o +OBJS-$(CONFIG_JPEGXS_PARSER) += jpegxs_parser.o OBJS-$(CONFIG_MISC4_PARSER) += misc4_parser.o OBJS-$(CONFIG_MJPEG_PARSER) += mjpeg_parser.o OBJS-$(CONFIG_MLP_PARSER) += mlp_parse.o mlp_parser.o mlp.o diff --git a/libavcodec/jpegxs.h b/libavcodec/jpegxs.h new file mode 100644 index 0000000000..951e2f0d69 --- /dev/null +++ b/libavcodec/jpegxs.h @@ -0,0 +1,37 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_JPEGXS_H +#define AVCODEC_JPEGXS_H + +enum { + JPEGXS_MARKER_SOC = 0xff10, // Start of codestream + JPEGXS_MARKER_EOC = 0xff11, // End of codestream + JPEGXS_MARKER_PIH = 0xff12, // Picture header + JPEGXS_MARKER_CDT = 0xff13, // Component table + JPEGXS_MARKER_WGT = 0xff14, // Weights table + JPEGXS_MARKER_COM = 0xff15, // Extension marker + JPEGXS_MARKER_NLT = 0xff16, // Nonlinearity marker + JPEGXS_MARKER_CWD = 0xff17, // Component-dependent wavelet decomposition marker + JPEGXS_MARKER_CTS = 0xff18, // Colour transformation specification marker + JPEGXS_MARKER_CRG = 0xff19, // Component registration marker + JPEGXS_MARKER_SLH = 0xff20, // Slice header + JPEGXS_MARKER_CAP = 0xff50, // Capabilities Marker +}; + +#endif /* AVCODEC_JPEGXS_H */ diff --git a/libavcodec/jpegxs_parser.c b/libavcodec/jpegxs_parser.c new file mode 100644 index 0000000000..a6a3d1fcce --- /dev/null +++ b/libavcodec/jpegxs_parser.c @@ -0,0 +1,207 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "bytestream.h" +#include "get_bits.h" +#include "jpegxs.h" +#include "parser.h" +#include "parser_internal.h" + +/** + * Find the end of the current frame in the bitstream. + * @return the position of the first byte of the next frame, or -1 + */ +static int jpegxs_find_frame_end(ParseContext *pc, const uint8_t *buf, + int buf_size) +{ + int pic_found, i = 0; + uint32_t state; + + pic_found = pc->frame_start_found; + state = pc->state; + + if (!pic_found) { + for (i = 0; i < buf_size; i++) { + state = (state << 8) | buf[i]; + if ((uint16_t)state == JPEGXS_MARKER_SOC) { + i++; + pic_found = 1; + break; + } + } + } + + if (pic_found) { + if (buf_size == 0) + return 0; + for(; i < buf_size; i++) { + state = (state << 8) | buf[i]; + if ((uint16_t)state == JPEGXS_MARKER_EOC) { + pc->frame_start_found = 0; + pc->state = -1; + return i + 1; + } + } + } + + pc->frame_start_found = pic_found; + pc->state = state; + return END_NOT_FOUND; +} + +static int jpegxs_parse_frame(AVCodecParserContext *s, AVCodecContext *avctx, + const uint8_t *buf, int buf_size) +{ + GetByteContext gbc; + GetBitContext gb; + int8_t bpc[3], log2_chroma_w[3], log2_chroma_h[3]; + int size, marker, components; + + s->key_frame = 1; + s->pict_type = AV_PICTURE_TYPE_I; + + if (buf_size < 4) + return 0; + + bytestream2_init(&gbc, buf, buf_size); + marker = bytestream2_get_be16(&gbc); + if (marker != JPEGXS_MARKER_SOC) + return 0; + + marker = bytestream2_get_be16(&gbc); + if (marker != JPEGXS_MARKER_CAP) + return 0; + size = bytestream2_get_be16(&gbc); + bytestream2_skip(&gbc, FFMAX(size - 2, 0)); + + marker = bytestream2_get_be16(&gbc); + if (marker != JPEGXS_MARKER_PIH) + return 0; + size = bytestream2_get_be16(&gbc); + bytestream2_skip(&gbc, 4); // Lcod + bytestream2_skip(&gbc, 2); // Ppih + bytestream2_skip(&gbc, 2); // Plev + size -= 8; + + s->width = bytestream2_get_be16(&gbc); + s->height = bytestream2_get_be16(&gbc); + size -= 4; + + bytestream2_skip(&gbc, 2); // Cw + bytestream2_skip(&gbc, 2); // Hsl + size -= 4; + + components = bytestream2_get_byte(&gbc); + if (components != 1 && components != 3) + return 0; + size--; + + bytestream2_skip(&gbc, FFMAX(size - 2, 0)); + + while (bytestream2_get_bytes_left(&gbc) > 0) { + marker = bytestream2_get_be16(&gbc); + + switch(marker) { + case JPEGXS_MARKER_CDT: + size = bytestream2_get_be16(&gbc); + init_get_bits8(&gb, gbc.buffer, FFMIN(FFMAX(size - 2, 0), bytestream2_get_bytes_left(&gbc))); + + for (int i = 0; i < components; i++) { + bpc[i] = get_bits(&gb, 8); + if (i && bpc[i] != bpc[i-1]) + return 0; + + log2_chroma_w[i] = get_bits(&gb, 4); + log2_chroma_h[i] = get_bits(&gb, 4); + + if (log2_chroma_h[i] > log2_chroma_w[i]) + return 0; + if (i == 2 && (log2_chroma_h[2] != log2_chroma_h[1] || + log2_chroma_w[2] != log2_chroma_w[1])) + return 0; + } + + switch (bpc[0]) { + case 8: + if (components == 1) s->format = AV_PIX_FMT_GRAY8; + else if (log2_chroma_w[1] == 1 && log2_chroma_h[1] == 1) s->format = AV_PIX_FMT_YUV444P; + else if (log2_chroma_w[1] == 2 && log2_chroma_h[1] == 1) s->format = AV_PIX_FMT_YUV422P; + else s->format = AV_PIX_FMT_YUV420P; + break; + case 10: + if (components == 1) s->format = AV_PIX_FMT_GRAY10; + else if (log2_chroma_w[1] == 1 && log2_chroma_h[1] == 1) s->format = AV_PIX_FMT_YUV444P10; + else if (log2_chroma_w[1] == 2 && log2_chroma_h[1] == 1) s->format = AV_PIX_FMT_YUV422P10; + else s->format = AV_PIX_FMT_YUV420P10; + break; + case 12: + if (components == 1) s->format = AV_PIX_FMT_GRAY12; + else if (log2_chroma_w[1] == 1 && log2_chroma_h[1] == 1) s->format = AV_PIX_FMT_YUV444P12; + else if (log2_chroma_w[1] == 2 && log2_chroma_h[1] == 1) s->format = AV_PIX_FMT_YUV422P12; + else s->format = AV_PIX_FMT_YUV420P12; + break; + case 14: + if (components == 1) s->format = AV_PIX_FMT_GRAY14; + else if (log2_chroma_w[1] == 1 && log2_chroma_h[1] == 1) s->format = AV_PIX_FMT_YUV444P14; + else if (log2_chroma_w[1] == 2 && log2_chroma_h[1] == 1) s->format = AV_PIX_FMT_YUV422P14; + else s->format = AV_PIX_FMT_YUV420P14; + break; + default: + s->format = AV_PIX_FMT_NONE; + break; + } + return 0; + default: + size = bytestream2_get_be16(&gbc); + bytestream2_skip(&gbc, FFMAX(size - 2, 0)); + break; + } + } + + return 0; +} + +static int jpegxsvideo_parse(AVCodecParserContext *s, + AVCodecContext *avctx, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size) +{ + ParseContext *pc = s->priv_data; + int next; + + next = jpegxs_find_frame_end(pc, buf, buf_size); + + if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) { + *poutbuf = NULL; + *poutbuf_size = 0; + return buf_size; + } + + jpegxs_parse_frame(s, avctx, buf, buf_size); + + *poutbuf = buf; + *poutbuf_size = buf_size; + return next; +} + +const FFCodecParser ff_jpegxs_parser = { + PARSER_CODEC_LIST(AV_CODEC_ID_JPEGXS), + .priv_data_size = sizeof(ParseContext), + .parse = jpegxsvideo_parse, + .close = ff_parse_close, +}; diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c index cae1f7213e..4561f6eb3d 100644 --- a/libavcodec/parsers.c +++ b/libavcodec/parsers.c @@ -77,6 +77,7 @@ extern const FFCodecParser ff_hdr_parser; extern const FFCodecParser ff_ipu_parser; extern const FFCodecParser ff_jpeg2000_parser; extern const FFCodecParser ff_jpegxl_parser; +extern const FFCodecParser ff_jpegxs_parser; extern const FFCodecParser ff_misc4_parser; extern const FFCodecParser ff_mjpeg_parser; extern const FFCodecParser ff_mlp_parser; -- 2.49.1 >From 758ccd447ef24617395b74e5bf61683e38038ec2 Mon Sep 17 00:00:00 2001 From: Tomasz Szumski <[email protected]> Date: Tue, 24 Oct 2023 12:42:00 +0200 Subject: [PATCH 3/4] avcodec: add JPEG-XS decoder and encoder using libsvtjpegxs Co-Authored-by: James Almer <[email protected]> Signed-off-by: James Almer <[email protected]> --- Changelog | 1 + configure | 5 + libavcodec/Makefile | 2 + libavcodec/allcodecs.c | 2 + libavcodec/libsvtjpegxsdec.c | 288 +++++++++++++++++++++++++++++++++ libavcodec/libsvtjpegxsenc.c | 298 +++++++++++++++++++++++++++++++++++ 6 files changed, 596 insertions(+) create mode 100644 libavcodec/libsvtjpegxsdec.c create mode 100644 libavcodec/libsvtjpegxsenc.c diff --git a/Changelog b/Changelog index 663c232e70..e98d0db07d 100644 --- a/Changelog +++ b/Changelog @@ -15,6 +15,7 @@ version <next>: - DPX Vulkan hwaccel - Rockchip H.264/HEVC hardware encoder - JPEG-XS parser +- JPEG-XS decoder and encoder through libsvtjpegxs version 8.0: diff --git a/configure b/configure index 883539e361..98841a79fb 100755 --- a/configure +++ b/configure @@ -281,6 +281,7 @@ External library support: --enable-libsrt enable Haivision SRT protocol via libsrt [no] --enable-libssh enable SFTP protocol via libssh [no] --enable-libsvtav1 enable AV1 encoding via SVT [no] + --enable-libsvtjpegxs enable JPEGXS encoding/decoding via SVT [no] --enable-libtensorflow enable TensorFlow as a DNN module backend for DNN based filters like sr [no] --enable-libtesseract enable Tesseract, needed for ocr filter [no] @@ -2010,6 +2011,7 @@ EXTERNAL_LIBRARY_LIST=" libsrt libssh libsvtav1 + libsvtjpegxs libtensorflow libtesseract libtheora @@ -3702,6 +3704,8 @@ libspeex_decoder_deps="libspeex" libspeex_encoder_deps="libspeex" libspeex_encoder_select="audio_frame_queue" libsvtav1_encoder_deps="libsvtav1" +libsvtjpegxs_encoder_deps="libsvtjpegxs" +libsvtjpegxs_decoder_deps="libsvtjpegxs" libsvtav1_encoder_select="dovi_rpuenc" libtheora_encoder_deps="libtheora" libtwolame_encoder_deps="libtwolame" @@ -7247,6 +7251,7 @@ enabled libssh && require_pkg_config libssh "libssh >= 0.6.0" libssh/ enabled libspeex && require_pkg_config libspeex speex speex/speex.h speex_decoder_init enabled libsrt && require_pkg_config libsrt "srt >= 1.3.0" srt/srt.h srt_socket enabled libsvtav1 && require_pkg_config libsvtav1 "SvtAv1Enc >= 0.9.0" EbSvtAv1Enc.h svt_av1_enc_init_handle +enabled libsvtjpegxs && require_pkg_config libsvtjpegxs "SvtJpegxs >= 0.10.0" SvtJpegxsEnc.h svt_jpeg_xs_encoder_init enabled libtensorflow && require libtensorflow tensorflow/c/c_api.h TF_Version -ltensorflow enabled libtesseract && require_pkg_config libtesseract tesseract tesseract/capi.h TessBaseAPICreate enabled libtheora && require libtheora theora/theoraenc.h th_info_init -ltheoraenc -ltheoradec -logg diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 5ed8a27b76..28ad85afb4 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1206,6 +1206,8 @@ OBJS-$(CONFIG_LIBSHINE_ENCODER) += libshine.o OBJS-$(CONFIG_LIBSPEEX_DECODER) += libspeexdec.o OBJS-$(CONFIG_LIBSPEEX_ENCODER) += libspeexenc.o OBJS-$(CONFIG_LIBSVTAV1_ENCODER) += libsvtav1.o +OBJS-$(CONFIG_LIBSVTJPEGXS_DECODER) += libsvtjpegxsdec.o +OBJS-$(CONFIG_LIBSVTJPEGXS_ENCODER) += libsvtjpegxsenc.o OBJS-$(CONFIG_LIBTHEORA_ENCODER) += libtheoraenc.o OBJS-$(CONFIG_LIBTWOLAME_ENCODER) += libtwolame.o OBJS-$(CONFIG_LIBUAVS3D_DECODER) += libuavs3d.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index a335e2bb82..042b07c895 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -815,6 +815,8 @@ extern const FFCodec ff_libshine_encoder; extern const FFCodec ff_libspeex_encoder; extern const FFCodec ff_libspeex_decoder; extern const FFCodec ff_libsvtav1_encoder; +extern const FFCodec ff_libsvtjpegxs_encoder; +extern const FFCodec ff_libsvtjpegxs_decoder; extern const FFCodec ff_libtheora_encoder; extern const FFCodec ff_libtwolame_encoder; extern const FFCodec ff_libuavs3d_decoder; diff --git a/libavcodec/libsvtjpegxsdec.c b/libavcodec/libsvtjpegxsdec.c new file mode 100644 index 0000000000..373bf26dab --- /dev/null +++ b/libavcodec/libsvtjpegxsdec.c @@ -0,0 +1,288 @@ +/* + * Copyright(c) 2024 Intel Corporation + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* +* Copyright(c) 2024 Intel Corporation +* SPDX - License - Identifier: BSD - 2 - Clause - Patent +*/ + +#include <SvtJpegxsDec.h> + +#include "libavutil/mem.h" +#include "libavutil/common.h" +#include "libavutil/cpu.h" +#include "libavutil/imgutils.h" +#include "libavutil/avassert.h" + +#include "avcodec.h" +#include "codec_internal.h" +#include "decode.h" +#include "profiles.h" + +typedef struct SvtJpegXsDecodeContext { + AVClass* class; + svt_jpeg_xs_image_config_t config; + svt_jpeg_xs_decoder_api_t decoder; + uint32_t decoder_initialized; + + /*0- AVPacket* avpkt have full frame*/ + /*1- AVPacket* avpkt have chunk of frame, need another buffer to merge packets*/ + uint32_t chunk_decoding; + uint32_t frame_size; + uint32_t buffer_filled_len; + uint8_t* bitstream_buffer; + int proxy_mode; +} SvtJpegXsDecodeContext; + +static int set_pix_fmt(AVCodecContext* avctx, svt_jpeg_xs_image_config_t config) +{ + int ret = 0; + + switch (config.format) { + case COLOUR_FORMAT_PLANAR_YUV420: + if (config.bit_depth == 8) + avctx->pix_fmt = AV_PIX_FMT_YUV420P; + else if (config.bit_depth == 10) + avctx->pix_fmt = AV_PIX_FMT_YUV420P10LE; + else if (config.bit_depth == 12) + avctx->pix_fmt = AV_PIX_FMT_YUV420P12LE; + else + avctx->pix_fmt = AV_PIX_FMT_YUV420P14LE; + break; + case COLOUR_FORMAT_PLANAR_YUV422: + if (config.bit_depth == 8) + avctx->pix_fmt = AV_PIX_FMT_YUV422P; + else if (config.bit_depth == 10) + avctx->pix_fmt = AV_PIX_FMT_YUV422P10LE; + else if (config.bit_depth == 12) + avctx->pix_fmt = AV_PIX_FMT_YUV422P12LE; + else + avctx->pix_fmt = AV_PIX_FMT_YUV422P14LE; + break; + case COLOUR_FORMAT_PLANAR_YUV444_OR_RGB: + if (config.bit_depth == 8) + avctx->pix_fmt = AV_PIX_FMT_YUV444P; + else if (config.bit_depth == 10) + avctx->pix_fmt = AV_PIX_FMT_YUV444P10LE; + else if (config.bit_depth == 12) + avctx->pix_fmt = AV_PIX_FMT_YUV444P12LE; + else + avctx->pix_fmt = AV_PIX_FMT_YUV444P14LE; + break; + default: + av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format.\n"); + ret = AVERROR_INVALIDDATA; + break; + } + + return ret; +} + +static int svt_jpegxs_dec_decode(AVCodecContext* avctx, AVFrame* picture, int* got_frame, AVPacket* avpkt) +{ + SvtJpegXsDecodeContext* svt_dec = avctx->priv_data; + SvtJxsErrorType_t err = SvtJxsErrorNone; + int ret; + svt_jpeg_xs_frame_t dec_input; + svt_jpeg_xs_frame_t dec_output; + uint32_t pixel_size; + + if (!svt_dec->decoder_initialized) { + err = svt_jpeg_xs_decoder_get_single_frame_size_with_proxy( + avpkt->data, avpkt->size, NULL, &svt_dec->frame_size, 1 /*quick search*/, svt_dec->decoder.proxy_mode); + if (err) { + av_log(avctx, AV_LOG_ERROR, "svt_jpeg_xs_decoder_get_single_frame_size_with_proxy failed, err=%d\n", err); + return err; + } + if (avpkt->size < svt_dec->frame_size) { + svt_dec->chunk_decoding = 1; + svt_dec->bitstream_buffer = av_malloc(svt_dec->frame_size); + if (!svt_dec->bitstream_buffer) { + av_log(avctx, AV_LOG_ERROR, "Failed to allocate svt_dec->bitstream_buffer.\n"); + return AVERROR(ENOMEM); + } + av_log(avctx, AV_LOG_DEBUG, "svt_jpegxs_dec_decode, bitstream_size=%d, chunk = %d\n", svt_dec->frame_size, avpkt->size); + } + if (avpkt->size > svt_dec->frame_size) { + av_log(avctx, AV_LOG_ERROR, "Single packet have data for more than one frame.\n"); + return AVERROR_EXTERNAL; + } + + err = svt_jpeg_xs_decoder_init(SVT_JPEGXS_API_VER_MAJOR, SVT_JPEGXS_API_VER_MINOR, + &svt_dec->decoder, avpkt->data, avpkt->size, &svt_dec->config); + if (err) { + av_log(avctx, AV_LOG_ERROR, "svt_jpeg_xs_decoder_init failed, err=%d\n", err); + return err; + } + + ret = set_pix_fmt(avctx, svt_dec->config); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "set_pix_fmt failed, err=%d\n", ret); + return ret; + } + + ret = ff_set_dimensions(avctx, svt_dec->config.width, svt_dec->config.height); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "ff_set_dimensions failed, err=%d\n", ret); + return ret; + } + + svt_dec->decoder_initialized = 1; + } + + if (svt_dec->chunk_decoding) { + uint8_t* bitstrream_addr = svt_dec->bitstream_buffer + svt_dec->buffer_filled_len; + int bytes_to_copy = avpkt->size; + //Do not copy more data than allocation + if ((bytes_to_copy + svt_dec->buffer_filled_len) > svt_dec->frame_size) { + bytes_to_copy = svt_dec->frame_size - svt_dec->buffer_filled_len; + } + + memcpy(bitstrream_addr, avpkt->data, bytes_to_copy); + svt_dec->buffer_filled_len += avpkt->size; + if (svt_dec->buffer_filled_len >= svt_dec->frame_size) { + dec_input.bitstream.buffer = svt_dec->bitstream_buffer; + dec_input.bitstream.allocation_size = svt_dec->frame_size; + dec_input.bitstream.used_size = svt_dec->frame_size; + } else { + *got_frame = 0; + return avpkt->size; + } + } else { + dec_input.bitstream.buffer = avpkt->data; + dec_input.bitstream.allocation_size = avpkt->size; + dec_input.bitstream.used_size = avpkt->size; + } + dec_input.user_prv_ctx_ptr = avpkt; + + ret = ff_get_buffer(avctx, picture, 0); + if (ret < 0) + return ret; + + pixel_size = svt_dec->config.bit_depth <= 8 ? 1 : 2; + + for (int comp = 0; comp < svt_dec->config.components_num; comp++) { + dec_input.image.data_yuv[comp] = picture->data[comp]; + dec_input.image.stride[comp] = picture->linesize[comp]/pixel_size; + dec_input.image.alloc_size[comp] = picture->linesize[comp] * svt_dec->config.components[comp].height; + } + + err = svt_jpeg_xs_decoder_send_frame(&svt_dec->decoder, &dec_input, 1 /*blocking*/); + if (err) { + av_log(avctx, AV_LOG_ERROR, "svt_jpeg_xs_decoder_send_frame failed, err=%d\n", err); + return err; + } + + err = svt_jpeg_xs_decoder_get_frame(&svt_dec->decoder, &dec_output, 1 /*blocking*/); + if (err == SvtJxsErrorDecoderConfigChange) { + av_log(avctx, AV_LOG_ERROR, "svt_jpeg_xs_decoder_get_frame return SvtJxsErrorDecoderConfigChange\n"); + return AVERROR_INPUT_CHANGED; + } + if (err) { + av_log(avctx, AV_LOG_ERROR, "svt_jpeg_xs_decoder_get_frame failed, err=%d\n", err); + return err; + } + + if (dec_output.user_prv_ctx_ptr != avpkt) { + av_log(avctx, AV_LOG_ERROR, "Returned different user_prv_ctx_ptr than expected\n"); + return AVERROR_EXTERNAL; + } + + //Copy leftover from AVPacket if it contain data from two frames + if (svt_dec->chunk_decoding) { + int bytes_to_copy = svt_dec->buffer_filled_len % svt_dec->frame_size; + int packet_offset = avpkt->size - bytes_to_copy; + uint8_t* packet_addr = avpkt->data + packet_offset; + + memcpy(svt_dec->bitstream_buffer, packet_addr, bytes_to_copy); + svt_dec->buffer_filled_len = bytes_to_copy; + } + + *got_frame = 1; + + return avpkt->size; +} + +static av_cold int svt_jpegxs_dec_free(AVCodecContext* avctx) +{ + SvtJpegXsDecodeContext* svt_dec = avctx->priv_data; + + svt_jpeg_xs_decoder_close(&svt_dec->decoder); + av_freep(&svt_dec->bitstream_buffer); + + return 0; +} + +static av_cold int svt_jpegxs_dec_init(AVCodecContext* avctx) +{ + SvtJpegXsDecodeContext* svt_dec = avctx->priv_data; + + if (av_log_get_level() < AV_LOG_DEBUG) + svt_dec->decoder.verbose = VERBOSE_ERRORS; + else if (av_log_get_level() == AV_LOG_DEBUG) + svt_dec->decoder.verbose = VERBOSE_SYSTEM_INFO; + else + svt_dec->decoder.verbose = VERBOSE_WARNINGS; + + if (svt_dec->proxy_mode == 1) + svt_dec->decoder.proxy_mode = proxy_mode_half; + else if (svt_dec->proxy_mode == 2) + svt_dec->decoder.proxy_mode = proxy_mode_quarter; + else + svt_dec->decoder.proxy_mode = proxy_mode_full; + + svt_dec->decoder.threads_num = FFMIN(avctx->thread_count ? avctx->thread_count : av_cpu_count(), 64); + svt_dec->decoder.use_cpu_flags = CPU_FLAGS_ALL; + + return 0; +} + +#define OFFSET(x) offsetof(SvtJpegXsDecodeContext, x) +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM +static const AVOption svtjpegxs_dec_options[] = { + { "proxy_mode", "Resolution scaling mode", OFFSET(proxy_mode), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 2, VE, .unit = "proxy_mode" }, + { "full", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, VE, .unit = "proxy_mode" }, + { "half", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1}, INT_MIN, INT_MAX, VE, .unit = "proxy_mode" }, + { "quarter", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2}, INT_MIN, INT_MAX, VE, .unit = "proxy_mode" }, + {NULL}, +}; + +static const AVClass svtjpegxs_dec_class = { + .class_name = "libsvtjpegxsdec", + .item_name = av_default_item_name, + .option = svtjpegxs_dec_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFCodec ff_libsvtjpegxs_decoder = { + .p.name = "libsvtjpegxs", + CODEC_LONG_NAME("SVT JPEG XS(Scalable Video Technology for JPEG XS) decoder"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_JPEGXS, + .priv_data_size = sizeof(SvtJpegXsDecodeContext), + .init = svt_jpegxs_dec_init, + .close = svt_jpegxs_dec_free, + FF_CODEC_DECODE_CB(svt_jpegxs_dec_decode), + .p.capabilities = AV_CODEC_CAP_OTHER_THREADS | AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | + FF_CODEC_CAP_AUTO_THREADS, + .p.wrapper_name = "libsvtjpegxs", + .p.priv_class = &svtjpegxs_dec_class, +}; diff --git a/libavcodec/libsvtjpegxsenc.c b/libavcodec/libsvtjpegxsenc.c new file mode 100644 index 0000000000..d296e03170 --- /dev/null +++ b/libavcodec/libsvtjpegxsenc.c @@ -0,0 +1,298 @@ +/* + * Copyright(c) 2024 Intel Corporation + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* +* Copyright(c) 2024 Intel Corporation +* SPDX - License - Identifier: BSD - 2 - Clause - Patent +*/ + +#include <SvtJpegxsEnc.h> + +#include "libavutil/common.h" +#include "libavutil/cpu.h" +#include "libavutil/imgutils.h" +#include "libavutil/rational.h" + +#include "avcodec.h" +#include "codec_internal.h" +#include "encode.h" +#include "profiles.h" + +typedef struct SvtJpegXsEncodeContext { + AVClass* class; + + int decomp_v; + int decomp_h; + int quant; + int coding_signs_handling; + int coding_significance; + int coding_vpred; + + svt_jpeg_xs_encoder_api_t encoder; + int bitstream_frame_size; +} SvtJpegXsEncodeContext; + +static int svt_jpegxs_enc_encode(AVCodecContext* avctx, AVPacket* pkt, + const AVFrame* frame, int* got_packet) +{ + SvtJpegXsEncodeContext* svt_enc = avctx->priv_data; + + svt_jpeg_xs_bitstream_buffer_t out_buf; + svt_jpeg_xs_image_buffer_t in_buf; + svt_jpeg_xs_frame_t enc_input; + svt_jpeg_xs_frame_t enc_output; + + SvtJxsErrorType_t err = SvtJxsErrorNone; + uint32_t pixel_size = svt_enc->encoder.input_bit_depth <= 8 ? 1 : 2; + + int ret = ff_get_encode_buffer(avctx, pkt, svt_enc->bitstream_frame_size, 0); + if (ret < 0) + return ret; + + out_buf.buffer = pkt->data;// output bitstream ptr + out_buf.allocation_size = pkt->size;// output bitstream size + out_buf.used_size = 0; + + for (int comp = 0; comp < 3; comp++) { + // svt-jpegxs require stride in pixel's not in bytes, this means that for 10 bit-depth, stride is half the linesize + in_buf.stride[comp] = frame->linesize[comp] / pixel_size; + in_buf.data_yuv[comp] = frame->data[comp]; + in_buf.alloc_size[comp] = in_buf.stride[comp] * svt_enc->encoder.source_height * pixel_size; + } + + enc_input.bitstream = out_buf; + enc_input.image = in_buf; + enc_input.user_prv_ctx_ptr = pkt; + + err = svt_jpeg_xs_encoder_send_picture(&svt_enc->encoder, &enc_input, 1 /*blocking*/); + if (err != SvtJxsErrorNone) { + av_log(avctx, AV_LOG_ERROR, "svt_jpeg_xs_encoder_send_picture failed\n"); + return AVERROR_EXTERNAL; + } + + err = svt_jpeg_xs_encoder_get_packet(&svt_enc->encoder, &enc_output, 1 /*blocking*/); + if (err != SvtJxsErrorNone) { + av_log(avctx, AV_LOG_ERROR, "svt_jpeg_xs_encoder_get_packet failed\n"); + return AVERROR_EXTERNAL; + } + + if (enc_output.user_prv_ctx_ptr != pkt) { + av_log(avctx, AV_LOG_ERROR, "Returned different user_prv_ctx_ptr than expected\n"); + return AVERROR_EXTERNAL; + } + + pkt->size = enc_output.bitstream.used_size; + + *got_packet = 1; + + return 0; +} + +static av_cold int svt_jpegxs_enc_free(AVCodecContext* avctx) { + SvtJpegXsEncodeContext* svt_enc = avctx->priv_data; + + svt_jpeg_xs_encoder_close(&svt_enc->encoder); + + return 0; +} + +static int set_pix_fmt(AVCodecContext* avctx, svt_jpeg_xs_encoder_api_t *encoder) +{ + switch (avctx->pix_fmt) { + case AV_PIX_FMT_YUV420P: + encoder->input_bit_depth = 8; + encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV420; + return 0; + case AV_PIX_FMT_YUV422P: + encoder->input_bit_depth = 8; + encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV422; + return 0; + case AV_PIX_FMT_YUV444P: + encoder->input_bit_depth = 8; + encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV444_OR_RGB; + return 0; + case AV_PIX_FMT_YUV420P10LE: + encoder->input_bit_depth = 10; + encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV420; + return 0; + case AV_PIX_FMT_YUV422P10LE: + encoder->input_bit_depth = 10; + encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV422; + return 0; + case AV_PIX_FMT_YUV444P10LE: + encoder->input_bit_depth = 10; + encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV444_OR_RGB; + return 0; + case AV_PIX_FMT_YUV420P12LE: + encoder->input_bit_depth = 12; + encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV420; + return 0; + case AV_PIX_FMT_YUV422P12LE: + encoder->input_bit_depth = 12; + encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV422; + return 0; + case AV_PIX_FMT_YUV444P12LE: + encoder->input_bit_depth = 12; + encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV444_OR_RGB; + return 0; + case AV_PIX_FMT_YUV420P14LE: + encoder->input_bit_depth = 14; + encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV420; + return 0; + case AV_PIX_FMT_YUV422P14LE: + encoder->input_bit_depth = 14; + encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV422; + return 0; + case AV_PIX_FMT_YUV444P14LE: + encoder->input_bit_depth = 14; + encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV444_OR_RGB; + return 0; + default: + break; + } + av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format.\n"); + return AVERROR_INVALIDDATA; +} + +static av_cold int svt_jpegxs_enc_init(AVCodecContext* avctx) { + SvtJpegXsEncodeContext* svt_enc = avctx->priv_data; + AVRational bpp; + SvtJxsErrorType_t err; + + err = svt_jpeg_xs_encoder_load_default_parameters(SVT_JPEGXS_API_VER_MAJOR, SVT_JPEGXS_API_VER_MINOR, &(svt_enc->encoder)); + if (err != SvtJxsErrorNone) { + av_log(avctx, AV_LOG_ERROR, "svt_jpeg_xs_encoder_load_default_parameters failed\n"); + return AVERROR_EXTERNAL; + } + + svt_enc->encoder.source_width = avctx->width; + svt_enc->encoder.source_height = avctx->height; + + set_pix_fmt(avctx, &(svt_enc->encoder)); + + svt_enc->encoder.threads_num = FFMIN(avctx->thread_count ? avctx->thread_count : av_cpu_count(), 64); + + if (av_log_get_level() < AV_LOG_DEBUG) + svt_enc->encoder.verbose = VERBOSE_ERRORS; + else if (av_log_get_level() == AV_LOG_DEBUG) + svt_enc->encoder.verbose = VERBOSE_SYSTEM_INFO; + else + svt_enc->encoder.verbose = VERBOSE_WARNINGS; + + if (avctx->bit_rate <= 0) { + av_log(avctx, AV_LOG_ERROR, "bitrate can't be 0\n"); + return AVERROR(EINVAL); + } + if (avctx->framerate.num <= 0 || avctx->framerate.den <= 0) { + av_log(avctx, AV_LOG_ERROR, "framerate must be set\n"); + return AVERROR(EINVAL); + } + + av_reduce(&bpp.num, &bpp.den, avctx->bit_rate, (int64_t)avctx->width * avctx->height, INT_MAX); + bpp = av_div_q(bpp, avctx->framerate); + svt_enc->encoder.bpp_numerator = bpp.num; + svt_enc->encoder.bpp_denominator = bpp.den; + + if (svt_enc->decomp_v >= 0) + svt_enc->encoder.ndecomp_v = svt_enc->decomp_v; + if (svt_enc->decomp_h >= 0) + svt_enc->encoder.ndecomp_h = svt_enc->decomp_h; + if (svt_enc->quant >= 0) + svt_enc->encoder.quantization = svt_enc->quant; + if (svt_enc->coding_signs_handling >= 0) + svt_enc->encoder.coding_signs_handling = svt_enc->coding_signs_handling; + if (svt_enc->coding_significance >= 0) + svt_enc->encoder.coding_significance = svt_enc->coding_significance; + if (svt_enc->coding_vpred >= 0) + svt_enc->encoder.coding_vertical_prediction_mode = svt_enc->coding_vpred; + if (avctx->slices > 0) + svt_enc->encoder.slice_height = avctx->height / avctx->slices; + + err = svt_jpeg_xs_encoder_init(SVT_JPEGXS_API_VER_MAJOR, SVT_JPEGXS_API_VER_MINOR, &svt_enc->encoder); + if (err != SvtJxsErrorNone) { + av_log(avctx, AV_LOG_ERROR, "svt_jpeg_xs_encoder_init failed\n"); + return AVERROR_EXTERNAL; + } + + svt_enc->bitstream_frame_size = (((int64_t)avctx->width * avctx->height * + svt_enc->encoder.bpp_numerator / svt_enc->encoder.bpp_denominator + 7) / 8); + + return 0; +} + +static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUV420P10LE, + AV_PIX_FMT_YUV422P10LE, + AV_PIX_FMT_YUV444P10LE, + AV_PIX_FMT_YUV420P12LE, + AV_PIX_FMT_YUV422P12LE, + AV_PIX_FMT_YUV444P12LE, + AV_PIX_FMT_YUV420P14LE, + AV_PIX_FMT_YUV422P14LE, + AV_PIX_FMT_YUV444P14LE, + AV_PIX_FMT_NONE +}; + +#define OFFSET(x) offsetof(SvtJpegXsEncodeContext, x) +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM +static const AVOption svtjpegxs_enc_options[] = { + { "decomp_v", "vertical decomposition level", OFFSET(decomp_v), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 2, VE }, + { "decomp_h", "horizontal decomposition level", OFFSET(decomp_h), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 5, VE }, + { "quantization", "Quantization algorithm", OFFSET(quant), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 1, VE, .unit = "quantization" }, + { "deadzone", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, VE, .unit = "quantization" }, + { "uniform", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1}, INT_MIN, INT_MAX, VE, .unit = "quantization" }, + { "coding-signs", "Enable Signs handling strategy", OFFSET(coding_signs_handling), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 2, VE, .unit = "coding-signs" }, + { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, VE, .unit = "coding-signs" }, + { "fast", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1}, INT_MIN, INT_MAX, VE, .unit = "coding-signs" }, + { "full", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2}, INT_MIN, INT_MAX, VE, .unit = "coding-signs" }, + { "coding-sigf", "Enable Significance coding", OFFSET(coding_significance), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + { "coding-vpred", "Enable Vertical Prediction coding", OFFSET(coding_vpred), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 2, VE, .unit = "coding-vpred" }, + { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, VE, .unit = "coding-vpred" }, + { "no_residuals", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1}, INT_MIN, INT_MAX, VE, .unit = "coding-vpred" }, + { "no_coeffs", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2}, INT_MIN, INT_MAX, VE, .unit = "coding-vpred" }, + { NULL }, +}; + +static const AVClass svtjpegxs_enc_class = { + .class_name = "libsvtjpegxs", + .item_name = av_default_item_name, + .option = svtjpegxs_enc_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFCodec ff_libsvtjpegxs_encoder = { + .p.name = "libsvtjpegxs", + CODEC_LONG_NAME("SVT JPEG XS(Scalable Video Technology for JPEG XS) encoder"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_JPEGXS, + .priv_data_size = sizeof(SvtJpegXsEncodeContext), + .init = svt_jpegxs_enc_init, + .close = svt_jpegxs_enc_free, + FF_CODEC_ENCODE_CB(svt_jpegxs_enc_encode), + .p.capabilities = AV_CODEC_CAP_OTHER_THREADS | AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | + FF_CODEC_CAP_AUTO_THREADS, + CODEC_PIXFMTS_ARRAY(pix_fmts), + .p.wrapper_name = "libsvtjpegxs", + .p.priv_class = &svtjpegxs_enc_class, +}; -- 2.49.1 >From ceb2b0801202ec15618aa5ab7dabdbf3c7191e7c Mon Sep 17 00:00:00 2001 From: James Almer <[email protected]> Date: Tue, 2 Dec 2025 17:44:59 -0300 Subject: [PATCH 4/4] avformat: add a raw JPEG-XS muxer and demuxer Signed-off-by: James Almer <[email protected]> --- libavformat/allformats.c | 1 + libavformat/img2.c | 1 + libavformat/img2dec.c | 10 ++++++++++ libavformat/img2enc.c | 2 +- 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 3a025da3db..6ec361fb7b 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -549,6 +549,7 @@ extern const FFInputFormat ff_image_j2k_pipe_demuxer; extern const FFInputFormat ff_image_jpeg_pipe_demuxer; extern const FFInputFormat ff_image_jpegls_pipe_demuxer; extern const FFInputFormat ff_image_jpegxl_pipe_demuxer; +extern const FFInputFormat ff_image_jpegxs_pipe_demuxer; extern const FFInputFormat ff_image_pam_pipe_demuxer; extern const FFInputFormat ff_image_pbm_pipe_demuxer; extern const FFInputFormat ff_image_pcx_pipe_demuxer; diff --git a/libavformat/img2.c b/libavformat/img2.c index 9981867f82..2c69a932da 100644 --- a/libavformat/img2.c +++ b/libavformat/img2.c @@ -92,6 +92,7 @@ TAG(GEM, timg ) \ TAG(VBN, vbn ) \ TAG(JPEGXL, jxl ) \ + TAG(JPEGXS, jxs ) \ TAG(QOI, qoi ) \ TAG(RADIANCE_HDR, hdr ) \ TAG(WBMP, wbmp ) \ diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c index 1f7e0fcce1..8f1c9013ca 100644 --- a/libavformat/img2dec.c +++ b/libavformat/img2dec.c @@ -824,6 +824,15 @@ static int jpegxl_probe(const AVProbeData *p) return 0; } +static int jpegxs_probe(const AVProbeData *p) +{ + const uint8_t *b = p->buf; + + if (AV_RB32(b) == 0xff10ff50) + return AVPROBE_SCORE_EXTENSION + 1; + return 0; +} + static int pcx_probe(const AVProbeData *p) { const uint8_t *b = p->buf; @@ -1204,6 +1213,7 @@ IMAGEAUTO_DEMUXER(gif, GIF) IMAGEAUTO_DEMUXER_EXT(hdr, RADIANCE_HDR, HDR) IMAGEAUTO_DEMUXER_EXT(j2k, JPEG2000, J2K) IMAGEAUTO_DEMUXER_EXT(jpeg, MJPEG, JPEG) +IMAGEAUTO_DEMUXER(jpegxs, JPEGXS) IMAGEAUTO_DEMUXER(jpegls, JPEGLS) IMAGEAUTO_DEMUXER(jpegxl, JPEGXL) IMAGEAUTO_DEMUXER(pam, PAM) diff --git a/libavformat/img2enc.c b/libavformat/img2enc.c index fb51151090..62ec5be64b 100644 --- a/libavformat/img2enc.c +++ b/libavformat/img2enc.c @@ -290,7 +290,7 @@ static const AVClass img2mux_class = { const FFOutputFormat ff_image2_muxer = { .p.name = "image2", .p.long_name = NULL_IF_CONFIG_SMALL("image2 sequence"), - .p.extensions = "bmp,dpx,exr,jls,jpeg,jpg,jxl,ljpg,pam,pbm,pcx,pfm,pgm,pgmyuv,phm," + .p.extensions = "bmp,dpx,exr,jls,jpeg,jpg,jxs,jxl,ljpg,pam,pbm,pcx,pfm,pgm,pgmyuv,phm," "png,ppm,sgi,tga,tif,tiff,jp2,j2c,j2k,xwd,sun,ras,rs,im1,im8," "im24,sunras,vbn,xbm,xface,pix,y,avif,qoi,hdr,wbmp", .priv_data_size = sizeof(VideoMuxData), -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
