Compare commits

..

2 Commits

55
main.py
View File

@@ -7,6 +7,8 @@ import argparse
import shutil import shutil
import time import time
import threading import threading
import subprocess
import json
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from pathlib import Path from pathlib import Path
from typing import List from typing import List
@@ -58,6 +60,7 @@ class MediaGrader:
self.segment_caps = [] self.segment_caps = []
self.segment_frames = [] self.segment_frames = []
self.segment_positions = [] self.segment_positions = []
self.segment_end_positions = [] # Track where each segment should loop back to
self.timeline_visible = True self.timeline_visible = True
@@ -486,34 +489,27 @@ class MediaGrader:
if not self.is_video(self.media_files[self.current_index]): if not self.is_video(self.media_files[self.current_index]):
return return
# Safety check for huge videos
safe_frame_count = max(1, int(self.total_frames * 0.6))
# Calculate actual memory usage based on frame dimensions # Calculate actual memory usage based on frame dimensions
frame_width = int(self.current_cap.get(cv2.CAP_PROP_FRAME_WIDTH)) frame_width = int(self.current_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(self.current_cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) frame_height = int(self.current_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
bytes_per_frame = frame_width * frame_height * 3 # RGB (3 bytes per pixel) total_mb = frame_width * frame_height * 3 / (1024 * 1024)
total_bytes = safe_frame_count * bytes_per_frame
total_mb = total_bytes / (1024 * 1024)
total_gb = total_mb / 1024
# Memory-based limits (not frame count) # Memory-based limits (not frame count)
if total_gb > 8: # 8GB limit if total_mb > 8000: # 8GB limit
print(f"Video too large for preloading!") print(f"Video too large for preloading!")
print(f" Resolution: {frame_width}x{frame_height}") print(f" Resolution: {frame_width}x{frame_height}")
print(f" Frames: {safe_frame_count} frames would use {total_gb:.1f}GB RAM") print(f" Frames: {self.total_frames} frames would use {total_mb:.1f}GB RAM")
print(f"Multi-segment mode not available for videos requiring >8GB RAM") print(f"Multi-segment mode not available for videos requiring >8GB RAM")
return return
elif total_mb > 500: # 500MB warning elif total_mb > 500: # 500MB warning
print(f"Large video detected:") print(f"Large video detected:")
print(f" Resolution: {frame_width}x{frame_height}") print(f" Resolution: {frame_width}x{frame_height}")
print(f" Memory: {safe_frame_count} frames will use {total_mb:.0f}MB RAM") print(f" Memory: {self.total_frames} frames will use {total_mb:.0f}GB RAM")
print("Press any key to continue or 'q' to cancel...") print("Press any key to continue or 'q' to cancel...")
# Note: In a real implementation, you'd want proper input handling here # Note: In a real implementation, you'd want proper input handling here
start_time = time.time() start_time = time.time()
print(f"Setting up {self.segment_count} segments with video preloading...") print(f"Setting up {self.segment_count} segments with video preloading...")
print(f"Will preload {safe_frame_count} frames ({frame_width}x{frame_height}) = {total_mb:.0f}MB RAM")
try: try:
print("Cleaning up existing captures...") print("Cleaning up existing captures...")
@@ -527,6 +523,7 @@ class MediaGrader:
self.segment_caps = [None] * self.segment_count # Keep for compatibility self.segment_caps = [None] * self.segment_count # Keep for compatibility
self.segment_frames = [None] * self.segment_count self.segment_frames = [None] * self.segment_count
self.segment_positions = [] self.segment_positions = []
self.segment_end_positions = []
self.segment_current_frames = [0] * self.segment_count # Track current frame for each segment self.segment_current_frames = [0] * self.segment_count # Track current frame for each segment
# Calculate target positions # Calculate target positions
@@ -538,13 +535,23 @@ class MediaGrader:
for i in range(self.segment_count): for i in range(self.segment_count):
if self.segment_count <= 1: if self.segment_count <= 1:
position_ratio = 0 position_ratio = 0
end_ratio = 1.0
else: else:
position_ratio = i / (self.segment_count - 1) position_ratio = i / (self.segment_count - 1)
start_frame = int(position_ratio * (safe_frame_count - 1)) end_ratio = (i + 1) / (self.segment_count - 1) if i < self.segment_count - 1 else 1.0
start_frame = max(0, min(start_frame, safe_frame_count - 1))
start_frame = int(position_ratio * (self.total_frames - 1))
end_frame = int(end_ratio * (self.total_frames - 1))
start_frame = max(0, min(start_frame, self.total_frames - 1))
end_frame = max(start_frame + 1, min(end_frame, self.total_frames - 1)) # Ensure at least 1 frame per segment
self.segment_positions.append(start_frame) self.segment_positions.append(start_frame)
self.segment_end_positions.append(end_frame)
self.segment_current_frames[i] = start_frame # Start each segment at its position self.segment_current_frames[i] = start_frame # Start each segment at its position
print(f"Segment positions: {self.segment_positions}") print(f"Segment positions: {self.segment_positions}")
print(f"Segment end positions: {self.segment_end_positions}")
# Preload the entire video into memory - simple and fast # Preload the entire video into memory - simple and fast
print("Preloading entire video into memory...") print("Preloading entire video into memory...")
@@ -557,7 +564,7 @@ class MediaGrader:
frames = [] frames = []
frame_count = 0 frame_count = 0
while frame_count < safe_frame_count: while frame_count < self.total_frames:
ret, frame = self.current_cap.read() ret, frame = self.current_cap.read()
if ret and frame is not None: if ret and frame is not None:
frames.append(frame) frames.append(frame)
@@ -600,25 +607,35 @@ class MediaGrader:
self.segment_caps = [] self.segment_caps = []
self.segment_frames = [] self.segment_frames = []
self.segment_positions = [] self.segment_positions = []
self.segment_end_positions = []
if hasattr(self, 'video_frame_cache'): if hasattr(self, 'video_frame_cache'):
self.video_frame_cache = [] self.video_frame_cache = []
if hasattr(self, 'segment_current_frames'): if hasattr(self, 'segment_current_frames'):
self.segment_current_frames = [] self.segment_current_frames = []
def update_segment_frames(self): def update_segment_frames(self):
"""Update frames for segments using the preloaded video array - smooth playback!""" """Update frames for segments - each segment loops within its own range"""
if not self.multi_segment_mode or not self.segment_frames or not hasattr(self, 'video_frame_cache'): if not self.multi_segment_mode or not self.segment_frames or not hasattr(self, 'video_frame_cache'):
return return
for i in range(len(self.segment_frames)): for i in range(len(self.segment_frames)):
if self.segment_frames[i] is not None and self.video_frame_cache: if self.segment_frames[i] is not None and self.video_frame_cache:
# Advance to next frame in this segment
self.segment_current_frames[i] += 1 self.segment_current_frames[i] += 1
if self.segment_current_frames[i] >= len(self.video_frame_cache): # Get the segment boundaries
self.segment_current_frames[i] = 0 start_frame = self.segment_positions[i]
end_frame = self.segment_end_positions[i]
# Direct reference - no copy needed for display # Loop within the segment bounds
self.segment_frames[i] = self.video_frame_cache[self.segment_current_frames[i]] if self.segment_current_frames[i] > end_frame:
# Loop back to start of segment
self.segment_current_frames[i] = start_frame
# Ensure we don't go beyond the video cache
if self.segment_current_frames[i] < len(self.video_frame_cache):
# Direct reference - no copy needed for display
self.segment_frames[i] = self.video_frame_cache[self.segment_current_frames[i]]
def display_current_frame(self): def display_current_frame(self):
"""Display the current cached frame with overlays""" """Display the current cached frame with overlays"""