62#define HASH_METHOD "SHA512/256"
64#define HEADER_MAGIC MKTAG(u'\xFF', 'S', 'h', '$')
65#define HEADER_VERSION 3
71#define MAX_CORRUPT_BLOCKS 10
118typedef struct Block {
134static_assert(offsetof(
Spacemap, blocks) == 128,
"Spacemap header layout mismatch");
137#define DEF_SET_ONCE(ctype, atype) \
138 static int set_once_##atype(atomic_##atype *const ptr, const ctype value) \
141 av_assert1(value != 0); \
142 if (atomic_compare_exchange_strong_explicit( \
143 ptr, &prev, value, memory_order_release, memory_order_relaxed)) \
145 else if (prev == value) \
148 return AVERROR(EINVAL); \
204 munmap(
s->cache_data,
s->cache_size);
206 munmap(
s->spacemap,
s->map_size);
216 s->nb_hit,
s->nb_miss);
229 if (
size > INT64_MAX)
246 ret = set_once_ullong(&
s->spacemap->filesize, new_size);
249 "%"PRId64
", got: %"PRIu64
"!\n", new_size,
281 if (!
s->cache_dir || !
s->cache_dir[0]) {
283 "directory using the -cache_dir option.\n");
287 s->fd =
s->mapfd = -1;
292 options,
h->protocol_whitelist,
h->protocol_blacklist,
h);
295 "Continuing with cache file only.\n",
av_err2str(ret));
306 char filename[2 * 16 + 1];
308 s->cache_path =
av_asprintf(
"%s/%s.cache",
s->cache_dir, filename);
309 s->map_path =
av_asprintf(
"%s/%s.spacemap",
s->cache_dir, filename);
310 if (!
s->cache_path || !
s->map_path) {
316 s->cache_path,
s->inner ?
s->inner->filename :
arg);
318 const int mode = O_RDWR | (
s->inner ? O_CREAT : 0);
321 if (
s->fd < 0 ||
s->mapfd < 0) {
333 s->block_size = 1 <<
s->block_shift;
334 s->blocks_max =
s->cache_size_max >>
s->block_shift;
352 int64_t last_block = last_pos >>
s->block_shift;
361 "back to normal read/write\n",
av_err2str(ret));
373 h->max_packet_size =
s->block_size;
374 h->min_packet_size =
s->block_size;
390 munmap(
s->cache_data,
s->cache_size);
391 s->cache_data =
NULL;
396 int ret = fstat(
s->fd, &st);
409 s->cache_data = mmap(
NULL,
filesize, PROT_READ | PROT_WRITE, MAP_SHARED,
s->fd, 0);
410 if (
s->cache_data == MAP_FAILED) {
411 s->cache_data =
NULL;
422 int ret, did_grow = 0, locked = 0;
423 if (map_size <= s->map_size)
428 ret = fstat(
s->mapfd, &st);
434 if (st.st_size >= map_size)
438 ret = flock(
s->mapfd, LOCK_EX);
446 ret = fstat(
s->mapfd, &st);
452 if (st.st_size >= map_size)
455 ret = ftruncate(
s->mapfd, map_size);
460 st.st_size = map_size;
465 munmap(
s->spacemap,
s->map_size);
466 s->map_size = st.st_size;
467 s->spacemap = mmap(
NULL,
s->map_size, PROT_READ | PROT_WRITE, MAP_SHARED,
s->mapfd, 0);
468 if (
s->spacemap == MAP_FAILED) {
476 flock(
s->mapfd, LOCK_UN);
484 flock(
s->mapfd, LOCK_UN);
493 size_t map_bytes =
sizeof(
Spacemap) + num_blocks *
sizeof(
Block);
505 if (map_bytes < num_blocks)
508 const off_t old_size =
s->map_size;
514 if (
s->map_size > old_size) {
517 "%s %zu bytes, capacity: %"PRId64
" blocks = %"PRId64
" MB\n",
518 ret ?
"Resized spacemap to" :
"Mapped spacemap with",
519 (
size_t)
s->map_size, num_blocks,
520 (num_blocks * (
int64_t)
s->block_size) >> 20);
534 if ((ret = set_once_uint(&
s->spacemap->header_magic,
HEADER_MAGIC)) < 0 ||
546 ret = set_once_ushort(&
s->spacemap->block_shift,
s->block_shift);
550 "but requested block shift is %d.\n",
shift,
s->block_shift);
559 ret = set_once_uchar(&
s->spacemap->hash[
i],
hash[
i]);
649 const int64_t block_id =
s->pos >>
s->block_shift;
651 const int64_t block_pos = block_id *
s->block_size;
657 Block *
const block = &
s->spacemap->blocks[block_id];
660 int verify_read = 0, acquired = 0, allocated = 0;
676 av_assert1(block_pos + block_size <= s->cache_size);
677 tmp =
s->cache_data + block_pos;
684 if (
s->retry_corrupt) {
697 "offset 0x%"PRIx64
": expected CRC: 0x%08X, got: 0x%08X\n",
698 block_id, block_pos,
state, crc);
699 if (
s->retry_corrupt) {
729 "assuming cache file is completely broken.\n");
735 if (
s->read_only ||
s->write_err || !
s->inner)
737 else if (
s->cache_size_max) {
740 if (cached >=
s->blocks_max) {
742 "blocks = %"PRId64
" bytes), switching to read-only mode.\n",
743 s->blocks_max,
s->blocks_max <<
s->block_shift);
767 }
else if (pending_since) {
769 if (
new - pending_since >=
s->timeout)
788#define RELEASE_PENDING(block, state) \
791 av_assert1(state == BLOCK_PENDING); \
792 atomic_compare_exchange_strong_explicit( \
793 &block->state, &state, BLOCK_NONE, memory_order_relaxed, \
794 memory_order_relaxed); \
803 "0x%"PRIx64
", but underlying protocol is not available!\n",
804 block_id, block_pos);
809 const int read_only =
s->read_only ||
s->write_err || verify_read;
810 int64_t inner_pos = read_only ?
s->pos : block_pos;
811 if (
s->inner_pos != inner_pos) {
812 inner_pos =
ffurl_seek(
s->inner, inner_pos, SEEK_SET);
821 s->inner_pos = inner_pos;
831 s->inner_pos = inner_pos + ret;
835 if (verify_read && memcmp(buf,
tmp, ret)) {
837 "in block 0x%"PRIx64
" at offset 0x%"PRIx64
" + %"PRId64
"!\n",
838 ret, block_id, block_pos,
offset);
842 s->pos =
s->inner_pos;
847 if (
s->cache_data && acquired) {
849 tmp =
s->cache_data + block_pos;
862 while (bytes_read < block_size) {
863 ret =
ffurl_read(
s->inner, &
tmp[bytes_read], block_size - bytes_read);
888 if (bytes_read < block_size) {
897 if (bytes_read > 0) {
909 "offset 0x%"PRIx64
", CRC 0x%08X\n", bytes_read, block_id,
950 "Continuing with cache file only.\n",
av_err2str(res));
970 "Continuing with cache file only.\n",
av_err2str(res));
980 return s->pos =
s->inner_pos = res;
1002 return ret > 0 ?
FFMAX(ret,
s->block_size) :
s->block_size;
1005#define OFFSET(x) offsetof(SharedContext, x)
1006#define D AV_OPT_FLAG_DECODING_PARAM
1010 {
"block_shift",
"Set the base 2 logarithm of the block size",
OFFSET(block_shift),
AV_OPT_TYPE_INT, {.i64 = 15}, 9, 30, .flags =
D },
1011 {
"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 },
1012 {
"cache_verify",
"Verify correctness of the cache against the source",
OFFSET(verify),
AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, .flags =
D },
1013 {
"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 },
1014 {
"ignore_errors",
"Continue even if the inner URL failed",
OFFSET(ignore_errors),
AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, .flags =
D },
1015 {
"retry_errors",
"Re-request blocks even if they previously failed",
OFFSET(retry_errors),
AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, .flags =
D },
1016 {
"retry_corrupt",
"Re-request blocks that fail the CRC check",
OFFSET(retry_corrupt),
AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, .flags =
D },
1017 {
"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 },
1022 .class_name =
"shared",
static int read_block(ALSDecContext *ctx, ALSBlockData *bd)
Read the block data.
static uint8_t hash[HASH_SIZE]
static AVFormatContext * ctx
static av_cold void close(AVCodecParserContext *s)
simple assert() macros that are a bit more flexible than ISO C assert().
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
#define av_assert0(cond)
assert() equivalent, that is always enabled.
int ff_check_interrupt(AVIOInterruptCB *cb)
Check if the user has requested to interrupt a blocking function associated with cb.
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.
int64_t ffurl_size(URLContext *h)
Return the filesize of the resource accessed by h, AVERROR(ENOSYS) if the operation is not supported ...
int ffurl_closep(URLContext **hh)
Close the resource accessed by the URLContext h, and free the memory used by it.
int ffurl_close(URLContext *h)
int ffurl_get_short_seek(void *urlcontext)
Return the current short seek threshold value for this URL.
int ffurl_get_file_handle(URLContext *h)
Return the file descriptor associated with this URL.
#define AVSEEK_SIZE
Passing this as the "whence" parameter to a seek function causes it to return the filesize without se...
#define AVIO_FLAG_NONBLOCK
Use non-blocking mode.
char * av_asprintf(const char *fmt,...)
#define flags(name, subs,...)
#define i(width, name, range_min, range_max)
Public header for CRC hash function implementation.
static struct @346255127015250356166251341105367306144006377143 state
static int64_t filesize(AVIOContext *pb)
@ AV_OPT_TYPE_INT64
Underlying C type is int64_t.
@ AV_OPT_TYPE_INT
Underlying C type is int.
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
@ 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...
const AVCRC * av_crc_get_table(AVCRCId crc_id)
Get an initialized standard CRC table.
uint32_t av_crc(const AVCRC *ctx, uint32_t crc, const uint8_t *buffer, size_t length)
Calculate the CRC of a block.
#define AVERROR_EXIT
Immediate exit was requested; the called function should not be restarted.
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
#define AVERROR_EOF
End of file.
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
void av_hash_freep(AVHashContext **ctx)
Free hash context and set hash context pointer to NULL.
void av_hash_init(AVHashContext *ctx)
Initialize or reset a hash context.
void av_hash_update(AVHashContext *ctx, const uint8_t *src, size_t len)
Update a hash context with additional data.
int av_hash_alloc(AVHashContext **ctx, const char *name)
Allocate a hash context for the algorithm specified by name.
void av_hash_final(AVHashContext *ctx, uint8_t *dst)
Finalize a hash context and compute the actual hash value.
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
#define AV_LOG_WARNING
Something somehow does not look correct.
#define AV_LOG_VERBOSE
Detailed information.
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
const char * av_default_item_name(void *ptr)
Return the context name.
int av_strstart(const char *str, const char *pfx, const char **ptr)
Return non-zero if pfx is a prefix of str.
#define LIBAVUTIL_VERSION_INT
int av_hash_get_size(const AVHashContext *ctx)
static int shift(int a, int b)
Macro definitions for various function/variable attributes.
int avpriv_open(const char *filename, int flags,...)
A wrapper for open() setting O_CLOEXEC.
Memory handling functions.
const URLProtocol ff_shared_protocol
static int shared_read(URLContext *h, unsigned char *buf, int size)
static int read_cache(SharedContext *s, uint8_t *buf, size_t size, off_t offset)
static int spacemap_grow(URLContext *h, int64_t block)
static int is_ignorable_error(int64_t err)
#define DEF_SET_ONCE(ctype, atype)
static int set_filesize(URLContext *h, int64_t new_size)
static int shared_get_short_seek(URLContext *h)
#define HASH_METHOD
This hash should be resistant against collision attacks, so that an attacker could not generate e....
static int shared_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
static int64_t shared_seek(URLContext *h, int64_t pos, int whence)
#define RELEASE_PENDING(block, state)
static int shared_close(URLContext *h)
static int shared_get_file_handle(URLContext *h)
static int64_t get_filesize(URLContext *h)
static int hash_uri(uint8_t hash[HASH_SIZE], const char *uri)
static int cache_map(URLContext *h, int64_t filesize)
static int write_cache(SharedContext *s, const uint8_t *buf, size_t size, off_t offset)
static const AVClass shared_context_class
static int spacemap_remap(URLContext *h, size_t map_size)
#define MAX_CORRUPT_BLOCKS
Hard watershed of consecutive failed blocks before we give up on the cache file altogether and assume...
static uint32_t get_block_crc(const uint8_t *block, size_t block_size)
static int spacemap_init(URLContext *h, const uint8_t hash[HASH_SIZE])
@ BLOCK_NONE
block is not cached
@ BLOCK_PENDING
a thread is currently trying to write this block
@ BLOCK_FAILED
the underlying I/O source failed to read this block
static int clamp_size(URLContext *h, int size, int64_t pos, int64_t filesize)
#define atomic_fetch_add_explicit(object, operand, order)
unsigned char atomic_uchar
FF_ATOMIC_ALIGN64 unsigned long long atomic_ullong
#define atomic_compare_exchange_strong_explicit(object, expected, desired, success, failure)
#define atomic_load_explicit(object, order)
unsigned short atomic_ushort
#define atomic_load(object)
#define atomic_store_explicit(object, desired, order)
Describe the class of an AVClass context structure.
int write_err
write error occurred
int block_shift
requested shift; updated on init if it disagrees
int64_t filesize
once known
uint8_t * cache_data
optional mmap of the cache file
int64_t blocks_max
maximum number of blocks to cache
int64_t pos
current logical position
off_t cache_size
size of mapped memory region (for munmap)
atomic_uchar hash[HASH_SIZE]
atomic_ushort block_shift
atomic_ullong blocks_cached
int av_usleep(unsigned usec)
Sleep for a period of time.
int64_t av_gettime_relative(void)
Get the current time in microseconds since some unspecified starting point.
unbuffered private I/O API
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.
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.