avformat/http: re-use keep-alive connection for small seeks

When the previous reply was a partial response (e.g. due to a seek to the
end of the file), and the remaining data from that partial response is
below the short seek size threshold, we can serve this seek by just draining
that data and re-using the existing connection.

This can currently only happen when using keep-alive connections
(-multiple_requests 1) and seeking from the end of the file to somewhere
else, in which case the file's tail can be drained and the connection re-used.

Under other circumstances, however, we still need to force a reconnection,
because we do not yet send partial range requests. (This will be changed in the
following commit)

We need to take special care not to regress the existing fallback logic
for when `http_open_cnx` fails, so here is a quick case analysis:

non-drain path:
- identical to the current

soft drain fails: (ffurl_read error path)
- s->hd = old_hd = NULL
- http_open_cnx() always opens a new connection
- on failure, old buffer is restored and s->hd remains NULL

soft drain succeeds, http_open_cnx() fails:
- s->hd is set to NULL by http_open_cnx() failure path
- old_hd was never set, so remains NULL
- old buffer is restored, s->hd remains NULL

In either case, the outcome that any (previously valid) buffer is left as-is,
the offset is unchanged, and the connection ends up closed (s->hd == NULL).
This is okay to do after the previous change to http_buf_read, which allows
it to internally re-open the connection if needed.
This commit is contained in:
Niklas Haas
2026-01-23 10:46:55 +01:00
committed by Niklas Haas
parent bf1722a9c6
commit e03b034e45
+23 -2
View File
@@ -2022,11 +2022,12 @@ static int http_close(URLContext *h)
static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int force_reconnect)
{
HTTPContext *s = h->priv_data;
URLContext *old_hd = s->hd;
URLContext *old_hd = NULL;
uint64_t old_off = s->off;
uint8_t old_buf[BUFFER_SIZE];
int old_buf_size, ret;
AVDictionary *options = NULL;
uint8_t discard[4096];
if (whence == AVSEEK_SIZE)
return s->filesize;
@@ -2068,7 +2069,27 @@ static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int fo
/* we save the old context in case the seek fails */
old_buf_size = s->buf_end - s->buf_ptr;
memcpy(old_buf, s->buf_ptr, old_buf_size);
s->hd = NULL;
/* try to reuse existing connection for small seeks */
uint64_t remaining = s->range_end - old_off - old_buf_size;
if (!s->willclose && s->range_end && remaining <= ffurl_get_short_seek(h)) {
/* drain remaining data left on the wire from previous request */
av_log(h, AV_LOG_DEBUG, "Soft-seeking to offset %"PRIu64" by draining "
"%"PRIu64" remaining byte(s)\n", s->off, remaining);
while (remaining) {
int ret = ffurl_read(s->hd, discard, FFMIN(remaining, sizeof(discard)));
if (ret < 0 || ret == AVERROR_EOF || (ret == 0 && remaining)) {
/* connection broken or stuck, need to reopen */
ffurl_closep(&s->hd);
break;
}
remaining -= ret;
}
} else {
/* can't soft seek; always open new connection */
old_hd = s->hd;
s->hd = NULL;
}
/* if it fails, continue on old connection */
if ((ret = http_open_cnx(h, &options)) < 0) {