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]

Reply via email to