feat(main.py): optimize video capture initialization and add backend fallback for improved performance and reliability

This commit is contained in:
2025-09-04 21:22:40 +02:00
parent 0a73926427
commit 887f735a27

116
main.py
View File

@@ -202,13 +202,44 @@ class MediaGrader:
self.current_cap.release() self.current_cap.release()
if self.is_video(file_path): if self.is_video(file_path):
# Suppress OpenCV error messages for unsupported codecs # Try different backends for better performance
self.current_cap = cv2.VideoCapture(str(file_path)) # Order of preference: DirectShow (Windows), FFmpeg, any available
if not self.current_cap.isOpened(): backends_to_try = []
if hasattr(cv2, 'CAP_DSHOW'): # Windows DirectShow
backends_to_try.append(cv2.CAP_DSHOW)
if hasattr(cv2, 'CAP_FFMPEG'): # FFmpeg
backends_to_try.append(cv2.CAP_FFMPEG)
backends_to_try.append(cv2.CAP_ANY) # Fallback
self.current_cap = None
for backend in backends_to_try:
try:
self.current_cap = cv2.VideoCapture(str(file_path), backend)
if self.current_cap.isOpened():
# Optimize buffer settings for better performance
self.current_cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) # Minimize buffer to reduce latency
# Try to set hardware acceleration if available
if hasattr(cv2, 'CAP_PROP_HW_ACCELERATION'):
self.current_cap.set(cv2.CAP_PROP_HW_ACCELERATION, cv2.VIDEO_ACCELERATION_ANY)
break
self.current_cap.release()
except:
continue
if not self.current_cap or not self.current_cap.isOpened():
print(f"Warning: Could not open video file {file_path.name} (unsupported codec)") print(f"Warning: Could not open video file {file_path.name} (unsupported codec)")
return False return False
self.total_frames = int(self.current_cap.get(cv2.CAP_PROP_FRAME_COUNT)) self.total_frames = int(self.current_cap.get(cv2.CAP_PROP_FRAME_COUNT))
self.current_frame = 0 self.current_frame = 0
# Get codec information for debugging
fourcc = int(self.current_cap.get(cv2.CAP_PROP_FOURCC))
codec = "".join([chr((fourcc >> 8 * i) & 0xFF) for i in range(4)])
backend = self.current_cap.getBackendName()
print(f"Loaded: {file_path.name} | Codec: {codec} | Backend: {backend} | Frames: {self.total_frames}")
else: else:
self.current_cap = None self.current_cap = None
self.total_frames = 1 self.total_frames = 1
@@ -499,7 +530,29 @@ class MediaGrader:
load_start = time.time() load_start = time.time()
shared_cap_start = time.time() shared_cap_start = time.time()
shared_cap = cv2.VideoCapture(str(current_file))
# Use same backend optimization as main capture
backends_to_try = []
if hasattr(cv2, 'CAP_DSHOW'): # Windows DirectShow
backends_to_try.append(cv2.CAP_DSHOW)
if hasattr(cv2, 'CAP_FFMPEG'): # FFmpeg
backends_to_try.append(cv2.CAP_FFMPEG)
backends_to_try.append(cv2.CAP_ANY) # Fallback
shared_cap = None
for backend in backends_to_try:
try:
shared_cap = cv2.VideoCapture(str(current_file), backend)
if shared_cap.isOpened():
shared_cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
break
shared_cap.release()
except:
continue
if not shared_cap:
shared_cap = cv2.VideoCapture(str(current_file)) # Fallback
shared_cap_create_time = (time.time() - shared_cap_start) * 1000 shared_cap_create_time = (time.time() - shared_cap_start) * 1000
print(f"Capture creation: {shared_cap_create_time:.1f}ms") print(f"Capture creation: {shared_cap_create_time:.1f}ms")
@@ -585,7 +638,29 @@ class MediaGrader:
if self.current_cap: if self.current_cap:
# Create a temporary capture to avoid interfering with main playback # Create a temporary capture to avoid interfering with main playback
current_file = self.media_files[self.current_index] current_file = self.media_files[self.current_index]
temp_cap = cv2.VideoCapture(str(current_file))
# Use optimized backend for temporary capture too
temp_cap = None
backends_to_try = []
if hasattr(cv2, 'CAP_DSHOW'):
backends_to_try.append(cv2.CAP_DSHOW)
if hasattr(cv2, 'CAP_FFMPEG'):
backends_to_try.append(cv2.CAP_FFMPEG)
backends_to_try.append(cv2.CAP_ANY)
for backend in backends_to_try:
try:
temp_cap = cv2.VideoCapture(str(current_file), backend)
if temp_cap.isOpened():
temp_cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
break
temp_cap.release()
except:
continue
if not temp_cap:
temp_cap = cv2.VideoCapture(str(current_file)) # Fallback
if temp_cap.isOpened(): if temp_cap.isOpened():
temp_cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number) temp_cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
ret, frame = temp_cap.read() ret, frame = temp_cap.read()
@@ -609,9 +684,31 @@ class MediaGrader:
"""Get or create a capture for a specific segment (lazy loading)""" """Get or create a capture for a specific segment (lazy loading)"""
if segment_index >= len(self.segment_caps) or self.segment_caps[segment_index] is None: if segment_index >= len(self.segment_caps) or self.segment_caps[segment_index] is None:
if segment_index < len(self.segment_caps): if segment_index < len(self.segment_caps):
# Create capture on demand # Create capture on demand with optimized backend
current_file = self.media_files[self.current_index] current_file = self.media_files[self.current_index]
cap = cv2.VideoCapture(str(current_file))
# Use optimized backend for segment capture
cap = None
backends_to_try = []
if hasattr(cv2, 'CAP_DSHOW'):
backends_to_try.append(cv2.CAP_DSHOW)
if hasattr(cv2, 'CAP_FFMPEG'):
backends_to_try.append(cv2.CAP_FFMPEG)
backends_to_try.append(cv2.CAP_ANY)
for backend in backends_to_try:
try:
cap = cv2.VideoCapture(str(current_file), backend)
if cap.isOpened():
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
break
cap.release()
except:
continue
if not cap:
cap = cv2.VideoCapture(str(current_file)) # Fallback
if cap.isOpened(): if cap.isOpened():
cap.set(cv2.CAP_PROP_POS_FRAMES, self.segment_positions[segment_index]) cap.set(cv2.CAP_PROP_POS_FRAMES, self.segment_positions[segment_index])
self.segment_caps[segment_index] = cap self.segment_caps[segment_index] = cap
@@ -1060,6 +1157,11 @@ class MediaGrader:
for _ in range(frames_to_skip + 1): for _ in range(frames_to_skip + 1):
ret, frame = self.current_cap.read() ret, frame = self.current_cap.read()
if not ret: if not ret:
# Hit actual end of video - check if frame count was wrong
actual_frame = int(self.current_cap.get(cv2.CAP_PROP_POS_FRAMES))
if actual_frame < self.total_frames - 5: # Allow some tolerance
print(f"Frame count mismatch! Reported: {self.total_frames}, Actual: {actual_frame}")
self.total_frames = actual_frame
return False return False
self.current_display_frame = frame self.current_display_frame = frame