Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PATCH] libsvtav1: Enable 2-pass encoding (PR #21239)
@ 2025-12-19 11:11 Werner Robitza via ffmpeg-devel
  0 siblings, 0 replies; only message in thread
From: Werner Robitza via ffmpeg-devel @ 2025-12-19 11:11 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Werner Robitza

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 <werner.robitza@gmail.com>


>From 7b70b050c059feea1a00cc0c9b0fbbca1b8ef2ab Mon Sep 17 00:00:00 2001
From: Werner Robitza <werner.robitza@gmail.com>
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 <werner.robitza@gmail.com>
---
 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 -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2025-12-19 11:12 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-12-19 11:11 [FFmpeg-devel] [PATCH] libsvtav1: Enable 2-pass encoding (PR #21239) Werner Robitza via ffmpeg-devel

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