Go to the documentation of this file.
38 #include <stdatomic.h>
57 #define HASH_METHOD "SHA512/256"
78 #define HEADER_MAGIC MKTAG(u'\xFF', 'S', 'h', '$')
79 #define HEADER_VERSION 2
106 typedef struct Block {
122 #define DEF_SET_ONCE(ctype, atype) \
123 static int set_once_##atype(atomic_##atype *const ptr, const ctype value) \
126 av_assert1(value != 0); \
127 if (atomic_compare_exchange_strong_explicit( \
128 ptr, &prev, value, memory_order_release, memory_order_relaxed)) \
130 else if (prev == value) \
133 return AVERROR(EINVAL); \
183 munmap(
s->cache_data,
s->cache_size);
185 munmap(
s->spacemap,
s->map_size);
195 s->nb_hit,
s->nb_miss);
217 ret = set_once_ullong(&
s->spacemap->filesize, new_size);
220 "%"PRId64
", got: %"PRIu64
"!\n", new_size,
237 if (!
s->cache_dir || !
s->cache_dir[0]) {
239 "directory using the -cache_dir option.\n");
243 s->fd =
s->mapfd = -1;
248 options,
h->protocol_whitelist,
h->protocol_blacklist,
h);
260 char filename[2 * 16 + 1];
262 sprintf(&filename[
i * 2],
"%02X",
hash[
i]);
263 s->cache_path =
av_asprintf(
"%s/%s.cache",
s->cache_dir, filename);
264 s->map_path =
av_asprintf(
"%s/%s.spacemap",
s->cache_dir, filename);
265 if (!
s->cache_path || !
s->map_path) {
271 s->cache_path,
s->inner->filename);
275 if (
s->fd < 0 ||
s->mapfd < 0) {
315 if (!
s->cache_data) {
324 h->max_packet_size =
s->block_size;
325 h->min_packet_size =
s->block_size;
340 munmap(
s->cache_data,
s->cache_size);
341 s->cache_data =
NULL;
346 int ret = fstat(
s->fd, &st);
359 s->cache_data = mmap(
NULL,
filesize, PROT_READ | PROT_WRITE, MAP_SHARED,
s->fd, 0);
360 if (
s->cache_data == MAP_FAILED) {
361 s->cache_data =
NULL;
372 struct flock fl = { .l_type = F_WRLCK };
373 int ret, did_grow = 0;
374 if (map_size <= s->map_size)
379 ret = fstat(
s->mapfd, &st);
385 if (st.st_size >= map_size)
389 ret = fcntl(
s->mapfd, F_SETLKW, &fl);
397 ret = fstat(
s->mapfd, &st);
403 if (st.st_size >= map_size)
406 ret = ftruncate(
s->mapfd, map_size);
411 st.st_size = map_size;
416 munmap(
s->spacemap,
s->map_size);
417 s->map_size = st.st_size;
418 s->spacemap = mmap(
NULL,
s->map_size, PROT_READ | PROT_WRITE, MAP_SHARED,
s->mapfd, 0);
419 if (
s->spacemap == MAP_FAILED) {
427 if (fl.l_type == F_UNLCK)
428 fcntl(
s->mapfd, F_SETLK, &fl);
433 if (fl.l_type == F_UNLCK)
434 fcntl(
s->mapfd, F_SETLK, &fl);
443 size_t map_bytes =
sizeof(
Spacemap) + num_blocks *
sizeof(
Block);
452 if (map_bytes < num_blocks)
455 const off_t old_size =
s->map_size;
461 if (
s->map_size > old_size) {
464 "%s %zu bytes, capacity: %"PRId64
" blocks = %zu MB\n",
465 ret ?
"Resized spacemap to" :
"Mapped spacemap with",
466 (
size_t)
s->map_size, num_blocks,
467 (num_blocks * (
int64_t)
s->block_size) >> 20);
493 ret = set_once_ushort(&
s->spacemap->block_shift,
s->block_shift);
497 "but requested block shift is %d.\n",
shift,
s->block_shift);
498 if (shift < 9 || shift > 30) {
505 ret = set_once_uchar(&
s->spacemap->hash[
i],
hash[
i]);
509 for (
int j = 0; j < 32; j++)
512 for (
int j = 0; j < 32; j++)
592 const int64_t block_pos = block_id *
s->block_size;
593 int block_size =
clamp_size(
h,
s->block_size, block_pos);
598 Block *
const block = &
s->spacemap->blocks[block_id];
609 av_assert1(block_pos + block_size <= s->cache_size);
610 tmp =
s->cache_data + block_pos;
623 "offset 0x%"PRIx64
": expected CRC: 0x%04X, got: 0x%04X\n",
624 block_id, block_pos,
state, crc);
641 if (!
s->retry_errors)
649 memory_order_acquire,
650 memory_order_acquire))
663 }
else if (pending_since) {
665 if (
new - pending_since >=
s->timeout)
680 const int read_only =
s->read_only ||
s->write_err || verify_read;
681 int64_t inner_pos = read_only ?
s->pos : block_pos;
682 if (
s->inner_pos != inner_pos) {
683 inner_pos =
ffurl_seek(
s->inner, inner_pos, SEEK_SET);
693 memory_order_relaxed,
694 memory_order_relaxed);
700 s->inner_pos = inner_pos;
710 if (verify_read && memcmp(buf,
tmp,
ret)) {
712 "in block 0x%"PRIx64
" at offset 0x%"PRIx64
" + %"PRId64
"!\n",
716 s->pos =
s->inner_pos = inner_pos +
ret;
723 tmp =
s->cache_data + block_pos;
736 while (bytes_read < block_size) {
748 memory_order_relaxed,
749 memory_order_relaxed);
757 if (bytes_read < block_size) {
764 if (bytes_read > 0) {
774 memory_order_relaxed,
775 memory_order_relaxed);
779 "offset 0x%"PRIx64
", CRC 0x%04X\n", bytes_read, block_id,
825 return s->pos =
s->inner_pos = res;
852 #define OFFSET(x) offsetof(SharedContext, x)
853 #define D AV_OPT_FLAG_DECODING_PARAM
857 {
"block_shift",
"Set the base 2 logarithm of the block size",
OFFSET(block_shift),
AV_OPT_TYPE_INT, {.i64 = 15}, 9, 30, .flags =
D },
858 {
"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 },
859 {
"cache_verify",
"Verify correctness of the cache against the source",
OFFSET(verify),
AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, .flags =
D },
860 {
"cache_timeout",
"Time in us to wait before re-fetching pending blocks",
OFFSET(timeout),
AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, .flags =
D },
861 {
"retry_errors",
"Re-request blocks even if they previously failed",
OFFSET(retry_errors),
AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, .flags =
D },
static int64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
Change the position that will be used by the next read/write operation on the resource accessed by h.
int64_t av_gettime_relative(void)
Get the current time in microseconds since some unspecified starting point.
#define AV_LOG_WARNING
Something somehow does not look correct.
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
static uint16_t get_block_crc(const uint8_t *block, size_t block_size)
static int shared_close(URLContext *h)
static int set_filesize(URLContext *h, int64_t new_size)
#define AVERROR_EOF
End of file.
static int64_t get_filesize(URLContext *h)
char * av_asprintf(const char *fmt,...)
static int write_cache(SharedContext *s, const uint8_t *buf, size_t size, off_t offset)
@ BLOCK_PENDING
a thread is currently trying to write this block
#define AVSEEK_SIZE
Passing this as the "whence" parameter to a seek function causes it to return the filesize without se...
#define atomic_compare_exchange_weak_explicit(object, expected, desired, success, failure)
const URLProtocol ff_shared_protocol
#define AV_LOG_VERBOSE
Detailed information.
int ffurl_close(URLContext *h)
static uint8_t hash[HASH_SIZE]
static int hash_uri(uint8_t hash[HASH_SIZE], const char *uri)
static av_cold void close(AVCodecParserContext *s)
static int read_cache(SharedContext *s, uint8_t *buf, size_t size, off_t offset)
@ BLOCK_FAILED
the underlying I/O source failed to read this block
int ffurl_get_short_seek(void *urlcontext)
Return the current short seek threshold value for this URL.
static int shared_get_file_handle(URLContext *h)
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
#define FF_ARRAY_ELEMS(a)
atomic_uchar hash[HASH_SIZE]
int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options, const char *whitelist, const char *blacklist, URLContext *parent)
Create an URLContext for accessing to the resource indicated by url, and open it.
static int spacemap_init(URLContext *h, const uint8_t hash[HASH_SIZE])
int avpriv_open(const char *filename, int flags,...)
A wrapper for open() setting O_CLOEXEC.
#define DEF_SET_ONCE(ctype, atype)
int av_hash_alloc(AVHashContext **ctx, const char *name)
Allocate a hash context for the algorithm specified by name.
@ AV_OPT_TYPE_INT64
Underlying C type is int64_t.
#define av_assert0(cond)
assert() equivalent, that is always enabled.
static const AVClass shared_context_class
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
static AVFormatContext * ctx
int write_err
write error occurred
int av_usleep(unsigned usec)
Sleep for a period of time.
#define atomic_load(object)
#define atomic_compare_exchange_strong_explicit(object, expected, desired, success, failure)
@ BLOCK_NONE
block is not cached
#define LIBAVUTIL_VERSION_INT
Describe the class of an AVClass context structure.
void av_hash_init(AVHashContext *ctx)
Initialize or reset a hash context.
const char * av_default_item_name(void *ptr)
Return the context name.
int block_shift
requested shift; may disagree with actual
static int spacemap_remap(URLContext *h, size_t map_size)
void av_hash_update(AVHashContext *ctx, const uint8_t *src, size_t len)
Update a hash context with additional data.
#define atomic_load_explicit(object, order)
void av_hash_freep(AVHashContext **ctx)
Free hash context and set hash context pointer to NULL.
off_t cache_size
size of mapped memory region (for munmap)
static int shift(int a, int b)
#define i(width, name, range_min, range_max)
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
void av_hash_final(AVHashContext *ctx, uint8_t *dst)
Finalize a hash context and compute the actual hash value.
static int shared_get_short_seek(URLContext *h)
const AVCRC * av_crc_get_table(AVCRCId crc_id)
Get an initialized standard CRC table.
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf offset
int av_strstart(const char *str, const char *pfx, const char **ptr)
Return non-zero if pfx is a prefix of str.
static int spacemap_grow(URLContext *h, int64_t block)
static int64_t filesize(AVIOContext *pb)
static int64_t shared_seek(URLContext *h, int64_t pos, int whence)
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
#define atomic_store_explicit(object, desired, order)
uint8_t * cache_data
optional mmap of the cache file
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
static const AVOption options[]
uint32_t av_crc(const AVCRC *ctx, uint32_t crc, const uint8_t *buffer, size_t length)
Calculate the CRC of a block.
static int shared_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
@ AV_OPT_TYPE_INT
Underlying C type is int.
int av_hash_get_size(const AVHashContext *ctx)
#define HASH_METHOD
This hash should be resistant against collision attacks, so that an attacker could not generate e....
static int cache_map(URLContext *h, int64_t filesize)
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
int64_t pos
current logical position
The exact code depends on how similar the blocks are and how related they are to the block
int64_t ffurl_size(URLContext *h)
Return the filesize of the resource accessed by h, AVERROR(ENOSYS) if the operation is not supported ...
static int shared_read(URLContext *h, unsigned char *buf, int size)
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
static size_t clamp_size(URLContext *h, size_t size, int64_t pos)
atomic_ushort block_shift
int ffurl_get_file_handle(URLContext *h)
Return the file descriptor associated with this URL.
static int ffurl_read(URLContext *h, uint8_t *buf, int size)
Read up to size bytes from the resource accessed by h, and store the read bytes in buf.