diff --git a/src/Qt/VideoCacheThread.cpp b/src/Qt/VideoCacheThread.cpp index f817c93f3..e1e53f5d4 100644 --- a/src/Qt/VideoCacheThread.cpp +++ b/src/Qt/VideoCacheThread.cpp @@ -94,8 +94,10 @@ namespace openshot while (!threadShouldExit() && is_playing) { // Cache frames before the other threads need them - // Cache frames up to the max frames - while (speed == 1 && (position - current_display_frame) < max_frames) + // Cache frames up to the max frames. Reset to current position + // if cache gets too far away from display frame. Cache frames + // even when player is paused (i.e. speed 0). + while ((position - current_display_frame) < max_frames) { // Only cache up till the max_frames amount... then sleep try @@ -104,6 +106,14 @@ namespace openshot ZmqLogger::Instance()->AppendDebugMethod("VideoCacheThread::run (cache frame)", "position", position, "current_display_frame", current_display_frame, "max_frames", max_frames, "needed_frames", (position - current_display_frame)); // Force the frame to be generated + if (reader->GetCache()->GetSmallestFrame()) { + int64_t smallest_cached_frame = reader->GetCache()->GetSmallestFrame()->number; + if (smallest_cached_frame > current_display_frame) { + // Cache position has gotten too far away from current display frame. + // Reset the position to the current display frame. + position = current_display_frame; + } + } reader->GetFrame(position); } @@ -113,14 +123,8 @@ namespace openshot // Ignore out of bounds frame exceptions } - // Is cache position behind current display frame? - if (position < current_display_frame) { - // Jump ahead - position = current_display_frame; - } - // Increment frame number - position++; + position++; } // Sleep for 1 frame length diff --git a/src/Qt/VideoCacheThread.h b/src/Qt/VideoCacheThread.h index 6fbf6d58f..f9551bdb8 100644 --- a/src/Qt/VideoCacheThread.h +++ b/src/Qt/VideoCacheThread.h @@ -45,10 +45,13 @@ namespace openshot */ class VideoCacheThread : Thread { + private: + std::atomic_int position; + + protected: std::shared_ptr frame; int speed; bool is_playing; - int64_t position; int64_t current_display_frame; ReaderBase *reader; int max_frames; diff --git a/src/Timeline.cpp b/src/Timeline.cpp index 2c5a9a279..abc02b6f8 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -695,6 +695,7 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame) // Check cache std::shared_ptr frame; + std::lock_guard guard(get_frame_mutex); #pragma omp critical (T_GetFrame) frame = final_cache->GetFrame(requested_frame); if (frame) { @@ -724,6 +725,15 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame) return frame; } + // Check if previous frame was cached? (if not, assume we are seeking somewhere else on the Timeline, and need + // to clear all cache (for continuity sake). For example, jumping back to a previous spot can cause issues with audio + // data where the new jump location doesn't match up with the previously cached audio data. + std::shared_ptr previous_frame = final_cache->GetFrame(requested_frame - 1); + if (!previous_frame) { + // Seeking to new place on timeline (destroy cache) + ClearAllCache(); + } + // Minimum number of frames to process (for performance reasons) int minimum_frames = OPEN_MP_NUM_PROCESSORS; diff --git a/src/Timeline.h b/src/Timeline.h index 227183428..1d156153b 100644 --- a/src/Timeline.h +++ b/src/Timeline.h @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -175,6 +176,7 @@ namespace openshot { std::set allocated_frame_mappers; ///< all the frame mappers we allocated and must free bool managed_cache; ///< Does this timeline instance manage the cache object std::string path; ///< Optional path of loaded UTF-8 OpenShot JSON project file + std::mutex get_frame_mutex; ///< Mutex to protect GetFrame method from different threads calling it /// Process a new layer of video or audio void add_layer(std::shared_ptr new_frame, openshot::Clip* source_clip, int64_t clip_frame_number, int64_t timeline_frame_number, bool is_top_clip, float max_volume);