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