Disk-backed image frame cache. Frames are indexed by (videoUri, elapsedTime) for O(1) lookups and evicted oldest-first when total on-disk size exceeds maxCacheSizeMB.
Improvements over JpegCache:
O(1) lookup via nested ConcurrentHashMap (URI → elapsedMs → Jpeg) instead of O(n) TreeSet.find
O(log n) eviction via ConcurrentSkipListSet ordered by creation time; pollFirst() is atomic
AtomicLong byte counter avoids floating-point drift from summing MB values
AtomicBoolean eviction guard prevents concurrent eviction storms without a global lock
scanCache preserves real file creation timestamps for correct age-ordering on restart
Value parameters
cacheClearPct
Fraction of current cache size to free when the limit is exceeded (0 < x <= 1)
Scan the cache root directory and rebuild the in-memory index from existing files. Uses each file's filesystem creation timestamp so eviction ordering reflects true file age after a restart (unlike put(), which stamps Instant.now()). Does NOT trigger eviction during the scan.
Scan the cache root directory and rebuild the in-memory index from existing files. Uses each file's filesystem creation timestamp so eviction ordering reflects true file age after a restart (unlike put(), which stamps Instant.now()). Does NOT trigger eviction during the scan.
Whether the wanted frame is the deinterlaced variant. Deinterlaced frames are separate cache entries, so this has to match what was stored or the lookup misses.