67#define HASH_METHOD "SHA512/256"
69#define HEADER_MAGIC MKTAG(u'\xFF', 'S', 'h', '$')
70#define HEADER_VERSION 3
76#define MAX_CORRUPT_BLOCKS 10
123typedef struct Block {
139static_assert(offsetof(
Spacemap, blocks) == 128,
"Spacemap header layout mismatch");
142#define DEF_SET_ONCE(ctype, atype) \
143 static int set_once_##atype(atomic_##atype *const ptr, const ctype value) \
146 av_assert1(value != 0); \
147 if (atomic_compare_exchange_strong_explicit( \
148 ptr, &prev, value, memory_order_release, memory_order_relaxed)) \
150 else if (prev == value) \
153 return AVERROR(EINVAL); \
219 s->nb_hit,
s->nb_miss);
232 if (
size > INT64_MAX)
249 ret = set_once_ullong(&
s->spacemap->filesize, new_size);
252 "%"PRId64
", got: %"PRIu64
"!\n", new_size,
284 if (!
s->cache_dir || !
s->cache_dir[0]) {
286 "directory using the -cache_dir option.\n");
290 s->fd =
s->mapfd = -1;
295 options,
h->protocol_whitelist,
h->protocol_blacklist,
h);
298 "Continuing with cache file only.\n",
av_err2str(ret));
309 char filename[2 * 16 + 1];
311 s->cache_path =
av_asprintf(
"%s/%s.cache",
s->cache_dir, filename);
312 s->map_path =
av_asprintf(
"%s/%s.spacemap",
s->cache_dir, filename);
313 if (!
s->cache_path || !
s->map_path) {
319 s->cache_path,
s->inner ?
s->inner->filename :
arg);
321 const int mode = O_RDWR |
O_BINARY | (
s->inner ? O_CREAT : 0);
324 if (
s->fd < 0 ||
s->mapfd < 0) {
336 s->block_size = 1 <<
s->block_shift;
337 s->blocks_max =
s->cache_size_max >>
s->block_shift;
355 int64_t last_block = last_pos >>
s->block_shift;
364 "back to normal read/write\n",
av_err2str(ret));
376 h->max_packet_size =
s->block_size;
377 h->min_packet_size =
s->block_size;
394 s->cache_data =
NULL;
414 int ret, did_grow = 0, locked = 0;
415 if (map_size <= s->map_size)
420 ret = fstat(
s->mapfd, &st);
426 if (st.st_size >= map_size)
430 ret = flock(
s->mapfd, LOCK_EX);
438 ret = fstat(
s->mapfd, &st);
444 if (st.st_size >= map_size)
448 st.st_size = map_size;
454 s->map_size = st.st_size;
465 flock(
s->mapfd, LOCK_UN);
473 flock(
s->mapfd, LOCK_UN);
482 size_t map_bytes =
sizeof(
Spacemap) + num_blocks *
sizeof(
Block);
494 if (map_bytes < num_blocks)
497 const off_t old_size =
s->map_size;
503 if (
s->map_size > old_size) {
506 "%s %zu bytes, capacity: %"PRId64
" blocks = %"PRId64
" MB\n",
507 ret ?
"Resized spacemap to" :
"Mapped spacemap with",
508 (
size_t)
s->map_size, num_blocks,
509 (num_blocks * (
int64_t)
s->block_size) >> 20);
523 if ((ret = set_once_uint(&
s->spacemap->header_magic,
HEADER_MAGIC)) < 0 ||
535 ret = set_once_ushort(&
s->spacemap->block_shift,
s->block_shift);
539 "but requested block shift is %d.\n",
shift,
s->block_shift);
548 ret = set_once_uchar(&
s->spacemap->hash[
i],
hash[
i]);
638 const int64_t block_id =
s->pos >>
s->block_shift;
640 const int64_t block_pos = block_id *
s->block_size;
646 Block *
const block = &
s->spacemap->blocks[block_id];
649 int verify_read = 0, acquired = 0, allocated = 0;
665 av_assert1(block_pos + block_size <= s->cache_size);
666 tmp =
s->cache_data + block_pos;
673 if (
s->retry_corrupt) {
686 "offset 0x%"PRIx64
": expected CRC: 0x%08X, got: 0x%08X\n",
687 block_id, block_pos,
state, crc);
688 if (
s->retry_corrupt) {
718 "assuming cache file is completely broken.\n");
724 if (
s->read_only ||
s->write_err || !
s->inner)
726 else if (
s->cache_size_max) {
729 if (cached >=
s->blocks_max) {
731 "blocks = %"PRId64
" bytes), switching to read-only mode.\n",
732 s->blocks_max,
s->blocks_max <<
s->block_shift);
756 }
else if (pending_since) {
758 if (
new - pending_since >=
s->timeout)
777#define RELEASE_PENDING(block, state) \
780 av_assert1(state == BLOCK_PENDING); \
781 atomic_compare_exchange_strong_explicit( \
782 &block->state, &state, BLOCK_NONE, memory_order_relaxed, \
783 memory_order_relaxed); \
792 "0x%"PRIx64
", but underlying protocol is not available!\n",
793 block_id, block_pos);
798 const int read_only =
s->read_only ||
s->write_err || verify_read;
799 int64_t inner_pos = read_only ?
s->pos : block_pos;
800 if (
s->inner_pos != inner_pos) {
801 inner_pos =
ffurl_seek(
s->inner, inner_pos, SEEK_SET);
810 s->inner_pos = inner_pos;
820 s->inner_pos = inner_pos + ret;
824 if (verify_read && memcmp(buf,
tmp, ret)) {
826 "in block 0x%"PRIx64
" at offset 0x%"PRIx64
" + %"PRId64
"!\n",
827 ret, block_id, block_pos,
offset);
831 s->pos =
s->inner_pos;
836 if (
s->cache_data && acquired) {
838 tmp =
s->cache_data + block_pos;
851 while (bytes_read < block_size) {
852 ret =
ffurl_read(
s->inner, &
tmp[bytes_read], block_size - bytes_read);
877 if (bytes_read < block_size) {
886 if (bytes_read > 0) {
898 "offset 0x%"PRIx64
", CRC 0x%08X\n", bytes_read, block_id,
939 "Continuing with cache file only.\n",
av_err2str(res));
959 "Continuing with cache file only.\n",
av_err2str(res));
969 return s->pos =
s->inner_pos = res;
991 return ret > 0 ?
FFMAX(ret,
s->block_size) :
s->block_size;
994#define OFFSET(x) offsetof(SharedContext, x)
995#define D AV_OPT_FLAG_DECODING_PARAM
999 {
"block_shift",
"Set the base 2 logarithm of the block size",
OFFSET(block_shift),
AV_OPT_TYPE_INT, {.i64 = 15}, 9, 30, .flags =
D },
1000 {
"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 },
1001 {
"cache_verify",
"Verify correctness of the cache against the source",
OFFSET(verify),
AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, .flags =
D },
1002 {
"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 },
1003 {
"ignore_errors",
"Continue even if the inner URL failed",
OFFSET(ignore_errors),
AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, .flags =
D },
1004 {
"retry_errors",
"Re-request blocks even if they previously failed",
OFFSET(retry_errors),
AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, .flags =
D },
1005 {
"retry_corrupt",
"Re-request blocks that fail the CRC check",
OFFSET(retry_corrupt),
AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, .flags =
D },
1006 {
"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 },
1011 .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)
const VDPAUPixFmtMap * map
static int shift(int a, int b)
Macro definitions for various function/variable attributes.
int av_file_map_shared(int fd, size_t size, void **bufptr)
Map the beginning of an open file into memory for shared read and write access.
void av_file_unmap_shared(void *bufptr, size_t size)
Unmap the memory mapped by av_file_map_shared().
int avpriv_open(const char *filename, int flags,...)
A wrapper for open() setting O_CLOEXEC.
Memory handling functions.
miscellaneous OS support macros and 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 mapping 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 unmapping)
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.