OpenShot Library | libopenshot 0.3.2
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
21namespace openshot
22{
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(8);
53 if (reader->info.has_video) {
54 videoCache->startThread(2);
55 videoPlayback->startThread(4);
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(1);
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}
Header file for all Exception classes.
Source file for PlayerPrivate class.
The audio playback thread.
This is the base class of all Renderers in libopenshot.
Definition: RendererBase.h:31
The video cache class.
The video playback class.
This namespace is the default namespace for all code in the openshot library.
Definition: Compressor.h:29
int close_to_sync