Commit c1b3b086e4 for ffmpeg
commit c1b3b086e4bba744a57613d101a7b861b1dd20af
Author: Niklas Haas <git@haasn.dev>
Date: Sun Nov 8 14:59:25 2026 +0100
avformat/shared: add -cache_key option
There are a number of cases in which deriving the filename from a hash of
the URL is not ideal: For example, when the URL embeds parameters that
don't affect the underlying file data, or when the user wants to be able to
clean up or access the cache files directly, e.g. by associating them with a
database.
Sponsored-by: nxtedition AB
Signed-off-by: Niklas Haas <git@haasn.dev>
diff --git a/doc/protocols.texi b/doc/protocols.texi
index ab2ec92d80..e802c9121b 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -1788,6 +1788,8 @@ This includes content selected by request options such as headers, cookies or
credentials: data cached for one user is returned to every other user of the
same cache directory.
+See also the @option{cache_key} option.
+
The accepted options are:
@table @option
@@ -1801,6 +1803,11 @@ writable by untrusted users.
Path to the directory where metadata files are stored. Optional. If not
specified, metadata is stored in the same directory as the cache file.
+@item cache_key
+Override the hash used to identify the cached content. Note that this is also
+used verbatim as the base name of the cache and metadata files, so it must be
+a valid file name.
+
@item block_shift
Shift factor (log2) of the block size used for internal reads/writes. Defaults
to 15, i.e. 32KB blocks. If this does not match the value that was specified
diff --git a/libavformat/shared.c b/libavformat/shared.c
index 38a64412a7..ecea2f0340 100644
--- a/libavformat/shared.c
+++ b/libavformat/shared.c
@@ -166,6 +166,7 @@ typedef struct SharedContext {
/* options */
char *cache_dir;
char *metadata_dir;
+ char *cache_key;
int block_shift; ///< requested shift; updated on init if it disagrees
int read_only;
int64_t timeout;
@@ -297,15 +298,31 @@ static int shared_open(URLContext *h, const char *arg, int flags, AVDictionary *
} else if (ret < 0)
goto fail;
+ const char *cache_key = s->cache_key;
+ if (cache_key) {
+ if (strchr(cache_key, '/') || strchr(cache_key, '\\')) {
+ av_log(h, AV_LOG_ERROR, "Invalid cache key '%s': must not contain "
+ "path separators\n", cache_key);
+ ret = AVERROR(EINVAL);
+ goto fail;
+ } else if (!cache_key[0])
+ cache_key = NULL;
+ }
+
uint8_t hash[HASH_SIZE];
- ret = hash_uri(hash, arg);
+ ret = hash_uri(hash, cache_key ? cache_key : arg);
if (ret < 0)
goto fail;
/* 128 bits is enough for collision resistance; we already store the full
* hash inside the header for verification */
- char filename[2 * 16 + 1];
- ff_data_to_hex(filename, hash, sizeof(filename)/2, 0);
+ const char *filename = cache_key;
+ char buf[2 * 16 + 1];
+ if (!filename) {
+ ff_data_to_hex(buf, hash, sizeof(buf)/2, 0);
+ filename = buf;
+ }
+
s->cache_path = av_asprintf("%s/%s.cache", s->cache_dir, filename);
s->map_path = av_asprintf("%s/%s.spacemap", spacemap_dir, filename);
if (!s->cache_path || !s->map_path) {
@@ -952,6 +969,7 @@ static const AVOption options[] = {
{ "retry_corrupt", "Re-request blocks that fail the CRC check", OFFSET(retry_corrupt), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, .flags = D },
{ "cache_size_max", "Limit the maximum amount of data cached", OFFSET(cache_size_max), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, .flags = D },
{ "offset", "Initial byte offset", OFFSET(offset), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, .flags = D },
+ { "cache_key", "Override the hash used to identify the cache file",OFFSET(cache_key), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = D },
{0},
};