OpenShot Library | libopenshot  0.4.0
PlayerPrivate.cpp
Go to the documentation of this file.
1 
10 // Copyright (c) 2008-2019 OpenShot Studios, LLC
11 //
12 // SPDX-License-Identifier: LGPL-3.0-or-later
13 
14 #include "PlayerPrivate.h"
15 #include "Exceptions.h"
16 
17 #include <queue>
18 #include <thread> // for std::this_thread::sleep_for
19 #include <chrono> // for std::chrono microseconds, high_resolution_clock
20 
21 namespace openshot
22 {
23  int close_to_sync = 5;
24  // Constructor
25  PlayerPrivate::PlayerPrivate(openshot::RendererBase *rb)
26  : renderer(rb), Thread("player"), video_position(1), audio_position(0),
27  speed(1), reader(NULL), last_video_position(1), max_sleep_ms(125000), playback_frames(0), is_dirty(true)
28  {
29  videoCache = new openshot::VideoCacheThread();
30  audioPlayback = new openshot::AudioPlaybackThread(videoCache);
31  videoPlayback = new openshot::VideoPlaybackThread(rb);
32  }
33 
34  // Destructor
35  PlayerPrivate::~PlayerPrivate()
36  {
37  stopPlayback();
38  delete audioPlayback;
39  delete videoCache;
40  delete videoPlayback;
41  }
42 
43  // Start thread
44  void PlayerPrivate::run()
45  {
46  // bail if no reader set
47  if (!reader)
48  return;
49 
50  // Start the threads
51  if (reader->info.has_audio)
52  audioPlayback->startThread(Priority::high);
53  if (reader->info.has_video) {
54  videoCache->startThread(Priority::high);
55  videoPlayback->startThread(Priority::high);
56  }
57 
58  using std::chrono::duration_cast;
59 
60  // Types for storing time durations in whole and fractional microseconds
61  using micro_sec = std::chrono::microseconds;
62  using double_micro_sec = std::chrono::duration<double, micro_sec::period>;
63 
64  // Init start_time of playback
65  std::chrono::time_point<std::chrono::system_clock, std::chrono::microseconds> start_time;
66  start_time = std::chrono::time_point_cast<micro_sec>(std::chrono::system_clock::now());
67 
68  while (!threadShouldExit()) {
69  // Calculate on-screen time for a single frame
70  int frame_speed = std::max(abs(speed), 1);
71  const auto frame_duration = double_micro_sec(1000000.0 / (reader->info.fps.ToDouble() * frame_speed));
72  const auto max_sleep = frame_duration * 4;
73 
74  // Pausing Code (which re-syncs audio/video times)
75  // - If speed is zero or speed changes
76  // - If pre-roll is not ready (This should allow scrubbing of the timeline without waiting on pre-roll)
77  if ((speed == 0 && video_position == last_video_position) ||
78  (speed != 0 && last_speed != speed) ||
79  (speed != 0 && !is_dirty && !videoCache->isReady()))
80  {
81  // Sleep for a fraction of frame duration
82  std::this_thread::sleep_for(frame_duration / 4);
83 
84  // Reset current playback start time
85  start_time = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now());
86  playback_frames = 0;
87  last_speed = speed;
88 
89  // Seek audio thread (since audio is also paused)
90  audioPlayback->Seek(video_position);
91 
92  continue;
93  }
94 
95  // Get the current video frame
96  frame = getFrame();
97 
98  // Set the video frame on the video thread and render frame
99  videoPlayback->frame = frame;
100  videoPlayback->render.signal();
101 
102  // Keep track of the last displayed frame
103  last_video_position = video_position;
104  last_speed = speed;
105 
106  // Calculate the diff between 'now' and the predicted frame end time
107  const auto current_time = std::chrono::system_clock::now();
108  const auto remaining_time = double_micro_sec(start_time +
109  (frame_duration * playback_frames) - current_time);
110 
111  // Sleep to display video image on screen
112  if (remaining_time > remaining_time.zero() ) {
113  if (remaining_time < max_sleep) {
114  std::this_thread::sleep_for(remaining_time);
115  } else {
116  // Protect against invalid or too-long sleep times
117  std::this_thread::sleep_for(max_sleep);
118  }
119  }
120  }
121  }
122 
123  // Get the next displayed frame (based on speed and direction)
124  std::shared_ptr<openshot::Frame> PlayerPrivate::getFrame()
125  {
126  try {
127  // Getting new frame, so clear this flag
128  is_dirty = false;
129 
130  // Get the next frame (based on speed)
131  if (video_position + speed >= 1 && video_position + speed <= reader->info.video_length) {
132  video_position = video_position + speed;
133 
134  } else if (video_position + speed < 1) {
135  // Start of reader (prevent negative frame number and pause playback)
136  video_position = 1;
137  speed = 0;
138  } else if (video_position + speed > reader->info.video_length) {
139  // End of reader (prevent negative frame number and pause playback)
140  video_position = reader->info.video_length;
141  speed = 0;
142  }
143 
144  if (frame && frame->number == video_position && video_position == last_video_position) {
145  // return cached frame
146  return frame;
147  }
148  else
149  {
150  // Increment playback frames (always in the positive direction)
151  playback_frames += std::abs(speed);
152 
153  // Update cache on which frame was retrieved
154  videoCache->Seek(video_position);
155 
156  // return frame from reader
157  return reader->GetFrame(video_position);
158  }
159 
160  } catch (const ReaderClosed & e) {
161  // ...
162  } catch (const OutOfBoundsFrame & e) {
163  // ...
164  }
165  return std::shared_ptr<openshot::Frame>();
166  }
167 
168  // Seek to a new position
169  void PlayerPrivate::Seek(int64_t new_position)
170  {
171  video_position = new_position;
172  last_video_position = 0;
173  is_dirty = true;
174  }
175 
176  // Start video/audio playback
177  bool PlayerPrivate::startPlayback()
178  {
179  if (video_position < 0) return false;
180 
181  stopPlayback();
182  startThread(Priority::high);
183  return true;
184  }
185 
186  // Stop video/audio playback
187  void PlayerPrivate::stopPlayback()
188  {
189  if (videoCache->isThreadRunning() && reader->info.has_video) videoCache->stopThread(max_sleep_ms);
190  if (audioPlayback->isThreadRunning() && reader->info.has_audio) audioPlayback->stopThread(max_sleep_ms);
191  if (videoPlayback->isThreadRunning() && reader->info.has_video) videoPlayback->stopThread(max_sleep_ms);
192  if (isThreadRunning()) stopThread(max_sleep_ms);
193  }
194 
195 }
openshot::VideoPlaybackThread
The video playback class.
Definition: VideoPlaybackThread.h:30
openshot
This namespace is the default namespace for all code in the openshot library.
Definition: Compressor.h:28
openshot::AudioPlaybackThread
The audio playback thread.
Definition: AudioPlaybackThread.h:77
openshot::close_to_sync
int close_to_sync
Definition: PlayerPrivate.cpp:23
openshot::VideoCacheThread
The video cache class.
Definition: VideoCacheThread.h:29
PlayerPrivate.h
Source file for PlayerPrivate class.
openshot::RendererBase
This is the base class of all Renderers in libopenshot.
Definition: RendererBase.h:30
Exceptions.h
Header file for all Exception classes.