From: James Almer via ffmpeg-devel <ffmpeg-devel@ffmpeg.org>
To: ffmpeg-devel@ffmpeg.org
Cc: James Almer <code@ffmpeg.org>
Subject: [FFmpeg-devel] [PATCH] avcodec: JPEG-XS support (PR #21084)
Date: Wed, 03 Dec 2025 01:34:23 -0000
Message-ID: <176472566368.39.17846005575720477963@2cb04c0e5124> (raw)
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 <tomasz.szumski@intel.com>
Date: Tue, 9 Jul 2024 10:04:16 +0200
Subject: [PATCH 1/4] avcodec/codec_id: add JPEG-XS
Signed-off-by: James Almer <jamrial@gmail.com>
---
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 <jamrial@gmail.com>
Date: Mon, 1 Dec 2025 22:59:01 -0300
Subject: [PATCH 2/4] avcodec: add a JPEG-XS parser
Signed-off-by: James Almer <jamrial@gmail.com>
---
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 <tomasz.szumski@intel.com>
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 <jamrial@gmail.com>
Signed-off-by: James Almer <jamrial@gmail.com>
---
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 <jamrial@gmail.com>
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 <jamrial@gmail.com>
---
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 -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org
reply other threads:[~2025-12-03 1:35 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=176472566368.39.17846005575720477963@2cb04c0e5124 \
--to=ffmpeg-devel@ffmpeg.org \
--cc=code@ffmpeg.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git