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 <[email protected]> >From 7b70b050c059feea1a00cc0c9b0fbbca1b8ef2ab Mon Sep 17 00:00:00 2001 From: Werner Robitza <[email protected]> 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 <[email protected]> --- 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 <stdint.h> +#include <inttypes.h> #include <EbSvtAv1ErrorCodes.h> #include <EbSvtAv1Enc.h> #include <EbSvtAv1Metadata.h> @@ -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_pass_stats.sz > 0 && first_pass_stats.buf) { + b64_size = AV_BASE64_SIZE(first_pass_stats.sz); + avctx->stats_out = av_malloc(b64_size); + if (!avctx->stats_out) { + av_log(avctx, AV_LOG_ERROR, + "Failed to allocate stats output buffer\n"); + svt_av1_enc_release_out_buffer(&headerPtr); + return AVERROR(ENOMEM); + } + + av_base64_encode(avctx->stats_out, b64_size, + first_pass_stats.buf, first_pass_stats.sz); + + av_log(avctx, AV_LOG_INFO, + "First pass stats: %"PRIu64" bytes, encoded to %d bytes\n", + first_pass_stats.sz, b64_size); + } + } + + svt_enc->eos_flag = EOS_RECEIVED; + svt_av1_enc_release_out_buffer(&headerPtr); + return AVERROR_EOF; } #endif @@ -684,6 +760,7 @@ static av_cold int eb_enc_close(AVCodecContext *avctx) av_buffer_pool_uninit(&svt_enc->pool); av_frame_free(&svt_enc->frame); ff_dovi_ctx_unref(&svt_enc->dovi); + av_freep(&svt_enc->stats_buf); return 0; } -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
