feat(main.py): optimize video capture initialization and add backend fallback for improved performance and reliability
This commit is contained in:
116
main.py
116
main.py
@@ -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
|
||||||
|
Reference in New Issue
Block a user