From 969a2015bfda009a305e9f59dacde9ed029c8afd Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Tue, 20 Sep 2022 16:55:07 -0500 Subject: [PATCH] Increasing max video cache frames, moving percentage-ahead caching to a setting, fixing a calculation error on bytes per frame --- src/Qt/VideoCacheThread.cpp | 43 ++++++++++++++++++++++++++----------- src/Qt/VideoCacheThread.h | 3 +++ src/Settings.cpp | 3 ++- src/Settings.h | 5 ++++- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/Qt/VideoCacheThread.cpp b/src/Qt/VideoCacheThread.cpp index 621d68c5..8873b95d 100644 --- a/src/Qt/VideoCacheThread.cpp +++ b/src/Qt/VideoCacheThread.cpp @@ -78,6 +78,19 @@ namespace openshot Seek(new_position); } + // 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(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 @@ -98,14 +111,14 @@ namespace openshot // Start the thread void VideoCacheThread::run() { - // Get settings - Settings *s = Settings::Instance(); - // Types for storing time durations in whole and fractional microseconds using micro_sec = std::chrono::microseconds; using double_micro_sec = std::chrono::duration; 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; @@ -137,18 +150,24 @@ namespace openshot // To allow the cache to fill-up only on the initial pause. should_pause_cache = true; - // Calculate bytes per frame. If we have a reference openshot::Frame, use that instead (the preview - // window can be smaller, can thus reduce the bytes per frame) - int64_t bytes_per_frame = (reader->info.height * reader->info.width * 4) + - (reader->info.sample_rate * reader->info.channels * 4); - if (last_cached_frame && last_cached_frame->has_image_data && last_cached_frame->has_audio_data) { - bytes_per_frame = last_cached_frame->GetBytes(); + // 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, use 1/2 the cache size (so our cache will be 50% before the play-head, and 50% after it) - max_frames_ahead = (reader->GetCache()->GetMaxBytes() / bytes_per_frame) / 2; + // 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; @@ -222,7 +241,7 @@ namespace openshot if (current_speed != speed) { break; } - // Check if playback has stopped + // Check if thread has stopped if (!is_playing) { break; } diff --git a/src/Qt/VideoCacheThread.h b/src/Qt/VideoCacheThread.h index 135418fb..6321b932 100644 --- a/src/Qt/VideoCacheThread.h +++ b/src/Qt/VideoCacheThread.h @@ -46,6 +46,9 @@ namespace openshot /// Destructor ~VideoCacheThread(); + /// Get the size in bytes of a frame (rough estimate) + int64_t getBytes(int width, int height, int sample_rate, int channels, float fps); + /// Get Speed (The speed and direction to playback a reader (1=normal, 2=fast, 3=faster, -1=rewind, etc...) int getSpeed() const { return speed; } diff --git a/src/Settings.cpp b/src/Settings.cpp index 82263bf3..c9a726d6 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -33,9 +33,10 @@ Settings *Settings::Instance() m_pInstance->DE_LIMIT_WIDTH_MAX = 1950; m_pInstance->HW_DE_DEVICE_SET = 0; m_pInstance->HW_EN_DEVICE_SET = 0; + m_pInstance->VIDEO_CACHE_PERCENT_AHEAD = 0.75; m_pInstance->VIDEO_CACHE_MIN_PREROLL_FRAMES = 2; m_pInstance->VIDEO_CACHE_MAX_PREROLL_FRAMES = 8; - m_pInstance->VIDEO_CACHE_MAX_FRAMES = 30 * 3; + m_pInstance->VIDEO_CACHE_MAX_FRAMES = 30 * 20; m_pInstance->ENABLE_PLAYBACK_CACHING = true; m_pInstance->PLAYBACK_AUDIO_DEVICE_NAME = ""; m_pInstance->PLAYBACK_AUDIO_DEVICE_TYPE = ""; diff --git a/src/Settings.h b/src/Settings.h index 7f88c451..b9a12903 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -82,6 +82,9 @@ namespace openshot { /// Which GPU to use to encode (0 is the first) int HW_EN_DEVICE_SET = 0; + /// Percentage of cache in front of the playhead (0.0 to 1.0) + float VIDEO_CACHE_PERCENT_AHEAD = 0.75; + /// Minimum number of frames to cache before playback begins int VIDEO_CACHE_MIN_PREROLL_FRAMES = 2; @@ -89,7 +92,7 @@ namespace openshot { int VIDEO_CACHE_MAX_PREROLL_FRAMES = 8; /// Max number of frames (when paused) to cache for playback - int VIDEO_CACHE_MAX_FRAMES = 30 * 3; + int VIDEO_CACHE_MAX_FRAMES = 30 * 20; /// Enable/Disable the cache thread to pre-fetch and cache video frames before we need them bool ENABLE_PLAYBACK_CACHING = true;