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

Git pushed a commit to branch master
in repository ffmpeg.

commit ac72d7905944206b6e036e61cfee38b927e6b0e0
Author:     Niklas Haas <[email protected]>
AuthorDate: Fri Aug 14 16:46:42 2026 +0200
Commit:     Niklas Haas <[email protected]>
CommitDate: Tue Sep 15 10:04:36 2026 +0000

    avformat/shared: add -ignore_errors option
    
    This allows the shared: cache protocol to ignore errors in the underlying
    URL (e.g. HTTP server went down) and continue serving the cached data
    purely from the cache file. I'm not sure what a good name would be here,
    since -ignore_errors overlaps somewhat with -retry_errors, but I think the
    distinction is clear enough. (-ignore_errors keeps going after the inner
    protocol fails, -retry_errors will retry blocks that previously failed in
    the inner protocol)
    
    One slight change is that we now call ffurl_size() on ourself during
    init. This is safe, because the spacamap is already initialized by this
    point in time, and it also embeds the correct error handling and spacemap
    update logic.
    
    Default off because it's a possibly surprising change in behavior.
    
    Sponsored-by: nxtedition AB
    Signed-off-by: Niklas Haas <[email protected]>
---
 doc/protocols.texi   |  6 +++++
 libavformat/shared.c | 71 +++++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 60 insertions(+), 17 deletions(-)

diff --git a/doc/protocols.texi b/doc/protocols.texi
index 2dad4aaed6..33f62e5d5b 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -1742,6 +1742,12 @@ assumed that the other process may have gotten stuck or 
died in the meantime.
 If set to zero, no waiting is done and all processes will immediately race
 to try and fetch the same missing blocks themselves. Defaults to 10000 (10 ms).
 
+@item ignore_errors
+If true, failures of the underlying input stream are not treated as fatal, and
+playback continues using only the data already present in the cache file.
+Default is false. Any attempt to read into uncached data will result in an
+unrecoverable IO error.
+
 @item retry_errors
 If true (the default), transient read errors from the underlying input stream
 are ignored and retried again. If false, any blocks that previously failed
diff --git a/libavformat/shared.c b/libavformat/shared.c
index 646976f507..23e435e571 100644
--- a/libavformat/shared.c
+++ b/libavformat/shared.c
@@ -162,6 +162,7 @@ typedef struct SharedContext {
     int block_shift; ///< requested shift; updated on init if it disagrees
     int read_only;
     int64_t timeout;
+    int ignore_errors;
     int retry_errors;
     int retry_corrupt;
     int verify;
@@ -254,6 +255,21 @@ static int set_filesize(URLContext *h, int64_t new_size)
     return ret;
 }
 
+static int is_ignorable_error(int64_t err)
+{
+    switch (err) {
+    case AVERROR_EXIT:
+    case AVERROR_EOF:
+    case AVERROR_BUG:
+    case AVERROR(EAGAIN):
+    case AVERROR(ENOSYS):
+    case AVERROR(EINVAL):
+        return 0;
+    default:
+        return err < 0;
+    }
+}
+
 static int shared_open(URLContext *h, const char *arg, int flags, AVDictionary 
**options)
 {
     SharedContext *s = h->priv_data;
@@ -271,8 +287,10 @@ static int shared_open(URLContext *h, const char *arg, int 
flags, AVDictionary *
     av_strstart(arg, "shared:", &arg);
     ret = ffurl_open_whitelist(&s->inner, arg, flags, &h->interrupt_callback,
                                options, h->protocol_whitelist, 
h->protocol_blacklist, h);
-
-    if (ret < 0)
+    if (is_ignorable_error(ret) && s->ignore_errors) {
+        av_log(h, AV_LOG_WARNING, "Underlying URL failed to open: %s. "
+               "Continuing with cache file only.\n", av_err2str(ret));
+    } else if (ret < 0)
         goto fail;
 
     uint8_t hash[HASH_SIZE];
@@ -292,10 +310,11 @@ static int shared_open(URLContext *h, const char *arg, 
int flags, AVDictionary *
     }
 
     av_log(h, AV_LOG_VERBOSE, "Opening cache file '%s' for URI: '%s'\n",
-           s->cache_path, s->inner->filename);
+           s->cache_path, s->inner ? s->inner->filename : arg);
 
-    s->fd    = avpriv_open(s->cache_path, O_RDWR | O_CREAT, 0660);
-    s->mapfd = s->fd >= 0 ? avpriv_open(s->map_path,   O_RDWR | O_CREAT, 0660) 
: -1;
+    const int mode = O_RDWR | (s->inner ? O_CREAT : 0);
+    s->fd    = avpriv_open(s->cache_path, mode, 0660);
+    s->mapfd = s->fd >= 0 ? avpriv_open(s->map_path, mode, 0660) : -1;
     if (s->fd < 0 || s->mapfd < 0) {
         ret = AVERROR(errno);
         av_log(h, AV_LOG_ERROR, "Failed to open '%s': %s\n",
@@ -315,15 +334,12 @@ static int shared_open(URLContext *h, const char *arg, 
int flags, AVDictionary *
         ret = (int) filesize;
         goto fail;
     } else if (!filesize) {
-        /* Filesize is not yet known, try to get it from the underlying URL */
-        filesize = ffurl_size(s->inner);
+        /* Filesize is not yet known, try to get it from the underlying URL;
+         * go through our own seek function to handle errors and updates */
+        filesize = ffurl_size(h);
         if (filesize < 0 && filesize != AVERROR(ENOSYS)) {
             ret = (int) filesize;
             goto fail;
-        } else if (filesize > 0) {
-            ret = set_filesize(h, filesize);
-            if (ret < 0)
-                goto fail;
         }
     }
 
@@ -712,7 +728,7 @@ read_block:
         av_fallthrough;
 
     case BLOCK_NONE:
-        if (s->read_only || s->write_err)
+        if (s->read_only || s->write_err || !s->inner)
             break; /* don't mark block as pending */
         if (atomic_compare_exchange_strong_explicit(&block->state, &state,
                                                     BLOCK_PENDING,
@@ -765,6 +781,14 @@ read_block:
     /* Cache miss, fetch this block from underlying protocol */
     s->nb_miss++;
 
+    if (!s->inner) {
+        av_log(h, AV_LOG_ERROR, "Cache miss for block 0x%"PRIx64" at offset "
+               "0x%"PRIx64", but underlying protocol is not available!\n",
+               block_id, block_pos);
+        av_assert0(!acquired);
+        return AVERROR(EIO);
+    }
+
     const int read_only = s->read_only || s->write_err || verify_read;
     int64_t inner_pos = read_only ? s->pos : block_pos;
     if (s->inner_pos != inner_pos) {
@@ -898,10 +922,15 @@ static int64_t shared_seek(URLContext *h, int64_t pos, 
int whence)
     case AVSEEK_SIZE:
         if (filesize)
             return filesize;
-        res = ffurl_seek(s->inner, pos, whence);
+        res = s->inner ? ffurl_seek(s->inner, pos, whence) : AVERROR(ENOSYS);
         if (res > 0) {
             if (set_filesize(h, res) < 0)
                 return AVERROR(EINVAL);
+        } else if (is_ignorable_error(res) && s->ignore_errors) {
+            av_log(h, AV_LOG_WARNING, "Underlying URL failed to get size: %s. "
+                   "Continuing with cache file only.\n", av_err2str(res));
+            ffurl_closep(&s->inner);
+            res = AVERROR(ENOSYS);
         }
         return res;
     case SEEK_SET:
@@ -914,10 +943,17 @@ static int64_t shared_seek(URLContext *h, int64_t pos, 
int whence)
             pos += filesize;
             break;
         }
+
         /* Defer to underlying protocol if filesize is unknown */
-        res = ffurl_seek(s->inner, pos, whence);
-        if (res < 0)
+        res = s->inner ? ffurl_seek(s->inner, pos, whence) : AVERROR(ENOSYS);
+        if (is_ignorable_error(res) && s->ignore_errors) {
+            av_log(h, AV_LOG_WARNING, "Underlying URL failed to seek: %s. "
+                   "Continuing with cache file only.\n", av_err2str(res));
+            ffurl_closep(&s->inner);
+            return AVERROR(ENOSYS);
+        } else if (res < 0)
             return res;
+
         /* Opportunistically update known filesize */
         if (set_filesize(h, res - pos) < 0)
             return AVERROR(EINVAL);
@@ -937,13 +973,13 @@ static int64_t shared_seek(URLContext *h, int64_t pos, 
int whence)
 static int shared_get_file_handle(URLContext *h)
 {
     SharedContext *s = h->priv_data;
-    return ffurl_get_file_handle(s->inner);
+    return s->inner ? ffurl_get_file_handle(s->inner) : -1;
 }
 
 static int shared_get_short_seek(URLContext *h)
 {
     SharedContext *s = h->priv_data;
-    int ret = ffurl_get_short_seek(s->inner);
+    int ret = s->inner ? ffurl_get_short_seek(s->inner) : 0;
     return ret > 0 ? FFMAX(ret, s->block_size) : s->block_size;
 }
 
@@ -956,6 +992,7 @@ static const AVOption options[] = {
     { "read_only",      "Don't write data to the cache, only read from it", 
OFFSET(read_only),      AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, .flags = D },
     { "cache_verify",   "Verify correctness of the cache against the source",  
 OFFSET(verify),     AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, .flags = D },
     { "cache_timeout",  "Time in us to wait before re-fetching pending 
blocks", OFFSET(timeout),    AV_OPT_TYPE_INT64, {.i64 = 10000}, 0, INT64_MAX, 
.flags = D },
+    { "ignore_errors",  "Continue even if the inner URL failed",            
OFFSET(ignore_errors),  AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, .flags = D },
     { "retry_errors",   "Re-request blocks even if they previously failed", 
OFFSET(retry_errors),   AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, .flags = D },
     { "retry_corrupt",  "Re-request blocks that fail the CRC check",        
OFFSET(retry_corrupt),  AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, .flags = D },
     {0},

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