OpenShot Library | libopenshot  0.2.5
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 "../../include/Qt/PlayerPrivate.h"
33 
34 namespace openshot
35 {
36  // Constructor
37  PlayerPrivate::PlayerPrivate(openshot::RendererBase *rb)
38  : renderer(rb), Thread("player"), video_position(1), audio_position(0)
39  , audioPlayback(new openshot::AudioPlaybackThread())
40  , videoPlayback(new openshot::VideoPlaybackThread(rb))
41  , videoCache(new openshot::VideoCacheThread())
42  , speed(1), reader(NULL), last_video_position(1)
43  { }
44 
45  // Destructor
46  PlayerPrivate::~PlayerPrivate()
47  {
48  stopPlayback(1000);
49  delete audioPlayback;
50  delete videoCache;
51  delete videoPlayback;
52  }
53 
54  // Start thread
55  void PlayerPrivate::run()
56  {
57  // bail if no reader set
58  if (!reader)
59  return;
60 
61  // Start the threads
62  if (reader->info.has_audio)
63  audioPlayback->startThread(8);
64  if (reader->info.has_video) {
65  videoCache->startThread(2);
66  videoPlayback->startThread(4);
67  }
68 
69  while (!threadShouldExit()) {
70 
71  // Calculate the milliseconds a single frame should stay on the screen
72  double frame_time = (1000.0 / reader->info.fps.ToDouble());
73 
74  // Get the start time (to track how long a frame takes to render)
75  const Time t1 = Time::getCurrentTime();
76 
77  // Get the current video frame (if it's different)
78  frame = getFrame();
79 
80  // Experimental Pausing Code (if frame has not changed)
81  if ((speed == 0 && video_position == last_video_position) || (video_position > reader->info.video_length)) {
82  speed = 0;
83  sleep(frame_time);
84  continue;
85  }
86 
87  // Set the video frame on the video thread and render frame
88  videoPlayback->frame = frame;
89  videoPlayback->render.signal();
90 
91  // Keep track of the last displayed frame
92  last_video_position = video_position;
93 
94  // How many frames ahead or behind is the video thread?
95  int64_t video_frame_diff = 0;
96  if (reader->info.has_audio && reader->info.has_video) {
97  if (speed != 1)
98  // Set audio frame again (since we are not in normal speed, and not paused)
99  audioPlayback->Seek(video_position);
100 
101  // Only calculate this if a reader contains both an audio and video thread
102  audio_position = audioPlayback->getCurrentFramePosition();
103  video_frame_diff = video_position - audio_position;
104  }
105 
106  // Get the end time (to track how long a frame takes to render)
107  const Time t2 = Time::getCurrentTime();
108 
109  // Determine how many milliseconds it took to render the frame
110  int64_t render_time = t2.toMilliseconds() - t1.toMilliseconds();
111 
112  // Calculate the amount of time to sleep (by subtracting the render time)
113  int sleep_time = int(frame_time - render_time);
114 
115  // Debug
116  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", render_time, "sleep_time", sleep_time);
117 
118  // Adjust drift (if more than a few frames off between audio and video)
119  if (video_frame_diff > 0 && reader->info.has_audio && reader->info.has_video)
120  // Since the audio and video threads are running independently, they will quickly get out of sync.
121  // To fix this, we calculate how far ahead or behind the video frame is, and adjust the amount of time
122  // the frame is displayed on the screen (i.e. the sleep time). If a frame is ahead of the audio,
123  // we sleep for longer. If a frame is behind the audio, we sleep less (or not at all), in order for
124  // the video to catch up.
125  sleep_time += (video_frame_diff * (1000.0 / reader->info.fps.ToDouble()));
126 
127 
128  else if (video_frame_diff < -10 && reader->info.has_audio && reader->info.has_video) {
129  // Skip frame(s) to catch up to the audio (if more than 10 frames behind)
130  video_position += abs(video_frame_diff) / 2; // Seek forward 1/2 the difference
131  sleep_time = 0; // Don't sleep now... immediately go to next position
132  }
133 
134  // Sleep (leaving the video frame on the screen for the correct amount of time)
135  if (sleep_time > 0) usleep(sleep_time * 1000);
136 
137  }
138  }
139 
140  // Get the next displayed frame (based on speed and direction)
141  std::shared_ptr<openshot::Frame> PlayerPrivate::getFrame()
142  {
143  try {
144  // Get the next frame (based on speed)
145  if (video_position + speed >= 1 && video_position + speed <= reader->info.video_length)
146  video_position = video_position + speed;
147 
148  if (frame && frame->number == video_position && video_position == last_video_position) {
149  // return cached frame
150  return frame;
151  }
152  else
153  {
154  // Update cache on which frame was retrieved
155  videoCache->setCurrentFramePosition(video_position);
156 
157  // return frame from reader
158  return reader->GetFrame(video_position);
159  }
160 
161  } catch (const ReaderClosed & e) {
162  // ...
163  } catch (const TooManySeeks & e) {
164  // ...
165  } catch (const OutOfBoundsFrame & e) {
166  // ...
167  }
168  return std::shared_ptr<openshot::Frame>();
169  }
170 
171  // Start video/audio playback
172  bool PlayerPrivate::startPlayback()
173  {
174  if (video_position < 0) return false;
175 
176  stopPlayback(-1);
177  startThread(1);
178  return true;
179  }
180 
181  // Stop video/audio playback
182  void PlayerPrivate::stopPlayback(int timeOutMilliseconds)
183  {
184  if (isThreadRunning()) stopThread(timeOutMilliseconds);
185  if (audioPlayback->isThreadRunning() && reader->info.has_audio) audioPlayback->stopThread(timeOutMilliseconds);
186  if (videoCache->isThreadRunning() && reader->info.has_video) videoCache->stopThread(timeOutMilliseconds);
187  if (videoPlayback->isThreadRunning() && reader->info.has_video) videoPlayback->stopThread(timeOutMilliseconds);
188  }
189 
190 }
This namespace is the default namespace for all code in the openshot library.
This is the base class of all Renderers in libopenshot.
Definition: RendererBase.h:48