OpenShot Library | libopenshot 0.3.3
Loading...
Searching...
No Matches
VideoCacheThread.cpp
Go to the documentation of this file.
1
9// Copyright (c) 2008-2019 OpenShot Studios, LLC
10//
11// SPDX-License-Identifier: LGPL-3.0-or-later
12
13#include "VideoCacheThread.h"
14
15#include "CacheBase.h"
16#include "Exceptions.h"
17#include "Frame.h"
18#include "OpenMPUtilities.h"
19#include "Settings.h"
20#include "Timeline.h"
21
22#include <algorithm>
23#include <thread> // for std::this_thread::sleep_for
24#include <chrono> // for std::chrono::microseconds
25
26namespace openshot
27{
28 // Constructor
30 : Thread("video-cache"), speed(0), last_speed(1), is_playing(false),
31 reader(NULL), current_display_frame(1), cached_frame_count(0),
32 min_frames_ahead(4), max_frames_ahead(8), should_pause_cache(false),
33 timeline_max_frame(0), should_break(false)
34 {
35 }
36
37 // Destructor
41
42 // Seek the reader to a particular frame number
43 void VideoCacheThread::Seek(int64_t new_position)
44 {
45 requested_display_frame = new_position;
46 }
47
48 // Seek the reader to a particular frame number and optionally start the pre-roll
49 void VideoCacheThread::Seek(int64_t new_position, bool start_preroll)
50 {
51 // Get timeline instance
52 Timeline *t = (Timeline *) reader;
53
54 // Calculate last frame # on timeline (to prevent caching past this point)
56
57 // Determine previous frame number (depending on last non-zero/non-paused speed)
58 int64_t previous_frame = new_position;
59 if (last_speed < 0) {
60 // backwards
61 previous_frame++;
62 } else if (last_speed > 0) {
63 // forward
64 previous_frame--;
65 }
66 if (previous_frame <= 0) {
67 // min frame is 1
68 previous_frame = 1;
69 }
70
71 // Clear cache if previous frame outside the cached range, which means we are
72 // requesting a non-contigous frame compared to our current cache range
73 if (new_position >= 1 && new_position <= timeline_max_frame && !reader->GetCache()->Contains(previous_frame)) {
74 // Clear cache
75 t->ClearAllCache();
76
77 // Break out of any existing cache loop
78 should_break = true;
79
80 // Force cache direction back to forward
81 last_speed = 1;
82 }
83
84 // Reset pre-roll when requested frame is not currently cached
85 if (start_preroll && reader && reader->GetCache() && !reader->GetCache()->Contains(new_position)) {
86 // Break out of any existing cache loop
87 should_break = true;
88
89 // Reset stats and allow cache to rebuild (if paused)
91 if (speed == 0) {
92 should_pause_cache = false;
93 }
94 }
95
96 // Actually update seek position
97 Seek(new_position);
98 }
99
100 // Set Speed (The speed and direction to playback a reader (1=normal, 2=fast, 3=faster, -1=rewind, etc...)
101 void VideoCacheThread::setSpeed(int new_speed) {
102 if (new_speed != 0) {
103 // Track last non-zero speed
104 last_speed = new_speed;
105 }
106 speed = new_speed;
107 }
108
109 // Get the size in bytes of a frame (rough estimate)
110 int64_t VideoCacheThread::getBytes(int width, int height, int sample_rate, int channels, float fps)
111 {
112 int64_t total_bytes = 0;
113 total_bytes += static_cast<int64_t>(width * height * sizeof(char) * 4);
114
115 // approximate audio size (sample rate / 24 fps)
116 total_bytes += ((sample_rate * channels) / fps) * sizeof(float);
117
118 // return size of this frame
119 return total_bytes;
120 }
121
122 // Play the video
124 // Start playing
125 is_playing = true;
126 }
127
128 // Stop the audio
130 // Stop playing
131 is_playing = false;
132 }
133
134 // Is cache ready for playback (pre-roll)
138
139 // Start the thread
141 {
142 // Types for storing time durations in whole and fractional microseconds
143 using micro_sec = std::chrono::microseconds;
144 using double_micro_sec = std::chrono::duration<double, micro_sec::period>;
145
146 while (!threadShouldExit() && is_playing) {
147 // Get settings
149
150 // init local vars
153
154 // Calculate on-screen time for a single frame
155 const auto frame_duration = double_micro_sec(1000000.0 / reader->info.fps.ToDouble());
156 int current_speed = speed;
157
158 // Increment and direction for cache loop
159 int64_t increment = 1;
160
161 // Check for empty cache (and re-trigger preroll)
162 // This can happen when the user manually empties the timeline cache
163 if (reader->GetCache()->Count() == 0) {
164 should_pause_cache = false;
166 }
167
168 // Update current display frame
170
171 if (current_speed == 0 && should_pause_cache || !s->ENABLE_PLAYBACK_CACHING) {
172 // Sleep during pause (after caching additional frames when paused)
173 // OR sleep when playback caching is disabled
174 std::this_thread::sleep_for(frame_duration / 2);
175 continue;
176
177 } else if (current_speed == 0) {
178 // Allow 'max frames' to increase when pause is detected (based on cache)
179 // To allow the cache to fill-up only on the initial pause.
180 should_pause_cache = true;
181
182 // Calculate bytes per frame
183 int64_t bytes_per_frame = getBytes(reader->info.width, reader->info.height,
186 Timeline *t = (Timeline *) reader;
188 // If we have a different timeline preview size, use that instead (the preview
189 // window can be smaller, can thus reduce the bytes per frame)
190 bytes_per_frame = getBytes(t->preview_width, t->preview_height,
193 }
194
195 // Calculate # of frames on Timeline cache (when paused)
196 if (reader->GetCache() && reader->GetCache()->GetMaxBytes() > 0) {
197 // When paused, limit the cached frames to the following % of total cache size.
198 // This allows for us to leave some cache behind the plahead, and some in front of the playhead.
201 // Ignore values that are too large, and default to a safer value
203 }
204 }
205
206 // Overwrite the increment to our cache position
207 // to fully cache frames while paused (support forward and rewind caching)
208 // Use `last_speed` which is the last non-zero/non-paused speed
209 if (last_speed < 0) {
210 increment = -1;
211 }
212
213 } else {
214 // normal playback
215 should_pause_cache = false;
216 }
217
218 // Always cache frames from the current display position to our maximum (based on the cache size).
219 // Frames which are already cached are basically free. Only uncached frames have a big CPU cost.
220 // By always looping through the expected frame range, we can fill-in missing frames caused by a
221 // fragmented cache object (i.e. the user clicking all over the timeline). The -1 is to always
222 // cache 1 frame previous to our current frame (to avoid our Seek method from clearing the cache).
223 int64_t starting_frame = std::min(current_display_frame, timeline_max_frame) - 1;
224 int64_t ending_frame = std::min(starting_frame + max_frames_ahead, timeline_max_frame);
225
226 // Adjust ending frame for cache loop
227 if (increment < 0) {
228 // Reverse loop (if we are going backwards)
229 ending_frame = starting_frame - max_frames_ahead;
230 }
231 if (starting_frame < 1) {
232 // Don't allow negative frame number caching
233 starting_frame = 1;
234 }
235 if (ending_frame < 1) {
236 // Don't allow negative frame number caching
237 ending_frame = 1;
238 }
239
240 // Reset cache break-loop flag
241 should_break = false;
242
243 // Loop through range of frames (and cache them)
244 for (int64_t cache_frame = starting_frame; cache_frame != (ending_frame + increment); cache_frame += increment) {
246 if (reader && reader->GetCache() && !reader->GetCache()->Contains(cache_frame)) {
247 try
248 {
249 // This frame is not already cached... so request it again (to force the creation & caching)
250 // This will also re-order the missing frame to the front of the cache
251 last_cached_frame = reader->GetFrame(cache_frame);
252 }
253 catch (const OutOfBoundsFrame & e) { }
254 }
255
256 // Check if thread has stopped OR should_break is triggered
258 should_break = false;
259 break;
260 }
261
262 }
263
264 // Sleep for a fraction of frame duration
265 std::this_thread::sleep_for(frame_duration / 2);
266 }
267
268 return;
269 }
270}
Header file for CacheBase class.
Header file for all Exception classes.
Header file for Frame class.
Header file for OpenMPUtilities (set some common macros)
Header file for global Settings class.
Header file for Timeline class.
Source file for VideoCacheThread class.
virtual bool Contains(int64_t frame_number)=0
Check if frame is already contained in cache.
virtual int64_t Count()=0
Count the frames in the queue.
int64_t GetMaxBytes()
Gets the maximum bytes value.
Definition CacheBase.h:97
float ToFloat()
Return this fraction as a float (i.e. 1/2 = 0.5)
Definition Fraction.cpp:35
double ToDouble() const
Return this fraction as a double (i.e. 1/2 = 0.5)
Definition Fraction.cpp:40
Exception for frames that are out of bounds.
Definition Exceptions.h:301
openshot::ReaderInfo info
Information about the current media file.
Definition ReaderBase.h:88
virtual std::shared_ptr< openshot::Frame > GetFrame(int64_t number)=0
virtual openshot::CacheBase * GetCache()=0
Get the cache object used by this reader (note: not all readers use cache)
This class is contains settings used by libopenshot (and can be safely toggled at any point)
Definition Settings.h:26
static Settings * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
Definition Settings.cpp:23
int VIDEO_CACHE_MIN_PREROLL_FRAMES
Minimum number of frames to cache before playback begins.
Definition Settings.h:89
int VIDEO_CACHE_MAX_FRAMES
Max number of frames (when paused) to cache for playback.
Definition Settings.h:95
float VIDEO_CACHE_PERCENT_AHEAD
Percentage of cache in front of the playhead (0.0 to 1.0)
Definition Settings.h:86
bool ENABLE_PLAYBACK_CACHING
Enable/Disable the cache thread to pre-fetch and cache video frames before we need them.
Definition Settings.h:98
int VIDEO_CACHE_MAX_PREROLL_FRAMES
Max number of frames (ahead of playhead) to cache during playback.
Definition Settings.h:92
int preview_height
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
int preview_width
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
This class represents a timeline.
Definition Timeline.h:148
int64_t GetMaxFrame()
Look up the end frame number of the latest element on the timeline.
Definition Timeline.cpp:469
void ClearAllCache(bool deep=false)
void setSpeed(int new_speed)
Set Speed (The speed and direction to playback a reader (1=normal, 2=fast, 3=faster,...
void Stop()
Stop the audio playback.
bool isReady()
Is cache ready for video/audio playback.
int64_t getBytes(int width, int height, int sample_rate, int channels, float fps)
Get the size in bytes of a frame (rough estimate)
void Seek(int64_t new_position)
Seek the reader to a particular frame number.
void run()
Start the thread.
std::shared_ptr< Frame > last_cached_frame
This namespace is the default namespace for all code in the openshot library.
Definition Compressor.h:29
int width
The width of the video (in pixesl)
Definition ReaderBase.h:46
int channels
The number of audio channels used in the audio stream.
Definition ReaderBase.h:61
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
Definition ReaderBase.h:48
int height
The height of the video (in pixels)
Definition ReaderBase.h:45
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
Definition ReaderBase.h:60