2014-01-25 03:38:38 +08:00
|
|
|
/**
|
|
|
|
|
* @file
|
|
|
|
|
* @brief Source file for PlayerPrivate class
|
|
|
|
|
* @author Duzy Chan <code@duzy.info>
|
2014-03-21 01:25:17 -05:00
|
|
|
* @author Jonathan Thomas <jonathan@openshot.org>
|
2014-01-25 03:38:38 +08:00
|
|
|
*
|
2019-06-09 08:31:04 -04:00
|
|
|
* @ref License
|
|
|
|
|
*/
|
|
|
|
|
|
2021-10-16 01:26:26 -04:00
|
|
|
// Copyright (c) 2008-2019 OpenShot Studios, LLC
|
|
|
|
|
//
|
|
|
|
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
2014-03-23 01:12:29 -05:00
|
|
|
|
2020-10-18 07:43:37 -04:00
|
|
|
#include "PlayerPrivate.h"
|
2021-01-26 10:52:04 -05:00
|
|
|
#include "Exceptions.h"
|
2021-11-01 11:04:31 -04:00
|
|
|
#include "ZmqLogger.h"
|
2014-01-25 03:38:38 +08:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
#include <thread> // for std::this_thread::sleep_for
|
|
|
|
|
#include <chrono> // for std::chrono milliseconds, high_resolution_clock
|
|
|
|
|
|
2014-01-25 03:38:38 +08:00
|
|
|
namespace openshot
|
|
|
|
|
{
|
2019-09-21 00:14:32 -04:00
|
|
|
// Constructor
|
|
|
|
|
PlayerPrivate::PlayerPrivate(openshot::RendererBase *rb)
|
|
|
|
|
: renderer(rb), Thread("player"), video_position(1), audio_position(0)
|
|
|
|
|
, audioPlayback(new openshot::AudioPlaybackThread())
|
|
|
|
|
, videoPlayback(new openshot::VideoPlaybackThread(rb))
|
|
|
|
|
, videoCache(new openshot::VideoCacheThread())
|
2021-12-06 17:30:45 -06:00
|
|
|
, speed(1), reader(NULL), last_video_position(1), max_sleep_ms(125000)
|
2015-02-04 23:56:43 -06:00
|
|
|
{ }
|
2014-01-25 03:38:38 +08:00
|
|
|
|
2014-03-23 01:12:29 -05:00
|
|
|
// Destructor
|
2014-01-25 03:38:38 +08:00
|
|
|
PlayerPrivate::~PlayerPrivate()
|
|
|
|
|
{
|
2021-11-04 17:33:14 -05:00
|
|
|
stopPlayback();
|
2019-09-21 00:14:32 -04:00
|
|
|
delete audioPlayback;
|
|
|
|
|
delete videoCache;
|
|
|
|
|
delete videoPlayback;
|
2014-01-25 03:38:38 +08:00
|
|
|
}
|
|
|
|
|
|
2014-03-23 01:12:29 -05:00
|
|
|
// Start thread
|
2014-01-27 19:03:46 +08:00
|
|
|
void PlayerPrivate::run()
|
2014-01-25 03:38:38 +08:00
|
|
|
{
|
2020-09-02 02:07:54 -04:00
|
|
|
// bail if no reader set
|
|
|
|
|
if (!reader)
|
|
|
|
|
return;
|
2014-04-02 16:48:27 -05:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
// Start the threads
|
|
|
|
|
if (reader->info.has_audio)
|
|
|
|
|
audioPlayback->startThread(8);
|
|
|
|
|
if (reader->info.has_video) {
|
|
|
|
|
videoCache->startThread(2);
|
|
|
|
|
videoPlayback->startThread(4);
|
|
|
|
|
}
|
2014-03-21 01:25:17 -05:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
using std::chrono::duration_cast;
|
2014-03-21 01:25:17 -05:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
// Types for storing time durations in whole and fractional milliseconds
|
2021-12-06 17:30:45 -06:00
|
|
|
using micro_sec = std::chrono::microseconds;
|
|
|
|
|
using double_micro_sec = std::chrono::duration<double, micro_sec::period>;
|
2014-03-23 01:12:29 -05:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
while (!threadShouldExit()) {
|
2021-12-03 15:21:11 -06:00
|
|
|
// Calculate on-screen time for a single frame in milliseconds
|
2021-12-06 17:30:45 -06:00
|
|
|
const auto frame_duration = double_micro_sec(1000000.0 / reader->info.fps.ToDouble());
|
2021-12-03 15:21:11 -06:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
// Get the start time (to track how long a frame takes to render)
|
|
|
|
|
const auto time1 = std::chrono::high_resolution_clock::now();
|
2014-02-23 14:08:01 +08:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
// Get the current video frame (if it's different)
|
|
|
|
|
frame = getFrame();
|
2015-02-04 23:56:43 -06:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
// Experimental Pausing Code (if frame has not changed)
|
|
|
|
|
if ((speed == 0 && video_position == last_video_position)
|
|
|
|
|
|| (video_position > reader->info.video_length)
|
|
|
|
|
) {
|
|
|
|
|
speed = 0;
|
|
|
|
|
std::this_thread::sleep_for(frame_duration);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2014-01-28 03:00:16 +08:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
// Set the video frame on the video thread and render frame
|
|
|
|
|
videoPlayback->frame = frame;
|
|
|
|
|
videoPlayback->render.signal();
|
2015-02-04 23:56:43 -06:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
// Keep track of the last displayed frame
|
|
|
|
|
last_video_position = video_position;
|
2015-02-04 23:56:43 -06:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
// How many frames ahead or behind is the video thread?
|
|
|
|
|
int64_t video_frame_diff = 0;
|
|
|
|
|
if (reader->info.has_audio && reader->info.has_video) {
|
|
|
|
|
if (speed != 1)
|
|
|
|
|
// Set audio frame again (since we are not in normal speed, and not paused)
|
|
|
|
|
audioPlayback->Seek(video_position);
|
2014-02-14 10:19:50 +08:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
// Only calculate this if a reader contains both an audio and video thread
|
|
|
|
|
audio_position = audioPlayback->getCurrentFramePosition();
|
|
|
|
|
video_frame_diff = video_position - audio_position;
|
|
|
|
|
}
|
2014-02-14 10:19:50 +08:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
// Get the end time (to track how long a frame takes to render)
|
|
|
|
|
const auto time2 = std::chrono::high_resolution_clock::now();
|
2014-02-14 10:19:50 +08:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
// Determine how many milliseconds it took to render the frame
|
2021-12-06 17:30:45 -06:00
|
|
|
const auto render_time = double_micro_sec(time2 - time1);
|
2014-03-21 01:25:17 -05:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
// Calculate the amount of time to sleep (by subtracting the render time)
|
2021-12-06 17:30:45 -06:00
|
|
|
auto sleep_time = duration_cast<micro_sec>(frame_duration - render_time);
|
2016-04-24 15:37:47 -05:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
// Debug
|
|
|
|
|
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());
|
2014-03-21 01:25:17 -05:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
// Adjust drift (if more than a few frames off between audio and video)
|
2021-12-06 15:47:32 -06:00
|
|
|
if (video_frame_diff > 6 && reader->info.has_audio && reader->info.has_video) {
|
2020-09-02 02:07:54 -04:00
|
|
|
// Since the audio and video threads are running independently,
|
|
|
|
|
// they will quickly get out of sync. To fix this, we calculate
|
|
|
|
|
// how far ahead or behind the video frame is, and adjust the amount
|
|
|
|
|
// of time the frame is displayed on the screen (i.e. the sleep time).
|
|
|
|
|
// If a frame is ahead of the audio, we sleep for longer.
|
|
|
|
|
// If a frame is behind the audio, we sleep less (or not at all),
|
|
|
|
|
// in order for the video to catch up.
|
2021-12-06 17:30:45 -06:00
|
|
|
sleep_time += duration_cast<micro_sec>((video_frame_diff / 2) * frame_duration);
|
2020-09-02 02:07:54 -04:00
|
|
|
}
|
2021-12-06 17:30:45 -06:00
|
|
|
else if (video_frame_diff < -3 && reader->info.has_audio && reader->info.has_video) {
|
|
|
|
|
// Video frames are a bit behind, sleep less, we need to display frames more quickly
|
|
|
|
|
sleep_time = duration_cast<micro_sec>(sleep_time * 0.75); // Sleep a little less
|
|
|
|
|
}
|
|
|
|
|
else if (video_frame_diff < -9 && reader->info.has_audio && reader->info.has_video) {
|
|
|
|
|
// Video frames are very behind, no sleep, we need to display frames more quickly
|
|
|
|
|
sleep_time = sleep_time.zero(); // Don't sleep now... immediately go to next position
|
|
|
|
|
}
|
|
|
|
|
else if (video_frame_diff < -12 && reader->info.has_audio && reader->info.has_video) {
|
|
|
|
|
// Video frames are very behind, jump forward the entire distance (catch up with the audio position)
|
2021-12-07 13:37:29 -06:00
|
|
|
// Skip frame(s) to catch up to the audio
|
2021-12-06 17:30:45 -06:00
|
|
|
video_position += std::fabs(video_frame_diff);
|
2020-09-02 02:07:54 -04:00
|
|
|
sleep_time = sleep_time.zero(); // Don't sleep now... immediately go to next position
|
|
|
|
|
}
|
2015-02-04 23:56:43 -06:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
// Sleep (leaving the video frame on the screen for the correct amount of time)
|
2021-11-04 17:33:14 -05:00
|
|
|
// Don't sleep too long though (in some extreme cases, for example when stopping threads
|
|
|
|
|
// and shutting down, the video_frame_diff can jump to a crazy big number, and we don't
|
|
|
|
|
// want to sleep too long (max of X seconds)
|
|
|
|
|
if (sleep_time > sleep_time.zero() && sleep_time.count() < max_sleep_ms) {
|
2020-09-02 02:07:54 -04:00
|
|
|
std::this_thread::sleep_for(sleep_time);
|
|
|
|
|
}
|
2014-03-21 01:25:17 -05:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
}
|
2014-01-27 19:03:46 +08:00
|
|
|
}
|
|
|
|
|
|
2014-03-23 01:12:29 -05:00
|
|
|
// Get the next displayed frame (based on speed and direction)
|
2019-09-21 00:14:32 -04:00
|
|
|
std::shared_ptr<openshot::Frame> PlayerPrivate::getFrame()
|
2014-01-31 16:27:16 +08:00
|
|
|
{
|
2020-09-02 02:07:54 -04:00
|
|
|
try {
|
|
|
|
|
// Get the next frame (based on speed)
|
|
|
|
|
if (video_position + speed >= 1 && video_position + speed <= reader->info.video_length)
|
|
|
|
|
video_position = video_position + speed;
|
2015-02-04 23:56:43 -06:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
if (frame && frame->number == video_position && video_position == last_video_position) {
|
|
|
|
|
// return cached frame
|
|
|
|
|
return frame;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Update cache on which frame was retrieved
|
|
|
|
|
videoCache->setCurrentFramePosition(video_position);
|
2015-06-01 00:20:14 -07:00
|
|
|
|
2020-09-02 02:07:54 -04:00
|
|
|
// return frame from reader
|
|
|
|
|
return reader->GetFrame(video_position);
|
|
|
|
|
}
|
2014-03-23 01:12:29 -05:00
|
|
|
|
2019-09-21 00:14:32 -04:00
|
|
|
} catch (const ReaderClosed & e) {
|
|
|
|
|
// ...
|
|
|
|
|
} catch (const OutOfBoundsFrame & e) {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
return std::shared_ptr<openshot::Frame>();
|
2014-01-31 16:27:16 +08:00
|
|
|
}
|
|
|
|
|
|
2014-03-23 01:12:29 -05:00
|
|
|
// Start video/audio playback
|
2014-01-27 19:03:46 +08:00
|
|
|
bool PlayerPrivate::startPlayback()
|
|
|
|
|
{
|
2019-09-21 00:14:32 -04:00
|
|
|
if (video_position < 0) return false;
|
2015-06-01 00:20:14 -07:00
|
|
|
|
2021-11-04 17:33:14 -05:00
|
|
|
stopPlayback();
|
2019-09-21 00:14:32 -04:00
|
|
|
startThread(1);
|
|
|
|
|
return true;
|
2014-01-27 19:03:46 +08:00
|
|
|
}
|
|
|
|
|
|
2014-03-23 01:12:29 -05:00
|
|
|
// Stop video/audio playback
|
2021-11-04 17:33:14 -05:00
|
|
|
void PlayerPrivate::stopPlayback()
|
2014-01-27 19:03:46 +08:00
|
|
|
{
|
2021-11-04 17:33:14 -05:00
|
|
|
if (audioPlayback->isThreadRunning() && reader->info.has_audio) audioPlayback->stopThread(max_sleep_ms);
|
|
|
|
|
if (videoCache->isThreadRunning() && reader->info.has_video) videoCache->stopThread(max_sleep_ms);
|
|
|
|
|
if (videoPlayback->isThreadRunning() && reader->info.has_video) videoPlayback->stopThread(max_sleep_ms);
|
|
|
|
|
if (isThreadRunning()) stopThread(max_sleep_ms);
|
2014-01-25 03:38:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|