OpenShot Library | libopenshot  0.4.0
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 
26 namespace 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
39  {
40  }
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)
137  }
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;
165  cached_frame_count = 0;
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,
185  reader->info.fps.ToFloat());
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,
192  reader->info.fps.ToFloat());
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.
199  max_frames_ahead = (reader->GetCache()->GetMaxBytes() / bytes_per_frame) * s->VIDEO_CACHE_PERCENT_AHEAD;
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 }
Settings.h
Header file for global Settings class.
openshot::ReaderInfo::sample_rate
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
Definition: ReaderBase.h:60
openshot::VideoCacheThread::VideoCacheThread
VideoCacheThread()
Constructor.
Definition: VideoCacheThread.cpp:29
openshot::Fraction::ToFloat
float ToFloat()
Return this fraction as a float (i.e. 1/2 = 0.5)
Definition: Fraction.cpp:35
openshot::Settings::VIDEO_CACHE_PERCENT_AHEAD
float VIDEO_CACHE_PERCENT_AHEAD
Percentage of cache in front of the playhead (0.0 to 1.0)
Definition: Settings.h:86
openshot::TimelineBase::preview_width
int preview_width
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
Definition: TimelineBase.h:44
openshot::ReaderBase::GetFrame
virtual std::shared_ptr< openshot::Frame > GetFrame(int64_t number)=0
openshot::VideoCacheThread::should_break
bool should_break
Definition: VideoCacheThread.h:44
openshot
This namespace is the default namespace for all code in the openshot library.
Definition: Compressor.h:28
openshot::TimelineBase::preview_height
int preview_height
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
Definition: TimelineBase.h:45
openshot::VideoCacheThread::min_frames_ahead
int64_t min_frames_ahead
Definition: VideoCacheThread.h:40
openshot::VideoCacheThread::reader
ReaderBase * reader
Definition: VideoCacheThread.h:39
openshot::ReaderBase::info
openshot::ReaderInfo info
Information about the current media file.
Definition: ReaderBase.h:88
openshot::Settings
This class is contains settings used by libopenshot (and can be safely toggled at any point)
Definition: Settings.h:26
Timeline.h
Header file for Timeline class.
openshot::Timeline::ClearAllCache
void ClearAllCache(bool deep=false)
Definition: Timeline.cpp:1715
openshot::Settings::ENABLE_PLAYBACK_CACHING
bool ENABLE_PLAYBACK_CACHING
Enable/Disable the cache thread to pre-fetch and cache video frames before we need them.
Definition: Settings.h:98
openshot::VideoCacheThread::last_cached_frame
std::shared_ptr< Frame > last_cached_frame
Definition: VideoCacheThread.h:32
openshot::ReaderInfo::width
int width
The width of the video (in pixesl)
Definition: ReaderBase.h:46
openshot::VideoCacheThread::Play
void Play()
Play the video.
Definition: VideoCacheThread.cpp:123
openshot::Settings::VIDEO_CACHE_MAX_FRAMES
int VIDEO_CACHE_MAX_FRAMES
Max number of frames (when paused) to cache for playback.
Definition: Settings.h:95
CacheBase.h
Header file for CacheBase class.
openshot::OutOfBoundsFrame
Exception for frames that are out of bounds.
Definition: Exceptions.h:300
openshot::Fraction::ToDouble
double ToDouble() const
Return this fraction as a double (i.e. 1/2 = 0.5)
Definition: Fraction.cpp:40
openshot::VideoCacheThread::timeline_max_frame
int64_t timeline_max_frame
Definition: VideoCacheThread.h:42
openshot::ReaderInfo::height
int height
The height of the video (in pixels)
Definition: ReaderBase.h:45
if
if(!codec) codec
openshot::Settings::VIDEO_CACHE_MAX_PREROLL_FRAMES
int VIDEO_CACHE_MAX_PREROLL_FRAMES
Max number of frames (ahead of playhead) to cache during playback.
Definition: Settings.h:92
openshot::VideoCacheThread::last_speed
int last_speed
Definition: VideoCacheThread.h:34
openshot::VideoCacheThread::~VideoCacheThread
~VideoCacheThread()
Destructor.
Definition: VideoCacheThread.cpp:38
openshot::Settings::VIDEO_CACHE_MIN_PREROLL_FRAMES
int VIDEO_CACHE_MIN_PREROLL_FRAMES
Minimum number of frames to cache before playback begins.
Definition: Settings.h:89
openshot::Timeline
This class represents a timeline.
Definition: Timeline.h:148
openshot::VideoCacheThread::setSpeed
void setSpeed(int new_speed)
Set Speed (The speed and direction to playback a reader (1=normal, 2=fast, 3=faster,...
Definition: VideoCacheThread.cpp:101
openshot::VideoCacheThread::should_pause_cache
bool should_pause_cache
Definition: VideoCacheThread.h:43
openshot::VideoCacheThread::speed
int speed
Definition: VideoCacheThread.h:33
openshot::Settings::Instance
static Settings * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
Definition: Settings.cpp:23
openshot::VideoCacheThread::Stop
void Stop()
Stop the audio playback.
Definition: VideoCacheThread.cpp:129
openshot::VideoCacheThread::run
void run()
Start the thread.
Definition: VideoCacheThread.cpp:140
Frame.h
Header file for Frame class.
openshot::CacheBase::Count
virtual int64_t Count()=0
Count the frames in the queue.
VideoCacheThread.h
Source file for VideoCacheThread class.
openshot::VideoCacheThread::getBytes
int64_t getBytes(int width, int height, int sample_rate, int channels, float fps)
Get the size in bytes of a frame (rough estimate)
Definition: VideoCacheThread.cpp:110
openshot::VideoCacheThread::max_frames_ahead
int64_t max_frames_ahead
Definition: VideoCacheThread.h:41
openshot::VideoCacheThread::current_display_frame
int64_t current_display_frame
Definition: VideoCacheThread.h:37
openshot::VideoCacheThread::cached_frame_count
int64_t cached_frame_count
Definition: VideoCacheThread.h:38
OpenMPUtilities.h
Header file for OpenMPUtilities (set some common macros)
openshot::CacheBase::GetMaxBytes
int64_t GetMaxBytes()
Gets the maximum bytes value.
Definition: CacheBase.h:97
openshot::ReaderInfo::fps
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
Definition: ReaderBase.h:48
openshot::CacheBase::Contains
virtual bool Contains(int64_t frame_number)=0
Check if frame is already contained in cache.
openshot::Timeline::GetMaxFrame
int64_t GetMaxFrame()
Look up the end frame number of the latest element on the timeline.
Definition: Timeline.cpp:469
openshot::VideoCacheThread::is_playing
bool is_playing
Definition: VideoCacheThread.h:35
openshot::VideoCacheThread::Seek
void Seek(int64_t new_position)
Seek the reader to a particular frame number.
Definition: VideoCacheThread.cpp:43
openshot::VideoCacheThread::requested_display_frame
int64_t requested_display_frame
Definition: VideoCacheThread.h:36
openshot::ReaderInfo::channels
int channels
The number of audio channels used in the audio stream.
Definition: ReaderBase.h:61
openshot::VideoCacheThread::isReady
bool isReady()
Is cache ready for video/audio playback.
Definition: VideoCacheThread.cpp:135
openshot::ReaderBase::GetCache
virtual openshot::CacheBase * GetCache()=0
Get the cache object used by this reader (note: not all readers use cache)
Exceptions.h
Header file for all Exception classes.