[FFmpeg-devel] [PATCH] avcodec/libx265: add pass and x265-stats option (PR #21232)
PR #21232 opened by Werner Robitza (slhck)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21232
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21232.patch
Add support for standard -pass and -passlogfile options, matching the behavior
of libx264.
Add the -x265-stats option to specify the stats filename.
Update documentation.
>From 4a40be44b9b252211684b8f2e3ee0b52ed8e5c45 Mon Sep 17 00:00:00 2001
From: Werner Robitza
Date: Thu, 18 Dec 2025 12:47:17 +0100
Subject: [PATCH] avcodec/libx265: add pass and x265-stats option
Add support for standard -pass and -passlogfile options, matching the behavior
of libx264.
Add the -x265-stats option to specify the stats filename.
Update documentation.
Signed-off-by: Werner Robitza
---
doc/encoders.texi | 4
fftools/ffmpeg_mux_init.c | 5 +
libavcodec/libx265.c | 20
3 files changed, 29 insertions(+)
diff --git a/doc/encoders.texi b/doc/encoders.texi
index e93a3cc275..610ec9a04c 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -3083,6 +3083,10 @@ Quantizer curve compression factor
Normally, when forcing a I-frame type, the encoder can select any type
of I-frame. This option forces it to choose an IDR-frame.
+@item x265-stats
+Specify the file name for 2-pass stats. This is set automatically when using
+the @option{-passlogfile} option.
+
@item udu_sei @var{boolean}
Import user data unregistered SEI if available into output. Default is 0 (off).
diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c
index 194a87875d..0569f62836 100644
--- a/fftools/ffmpeg_mux_init.c
+++ b/fftools/ffmpeg_mux_init.c
@@ -730,6 +730,11 @@ static int new_stream_video(Muxer *mux, const
OptionsContext *o,
AV_OPT_SEARCH_CHILDREN) >
0)
av_opt_set(video_enc, "stats", logfilename,
AV_OPT_SEARCH_CHILDREN);
+} else if (!strcmp(video_enc->codec->name, "libx265")) {
+if (av_opt_is_set_to_default_by_name(video_enc, "x265-stats",
+ AV_OPT_SEARCH_CHILDREN) >
0)
+av_opt_set(video_enc, "x265-stats", logfilename,
+ AV_OPT_SEARCH_CHILDREN);
} else {
if (video_enc->flags & AV_CODEC_FLAG_PASS2) {
char *logbuffer = read_file_to_string(logfilename);
diff --git a/libavcodec/libx265.c b/libavcodec/libx265.c
index 341868e7cd..89cd7d5dbe 100644
--- a/libavcodec/libx265.c
+++ b/libavcodec/libx265.c
@@ -71,6 +71,7 @@ typedef struct libx265Context {
char *preset;
char *tune;
char *profile;
+char *stats;
AVDictionary *x265_opts;
void *sei_data;
@@ -529,6 +530,24 @@ static av_cold int libx265_encode_init(AVCodecContext
*avctx)
}
}
+if (avctx->flags & AV_CODEC_FLAG_PASS1) {
+if (ctx->api->param_parse(ctx->params, "pass", "1") ==
X265_PARAM_BAD_VALUE) {
+av_log(avctx, AV_LOG_ERROR, "Invalid value for param \"pass\".\n");
+return AVERROR(EINVAL);
+}
+} else if (avctx->flags & AV_CODEC_FLAG_PASS2) {
+if (ctx->api->param_parse(ctx->params, "pass", "2") ==
X265_PARAM_BAD_VALUE) {
+av_log(avctx, AV_LOG_ERROR, "Invalid value for param \"pass\".\n");
+return AVERROR(EINVAL);
+}
+}
+if (ctx->stats) {
+if (ctx->api->param_parse(ctx->params, "stats", ctx->stats) ==
X265_PARAM_BAD_VALUE) {
+av_log(avctx, AV_LOG_ERROR, "Invalid value \"%s\" for param
\"stats\".\n", ctx->stats);
+return AVERROR(EINVAL);
+}
+}
+
if (ctx->params->rc.vbvBufferSize && avctx->rc_initial_buffer_occupancy >
1000 &&
ctx->params->rc.vbvBufferInit == 0.9) {
ctx->params->rc.vbvBufferInit =
(float)avctx->rc_initial_buffer_occupancy / 1000;
@@ -1009,6 +1028,7 @@ static const AVOption options[] = {
{ "preset", "set the x265 preset",
OFFSET(preset),AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE },
{ "tune","set the x265 tune parameter",
OFFSET(tune), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE },
{ "profile", "set the x265 profile",
OFFSET(profile), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE },
+{ "x265-stats", "Filename for 2 pass stats",
OFFSET(stats), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE },
{ "udu_sei", "Use user data unregistered SEI if available",
OFFSET(udu_sei), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1,
VE },
{ "a53cc", "Use A53 Closed Captions (if available)",
OFFSET(a53_cc),AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1,
VE },
{ "x265-params", "set
[FFmpeg-devel] [PATCH] libsvtav1: Enable 2-pass encoding (PR #21239)
PR #21239 opened by Werner Robitza (slhck)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21239
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21239.patch
This patch enables two-pass encoding for libsvtav1 by implementing
support for AV_CODEC_FLAG_PASS1 and AV_CODEC_FLAG_PASS2.
Previously, users requiring two-pass encoding with SVT-AV1 had to use
the standalone SvtAv1EncApp tool. This patch allows 2-pass encoding
directly through FFmpeg.
Based on patch by Fredrik Lundkvist, with review feedback from James
Almer and Andreas Rheinhardt.
See: https://ffmpeg.org/pipermail/ffmpeg-devel/2024-May/327452.html
Changes:
- Use AV_BASE64_DECODE_SIZE macro for buffer size calculation
- Allocate own buffer for rc_stats_buffer (non-ownership pointer)
- Error handling with buffer cleanup
Signed-off-by: Werner Robitza
>From 7b70b050c059feea1a00cc0c9b0fbbca1b8ef2ab Mon Sep 17 00:00:00 2001
From: Werner Robitza
Date: Fri, 19 Dec 2025 11:37:28 +0100
Subject: [PATCH] libsvtav1: Enable 2-pass encoding
This patch enables two-pass encoding for libsvtav1 by implementing
support for AV_CODEC_FLAG_PASS1 and AV_CODEC_FLAG_PASS2.
Previously, users requiring two-pass encoding with SVT-AV1 had to use
the standalone SvtAv1EncApp tool. This patch allows 2-pass encoding
directly through FFmpeg.
Based on patch by Fredrik Lundkvist, with review feedback from James
Almer and Andreas Rheinhardt.
See: https://ffmpeg.org/pipermail/ffmpeg-devel/2024-May/327452.html
Changes:
- Use AV_BASE64_DECODE_SIZE macro for buffer size calculation
- Allocate own buffer for rc_stats_buffer (non-ownership pointer)
- Error handling with buffer cleanup
Signed-off-by: Werner Robitza
---
libavcodec/libsvtav1.c | 83 --
1 file changed, 80 insertions(+), 3 deletions(-)
diff --git a/libavcodec/libsvtav1.c b/libavcodec/libsvtav1.c
index 7047b72422..e2b589ec89 100644
--- a/libavcodec/libsvtav1.c
+++ b/libavcodec/libsvtav1.c
@@ -21,6 +21,7 @@
*/
#include
+#include
#include
#include
#include
@@ -28,6 +29,7 @@
#include "libavutil/common.h"
#include "libavutil/frame.h"
#include "libavutil/imgutils.h"
+#include "libavutil/base64.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mastering_display_metadata.h"
#include "libavutil/mem.h"
@@ -65,6 +67,8 @@ typedef struct SvtContext {
DOVIContext dovi;
+uint8_t *stats_buf;
+
// User options.
AVDictionary *svtav1_opts;
int enc_mode;
@@ -337,6 +341,42 @@ static int config_enc_params(EbSvtAv1EncConfiguration
*param,
return AVERROR(ENOSYS);
}
#endif
+if (avctx->flags & AV_CODEC_FLAG_PASS2) {
+int stats_sz;
+
+if (!avctx->stats_in) {
+av_log(avctx, AV_LOG_ERROR, "No stats file for second pass\n");
+return AVERROR(EINVAL);
+}
+
+stats_sz = AV_BASE64_DECODE_SIZE(strlen(avctx->stats_in));
+if (stats_sz <= 0) {
+av_log(avctx, AV_LOG_ERROR, "Invalid stats file size\n");
+return AVERROR(EINVAL);
+}
+
+svt_enc->stats_buf = av_malloc(stats_sz);
+if (!svt_enc->stats_buf) {
+av_log(avctx, AV_LOG_ERROR, "Failed to allocate stats buffer\n");
+return AVERROR(ENOMEM);
+}
+
+stats_sz = av_base64_decode(svt_enc->stats_buf, avctx->stats_in,
stats_sz);
+if (stats_sz < 0) {
+av_log(avctx, AV_LOG_ERROR, "Failed to decode stats file\n");
+av_freep(&svt_enc->stats_buf);
+return AVERROR(EINVAL);
+}
+
+param->rc_stats_buffer.buf = svt_enc->stats_buf;
+param->rc_stats_buffer.sz = stats_sz;
+param->pass = 2;
+
+av_log(avctx, AV_LOG_INFO, "Using %d bytes of 2-pass stats\n",
stats_sz);
+} else if (avctx->flags & AV_CODEC_FLAG_PASS1) {
+param->pass = 1;
+av_log(avctx, AV_LOG_INFO, "Starting first pass\n");
+}
param->source_width = avctx->width;
param->source_height= avctx->height;
@@ -614,9 +654,45 @@ static int eb_receive_packet(AVCodecContext *avctx,
AVPacket *pkt)
#if SVT_AV1_CHECK_VERSION(2, 0, 0)
if (headerPtr->flags & EB_BUFFERFLAG_EOS) {
- svt_enc->eos_flag = EOS_RECEIVED;
- svt_av1_enc_release_out_buffer(&headerPtr);
- return AVERROR_EOF;
+if (avctx->flags & AV_CODEC_FLAG_PASS1) {
+SvtAv1FixedBuf first_pass_stats = { NULL, 0 };
+EbErrorType svt_ret_stats;
+int b64_size;
+
+svt_ret_stats = svt_av1_enc_get_stream_info(
+svt_enc->svt_handle,
+SVT_AV1_STREAM_INFO_FIRST_PASS_STATS_OUT,
+&first_pass_stats);
+
+if (svt_ret_stats != EB_ErrorNone) {
+av_log(avctx, AV_LOG_ERROR,
+ "Failed to get first pass stats\n");
+svt_av1_enc_release_out_buffer(&headerPtr);
+return AVERROR_EXTERNAL;
+}
+
+if (first_p
