OpenShot Library | libopenshot 1.1.1
Loading...
Searching...
No Matches
VideoCacheThread.h
Go to the documentation of this file.
1
9// Copyright (c) 2008-2025 OpenShot Studios, LLC
10//
11// SPDX-License-Identifier: LGPL-3.0-or-later
12
13#ifndef OPENSHOT_VIDEO_CACHE_THREAD_H
14#define OPENSHOT_VIDEO_CACHE_THREAD_H
15
16#include "ReaderBase.h"
17#include "AdaptivePreroll.h"
18
19#include <AppConfig.h>
20#include <juce_audio_basics/juce_audio_basics.h>
21#include <atomic>
22#include <cstdint>
23#include <mutex>
24#include <memory>
25
26namespace openshot
27{
28 class Settings;
29 using juce::Thread;
30
38 class VideoCacheThread : public Thread
39 {
40 public:
43 ~VideoCacheThread() override;
44
46 bool isReady();
48 bool UsesCachedAudio() const { return cache_audio_only.load(); }
49 void NotifyAudioCacheMiss() { audio_cache_miss.store(true); }
51
53 void Play() { notify(); };
54
56 void Stop();
57
65 void setSpeed(int new_speed);
66
68 int getSpeed() const { return speed.load(); }
69
71 void Seek(int64_t new_position);
72
78 void Seek(int64_t new_position, bool start_preroll);
79
81 void NotifyPlaybackPosition(int64_t new_position);
82
84 bool StartThread();
85
87 bool StopThread(int timeoutMs = 0);
88
91 void EnableRequestDiagnostics(bool enabled) { diagnostics_enabled.store(enabled); }
92 int64_t CancellationAcknowledgementUs() const { return acknowledgement_us.load(); }
93 int64_t ObsoleteCompletionUs() const { return obsolete_completion_us.load(); }
94 int64_t FirstCurrentFrameUs() const { return first_current_frame_us.load(); }
95
100 void Reader(ReaderBase* new_reader);
101
102 protected:
104 void run() override;
105
115 int64_t getBytes(int width, int height, int sample_rate, int channels, float fps);
116
117 //---------- Helper functions, broken out for clarity & unit testing ----------
118
120 int computeDirection() const;
121
127 void handleUserSeek(int64_t playhead, int dir);
128
136 void handleUserSeekWithPreroll(int64_t playhead,
137 int dir,
138 int64_t timeline_end,
139 int64_t preroll_frames);
140
142 int64_t computePrerollFrames(const Settings* settings) const;
143
145 int64_t resolveTimelineEnd() const;
146
148 int64_t clampToTimelineRange(int64_t frame, int64_t timeline_end) const;
149
157 bool clearCacheIfPaused(int64_t playhead, bool paused, CacheBase* cache);
158
172 void computeWindowBounds(int64_t playhead,
173 int dir,
174 int64_t ahead_count,
175 int64_t timeline_end,
176 int64_t& window_begin,
177 int64_t& window_end) const;
178
192 bool prefetchWindow(CacheBase* cache,
193 int64_t window_begin,
194 int64_t window_end,
195 int dir,
197 int64_t max_frames_to_fetch = -1,
198 uint64_t generation = UINT64_MAX);
199
201 void updateReadiness(CacheBase* cache, int64_t playhead, int dir);
202 void resetReadiness(); // Requires seek_state_mutex.
203
205 int64_t contiguous_ahead = 0;
208 std::atomic<bool> audio_cache_miss{false};
209 std::atomic<bool> cache_audio_only{false};
215 uint64_t readiness_epoch = 0;
216 uint64_t readiness_generation = UINT64_MAX;
218
219 //---------- Internal state ----------
220
221 std::shared_ptr<Frame> last_cached_frame;
222
223 std::atomic<int> speed;
224 std::atomic<int> last_speed;
225 std::atomic<int> last_dir;
226 std::atomic<bool> userSeeked;
227 std::atomic<bool> preroll_on_next_fill;
228 std::atomic<bool> clear_cache_on_next_fill;
229 std::atomic<bool> scrub_active;
230
231 std::atomic<int64_t> requested_display_frame;
233 std::atomic<int64_t> cached_frame_count;
234
235 std::atomic<int64_t> min_frames_ahead;
237
242
243 std::atomic<int64_t> last_cached_index;
244 std::atomic<uint64_t> processed_generation{UINT64_MAX};
245 std::atomic<uint64_t> request_generation{0};
246 std::atomic<bool> diagnostics_enabled{false};
247 std::atomic<int64_t> request_time_us{0};
248 std::atomic<int64_t> acknowledgement_us{0};
249 std::atomic<int64_t> obsolete_completion_us{0};
250 std::atomic<int64_t> first_current_frame_us{0};
251 void acknowledgeRequest(int64_t started_us);
252 static int64_t monotonicUs();
253 mutable std::mutex lifecycle_mutex;
254 mutable std::mutex seek_state_mutex;
255 };
256
257} // namespace openshot
258
259#endif // OPENSHOT_VIDEO_CACHE_THREAD_H
Adaptive preview pre-roll policy.
Header file for ReaderBase class.
All cache managers in libopenshot are based on this CacheBase class.
Definition CacheBase.h:35
This abstract class is the base class, used by all readers in libopenshot.
Definition ReaderBase.h:76
This class is contains settings used by libopenshot (and can be safely toggled at any point)
Definition Settings.h:26
Handles prefetching and caching of video/audio frames for smooth playback.
std::atomic< bool > diagnostics_enabled
int64_t ObsoleteCompletionUs() const
bool StopThread(int timeoutMs=0)
Stop the cache thread (wait up to timeoutMs ms). Returns true if it stopped.
void setSpeed(int new_speed)
Set playback speed/direction. Positive = forward, negative = rewind, zero = pause.
bool force_directional_cache
(Reserved for future use).
std::atomic< int64_t > acknowledgement_us
void handleUserSeekWithPreroll(int64_t playhead, int dir, int64_t timeline_end, int64_t preroll_frames)
Reset last_cached_index to start caching with a directional preroll offset.
int64_t resolveTimelineEnd() const
Resolve timeline end frame from reader/timeline metadata.
int64_t timeline_max_frame
Highest valid frame index in the timeline.
void Play()
Play method is unimplemented.
int64_t clampToTimelineRange(int64_t frame, int64_t timeline_end) const
Clamp frame index to [1, timeline_end] when timeline_end is valid.
void Stop()
Stop method is unimplemented.
std::atomic< bool > preroll_on_next_fill
True if next cache rebuild should include preroll offset.
bool clearCacheIfPaused(int64_t playhead, bool paused, CacheBase *cache)
When paused and playhead is outside current cache, clear all frames.
std::atomic< int64_t > last_cached_index
Index of the most recently cached frame.
std::atomic< int > speed
Current playback speed (0=paused, >0 forward, <0 backward).
void updateReadiness(CacheBase *cache, int64_t playhead, int dir)
Worker-only cache probing; never runs in the audio callback.
void run() override
Thread entry point: loops until threadShouldExit() is true.
int64_t computePrerollFrames(const Settings *settings) const
Compute preroll frame count from settings.
std::atomic< uint64_t > processed_generation
Last request whose scheduling baseline was consumed.
bool isReady()
Shared startup gate; bounded fallback resumes established video decoding.
std::mutex seek_state_mutex
Protects coherent seek state updates/consumption.
uint64_t readiness_generation
Request covered by the readiness snapshot.
bool prefetchWindow(CacheBase *cache, int64_t window_begin, int64_t window_end, int dir, ReaderBase *reader, int64_t max_frames_to_fetch=-1, uint64_t generation=UINT64_MAX)
Prefetch all missing frames in [window_begin ... window_end] or [window_end ... window_begin].
std::atomic< bool > cache_audio_only
int64_t current_display_frame
Currently displayed frame (unused here, reserved).
VideoCacheThread()
Constructor: initializes member variables and assumes forward direction on first launch.
uint64_t seen_timeline_cache_epoch
Last observed Timeline cache invalidation epoch.
std::atomic< int64_t > requested_display_frame
Frame index the user requested.
void Reader(ReaderBase *new_reader)
Attach a ReaderBase (e.g. Timeline, FFmpegReader) and begin caching.
void handleUserSeek(int64_t playhead, int dir)
If userSeeked is true, reset last_cached_index just behind the playhead.
int64_t getBytes(int width, int height, int sample_rate, int channels, float fps)
Estimate memory usage for a single frame (video + audio).
ReaderBase * reader
The source reader (e.g., Timeline, FFmpegReader).
std::atomic< int64_t > first_current_frame_us
void Seek(int64_t new_position)
Backward-compatible alias for playback position updates (no seek side effects).
std::atomic< int64_t > request_time_us
void EnableRequestDiagnostics(bool enabled)
std::atomic< int64_t > cached_frame_count
Estimated count of frames currently stored in cache.
std::atomic< int > last_dir
Last direction sign (+1 forward, –1 backward).
std::atomic< int64_t > min_frames_ahead
Minimum number of frames considered “ready” (pre-roll).
std::mutex lifecycle_mutex
Start/stop/reader drain; never acquired by worker.
std::atomic< bool > userSeeked
True if Seek(..., true) was called (forces a cache reset).
std::atomic< bool > audio_cache_miss
std::atomic< uint64_t > request_generation
Invalidates obsolete fills, including seeks retaining cache.
void computeWindowBounds(int64_t playhead, int dir, int64_t ahead_count, int64_t timeline_end, int64_t &window_begin, int64_t &window_end) const
Compute the “window” of frames to cache around playhead.
int64_t CancellationAcknowledgementUs() const
std::atomic< int64_t > obsolete_completion_us
void acknowledgeRequest(int64_t started_us)
bool StartThread()
Start the cache thread at high priority. Returns true if it’s actually running.
bool timeline_cache_epoch_initialized
True once an initial epoch snapshot has been taken.
std::atomic< bool > scrub_active
True while user is dragging/scrubbing the playhead.
bool UsesCachedAudio() const
Callback miss holds the shared gate until the player resynchronizes audio.
void NotifyPlaybackPosition(int64_t new_position)
Update playback position without triggering seek behavior or cache invalidation.
std::atomic< int > last_speed
Last non-zero speed (for tracking).
std::shared_ptr< Frame > last_cached_frame
Last frame pointer added to cache.
std::atomic< bool > clear_cache_on_next_fill
True if next cache loop should clear existing cache ranges.
int64_t FirstCurrentFrameUs() const
This namespace is the default namespace for all code in the openshot library.