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

Git pushed a commit to branch master
in repository ffmpeg.

The following commit(s) were added to refs/heads/master by this push:
     new 7ee873656c avformat/libcurl: implement -short_seek_size
7ee873656c is described below

commit 7ee873656cb5fc489c6d2966e45d448ca30aedda
Author:     Niklas Haas <[email protected]>
AuthorDate: Wed Jun 24 19:21:41 2026 +0200
Commit:     Niklas Haas <[email protected]>
CommitDate: Fri Sep 4 17:37:20 2026 +0000

    avformat/libcurl: implement -short_seek_size
    
    Since libcurl.c doesn't have to care about the exact number of bytes read,
    we just perform the seek but defer the actual `start_request()` call until
    the next on_done() callback, while simply discarding all data read in the
    meantime.
    
    Sponsored-by: nxtedition AB
    Signed-off-by: Niklas Haas <[email protected]>
---
 doc/protocols.texi    |  9 +++++++++
 libavformat/libcurl.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/doc/protocols.texi b/doc/protocols.texi
index 9790317d28..4a82dae2ee 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -1166,9 +1166,18 @@ read request larger than this size (without a seek in 
between), after which
 the implementation will continue using requests as usual. Disabled (set to 0)
 by default.
 
+Note that if enabling this option, it's strongly recommended to also set
+@option{short_seek_size} to the same value or higher, otherwise the HTTP
+connection may be closed and recreated for each subsequent initial request.
+
 @item max_retries
 Maximum number of retries after a recoverable error on a seekable transfer.
 Default is @code{5}.
+
+@item short_seek_size
+Set the threshold, in bytes, for when a readahead should be preferred over a 
seek and
+new HTTP request. This is useful, for example, to make sure the same connection
+is used for reading large video packets with small audio packets in between.
 @end table
 
 For more information see: @url{https://curl.se/libcurl/}.
diff --git a/libavformat/libcurl.c b/libavformat/libcurl.c
index a2f7e6201e..2196e7913f 100644
--- a/libavformat/libcurl.c
+++ b/libavformat/libcurl.c
@@ -120,6 +120,7 @@ struct CurlContext {
     int64_t         buffer_size;
     int64_t         request_size;
     int64_t         initial_request_size;
+    int64_t         short_seek_size;
     int             max_retries;
 
     int64_t         logical_pos; /* next byte url_read() will return, caller 
side */
@@ -131,6 +132,7 @@ struct CurlContext {
     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 */
 
     /* Per-response-block header scratch, loop thread only. */
     int             hdr_accept_ranges;
@@ -211,6 +213,11 @@ static size_t write_callback(char *ptr, size_t size, 
size_t nmemb, void *userdat
         return CURL_WRITEFUNC_ERROR;
     }
 
+    if (c->seek_queued) {
+        pthread_mutex_unlock(&c->mutex);
+        return bytes; /* discard */
+    }
+
     space = av_fifo_can_write(c->fifo);
     if (space < bytes) {
         /* pause the transfer and wait for the consumer to drain. */
@@ -487,6 +494,13 @@ static void on_done(CurlContext *c, CURLcode code)
     if (aborted)
         return;
 
+    if (c->seek_queued) {
+        /* previous soft seek drain finished; can start new request now */
+        c->seek_queued = 0;
+        start_request(c);
+        return;
+    }
+
     if (code == CURLE_OK && c->stream_ok) {
         c->retry_count = 0;
         int64_t file_end = c->content_size > 0 ? c->content_size - 1 : -1;
@@ -531,6 +545,19 @@ static void on_done(CurlContext *c, CURLcode code)
 /* event loop thread + command queue                                         */
 /* ------------------------------------------------------------------------- */
 
+static int test_short_seek(CurlContext *c)
+{
+    if (c->seek_queued)
+        return 1; /* short seek already queued */
+
+    if (c->short_seek_size <= 0 || /* short seek disabled */
+        c->request_end < 0)        /* content size not known */
+        return 0;
+
+    const int64_t last = c->request_end - c->request_start;
+    return last - c->request_received < c->short_seek_size;
+}
+
 static void execute_command(CurlLoop *loop, CurlCmd *cmd)
 {
     CurlContext *c = cmd->ctx;
@@ -553,18 +580,25 @@ static void execute_command(CurlLoop *loop, CurlCmd *cmd)
         curl_easy_pause(c->easy, CURLPAUSE_CONT);
         break;
     case CMD_SEEK:
-        if (c->active) {
+        if (c->active && test_short_seek(c)) {
+            c->seek_queued = 1;
+        } else if (c->active) {
             curl_multi_remove_handle(loop->multi, c->easy);
             c->active = 0;
         }
         pthread_mutex_lock(&c->mutex);
         av_fifo_reset2(c->fifo);
+        const int was_paused = c->paused;
         c->paused = 0;
         c->status = 0;
         pthread_mutex_unlock(&c->mutex);
-        c->request_start = cmd->pos;
-        c->retry_count   = 0;
-        start_request(c);
+        c->request_start    = cmd->pos;
+        c->request_received = 0;
+        c->retry_count      = 0;
+        if (!c->seek_queued)
+            start_request(c);
+        else if (was_paused)
+            curl_easy_pause(c->easy, CURLPAUSE_CONT);
         break;
     }
 }
@@ -1174,6 +1208,14 @@ static int libcurl_close(URLContext *h)
     return 0;
 }
 
+static int libcurl_get_short_seek(URLContext *h)
+{
+    CurlContext *c = h->priv_data;
+    if (c->short_seek_size >= 1)
+        return FFMIN(c->short_seek_size, INT_MAX);
+    return AVERROR(ENOSYS);
+}
+
 #define OFFSET(x) offsetof(CurlContext, x)
 #define D AV_OPT_FLAG_DECODING_PARAM
 #define E AV_OPT_FLAG_ENCODING_PARAM
@@ -1207,6 +1249,7 @@ static const AVOption options[] = {
         { "2-prior-knowledge", "HTTP/2 without an upgrade handshake",   0, 
AV_OPT_TYPE_CONST, { .i64 = CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE },   0, 0, D, 
.unit = "http_version" },
         { "3",                 "HTTP/3, fall back to earlier versions", 0, 
AV_OPT_TYPE_CONST, { .i64 = CURL_HTTP_VERSION_3 },                   0, 0, D, 
.unit = "http_version" },
         { "3only",             "HTTP/3 only",                           0, 
AV_OPT_TYPE_CONST, { .i64 = CURL_HTTP_VERSION_3ONLY },               0, 0, D, 
.unit = "http_version" },
+    { "short_seek_size", "threshold to favor readahead over seek", 
OFFSET(short_seek_size), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D },
     { NULL }
 };
 
@@ -1223,6 +1266,7 @@ const URLProtocol ff_libcurl_protocol = {
     .url_read        = libcurl_read,
     .url_seek        = libcurl_seek,
     .url_close       = libcurl_close,
+    .url_get_short_seek = libcurl_get_short_seek,
     .priv_data_size  = sizeof(CurlContext),
     .priv_data_class = &libcurl_context_class,
     .flags           = URL_PROTOCOL_FLAG_NETWORK,

-- 
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