This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit 9e23b2249fb5d1494e0f654a98fa35a880e729c0
Author:     Niklas Haas <[email protected]>
AuthorDate: Tue Sep 29 20:27:06 2026 +0200
Commit:     Niklas Haas <[email protected]>
CommitDate: Tue Sep 29 21:36:00 2026 +0200

    avformat/libcurl: move retry handling to the URL thread
    
    Instead of the loop worker immediately retrying requests, this change moves
    both the retry decision and accounting logic to the calling thread instead.
    
    The main motivation here is that it allows us to add a configurable delay
    between retries, e.g. for exponential backoff or respecting server 
advertised
    Retry-After headers.
    
    There is some ambiquity about when exactly we want to reset retry_count, 
with
    the options roughly being:
    
    1. After fully reading a successful request to the end (old behavior)
    2. After successfully reading any number of bytes (new behavior)
    3. Never / only on explicit ffurl_seek()
    
    I think that both 2 and 3 are defensible, but 1 is arguably the only one
    that's not really useful; so this patch also represents a mild improvement
    in that regard - the retry counter only starts ticking up if the request
    fails several times in a row.
    
    The reason I think the old behavior is not useful here is because the rate
    of *requests* is a bit arbitrary and depends on options like -request_size.
    
    Sponsored-by: nxtedition AB
    Signed-off-by: Niklas Haas <[email protected]>
---
 libavformat/libcurl.c | 51 +++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 37 insertions(+), 14 deletions(-)

diff --git a/libavformat/libcurl.c b/libavformat/libcurl.c
index 1ed2d85b0b..6852b3bed7 100644
--- a/libavformat/libcurl.c
+++ b/libavformat/libcurl.c
@@ -137,7 +137,9 @@ struct CurlContext {
     char           *icy_metadata_packet;  /* last in-band block (output) */
     AVDictionary   *metadata;             /* ICY metadata (output) */
 
-    int64_t         logical_pos; /* next byte url_read() will return, caller 
side */
+    /* URL thread bookkeeping, not touched by loop thread */
+    int64_t         logical_pos;    /* next byte url_read() will return, 
caller side */
+    int             retry_count;    /* consecutive recoverable failures */
     int64_t         icy_data_read;    /* payload bytes since the last block, 
caller side */
     int             icy_block_len;    /* -1 while the length byte is pending */
     int             icy_block_filled;
@@ -148,7 +150,6 @@ struct CurlContext {
     int64_t         request_start;   /* absolute offset the current request 
began at */
     int64_t         request_received;/* bytes delivered in the current request 
*/
     int64_t         request_end;     /* expected end of request, or -1 if 
unknown */
-    int             retry_count;     /* consecutive recoverable failures */
     int             is_initial;      /* using reduced request size */
     int             seek_queued;     /* soft seeking; drain remaining bytes 
until done */
 
@@ -731,7 +732,6 @@ static void on_done(CurlContext *c, CURLcode code)
     }
 
     if (code == CURLE_OK && c->stream_ok) {
-        c->retry_count = 0;
         int64_t file_end = c->content_size > 0 ? c->content_size - 1 : -1;
         if (c->end_off > 0)
             file_end = FFMIN(file_end, c->end_off - 1);
@@ -753,16 +753,6 @@ static void on_done(CurlContext *c, CURLcode code)
         c->loop->num_errors++;
     }
 
-    /* Resume seekable transfers after a recoverable error. */
-    if (c->seekable && is_recoverable(code) &&
-        c->retry_count < c->max_retries) {
-        c->retry_count++;
-        av_log(c->h, AV_LOG_WARNING, "Retrying (#%d) from %"PRId64"\n",
-               c->retry_count, c->request_start);
-        start_request(c);
-        return;
-    }
-
     /* Unhandled generic curl error */
     pthread_mutex_lock(&c->mutex);
     update_status_locked(c, curlcode_to_averror(code), code);
@@ -824,7 +814,6 @@ static void execute_command(CurlLoop *loop, CurlCmd *cmd)
         pthread_mutex_unlock(&c->mutex);
         c->request_start    = cmd->pos;
         c->request_received = 0;
-        c->retry_count      = 0;
         if (!c->seek_queued)
             start_request(c);
         else if (was_paused)
@@ -1263,6 +1252,35 @@ static int wait_for_probe(CurlContext *c)
     return ret;
 }
 
+static int retry_request_locked(URLContext *h)
+{
+    CurlContext *c = h->priv_data;
+    const int status = c->status;
+    const CURLcode code = c->curl_status;
+    pthread_mutex_unlock(&c->mutex);
+
+    if (c->retry_count >= c->max_retries) {
+        av_log(h, AV_LOG_ERROR, "Maximum number of retries (%d) reached\n",
+               c->max_retries);
+        return status;
+    }
+
+    c->retry_count++;
+    av_log(h, AV_LOG_WARNING, "Retrying (#%d) from %"PRId64" after: %s (%s)\n",
+           c->retry_count, c->logical_pos, av_err2str(status), 
curl_easy_strerror(code));
+
+    /**
+     * Use a synchronous request to ensure that the seek is registered, and
+     * the reset of c->state is observable, before the next libcurl_read()
+     * call, otherwise this might hit the exact same retry path a second time.
+     */
+    int ret = curl_dispatch(c->loop, CMD_SEEK, c, c->logical_pos, 1);
+    if (ret < 0)
+        return ret;
+
+    return AVERROR(EAGAIN); /* allow caller to handle interrupts and retry */
+}
+
 static int libcurl_open(URLContext *h, const char *url, int flags,
                         AVDictionary **options)
 {
@@ -1415,9 +1433,13 @@ static int libcurl_read(URLContext *h, unsigned char 
*buf, int size)
             av_fifo_read(c->fifo, buf, ret);
             c->icy_data_read += ret;
             c->logical_pos   += ret;
+            c->retry_count    = 0;
             break;
         }
+
         if (c->status) {
+            if (c->seekable && is_recoverable(c->curl_status))
+                return retry_request_locked(h);
             if (c->status == AVERROR_EOF && c->icy_block_len >= 0)
                 av_log(h, AV_LOG_WARNING,
                        "Stream ended inside an ICY metadata block\n");
@@ -1488,6 +1510,7 @@ static int64_t libcurl_seek(URLContext *h, int64_t pos, 
int whence)
      * surfaces on the following url_read(). */
     curl_dispatch(c->loop, CMD_SEEK, c, newpos, 1);
     c->logical_pos = newpos;
+    c->retry_count = 0;
 
     return newpos;
 }

-- 
To stop receiving notification emails like this one, please contact
[email protected].
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to