26 : Thread(
"player"), playback_frames(0), video_position(1), audio_position(0),
27 reader(nullptr), speed(1), last_speed(1), renderer(rb),
28 last_video_position(1), max_sleep_ms(125000), is_dirty(true)
36 PlayerPrivate::~PlayerPrivate()
45 void PlayerPrivate::run()
52 if (reader->info.has_audio)
53 audioPlayback->startThread(Priority::high);
54 if (reader->info.has_video) {
55 videoCache->startThread(Priority::high);
56 videoPlayback->startThread(Priority::high);
59 using std::chrono::duration_cast;
62 using micro_sec = std::chrono::microseconds;
63 using double_micro_sec = std::chrono::duration<double, micro_sec::period>;
66 std::chrono::time_point<std::chrono::system_clock, std::chrono::microseconds> start_time;
67 start_time = std::chrono::time_point_cast<micro_sec>(std::chrono::system_clock::now());
69 while (!threadShouldExit()) {
71 int frame_speed = std::max(abs(speed), 1);
72 const auto frame_duration = double_micro_sec(1000000.0 / (reader->info.fps.ToDouble() * frame_speed));
73 const auto max_sleep = frame_duration * 4;
78 bool wait_paused_hold = (speed == 0 && video_position == last_video_position);
79 bool wait_speed_change = (speed != 0 && last_speed != speed);
80 bool cache_ready = videoCache->isReady();
81 bool wait_preroll = (speed != 0 && !is_dirty && !cache_ready);
82 bool should_wait = (wait_paused_hold || wait_speed_change || wait_preroll);
87 std::this_thread::sleep_for(frame_duration / 4);
90 start_time = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now());
95 audioPlayback->Seek(video_position);
96 videoCache->AcknowledgeAudioCacheMiss();
102 const uint64_t frame_seek_generation = seek_generation.load();
103 auto frame_to_render = getFrame();
106 if (seek_generation.load() != frame_seek_generation)
108 const int64_t rendered_position = video_position.load();
111 videoPlayback->SetFrame(std::move(frame_to_render));
112 videoPlayback->rendered.reset();
113 videoPlayback->render.signal();
117 const int render_wait_ms = std::max(
119 static_cast<int>(frame_duration.count() / 1000.0 * 2.0)
121 videoPlayback->rendered.wait(render_wait_ms);
125 std::lock_guard<std::mutex> lock(frame_mutex);
126 if (seek_generation.load() == frame_seek_generation)
127 last_video_position = rendered_position;
132 const auto current_time = std::chrono::system_clock::now();
133 const auto remaining_time = double_micro_sec(start_time +
134 (frame_duration * playback_frames) - current_time);
137 if (remaining_time > remaining_time.zero() ) {
138 if (remaining_time < max_sleep) {
139 std::this_thread::sleep_for(remaining_time);
142 std::this_thread::sleep_for(max_sleep);
148 start_time = std::chrono::time_point_cast<micro_sec>(current_time);
155 std::shared_ptr<openshot::Frame> PlayerPrivate::getFrame()
159 ReaderBase* current_reader;
161 std::lock_guard<std::mutex> lock(frame_mutex);
166 const int current_speed = speed.load();
167 const int64_t next_position = video_position.load() + current_speed;
168 if (next_position >= 1 && next_position <= reader->info.video_length) {
169 video_position = next_position;
170 }
else if (next_position < 1) {
174 video_position = reader->info.video_length;
178 position = video_position.load();
179 if (frame && frame->number == position && position == last_video_position)
182 playback_frames += std::abs(speed.load());
184 videoCache->NotifyPlaybackPosition(position);
185 current_reader = reader;
186 generation = seek_generation.load();
191 std::shared_ptr<Frame> decoded;
193 decoded = current_reader->GetFrame(position);
194 }
catch (
const ReaderClosed &) {
195 }
catch (
const OutOfBoundsFrame &) {
198 std::lock_guard<std::mutex> lock(frame_mutex);
199 if (seek_generation.load() != generation)
201 frame = std::move(decoded);
207 void PlayerPrivate::Seek(int64_t new_position)
209 std::lock_guard<std::mutex> lock(frame_mutex);
211 video_position = new_position;
212 last_video_position = 0;
221 bool PlayerPrivate::startPlayback()
223 if (video_position < 0)
return false;
226 startThread(Priority::high);
231 void PlayerPrivate::stopPlayback()
236 signalThreadShouldExit();
239 audioPlayback->signalThreadShouldExit();
240 audioPlayback->notify();
241 videoPlayback->signalThreadShouldExit();
242 videoPlayback->render.signal();
243 videoPlayback->notify();
244 waitForThreadToExit(-1);
247 audioPlayback->signalThreadShouldExit();
248 audioPlayback->notify();
249 videoPlayback->signalThreadShouldExit();
250 videoPlayback->render.signal();
251 videoPlayback->notify();
252 videoCache->StopThread(-1);
253 audioPlayback->waitForThreadToExit(-1);
254 videoPlayback->waitForThreadToExit(-1);
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.
Handles prefetching and caching of video/audio frames for smooth playback.
The video playback class.
This namespace is the default namespace for all code in the openshot library.