|
OpenShot Library | libopenshot
0.5.0
|
Go to the documentation of this file.
27 : Thread(
"video-cache")
32 , preroll_on_next_fill(false)
33 , clear_cache_on_next_fill(false)
35 , requested_display_frame(1)
36 , current_display_frame(1)
37 , cached_frame_count(0)
39 , timeline_max_frame(0)
41 , force_directional_cache(false)
42 , last_cached_index(0)
43 , seen_timeline_cache_epoch(0)
44 , timeline_cache_epoch_initialized(false)
72 const int64_t timeline_max = timeline->GetMaxFrame();
73 if (timeline_max > 0) {
74 max_frame = timeline_max;
82 int64_t required_ahead = ready_min;
83 int64_t available_ahead = (dir > 0)
84 ? std::max<int64_t>(0, max_frame - playhead)
85 : std::max<int64_t>(0, playhead - 1);
86 required_ahead = std::min(required_ahead, available_ahead);
89 return (cached_index >= playhead + required_ahead);
91 return (cached_index <= playhead - required_ahead);
99 last_dir.store(new_speed > 0 ? 1 : -1);
103 speed.store(new_speed);
114 int64_t bytes =
static_cast<int64_t
>(width) * height *
sizeof(
char) * 4;
116 bytes += ((sample_rate * channels) / fps) *
sizeof(float);
125 startThread(Priority::high);
126 return isThreadRunning();
132 stopThread(timeoutMs);
133 return !isThreadRunning();
151 bool should_mark_seek =
false;
152 bool should_preroll =
false;
154 bool entering_scrub =
false;
155 bool leaving_scrub =
false;
156 bool cache_contains =
false;
157 bool should_clear_cache =
false;
159 const bool same_frame_refresh = (new_position == current_requested);
161 cache_contains = cache->
Contains(clamped_new_position);
165 if (same_frame_refresh) {
166 const bool is_paused = (
speed.load() == 0);
169 if (was_scrubbing && cache && cache_contains) {
171 should_mark_seek =
false;
172 should_preroll =
false;
173 should_clear_cache =
false;
174 new_cached_count = cache->
Count();
178 timeline->ClearAllCache();
180 new_cached_count = 0;
181 should_mark_seek =
true;
182 should_preroll =
true;
183 should_clear_cache =
false;
187 should_mark_seek =
false;
188 should_preroll =
false;
189 should_clear_cache =
false;
190 if (cache && cache_contains) {
191 cache->
Remove(clamped_new_position);
194 new_cached_count = cache->
Count();
198 if (cache && !cache_contains) {
199 should_mark_seek =
true;
201 new_cached_count = 0;
202 should_preroll =
true;
203 should_clear_cache =
true;
208 should_mark_seek =
false;
209 should_preroll =
false;
210 should_clear_cache =
false;
211 new_cached_count = cache->
Count();
214 should_mark_seek =
true;
217 leaving_scrub =
true;
221 const bool is_paused = (
speed.load() == 0);
222 if (is_paused && same_frame_refresh) {
224 should_mark_seek =
false;
225 should_preroll =
false;
226 should_clear_cache =
false;
227 if (cache && cache_contains) {
228 cache->
Remove(clamped_new_position);
231 new_cached_count = cache->
Count();
233 leaving_scrub =
true;
235 else if (is_paused) {
236 if (cache && !cache_contains) {
237 should_mark_seek =
true;
238 new_cached_count = 0;
239 should_clear_cache =
true;
243 should_mark_seek =
false;
244 new_cached_count = cache->
Count();
246 should_mark_seek =
true;
248 entering_scrub =
true;
251 should_mark_seek =
false;
252 should_preroll =
false;
253 should_clear_cache =
false;
255 new_cached_count = cache->
Count();
257 leaving_scrub =
true;
265 if (should_mark_seek || should_preroll || should_clear_cache) {
274 if (entering_scrub) {
290 if (new_position <= 0) {
299 new_cached_count = cache->Count();
311 const int current_speed =
speed.load();
312 if (current_speed != 0) {
313 return (current_speed > 0 ? 1 : -1);
326 int64_t timeline_end,
327 int64_t preroll_frames)
329 int64_t preroll_start = playhead;
330 if (preroll_frames > 0) {
332 preroll_start = std::max<int64_t>(1, playhead - preroll_frames);
335 preroll_start = std::min<int64_t>(timeline_end, playhead + preroll_frames);
348 if (min_frames < 0) {
351 if (max_frames > 0 && min_frames > max_frames) {
352 min_frames = max_frames;
364 const int64_t timeline_max = timeline->GetMaxFrame();
365 if (timeline_max > 0) {
366 timeline_end = timeline_max;
374 if (timeline_end < 1) {
377 return std::clamp<int64_t>(frame, 1, timeline_end);
385 int64_t cache_playhead = playhead;
389 if (paused && !cache->
Contains(cache_playhead)) {
392 timeline->ClearAllCache();
403 int64_t timeline_end,
404 int64_t& window_begin,
405 int64_t& window_end)
const
409 window_begin = playhead;
410 window_end = playhead + ahead_count;
414 window_begin = playhead - ahead_count;
415 window_end = playhead;
418 window_begin = std::max<int64_t>(window_begin, 1);
419 window_end = std::min<int64_t>(window_end, timeline_end);
423 int64_t window_begin,
427 int64_t max_frames_to_fetch)
429 bool window_full =
true;
431 int64_t fetched_this_pass = 0;
434 while ((dir > 0 && next_frame <= window_end) ||
435 (dir < 0 && next_frame >= window_begin))
437 if (threadShouldExit()) {
449 cache->
Add(framePtr);
459 cache->
Touch(next_frame);
467 if (max_frames_to_fetch > 0 && fetched_this_pass >= max_frames_to_fetch) {
477 using micro_sec = std::chrono::microseconds;
478 using double_micro_sec = std::chrono::duration<double, micro_sec::period>;
480 while (!threadShouldExit()) {
488 if (should_clear_cache && timeline) {
504 std::this_thread::sleep_for(double_micro_sec(50000));
512 std::this_thread::sleep_for(double_micro_sec(50000));
518 bool paused = (
speed.load() == 0);
525 if (
speed.load() != 0) {
532 bool epoch_changed =
false;
535 const uint64_t timeline_epoch = timeline->
CacheEpoch();
542 epoch_changed =
true;
559 int64_t capacity = 0;
560 if (max_bytes > 0 && bytes_per_frame > 0) {
561 capacity = max_bytes / bytes_per_frame;
568 bool did_user_seek =
false;
569 bool use_preroll =
false;
585 if (use_preroll && paused) {
592 else if (!paused && capacity >= 1) {
596 int64_t window_begin, window_end;
606 bool outside_window =
609 if (outside_window) {
618 if (should_clear_mid_loop && timeline) {
626 std::this_thread::sleep_for(double_micro_sec(10000));
632 std::this_thread::sleep_for(double_micro_sec(50000));
635 int64_t ahead_count =
static_cast<int64_t
>(capacity *
637 int64_t window_size = ahead_count + 1;
638 if (window_size < 1) {
641 int64_t ready_target = window_size - 1;
642 if (ready_target < 0) {
646 const int64_t required_ahead = std::min<int64_t>(configured_min, ready_target);
656 int64_t window_begin, window_end;
665 int64_t max_frames_to_fetch = -1;
670 max_frames_to_fetch = 8;
682 if (paused && window_full) {
683 cache->
Touch(playhead);
687 int64_t sleep_us =
static_cast<int64_t
>(
690 std::this_thread::sleep_for(double_micro_sec(sleep_us));
Header file for global Settings class.
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
VideoCacheThread()
Constructor: initializes member variables and assumes forward direction on first launch.
float ToFloat()
Return this fraction as a float (i.e. 1/2 = 0.5)
float VIDEO_CACHE_PERCENT_AHEAD
Percentage of cache in front of the playhead (0.0 to 1.0)
std::mutex seek_state_mutex
Protects coherent seek state updates/consumption.
int preview_width
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
bool StartThread()
Start the cache thread at high priority. Returns true if it’s actually running.
std::atomic< int > last_speed
Last non-zero speed (for tracking).
virtual std::shared_ptr< openshot::Frame > GetFrame(int64_t number)=0
int64_t clampToTimelineRange(int64_t frame, int64_t timeline_end) const
Clamp frame index to [1, timeline_end] when timeline_end is valid.
std::atomic< bool > preroll_on_next_fill
True if next cache rebuild should include preroll offset.
This namespace is the default namespace for all code in the openshot library.
int preview_height
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
virtual void Add(std::shared_ptr< openshot::Frame > frame)=0
Add a Frame to the cache.
std::atomic< int64_t > last_cached_index
Index of the most recently cached frame.
int computeDirection() const
ReaderBase * reader
The source reader (e.g., Timeline, FFmpegReader).
openshot::ReaderInfo info
Information about the current media file.
This class is contains settings used by libopenshot (and can be safely toggled at any point)
Header file for Timeline class.
void handleUserSeek(int64_t playhead, int dir)
If userSeeked is true, reset last_cached_index just behind the playhead.
void ClearAllCache(bool deep=false)
int64_t computePrerollFrames(const Settings *settings) const
Compute preroll frame count from settings.
bool ENABLE_PLAYBACK_CACHING
Enable/Disable the cache thread to pre-fetch and cache video frames before we need them.
int width
The width of the video (in pixesl)
All cache managers in libopenshot are based on this CacheBase class.
void Play()
Play method is unimplemented.
int VIDEO_CACHE_MAX_FRAMES
Max number of frames (when paused) to cache for playback.
Header file for CacheBase class.
std::atomic< int64_t > cached_frame_count
Estimated count of frames currently stored in cache.
Exception for frames that are out of bounds.
virtual void Remove(int64_t frame_number)=0
Remove a specific frame.
std::atomic< bool > clear_cache_on_next_fill
True if next cache loop should clear existing cache ranges.
bool prefetchWindow(CacheBase *cache, int64_t window_begin, int64_t window_end, int dir, ReaderBase *reader, int64_t max_frames_to_fetch=-1)
Prefetch all missing frames in [window_begin ... window_end] or [window_end ... window_begin].
~VideoCacheThread() override
int64_t video_length
The number of frames in the video stream.
int height
The height of the video (in pixels)
int VIDEO_CACHE_MAX_PREROLL_FRAMES
Max number of frames (ahead of playhead) to cache during playback.
int64_t resolveTimelineEnd() const
Resolve timeline end frame from reader/timeline metadata.
std::atomic< bool > userSeeked
True if Seek(..., true) was called (forces a cache reset).
int VIDEO_CACHE_MIN_PREROLL_FRAMES
Minimum number of frames to cache before playback begins.
This class represents a timeline.
void setSpeed(int new_speed)
Set playback speed/direction. Positive = forward, negative = rewind, zero = pause.
uint64_t seen_timeline_cache_epoch
Last observed Timeline cache invalidation epoch.
std::atomic< int > speed
Current playback speed (0=paused, >0 forward, <0 backward).
uint64_t CacheEpoch() const
Return the current cache invalidation epoch.
void Reader(ReaderBase *new_reader)
Attach a ReaderBase (e.g. Timeline, FFmpegReader) and begin caching.
static Settings * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
std::atomic< int64_t > requested_display_frame
Frame index the user requested.
void NotifyPlaybackPosition(int64_t new_position)
Update playback position without triggering seek behavior or cache invalidation.
virtual void Touch(int64_t frame_number)=0
Move frame to front of queue (so it lasts longer)
bool timeline_cache_epoch_initialized
True once an initial epoch snapshot has been taken.
Header file for Frame class.
void run() override
Thread entry point: loops until threadShouldExit() is true.
virtual int64_t Count()=0
Count the frames in the queue.
Header file for VideoCacheThread class.
int64_t getBytes(int width, int height, int sample_rate, int channels, float fps)
Estimate memory usage for a single frame (video + audio).
bool clearCacheIfPaused(int64_t playhead, bool paused, CacheBase *cache)
When paused and playhead is outside current cache, clear all frames.
std::atomic< int > last_dir
Last direction sign (+1 forward, –1 backward).
bool StopThread(int timeoutMs=0)
Stop the cache thread (wait up to timeoutMs ms). Returns true if it stopped.
int64_t GetMaxBytes()
Gets the maximum bytes value.
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
virtual bool Contains(int64_t frame_number)=0
Check if frame is already contained in cache.
This abstract class is the base class, used by all readers in libopenshot.
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.
std::atomic< bool > scrub_active
True while user is dragging/scrubbing the playhead.
std::atomic< int64_t > min_frames_ahead
Minimum number of frames considered “ready” (pre-roll).
void Seek(int64_t new_position)
Backward-compatible alias for playback position updates (no seek side effects).
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.
int channels
The number of audio channels used in the audio stream.
virtual openshot::CacheBase * GetCache()=0
Get the cache object used by this reader (note: not all readers use cache)
Header file for all Exception classes.