Skip to content

Commit

Permalink
Replace sleep()/usleep() with std::chrono calls (#473)
Browse files Browse the repository at this point in the history
  • Loading branch information
ferdnyc committed Sep 2, 2020
1 parent f71051e commit e500cae
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 107 deletions.
7 changes: 5 additions & 2 deletions src/FFmpegReader.cpp
Expand Up @@ -33,6 +33,9 @@

#include "../include/FFmpegReader.h"

#include <thread> // for std::this_thread::sleep_for
#include <chrono> // for std::chrono::milliseconds

#define ENABLE_VAAPI 0

#if HAVE_HW_ACCEL
Expand Down Expand Up @@ -925,7 +928,7 @@ std::shared_ptr<Frame> FFmpegReader::ReadStream(int64_t requested_frame) {

// Wait if too many frames are being processed
while (processing_video_frames_size + processing_audio_frames_size >= minimum_packets) {
usleep(2500);
std::this_thread::sleep_for(std::chrono::milliseconds(3));
const GenericScopedLock <CriticalSection> lock(processingCriticalSection);
processing_video_frames_size = processing_video_frames.size();
processing_audio_frames_size = processing_audio_frames.size();
Expand Down Expand Up @@ -1716,7 +1719,7 @@ void FFmpegReader::Seek(int64_t requested_frame) {

// Wait for any processing frames to complete
while (processing_video_frames_size + processing_audio_frames_size > 0) {
usleep(2500);
std::this_thread::sleep_for(std::chrono::milliseconds(3));
const GenericScopedLock <CriticalSection> lock(processingCriticalSection);
processing_video_frames_size = processing_video_frames.size();
processing_audio_frames_size = processing_audio_frames.size();
Expand Down
5 changes: 4 additions & 1 deletion src/Frame.cpp
Expand Up @@ -30,6 +30,9 @@

#include "../include/Frame.h"

#include <thread> // for std::this_thread::sleep_for
#include <chrono> // for std::chrono::milliseconds

using namespace std;
using namespace openshot;

Expand Down Expand Up @@ -1028,7 +1031,7 @@ void Frame::Play()
while (transport1.isPlaying())
{
cout << "playing" << endl;
usleep(1000000);
std::this_thread::sleep_for(std::chrono::seconds(1));
}

cout << "DONE!!!" << endl;
Expand Down
5 changes: 4 additions & 1 deletion src/Qt/AudioPlaybackThread.cpp
Expand Up @@ -31,6 +31,9 @@

#include "../../include/Qt/AudioPlaybackThread.h"

#include <thread> // for std::this_thread::sleep_for
#include <chrono> // for std::chrono::milliseconds

namespace openshot
{

Expand Down Expand Up @@ -194,7 +197,7 @@ namespace openshot
transport.start();

while (!threadShouldExit() && transport.isPlaying() && is_playing)
usleep(2500);
std::this_thread::sleep_for(std::chrono::milliseconds(2));

// Stop audio and shutdown transport
Stop();
Expand Down
210 changes: 112 additions & 98 deletions src/Qt/PlayerPrivate.cpp
Expand Up @@ -31,6 +31,9 @@

#include "../../include/Qt/PlayerPrivate.h"

#include <thread> // for std::this_thread::sleep_for
#include <chrono> // for std::chrono milliseconds, high_resolution_clock

namespace openshot
{
// Constructor
Expand All @@ -54,109 +57,120 @@ namespace openshot
// Start thread
void PlayerPrivate::run()
{
// bail if no reader set
if (!reader)
return;

// Start the threads
if (reader->info.has_audio)
audioPlayback->startThread(8);
if (reader->info.has_video) {
videoCache->startThread(2);
videoPlayback->startThread(4);
}

while (!threadShouldExit()) {

// Calculate the milliseconds a single frame should stay on the screen
double frame_time = (1000.0 / reader->info.fps.ToDouble());

// Get the start time (to track how long a frame takes to render)
const Time t1 = Time::getCurrentTime();

// Get the current video frame (if it's different)
frame = getFrame();

// Experimental Pausing Code (if frame has not changed)
if ((speed == 0 && video_position == last_video_position) || (video_position > reader->info.video_length)) {
speed = 0;
sleep(frame_time);
continue;
}

// Set the video frame on the video thread and render frame
videoPlayback->frame = frame;
videoPlayback->render.signal();

// Keep track of the last displayed frame
last_video_position = video_position;

// 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);

// Only calculate this if a reader contains both an audio and video thread
audio_position = audioPlayback->getCurrentFramePosition();
video_frame_diff = video_position - audio_position;
}

// Get the end time (to track how long a frame takes to render)
const Time t2 = Time::getCurrentTime();

// Determine how many milliseconds it took to render the frame
int64_t render_time = t2.toMilliseconds() - t1.toMilliseconds();

// Calculate the amount of time to sleep (by subtracting the render time)
int sleep_time = int(frame_time - render_time);

// 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", render_time, "sleep_time", sleep_time);

// Adjust drift (if more than a few frames off between audio and video)
if (video_frame_diff > 0 && reader->info.has_audio && reader->info.has_video)
// 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.
sleep_time += (video_frame_diff * (1000.0 / reader->info.fps.ToDouble()));


else if (video_frame_diff < -10 && reader->info.has_audio && reader->info.has_video) {
// Skip frame(s) to catch up to the audio (if more than 10 frames behind)
video_position += abs(video_frame_diff) / 2; // Seek forward 1/2 the difference
sleep_time = 0; // Don't sleep now... immediately go to next position
}

// Sleep (leaving the video frame on the screen for the correct amount of time)
if (sleep_time > 0) usleep(sleep_time * 1000);

}
// bail if no reader set
if (!reader)
return;

// Start the threads
if (reader->info.has_audio)
audioPlayback->startThread(8);
if (reader->info.has_video) {
videoCache->startThread(2);
videoPlayback->startThread(4);
}

using std::chrono::duration_cast;

// Types for storing time durations in whole and fractional milliseconds
using ms = std::chrono::milliseconds;
using double_ms = std::chrono::duration<double, ms::period>;

// Calculate on-screen time for a single frame in milliseconds
const auto frame_duration = double_ms(1000.0 / reader->info.fps.ToDouble());

while (!threadShouldExit()) {
// Get the start time (to track how long a frame takes to render)
const auto time1 = std::chrono::high_resolution_clock::now();

// Get the current video frame (if it's different)
frame = getFrame();

// 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;
}

// Set the video frame on the video thread and render frame
videoPlayback->frame = frame;
videoPlayback->render.signal();

// Keep track of the last displayed frame
last_video_position = video_position;

// 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);

// Only calculate this if a reader contains both an audio and video thread
audio_position = audioPlayback->getCurrentFramePosition();
video_frame_diff = video_position - audio_position;
}

// Get the end time (to track how long a frame takes to render)
const auto time2 = std::chrono::high_resolution_clock::now();

// Determine how many milliseconds it took to render the frame
const auto render_time = double_ms(time2 - time1);

// Calculate the amount of time to sleep (by subtracting the render time)
auto sleep_time = duration_cast<ms>(frame_duration - render_time);

// 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());

// Adjust drift (if more than a few frames off between audio and video)
if (video_frame_diff > 0 && reader->info.has_audio && reader->info.has_video) {
// 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.
sleep_time += duration_cast<ms>(video_frame_diff * frame_duration);
}

else if (video_frame_diff < -10 && reader->info.has_audio && reader->info.has_video) {
// Skip frame(s) to catch up to the audio (if more than 10 frames behind)
video_position += std::fabs(video_frame_diff) / 2; // Seek forward 1/2 the difference
sleep_time = sleep_time.zero(); // Don't sleep now... immediately go to next position
}

// Sleep (leaving the video frame on the screen for the correct amount of time)
if (sleep_time > sleep_time.zero()) {
std::this_thread::sleep_for(sleep_time);
}

}
}

// Get the next displayed frame (based on speed and direction)
std::shared_ptr<openshot::Frame> PlayerPrivate::getFrame()
{
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;

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);

// return frame from reader
return reader->GetFrame(video_position);
}
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;

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);

// return frame from reader
return reader->GetFrame(video_position);
}

} catch (const ReaderClosed & e) {
// ...
Expand Down
15 changes: 11 additions & 4 deletions src/Qt/VideoCacheThread.cpp
Expand Up @@ -31,6 +31,9 @@
#include "../../include/Qt/VideoCacheThread.h"
#include <algorithm>

#include <thread> // for std::this_thread::sleep_for
#include <chrono> // for std::chrono::milliseconds

namespace openshot
{
// Constructor
Expand Down Expand Up @@ -81,10 +84,14 @@ namespace openshot
// Start the thread
void VideoCacheThread::run()
{
while (!threadShouldExit() && is_playing) {
// Types for storing time durations in whole and fractional milliseconds
using ms = std::chrono::milliseconds;
using double_ms = std::chrono::duration<double, ms::period>;

// Calculate on-screen time for a single frame in milliseconds
const auto frame_duration = double_ms(1000.0 / reader->info.fps.ToDouble());

// Calculate sleep time for frame rate
double frame_time = (1000.0 / reader->info.fps.ToDouble());
while (!threadShouldExit() && is_playing) {

// Cache frames before the other threads need them
// Cache frames up to the max frames
Expand Down Expand Up @@ -117,7 +124,7 @@ namespace openshot
}

// Sleep for 1 frame length
usleep(frame_time * 1000);
std::this_thread::sleep_for(frame_duration);
}

return;
Expand Down
8 changes: 7 additions & 1 deletion src/ZmqLogger.cpp
Expand Up @@ -36,6 +36,12 @@

using namespace std;
using namespace openshot;
#include <sstream>
#include <iostream>
#include <iomanip>
#include <ctime>
#include <thread> // for std::this_thread::sleep_for
#include <chrono> // for std::duration::microseconds


// Global reference to logger
Expand Down Expand Up @@ -108,7 +114,7 @@ void ZmqLogger::Connection(std::string new_connection)
}

// Sleeping to allow connection to wake up (0.25 seconds)
usleep(250000);
std::this_thread::sleep_for(std::chrono::milliseconds(250));
}

void ZmqLogger::Log(std::string message)
Expand Down

0 comments on commit e500cae

Please sign in to comment.