PR #23979 opened by cdcxd
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23979
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23979.patch

# Summary of changes

Change the eb_receive_packet to drain available packet when possible, not only 
attempt after eb_send_frame

Reason: 

The current implementation only attempts svt_av1_enc_get_packet after 
successful ff_encode_get_frame + eb_send_frame .  Meaning we can only get 0 or 
1 packet after sending a frame. The unharvested packet count will never 
decrease. It won't catch up burst output packets either. 

SVT-AV1 releases packets in mini-GOP sized bursts — measured at up to 39 at 
once with `640x360, lp=1:hierarchical-levels=5:lookahead=120`. So finished 
packets are withheld from the application until it supplies more input.  Can't 
drain it asap.

Changes:

I changed it to always try svt_av1_enc_get_packet if possible.  In the 
LOW_DELAY and rtc mode (available since v3.1.0), the function will block, so we 
can't attempt it. If EB_NoErrorEmptyQueue is returned, it fallthrough to the 
send frame as before.  Otherwise it just returns the packet and wait for next 
avcodec_receive_packet call 

# Verification
- ffmpeg cli, I built the patched one locally, asked coding agent to draft 
byte-identical comparison. It did pass, the script provenance is  
https://code.ffmpeg.org/cdcxd/FFmpeg/src/branch/libsvtav1-drain-first-provenance/verification_provenance/verify.sh

<!--
If this PR requires new FATE test samples, attach them to the PR and
list their target paths below (relative to the fate-suite root).

Attached filenames must match the sample's filename:

```fate-samples
# e.g. vorbis/new-sample.ogg
```
-->



From 0f43599289701a7254a9846cc0b820a2caa84f50 Mon Sep 17 00:00:00 2001
From: cdong <[email protected]>
Date: Sat, 1 Aug 2026 22:50:03 -0700
Subject: [PATCH] avcodec/libsvtav1 : make eb_receive_packet drain available
 packets before demanding new input

---
 libavcodec/libsvtav1.c | 51 +++++++++++++++++++++++++++++++++---------
 1 file changed, 41 insertions(+), 10 deletions(-)

diff --git a/libavcodec/libsvtav1.c b/libavcodec/libsvtav1.c
index 0bcc176243..3fe27c5b7e 100644
--- a/libavcodec/libsvtav1.c
+++ b/libavcodec/libsvtav1.c
@@ -653,22 +653,53 @@ static int eb_receive_packet(AVCodecContext *avctx, 
AVPacket *pkt)
     EbErrorType svt_ret;
     AVBufferRef *ref;
     int ret = 0;
+    int drain_packet_first, have_packet = 0;
 
     if (svt_enc->eos_flag == EOS_RECEIVED)
         return AVERROR_EOF;
 
-    ret = ff_encode_get_frame(avctx, frame);
-    if (ret < 0 && ret != AVERROR_EOF)
-        return ret;
-    if (ret == AVERROR_EOF)
-        frame = NULL;
+    /* Take any available packet, don't let them accumulate in output fifo.
+     *
+     * svt_av1_enc_get_packet() is documented in EbSvtAv1Enc.h to block when
+     * pic_send_done is set or when the library runs in low delay, so only poll
+     * ahead of sending when it is guaranteed not to. Low delay gained that
+     * behaviour in v2.3.0, testing for it on older versions is merely
+     * conservative. The library's copy_api_from_app() forces low delay for rtc
+     * without reflecting it in the configuration we hold, hence the separate
+     * check.
+     */
+    drain_packet_first = svt_enc->eos_flag == EOS_NOT_REACHED &&
+#if SVT_AV1_CHECK_VERSION(4, 1, 0)
+                         svt_enc->enc_params.pred_structure != LOW_DELAY
+#else
+                         /* LOW_DELAY is not exported before v4.1.0 */
+                         svt_enc->enc_params.pred_structure != 1
+#endif
+#if SVT_AV1_CHECK_VERSION(3, 1, 0)
+                         && !svt_enc->enc_params.rtc
+#endif
+                         ;
 
-    ret = eb_send_frame(avctx, frame);
-    if (ret < 0)
-        return ret;
-    av_frame_unref(svt_enc->frame);
+    if (drain_packet_first) {
+        svt_ret = svt_av1_enc_get_packet(svt_enc->svt_handle, &headerPtr, 
svt_enc->eos_flag);
+        have_packet = svt_ret != EB_NoErrorEmptyQueue;
+    }
+
+    if (!have_packet) {
+        ret = ff_encode_get_frame(avctx, frame);
+        if (ret < 0 && ret != AVERROR_EOF)
+            return ret;
+        if (ret == AVERROR_EOF)
+            frame = NULL;
+
+        ret = eb_send_frame(avctx, frame);
+        if (ret < 0)
+            return ret;
+        av_frame_unref(svt_enc->frame);
+
+        svt_ret = svt_av1_enc_get_packet(svt_enc->svt_handle, &headerPtr, 
svt_enc->eos_flag);
+    }
 
-    svt_ret = svt_av1_enc_get_packet(svt_enc->svt_handle, &headerPtr, 
svt_enc->eos_flag);
     if (svt_ret == EB_NoErrorEmptyQueue)
         return AVERROR(EAGAIN);
     else if (svt_ret != EB_ErrorNone)
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to