Loading...
Searching...
No Matches
VideoCacheThread.cpp
Go to the documentation of this file.
// Copyright (c) 2008-2019 OpenShot Studios, LLC
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "VideoCacheThread.h"
#include "CacheBase.h"
#include "Exceptions.h"
#include "Frame.h"
#include "OpenMPUtilities.h"
#include "Settings.h"
#include "Timeline.h"
#include <algorithm>
#include <thread> // for std::this_thread::sleep_for
#include <chrono> // for std::chrono::microseconds
namespace openshot
{
// Constructor
VideoCacheThread::VideoCacheThread()
: Thread("video-cache"), speed(0), last_speed(1), is_playing(false),
reader(NULL), current_display_frame(1), cached_frame_count(0),
min_frames_ahead(4), max_frames_ahead(8), should_pause_cache(false),
timeline_max_frame(0), should_break(false)
{
}
// Destructor
VideoCacheThread::~VideoCacheThread()
{
}
// Seek the reader to a particular frame number
void VideoCacheThread::Seek(int64_t new_position)
{
requested_display_frame = new_position;
}
// Seek the reader to a particular frame number and optionally start the pre-roll
void VideoCacheThread::Seek(int64_t new_position, bool start_preroll)
{
// Get timeline instance
Timeline *t = (Timeline *) reader;
// Calculate last frame # on timeline (to prevent caching past this point)
timeline_max_frame = t->GetMaxFrame();
// Determine previous frame number (depending on last non-zero/non-paused speed)
int64_t previous_frame = new_position;
if (last_speed < 0) {
// backwards
previous_frame++;
} else if (last_speed > 0) {
// forward
previous_frame--;
}
if (previous_frame <= 0) {
// min frame is 1
previous_frame = 1;
}
// Clear cache if previous frame outside the cached range, which means we are
// requesting a non-contigous frame compared to our current cache range
if (new_position >= 1 && new_position <= timeline_max_frame && !reader->GetCache()->Contains(previous_frame)) {
// Clear cache
t->ClearAllCache();
// Break out of any existing cache loop
should_break = true;
// Force cache direction back to forward
last_speed = 1;
}
// Reset pre-roll when requested frame is not currently cached
if (start_preroll && reader && reader->GetCache() && !reader->GetCache()->Contains(new_position)) {
// Break out of any existing cache loop
should_break = true;
// Reset stats and allow cache to rebuild (if paused)
cached_frame_count = 0;
if (speed == 0) {
should_pause_cache = false;
}
}
// Actually update seek position
Seek(new_position);
}
// Set Speed (The speed and direction to playback a reader (1=normal, 2=fast, 3=faster, -1=rewind, etc...)
void VideoCacheThread::setSpeed(int new_speed) {
if (new_speed != 0) {
// Track last non-zero speed
last_speed = new_speed;
}
speed = new_speed;
}
// Get the size in bytes of a frame (rough estimate)
int64_t VideoCacheThread::getBytes(int width, int height, int sample_rate, int channels, float fps)
{
int64_t total_bytes = 0;
total_bytes += static_cast<int64_t>(width * height * sizeof(char) * 4);
// approximate audio size (sample rate / 24 fps)
total_bytes += ((sample_rate * channels) / fps) * sizeof(float);
// return size of this frame
return total_bytes;
}
// Play the video
void VideoCacheThread::Play() {
// Start playing
is_playing = true;
}
// Stop the audio
void VideoCacheThread::Stop() {
// Stop playing
is_playing = false;
}
// Is cache ready for playback (pre-roll)
bool VideoCacheThread::isReady() {
return (cached_frame_count > min_frames_ahead);
}
// Start the thread
void VideoCacheThread::run()
{
// Types for storing time durations in whole and fractional microseconds
using micro_sec = std::chrono::microseconds;
using double_micro_sec = std::chrono::duration<double, micro_sec::period>;
while (!threadShouldExit() && is_playing) {
// Get settings
Settings *s = Settings::Instance();
// init local vars
min_frames_ahead = s->VIDEO_CACHE_MIN_PREROLL_FRAMES;
max_frames_ahead = s->VIDEO_CACHE_MAX_PREROLL_FRAMES;
// Calculate on-screen time for a single frame
const auto frame_duration = double_micro_sec(1000000.0 / reader->info.fps.ToDouble());
int current_speed = speed;
// Increment and direction for cache loop
int64_t increment = 1;
// Check for empty cache (and re-trigger preroll)
// This can happen when the user manually empties the timeline cache
if (reader->GetCache()->Count() == 0) {
should_pause_cache = false;
cached_frame_count = 0;
}
// Update current display frame
current_display_frame = requested_display_frame;
if (current_speed == 0 && should_pause_cache || !s->ENABLE_PLAYBACK_CACHING) {
// Sleep during pause (after caching additional frames when paused)
// OR sleep when playback caching is disabled
std::this_thread::sleep_for(frame_duration / 2);
continue;
} else if (current_speed == 0) {
// Allow 'max frames' to increase when pause is detected (based on cache)
// To allow the cache to fill-up only on the initial pause.
should_pause_cache = true;
// Calculate bytes per frame
int64_t bytes_per_frame = getBytes(reader->info.width, reader->info.height,
reader->info.sample_rate, reader->info.channels,
reader->info.fps.ToFloat());
Timeline *t = (Timeline *) reader;
if (t->preview_width != reader->info.width || t->preview_height != reader->info.height) {
// If we have a different timeline preview size, use that instead (the preview
// window can be smaller, can thus reduce the bytes per frame)
bytes_per_frame = getBytes(t->preview_width, t->preview_height,
reader->info.sample_rate, reader->info.channels,
reader->info.fps.ToFloat());
}
// Calculate # of frames on Timeline cache (when paused)
if (reader->GetCache() && reader->GetCache()->GetMaxBytes() > 0) {
// When paused, limit the cached frames to the following % of total cache size.
// This allows for us to leave some cache behind the plahead, and some in front of the playhead.
max_frames_ahead = (reader->GetCache()->GetMaxBytes() / bytes_per_frame) * s->VIDEO_CACHE_PERCENT_AHEAD;
if (max_frames_ahead > s->VIDEO_CACHE_MAX_FRAMES) {
// Ignore values that are too large, and default to a safer value
max_frames_ahead = s->VIDEO_CACHE_MAX_FRAMES;
}
}
// Overwrite the increment to our cache position
// to fully cache frames while paused (support forward and rewind caching)
// Use `last_speed` which is the last non-zero/non-paused speed
if (last_speed < 0) {
increment = -1;
}
} else {
// normal playback
should_pause_cache = false;
}
// Always cache frames from the current display position to our maximum (based on the cache size).
// Frames which are already cached are basically free. Only uncached frames have a big CPU cost.
// By always looping through the expected frame range, we can fill-in missing frames caused by a
// fragmented cache object (i.e. the user clicking all over the timeline). The -1 is to always
// cache 1 frame previous to our current frame (to avoid our Seek method from clearing the cache).
int64_t starting_frame = std::min(current_display_frame, timeline_max_frame) - 1;
int64_t ending_frame = std::min(starting_frame + max_frames_ahead, timeline_max_frame);
// Adjust ending frame for cache loop
if (increment < 0) {
// Reverse loop (if we are going backwards)
ending_frame = starting_frame - max_frames_ahead;
}
if (starting_frame < 1) {
// Don't allow negative frame number caching
starting_frame = 1;
}
if (ending_frame < 1) {
// Don't allow negative frame number caching
ending_frame = 1;
}
// Reset cache break-loop flag
should_break = false;
// Loop through range of frames (and cache them)
for (int64_t cache_frame = starting_frame; cache_frame != (ending_frame + increment); cache_frame += increment) {
cached_frame_count++;
if (reader && reader->GetCache() && !reader->GetCache()->Contains(cache_frame)) {
try
{
// This frame is not already cached... so request it again (to force the creation & caching)
// This will also re-order the missing frame to the front of the cache
last_cached_frame = reader->GetFrame(cache_frame);
}
catch (const OutOfBoundsFrame & e) { }
}
// Check if thread has stopped OR should_break is triggered
if (!is_playing || should_break || !s->ENABLE_PLAYBACK_CACHING) {
should_break = false;
break;
}
}
// Sleep for a fraction of frame duration
std::this_thread::sleep_for(frame_duration / 2);
}
return;
}
}
CacheBase.h
Header file for CacheBase class.
Exceptions.h
Header file for all Exception classes.
Frame.h
Header file for Frame class.
OpenMPUtilities.h
Header file for OpenMPUtilities (set some common macros)
Settings.h
Header file for global Settings class.
Timeline.h
Header file for Timeline class.
VideoCacheThread.h
Source file for VideoCacheThread class.
openshot::CacheBase::Contains
virtual bool Contains(int64_t frame_number)=0
Check if frame is already contained in cache.
openshot::CacheBase::Count
virtual int64_t Count()=0
Count the frames in the queue.
openshot::CacheBase::GetMaxBytes
int64_t GetMaxBytes()
Gets the maximum bytes value.
Definition CacheBase.h:97
openshot::Fraction::ToFloat
float ToFloat()
Return this fraction as a float (i.e. 1/2 = 0.5)
Definition Fraction.cpp:35
openshot::Fraction::ToDouble
double ToDouble() const
Return this fraction as a double (i.e. 1/2 = 0.5)
Definition Fraction.cpp:40
openshot::OutOfBoundsFrame
Exception for frames that are out of bounds.
Definition Exceptions.h:301
openshot::ReaderBase::info
openshot::ReaderInfo info
Information about the current media file.
Definition ReaderBase.h:88
openshot::ReaderBase::GetFrame
virtual std::shared_ptr< openshot::Frame > GetFrame(int64_t number)=0
openshot::ReaderBase::GetCache
virtual openshot::CacheBase * GetCache()=0
Get the cache object used by this reader (note: not all readers use cache)
openshot::Settings
This class is contains settings used by libopenshot (and can be safely toggled at any point)
Definition Settings.h:26
openshot::Settings::Instance
static Settings * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
Definition Settings.cpp:23
openshot::Settings::VIDEO_CACHE_MIN_PREROLL_FRAMES
int VIDEO_CACHE_MIN_PREROLL_FRAMES
Minimum number of frames to cache before playback begins.
Definition Settings.h:89
openshot::Settings::VIDEO_CACHE_MAX_FRAMES
int VIDEO_CACHE_MAX_FRAMES
Max number of frames (when paused) to cache for playback.
Definition Settings.h:95
openshot::Settings::VIDEO_CACHE_PERCENT_AHEAD
float VIDEO_CACHE_PERCENT_AHEAD
Percentage of cache in front of the playhead (0.0 to 1.0)
Definition Settings.h:86
openshot::Settings::ENABLE_PLAYBACK_CACHING
bool ENABLE_PLAYBACK_CACHING
Enable/Disable the cache thread to pre-fetch and cache video frames before we need them.
Definition Settings.h:98
openshot::Settings::VIDEO_CACHE_MAX_PREROLL_FRAMES
int VIDEO_CACHE_MAX_PREROLL_FRAMES
Max number of frames (ahead of playhead) to cache during playback.
Definition Settings.h:92
openshot::TimelineBase::preview_height
int preview_height
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
Definition TimelineBase.h:45
openshot::TimelineBase::preview_width
int preview_width
Optional preview width of timeline image. If your preview window is smaller than the timeline,...
Definition TimelineBase.h:44
openshot::Timeline
This class represents a timeline.
Definition Timeline.h:148
openshot::Timeline::GetMaxFrame
int64_t GetMaxFrame()
Look up the end frame number of the latest element on the timeline.
Definition Timeline.cpp:469
openshot::Timeline::ClearAllCache
void ClearAllCache(bool deep=false)
Definition Timeline.cpp:1668
openshot::VideoCacheThread::setSpeed
void setSpeed(int new_speed)
Set Speed (The speed and direction to playback a reader (1=normal, 2=fast, 3=faster,...
Definition VideoCacheThread.cpp:101
openshot::VideoCacheThread::is_playing
bool is_playing
Definition VideoCacheThread.h:35
openshot::VideoCacheThread::max_frames_ahead
int64_t max_frames_ahead
Definition VideoCacheThread.h:41
openshot::VideoCacheThread::requested_display_frame
int64_t requested_display_frame
Definition VideoCacheThread.h:36
openshot::VideoCacheThread::timeline_max_frame
int64_t timeline_max_frame
Definition VideoCacheThread.h:42
openshot::VideoCacheThread::should_break
bool should_break
Definition VideoCacheThread.h:44
openshot::VideoCacheThread::Play
void Play()
Play the video.
Definition VideoCacheThread.cpp:123
openshot::VideoCacheThread::Stop
void Stop()
Stop the audio playback.
Definition VideoCacheThread.cpp:129
openshot::VideoCacheThread::cached_frame_count
int64_t cached_frame_count
Definition VideoCacheThread.h:38
openshot::VideoCacheThread::~VideoCacheThread
~VideoCacheThread()
Destructor.
Definition VideoCacheThread.cpp:38
openshot::VideoCacheThread::last_speed
int last_speed
Definition VideoCacheThread.h:34
openshot::VideoCacheThread::isReady
bool isReady()
Is cache ready for video/audio playback.
Definition VideoCacheThread.cpp:135
openshot::VideoCacheThread::current_display_frame
int64_t current_display_frame
Definition VideoCacheThread.h:37
openshot::VideoCacheThread::VideoCacheThread
VideoCacheThread()
Constructor.
Definition VideoCacheThread.cpp:29
openshot::VideoCacheThread::getBytes
int64_t getBytes(int width, int height, int sample_rate, int channels, float fps)
Get the size in bytes of a frame (rough estimate)
Definition VideoCacheThread.cpp:110
openshot::VideoCacheThread::reader
ReaderBase * reader
Definition VideoCacheThread.h:39
openshot::VideoCacheThread::Seek
void Seek(int64_t new_position)
Seek the reader to a particular frame number.
Definition VideoCacheThread.cpp:43
openshot::VideoCacheThread::should_pause_cache
bool should_pause_cache
Definition VideoCacheThread.h:43
openshot::VideoCacheThread::run
void run()
Start the thread.
Definition VideoCacheThread.cpp:140
openshot::VideoCacheThread::min_frames_ahead
int64_t min_frames_ahead
Definition VideoCacheThread.h:40
openshot::VideoCacheThread::speed
int speed
Definition VideoCacheThread.h:33
openshot::VideoCacheThread::last_cached_frame
std::shared_ptr< Frame > last_cached_frame
Definition VideoCacheThread.h:32
openshot
This namespace is the default namespace for all code in the openshot library.
Definition Compressor.h:29
openshot::ReaderInfo::width
int width
The width of the video (in pixesl)
Definition ReaderBase.h:46
openshot::ReaderInfo::channels
int channels
The number of audio channels used in the audio stream.
Definition ReaderBase.h:61
openshot::ReaderInfo::fps
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
Definition ReaderBase.h:48
openshot::ReaderInfo::height
int height
The height of the video (in pixels)
Definition ReaderBase.h:45
openshot::ReaderInfo::sample_rate
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
Definition ReaderBase.h:60