PR #24359 opened by Niklas Haas (haasn) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24359 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24359.patch
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]> >From c386d347d1038ad675de669a7a3c4637c37d9605 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Wed, 24 Jun 2026 19:21:41 +0200 Subject: [PATCH] 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 | 8 +++++++ libavformat/libcurl.c | 54 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/doc/protocols.texi b/doc/protocols.texi index 9790317d28..af0d006762 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -1166,9 +1166,17 @@ 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. + @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..b44c4516ea 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 && !aborted) { + /* 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,20 @@ 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 total = c->request_end - c->request_start + 1; + const int64_t remaining = total - c->request_received; + return remaining <= c->short_seek_size; +} + static void execute_command(CurlLoop *loop, CurlCmd *cmd) { CurlContext *c = cmd->ctx; @@ -553,7 +581,13 @@ 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; + if (c->paused) { + curl_easy_pause(c->easy, CURLPAUSE_CONT); + c->paused = 0; + } + } else if (c->active) { curl_multi_remove_handle(loop->multi, c->easy); c->active = 0; } @@ -562,9 +596,11 @@ static void execute_command(CurlLoop *loop, CurlCmd *cmd) 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); break; } } @@ -1174,6 +1210,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 +1251,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 +1268,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, -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
