mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2026-01-12 00:06:51 +08:00
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>
This commit is contained in:
committed by
James Almer
parent
52c097065c
commit
08db850159
@@ -16,6 +16,7 @@ version <next>:
|
||||
- Rockchip H.264/HEVC hardware encoder
|
||||
- Add vf_scale_d3d12 filter
|
||||
- JPEG-XS parser
|
||||
- JPEG-XS decoder and encoder through libsvtjpegxs
|
||||
|
||||
|
||||
version 8.0:
|
||||
|
||||
5
configure
vendored
5
configure
vendored
@@ -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]
|
||||
@@ -2013,6 +2014,7 @@ EXTERNAL_LIBRARY_LIST="
|
||||
libsrt
|
||||
libssh
|
||||
libsvtav1
|
||||
libsvtjpegxs
|
||||
libtensorflow
|
||||
libtesseract
|
||||
libtheora
|
||||
@@ -3710,6 +3712,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"
|
||||
@@ -7256,6 +7260,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
292
libavcodec/libsvtjpegxsdec.c
Normal file
292
libavcodec/libsvtjpegxsdec.c
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* 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, const 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 (avctx->skip_frame == AVDISCARD_ALL)
|
||||
return 0;
|
||||
|
||||
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_SKIP_FRAME_FILL_PARAM |
|
||||
FF_CODEC_CAP_AUTO_THREADS,
|
||||
.p.wrapper_name = "libsvtjpegxs",
|
||||
.p.priv_class = &svtjpegxs_dec_class,
|
||||
};
|
||||
298
libavcodec/libsvtjpegxsenc.c
Normal file
298
libavcodec/libsvtjpegxsenc.c
Normal file
@@ -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,
|
||||
};
|
||||
@@ -29,8 +29,8 @@
|
||||
|
||||
#include "version_major.h"
|
||||
|
||||
#define LIBAVCODEC_VERSION_MINOR 22
|
||||
#define LIBAVCODEC_VERSION_MICRO 101
|
||||
#define LIBAVCODEC_VERSION_MINOR 23
|
||||
#define LIBAVCODEC_VERSION_MICRO 100
|
||||
|
||||
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
|
||||
LIBAVCODEC_VERSION_MINOR, \
|
||||
|
||||
Reference in New Issue
Block a user