Improving cache invalidation during playback, to add an epoch counter to Timeline, so we know when an update took place outside of timeline (i.e. in our video cache thread) and can rescan our entire cache range for potentially missing frames after cache invalidation).

This commit is contained in:
Jonathan Thomas
2026-03-01 17:45:02 -06:00
parent 90d0710d78
commit 83d4d1f8ea
5 changed files with 64 additions and 7 deletions

View File

@@ -40,6 +40,8 @@ namespace openshot
, reader(nullptr)
, force_directional_cache(false)
, last_cached_index(0)
, seen_timeline_cache_epoch(0)
, timeline_cache_epoch_initialized(false)
{
}
@@ -130,6 +132,15 @@ namespace openshot
return !isThreadRunning();
}
void VideoCacheThread::Reader(ReaderBase* new_reader)
{
std::lock_guard<std::mutex> guard(seek_state_mutex);
reader = new_reader;
seen_timeline_cache_epoch = 0;
timeline_cache_epoch_initialized = false;
Play();
}
void VideoCacheThread::Seek(int64_t new_position, bool start_preroll)
{
bool should_mark_seek = false;
@@ -471,6 +482,27 @@ namespace openshot
last_dir.store(dir);
}
// If timeline-side cache invalidation occurred (e.g. ApplyJsonDiff / SetJson),
// restart fill from the active playhead window so invalidated gaps self-heal.
if (timeline) {
bool epoch_changed = false;
{
std::lock_guard<std::mutex> guard(seek_state_mutex);
const uint64_t timeline_epoch = timeline->CacheEpoch();
if (!timeline_cache_epoch_initialized) {
seen_timeline_cache_epoch = timeline_epoch;
timeline_cache_epoch_initialized = true;
}
else if (timeline_epoch != seen_timeline_cache_epoch) {
seen_timeline_cache_epoch = timeline_epoch;
epoch_changed = true;
}
}
if (epoch_changed) {
handleUserSeek(playhead, dir);
}
}
// Compute bytes_per_frame, max_bytes, and capacity once
int64_t bytes_per_frame = getBytes(
(timeline->preview_width ? timeline->preview_width : reader->info.width),