From ce0232846ebb47943d6fb6cc7986cdfd8af76b87 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Wed, 20 Aug 2025 12:34:56 +0200 Subject: [PATCH] feat(main.py): implement frame caching to improve playback performance --- main.py | 48 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/main.py b/main.py index 9a67fef..1df3234 100644 --- a/main.py +++ b/main.py @@ -59,6 +59,10 @@ class MediaGrader: # Timeline visibility state self.timeline_visible = True + + # Simple frame cache for frequently accessed frames + self.frame_cache = {} # Dict[frame_number: frame_data] + self.cache_size_limit = 50 # Keep it small and simple # Key repeat tracking with rate limiting self.last_seek_time = 0 @@ -558,6 +562,32 @@ class MediaGrader: self.segment_caps = [] self.segment_frames = [] self.segment_positions = [] + # Clear frame cache + self.frame_cache.clear() + + def get_cached_frame(self, frame_number: int): + """Get frame from cache or load it if not cached""" + if frame_number in self.frame_cache: + return self.frame_cache[frame_number] + + # Load frame and cache it (lazy loading) + if self.current_cap: + original_pos = int(self.current_cap.get(cv2.CAP_PROP_POS_FRAMES)) + self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number) + ret, frame = self.current_cap.read() + self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, original_pos) + + if ret: + # Cache the frame (with size limit) + if len(self.frame_cache) >= self.cache_size_limit: + # Remove oldest cached frame + oldest_key = min(self.frame_cache.keys()) + del self.frame_cache[oldest_key] + + self.frame_cache[frame_number] = frame.copy() + return frame + + return None def update_segment_frames(self): """Update frames for all segments during playback""" @@ -620,14 +650,20 @@ class MediaGrader: segment_end = min(self.total_frames - 1, segment_start + segment_duration) target_frame = max(segment_start, min(current_frame + frames_delta, segment_end)) - cap.set(cv2.CAP_PROP_POS_FRAMES, target_frame) - # Load new frame - ret, frame = cap.read() - if ret: - self.segment_frames[i] = frame - # Reset position for next read + # Try cache first, then load if needed + cached_frame = self.get_cached_frame(target_frame) + if cached_frame is not None: + self.segment_frames[i] = cached_frame cap.set(cv2.CAP_PROP_POS_FRAMES, target_frame) + else: + # Fall back to normal seeking + cap.set(cv2.CAP_PROP_POS_FRAMES, target_frame) + ret, frame = cap.read() + if ret: + self.segment_frames[i] = frame + # Reset position for next read + cap.set(cv2.CAP_PROP_POS_FRAMES, target_frame) def display_current_frame(self): """Display the current cached frame with overlays"""