1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-12-25 22:17:24 +02:00
Files
FFmpeg/libavcodec/libsvtjpegxsenc.c
James Almer c639ea5eeb avcodec/libsvtjpegxsenc: set bitrate to a sane default if unset
Better than failing with an impossibly low bitrate.

Signed-off-by: James Almer <jamrial@gmail.com>
2025-12-14 17:34:57 -03:00

304 lines
12 KiB
C

/*
* 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/avassert.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_frame_t enc_input;
svt_jpeg_xs_bitstream_buffer_t *const out_buf = &enc_input.bitstream;
svt_jpeg_xs_image_buffer_t *const in_buf = &enc_input.image;
svt_jpeg_xs_frame_t enc_output;
SvtJxsErrorType_t err = SvtJxsErrorNone;
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;
unsigned pixel_shift = svt_enc->encoder.input_bit_depth <= 8 ? 0 : 1;
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_shift;
in_buf->data_yuv[comp] = frame->data[comp];
in_buf->alloc_size[comp] = frame->linesize[comp] * svt_enc->encoder.source_height;
}
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 void 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;
case AV_PIX_FMT_YUV422P:
encoder->input_bit_depth = 8;
encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV422;
return;
case AV_PIX_FMT_YUV444P:
encoder->input_bit_depth = 8;
encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV444_OR_RGB;
return;
case AV_PIX_FMT_YUV420P10LE:
encoder->input_bit_depth = 10;
encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV420;
return;
case AV_PIX_FMT_YUV422P10LE:
encoder->input_bit_depth = 10;
encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV422;
return;
case AV_PIX_FMT_YUV444P10LE:
encoder->input_bit_depth = 10;
encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV444_OR_RGB;
return;
case AV_PIX_FMT_YUV420P12LE:
encoder->input_bit_depth = 12;
encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV420;
return;
case AV_PIX_FMT_YUV422P12LE:
encoder->input_bit_depth = 12;
encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV422;
return;
case AV_PIX_FMT_YUV444P12LE:
encoder->input_bit_depth = 12;
encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV444_OR_RGB;
return;
case AV_PIX_FMT_YUV420P14LE:
encoder->input_bit_depth = 14;
encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV420;
return;
case AV_PIX_FMT_YUV422P14LE:
encoder->input_bit_depth = 14;
encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV422;
return;
case AV_PIX_FMT_YUV444P14LE:
encoder->input_bit_depth = 14;
encoder->colour_format = COLOUR_FORMAT_PLANAR_YUV444_OR_RGB;
return;
default:
av_unreachable("Already checked via CODEC_PIXFMTS_ARRAY");
break;
}
}
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);
int thread_count = avctx->thread_count ? avctx->thread_count : av_cpu_count();
svt_enc->encoder.threads_num = FFMIN(thread_count, 64);
int log_level = av_log_get_level();
svt_enc->encoder.verbose = log_level < AV_LOG_DEBUG ? VERBOSE_ERRORS :
log_level == AV_LOG_DEBUG ? VERBOSE_SYSTEM_INFO : VERBOSE_WARNINGS;
if (avctx->framerate.num <= 0 || avctx->framerate.den <= 0) {
av_log(avctx, AV_LOG_ERROR, "framerate must be set\n");
return AVERROR(EINVAL);
}
if (avctx->bit_rate == 0) {
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt);
// default to a 1.5 compression ratio
avctx->bit_rate = (int64_t)avctx->width * avctx->height *
(av_get_bits_per_pixel(desc) * 2 / 3) * av_q2d(avctx->framerate);
av_log(avctx, AV_LOG_WARNING, "No bitrate set, defaulting to %"PRId64"\n", avctx->bit_rate);
}
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
};
static const FFCodecDefault svt_jpegxs_defaults[] = {
{ "b", "0" },
{ NULL },
};
#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,
.defaults = svt_jpegxs_defaults,
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,
};