feat(main.py): implement smart video seeking with keyframe optimization
This commit is contained in:
@@ -13,8 +13,6 @@ import json
|
||||
class VideoEditor:
|
||||
# Configuration constants
|
||||
BASE_FRAME_DELAY_MS = 16 # ~60 FPS
|
||||
KEY_REPEAT_RATE_SEC = 0.3
|
||||
FAST_SEEK_ACTIVATION_TIME = 1.5
|
||||
SPEED_INCREMENT = 0.2
|
||||
MIN_PLAYBACK_SPEED = 0.1
|
||||
MAX_PLAYBACK_SPEED = 10.0
|
||||
@@ -502,18 +500,69 @@ class VideoEditor:
|
||||
self.current_frame = target_frame
|
||||
self.load_current_frame()
|
||||
|
||||
def seek_video_smart(self, frames_delta: int):
|
||||
"""Seek using keyframe jumping for large jumps"""
|
||||
target_frame = max(
|
||||
0, min(self.current_frame + frames_delta, self.total_frames - 1)
|
||||
)
|
||||
|
||||
# For small jumps, use regular seeking
|
||||
if abs(frames_delta) <= 10:
|
||||
self.current_frame = target_frame
|
||||
self.load_current_frame()
|
||||
return
|
||||
|
||||
# For larger jumps, use keyframe seeking
|
||||
# Jump to nearest keyframe first, then seek forward
|
||||
keyframe_interval = self._detect_keyframe_interval()
|
||||
nearest_keyframe = (target_frame // keyframe_interval) * keyframe_interval
|
||||
|
||||
# Seek to keyframe first
|
||||
self.cap.set(cv2.CAP_PROP_POS_FRAMES, nearest_keyframe)
|
||||
self.current_frame = nearest_keyframe
|
||||
|
||||
# Then seek forward to target frame
|
||||
frames_to_seek = target_frame - nearest_keyframe
|
||||
for _ in range(frames_to_seek):
|
||||
ret, frame = self.cap.read()
|
||||
if not ret:
|
||||
break
|
||||
self.current_frame += 1
|
||||
|
||||
self.current_display_frame = frame if 'frame' in locals() else None
|
||||
|
||||
def _detect_keyframe_interval(self):
|
||||
"""Detect the keyframe interval for the current video"""
|
||||
# Try to get keyframe interval from video properties
|
||||
try:
|
||||
# Some backends support keyframe interval detection
|
||||
keyframe_interval = self.cap.get(cv2.CAP_PROP_FRAME_COUNT) / self.cap.get(cv2.CAP_PROP_FPS)
|
||||
if keyframe_interval > 0:
|
||||
return int(keyframe_interval)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Fallback: estimate based on video characteristics
|
||||
if self.fps >= 60:
|
||||
return 60 # High FPS videos often have keyframes every 60 frames
|
||||
elif self.fps >= 30:
|
||||
return 30 # Standard videos have keyframes every 30 frames
|
||||
else:
|
||||
return 15 # Lower FPS videos have keyframes every 15 frames
|
||||
|
||||
def seek_video_with_modifier(
|
||||
self, direction: int, shift_pressed: bool, ctrl_pressed: bool
|
||||
):
|
||||
"""Seek video with different frame counts based on modifiers"""
|
||||
if ctrl_pressed:
|
||||
frames = direction * 60 # Ctrl: 60 frames
|
||||
self.seek_video_smart(frames)
|
||||
elif shift_pressed:
|
||||
frames = direction * 10 # Shift: 10 frames
|
||||
self.seek_video_smart(frames)
|
||||
else:
|
||||
frames = direction * 1 # Default: 1 frame
|
||||
|
||||
self.seek_video(frames)
|
||||
self.seek_video(frames)
|
||||
|
||||
def start_auto_repeat_seek(self, key: int, direction: int, shift_pressed: bool, ctrl_pressed: bool):
|
||||
"""Start auto-repeat seeking for a held key"""
|
||||
@@ -544,12 +593,9 @@ class VideoEditor:
|
||||
|
||||
# Do initial seek immediately
|
||||
self.seek_video_with_modifier(direction, shift_pressed, ctrl_pressed)
|
||||
print(f"DEBUG: Started auto-repeat for key {key}")
|
||||
|
||||
def stop_auto_repeat_seek(self):
|
||||
"""Stop auto-repeat seeking"""
|
||||
if self.auto_repeat_active:
|
||||
print(f"DEBUG: Stopped auto-repeat")
|
||||
self.auto_repeat_active = False
|
||||
self.auto_repeat_key = None
|
||||
self.auto_repeat_direction = 0
|
||||
@@ -1826,26 +1872,19 @@ class VideoEditor:
|
||||
delay = self.calculate_frame_delay() if self.is_playing else 1 # Very short delay for responsive key detection
|
||||
key = cv2.waitKey(delay) & 0xFF
|
||||
|
||||
# Debug key detection
|
||||
if key != 255:
|
||||
print(f"DEBUG: Key detected: {key} (auto_repeat_active: {self.auto_repeat_active}, auto_repeat_key: {self.auto_repeat_key})")
|
||||
|
||||
# Handle auto-repeat - stop if a different key is pressed or no key for too long
|
||||
if key == 255 and self.auto_repeat_active: # 255 means no key pressed
|
||||
# Check if enough time has passed since last key activity (key was released)
|
||||
if time.time() - self.last_key_activity > 0.1: # 100ms timeout
|
||||
print(f"DEBUG: Key released - stopping auto-repeat")
|
||||
self.stop_auto_repeat_seek()
|
||||
elif key != 255 and self.auto_repeat_active:
|
||||
# A key is pressed, update the last key activity time
|
||||
self.last_key_activity = time.time()
|
||||
# If it's a different key, stop auto-repeat
|
||||
if key != self.auto_repeat_key:
|
||||
print(f"DEBUG: Different key pressed ({key} vs {self.auto_repeat_key}) - stopping auto-repeat")
|
||||
self.stop_auto_repeat_seek()
|
||||
# If it's the same key, just update the last activity time (don't restart auto-repeat)
|
||||
else:
|
||||
print(f"DEBUG: Same key pressed ({key}) - updating last activity time")
|
||||
|
||||
# Get modifier key states
|
||||
window_title = "Image Editor" if self.is_image_mode else "Video Editor"
|
||||
|
Reference in New Issue
Block a user