diff --git a/main.py b/main.py index 2f0d901..426603e 100644 --- a/main.py +++ b/main.py @@ -518,7 +518,7 @@ class MediaGrader: return start_time = time.time() - print(f"Setting up {self.segment_count} segments with frame caching...") + print(f"Setting up {self.segment_count} segments with video preloading...") try: # Clean up existing segment captures @@ -528,11 +528,12 @@ class MediaGrader: current_file = self.media_files[self.current_index] print(f"Working with file: {current_file}") - # Initialize arrays - no individual captures, just cached frames + # Initialize arrays print("Initializing arrays...") - self.segment_caps = [None] * self.segment_count # Keep for compatibility, but won't use + self.segment_caps = [None] * self.segment_count # Keep for compatibility self.segment_frames = [None] * self.segment_count self.segment_positions = [] + self.segment_current_frames = [0] * self.segment_count # Track current frame for each segment # Calculate target positions print("Calculating segment positions...") @@ -552,27 +553,44 @@ class MediaGrader: start_frame = int(position_ratio * (safe_frame_count - 1)) start_frame = max(0, min(start_frame, safe_frame_count - 1)) self.segment_positions.append(start_frame) + self.segment_current_frames[i] = start_frame # Start each segment at its position print(f"Segment positions: {self.segment_positions}") - # Use the existing main capture to quickly read all segment frames - print("Pre-loading segment frames...") - cache_start = time.time() + # Preload the entire video into memory + print("Preloading entire video into memory...") + preload_start = time.time() + + self.video_frame_cache = [] # Array to hold all frames if self.current_cap and self.current_cap.isOpened(): - for i, frame_pos in enumerate(self.segment_positions): - self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, frame_pos) + # Reset to beginning + self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, 0) + + frame_count = 0 + while frame_count < safe_frame_count: ret, frame = self.current_cap.read() if ret and frame is not None: - self.segment_frames[i] = frame.copy() - print(f"Cached segment {i} at frame {frame_pos}") + self.video_frame_cache.append(frame.copy()) + frame_count += 1 + + # Show progress for large videos + if frame_count % 50 == 0: + print(f"Preloaded {frame_count}/{safe_frame_count} frames...") else: - print(f"Failed to cache segment {i} at frame {frame_pos}") + print(f"Reached end of video at frame {frame_count}") + break # Reset main capture to original position self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, self.current_frame) - cache_time = (time.time() - cache_start) * 1000 - print(f"Frame caching: {cache_time:.1f}ms") + preload_time = (time.time() - preload_start) * 1000 + print(f"Video preloading: {preload_time:.1f}ms ({len(self.video_frame_cache)} frames)") + + # Initialize segment frames from the preloaded cache + print("Initializing segment frames...") + for i in range(self.segment_count): + if self.segment_current_frames[i] < len(self.video_frame_cache): + self.segment_frames[i] = self.video_frame_cache[self.segment_current_frames[i]].copy() except Exception as e: print(f"Error in setup: {e}") @@ -585,7 +603,7 @@ class MediaGrader: # Report success successful_segments = sum(1 for frame in self.segment_frames if frame is not None) - print(f"Successfully cached {successful_segments}/{self.segment_count} segments") + print(f"Successfully preloaded video with {successful_segments}/{self.segment_count} active segments") def _create_segment_parallel(self, segment_index: int, file_path: str, start_frame: int): """Create a single segment capture and load its initial frame (runs in thread)""" @@ -645,13 +663,17 @@ class MediaGrader: return None, None def cleanup_segment_captures(self): - """Clean up all segment video captures""" + """Clean up all segment video captures and preloaded cache""" for cap in self.segment_caps: if cap: cap.release() self.segment_caps = [] self.segment_frames = [] self.segment_positions = [] + if hasattr(self, 'video_frame_cache'): + self.video_frame_cache = [] # Clear preloaded video cache + if hasattr(self, 'segment_current_frames'): + self.segment_current_frames = [] # Clear frame tracking # Clear frame cache self.frame_cache.clear() @@ -769,15 +791,22 @@ class MediaGrader: return segment_index, None def update_segment_frames(self): - """Update frames for segments - now using static cached frames for simplicity""" - if not self.multi_segment_mode or not self.segment_frames: + """Update frames for segments using the preloaded video array - smooth playback!""" + if not self.multi_segment_mode or not self.segment_frames or not hasattr(self, 'video_frame_cache'): return - # Since we're using cached static frames, no updates needed during playback - # This dramatically improves performance by eliminating I/O during playback - # The segments show different parts of the video but don't animate - # For animation, we'd need to implement frame interpolation or periodic cache updates - pass + # Each segment advances through the video at its own pace + for i in range(len(self.segment_frames)): + if self.segment_frames[i] is not None and self.video_frame_cache: + # Advance this segment's current frame + self.segment_current_frames[i] += 1 + + # Loop back to start if we reach the end + if self.segment_current_frames[i] >= len(self.video_frame_cache): + self.segment_current_frames[i] = 0 + + # Update the segment frame from the cache + self.segment_frames[i] = self.video_frame_cache[self.segment_current_frames[i]].copy() def reposition_segments_around_frame(self, center_frame: int): """Reposition all segments around a center frame while maintaining spacing"""