OpenShot Library | libopenshot  0.5.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  bool wait_paused_hold = (speed == 0 && video_position == last_video_position);
78  bool wait_speed_change = (speed != 0 && last_speed != speed);
79  bool cache_ready = videoCache->isReady();
80  bool wait_preroll = (speed != 0 && !is_dirty && !cache_ready);
81  bool should_wait = (wait_paused_hold || wait_speed_change || wait_preroll);
82 
83  if (should_wait)
84  {
85  // Sleep for a fraction of frame duration
86  std::this_thread::sleep_for(frame_duration / 4);
87 
88  // Reset current playback start time
89  start_time = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now());
90  playback_frames = 0;
91  last_speed = speed;
92 
93  // Seek audio thread (since audio is also paused)
94  audioPlayback->Seek(video_position);
95 
96  continue;
97  }
98 
99  // Get the current video frame
100  frame = getFrame();
101 
102  // Set the video frame on the video thread and render frame
103  videoPlayback->frame = frame;
104  videoPlayback->rendered.reset();
105  videoPlayback->render.signal();
106  // Keep decode/position advancement aligned with actual preview updates.
107  // This avoids occasional "silent advance then jump" behavior when
108  // preroll transitions and rendering are briefly out-of-sync.
109  const int render_wait_ms = std::max(
110  1,
111  static_cast<int>(frame_duration.count() / 1000.0 * 2.0)
112  );
113  videoPlayback->rendered.wait(render_wait_ms);
114 
115  // Keep track of the last displayed frame
116  last_video_position = video_position;
117  last_speed = speed;
118 
119  // Calculate the diff between 'now' and the predicted frame end time
120  const auto current_time = std::chrono::system_clock::now();
121  const auto remaining_time = double_micro_sec(start_time +
122  (frame_duration * playback_frames) - current_time);
123 
124  // Sleep to display video image on screen
125  if (remaining_time > remaining_time.zero() ) {
126  if (remaining_time < max_sleep) {
127  std::this_thread::sleep_for(remaining_time);
128  } else {
129  // Protect against invalid or too-long sleep times
130  std::this_thread::sleep_for(max_sleep);
131  }
132  } else {
133  // If we're behind schedule (e.g. preroll/render stall), do not
134  // burst through delayed frames. Resync timing baseline so
135  // playback continues smoothly at normal cadence.
136  start_time = std::chrono::time_point_cast<micro_sec>(current_time);
137  playback_frames = 0;
138  }
139  }
140  }
141 
142  // Get the next displayed frame (based on speed and direction)
143  std::shared_ptr<openshot::Frame> PlayerPrivate::getFrame()
144  {
145  try {
146  // Getting new frame, so clear this flag
147  is_dirty = false;
148 
149  // Get the next frame (based on speed)
150  if (video_position + speed >= 1 && video_position + speed <= reader->info.video_length) {
151  video_position = video_position + speed;
152 
153  } else if (video_position + speed < 1) {
154  // Start of reader (prevent negative frame number and pause playback)
155  video_position = 1;
156  speed = 0;
157  } else if (video_position + speed > reader->info.video_length) {
158  // End of reader (prevent negative frame number and pause playback)
159  video_position = reader->info.video_length;
160  speed = 0;
161  }
162 
163  if (frame && frame->number == video_position && video_position == last_video_position) {
164  // return cached frame
165  return frame;
166  }
167  else
168  {
169  // Increment playback frames (always in the positive direction)
170  playback_frames += std::abs(speed);
171 
172  // Update playhead hint for cache window tracking without triggering seek behavior.
173  videoCache->NotifyPlaybackPosition(video_position);
174 
175  // return frame from reader
176  return reader->GetFrame(video_position);
177  }
178 
179  } catch (const ReaderClosed & e) {
180  // ...
181  } catch (const OutOfBoundsFrame & e) {
182  // ...
183  }
184  return std::shared_ptr<openshot::Frame>();
185  }
186 
187  // Seek to a new position
188  void PlayerPrivate::Seek(int64_t new_position)
189  {
190  video_position = new_position;
191  last_video_position = 0;
192  // Drop local frame reference so same-frame refreshes cannot reuse stale
193  // content after timeline/clip property updates.
194  frame.reset();
195  // Always force immediate refresh after seek/update, even while playing.
196  is_dirty = true;
197  }
198 
199  // Start video/audio playback
200  bool PlayerPrivate::startPlayback()
201  {
202  if (video_position < 0) return false;
203 
204  stopPlayback();
205  startThread(Priority::high);
206  return true;
207  }
208 
209  // Stop video/audio playback
210  void PlayerPrivate::stopPlayback()
211  {
212  if (videoCache->isThreadRunning() && reader->info.has_video) videoCache->stopThread(max_sleep_ms);
213  if (audioPlayback->isThreadRunning() && reader->info.has_audio) audioPlayback->stopThread(max_sleep_ms);
214  if (videoPlayback->isThreadRunning() && reader->info.has_video) videoPlayback->stopThread(max_sleep_ms);
215  if (isThreadRunning()) stopThread(max_sleep_ms);
216  }
217 
218 }
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
Handles prefetching and caching of video/audio frames for smooth playback.
Definition: VideoCacheThread.h:37
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.