OpenShot Library | libopenshot  0.2.7
PlayerPrivate.cpp
Go to the documentation of this file.
1 /**
2  * @file
3  * @brief Source file for PlayerPrivate class
4  * @author Duzy Chan <code@duzy.info>
5  * @author Jonathan Thomas <jonathan@openshot.org>
6  *
7  * @ref License
8  */
9 
10 /* LICENSE
11  *
12  * Copyright (c) 2008-2019 OpenShot Studios, LLC
13  * <http://www.openshotstudios.com/>. This file is part of
14  * OpenShot Library (libopenshot), an open-source project dedicated to
15  * delivering high quality video editing and animation solutions to the
16  * world. For more information visit <http://www.openshot.org/>.
17  *
18  * OpenShot Library (libopenshot) is free software: you can redistribute it
19  * and/or modify it under the terms of the GNU Lesser General Public License
20  * as published by the Free Software Foundation, either version 3 of the
21  * License, or (at your option) any later version.
22  *
23  * OpenShot Library (libopenshot) is distributed in the hope that it will be
24  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26  * GNU Lesser General Public License for more details.
27  *
28  * You should have received a copy of the GNU Lesser General Public License
29  * along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
30  */
31 
32 #include "PlayerPrivate.h"
33 #include "Exceptions.h"
34 
35 #include <thread> // for std::this_thread::sleep_for
36 #include <chrono> // for std::chrono milliseconds, high_resolution_clock
37 
38 namespace openshot
39 {
40  // Constructor
41  PlayerPrivate::PlayerPrivate(openshot::RendererBase *rb)
42  : renderer(rb), Thread("player"), video_position(1), audio_position(0)
43  , audioPlayback(new openshot::AudioPlaybackThread())
44  , videoPlayback(new openshot::VideoPlaybackThread(rb))
45  , videoCache(new openshot::VideoCacheThread())
46  , speed(1), reader(NULL), last_video_position(1)
47  { }
48 
49  // Destructor
50  PlayerPrivate::~PlayerPrivate()
51  {
52  stopPlayback(1000);
53  delete audioPlayback;
54  delete videoCache;
55  delete videoPlayback;
56  }
57 
58  // Start thread
59  void PlayerPrivate::run()
60  {
61  // bail if no reader set
62  if (!reader)
63  return;
64 
65  // Start the threads
66  if (reader->info.has_audio)
67  audioPlayback->startThread(8);
68  if (reader->info.has_video) {
69  videoCache->startThread(2);
70  videoPlayback->startThread(4);
71  }
72 
73  using std::chrono::duration_cast;
74 
75  // Types for storing time durations in whole and fractional milliseconds
76  using ms = std::chrono::milliseconds;
77  using double_ms = std::chrono::duration<double, ms::period>;
78 
79  // Calculate on-screen time for a single frame in milliseconds
80  const auto frame_duration = double_ms(1000.0 / reader->info.fps.ToDouble());
81 
82  while (!threadShouldExit()) {
83  // Get the start time (to track how long a frame takes to render)
84  const auto time1 = std::chrono::high_resolution_clock::now();
85 
86  // Get the current video frame (if it's different)
87  frame = getFrame();
88 
89  // Experimental Pausing Code (if frame has not changed)
90  if ((speed == 0 && video_position == last_video_position)
91  || (video_position > reader->info.video_length)
92  ) {
93  speed = 0;
94  std::this_thread::sleep_for(frame_duration);
95  continue;
96  }
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 
105  // How many frames ahead or behind is the video thread?
106  int64_t video_frame_diff = 0;
107  if (reader->info.has_audio && reader->info.has_video) {
108  if (speed != 1)
109  // Set audio frame again (since we are not in normal speed, and not paused)
110  audioPlayback->Seek(video_position);
111 
112  // Only calculate this if a reader contains both an audio and video thread
113  audio_position = audioPlayback->getCurrentFramePosition();
114  video_frame_diff = video_position - audio_position;
115  }
116 
117  // Get the end time (to track how long a frame takes to render)
118  const auto time2 = std::chrono::high_resolution_clock::now();
119 
120  // Determine how many milliseconds it took to render the frame
121  const auto render_time = double_ms(time2 - time1);
122 
123  // Calculate the amount of time to sleep (by subtracting the render time)
124  auto sleep_time = duration_cast<ms>(frame_duration - render_time);
125 
126  // Debug
127  ZmqLogger::Instance()->AppendDebugMethod("PlayerPrivate::run (determine sleep)", "video_frame_diff", video_frame_diff, "video_position", video_position, "audio_position", audio_position, "speed", speed, "render_time(ms)", render_time.count(), "sleep_time(ms)", sleep_time.count());
128 
129  // Adjust drift (if more than a few frames off between audio and video)
130  if (video_frame_diff > 0 && reader->info.has_audio && reader->info.has_video) {
131  // Since the audio and video threads are running independently,
132  // they will quickly get out of sync. To fix this, we calculate
133  // how far ahead or behind the video frame is, and adjust the amount
134  // of time the frame is displayed on the screen (i.e. the sleep time).
135  // If a frame is ahead of the audio, we sleep for longer.
136  // If a frame is behind the audio, we sleep less (or not at all),
137  // in order for the video to catch up.
138  sleep_time += duration_cast<ms>(video_frame_diff * frame_duration);
139  }
140 
141  else if (video_frame_diff < -10 && reader->info.has_audio && reader->info.has_video) {
142  // Skip frame(s) to catch up to the audio (if more than 10 frames behind)
143  video_position += std::fabs(video_frame_diff) / 2; // Seek forward 1/2 the difference
144  sleep_time = sleep_time.zero(); // Don't sleep now... immediately go to next position
145  }
146 
147  // Sleep (leaving the video frame on the screen for the correct amount of time)
148  if (sleep_time > sleep_time.zero()) {
149  std::this_thread::sleep_for(sleep_time);
150  }
151 
152  }
153  }
154 
155  // Get the next displayed frame (based on speed and direction)
156  std::shared_ptr<openshot::Frame> PlayerPrivate::getFrame()
157  {
158  try {
159  // Get the next frame (based on speed)
160  if (video_position + speed >= 1 && video_position + speed <= reader->info.video_length)
161  video_position = video_position + speed;
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  // Update cache on which frame was retrieved
170  videoCache->setCurrentFramePosition(video_position);
171 
172  // return frame from reader
173  return reader->GetFrame(video_position);
174  }
175 
176  } catch (const ReaderClosed & e) {
177  // ...
178  } catch (const OutOfBoundsFrame & e) {
179  // ...
180  }
181  return std::shared_ptr<openshot::Frame>();
182  }
183 
184  // Start video/audio playback
185  bool PlayerPrivate::startPlayback()
186  {
187  if (video_position < 0) return false;
188 
189  stopPlayback(-1);
190  startThread(1);
191  return true;
192  }
193 
194  // Stop video/audio playback
195  void PlayerPrivate::stopPlayback(int timeOutMilliseconds)
196  {
197  if (audioPlayback->isThreadRunning() && reader->info.has_audio) audioPlayback->stopThread(timeOutMilliseconds);
198  if (videoCache->isThreadRunning() && reader->info.has_video) videoCache->stopThread(timeOutMilliseconds);
199  if (videoPlayback->isThreadRunning() && reader->info.has_video) videoPlayback->stopThread(timeOutMilliseconds);
200  if (isThreadRunning()) stopThread(timeOutMilliseconds);
201  }
202 
203 }
Header file for all Exception classes.
Source file for PlayerPrivate class.
This is the base class of all Renderers in libopenshot.
Definition: RendererBase.h:49
This namespace is the default namespace for all code in the openshot library.
Definition: Compressor.h:47