Compare commits

..

3 Commits

Author SHA1 Message Date
04e391551e Remove unused trash 2025-09-18 17:51:29 +02:00
f9f442a2d0 "optimnize" playback by removing retardation 2025-09-18 17:49:09 +02:00
0fd108bc9a Don't seek EVERY GOD DAMN FUCKING FRAME 2025-09-18 17:31:10 +02:00

View File

@@ -28,55 +28,25 @@ class Cv2BufferedCap:
self.frame_width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)) self.frame_width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
self.frame_height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) self.frame_height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# Frame cache
self.frame_cache = {}
self.cache_access_order = []
self.MAX_CACHE_FRAMES = 3000
# Current position tracking # Current position tracking
self.current_frame = 0 self.current_frame = 0
def _manage_cache(self):
"""Manage cache size using LRU eviction"""
while len(self.frame_cache) > self.MAX_CACHE_FRAMES:
oldest_frame = self.cache_access_order.pop(0)
if oldest_frame in self.frame_cache:
del self.frame_cache[oldest_frame]
def _add_to_cache(self, frame_number, frame):
"""Add frame to cache"""
self.frame_cache[frame_number] = frame.copy()
if frame_number in self.cache_access_order:
self.cache_access_order.remove(frame_number)
self.cache_access_order.append(frame_number)
self._manage_cache()
def _get_from_cache(self, frame_number):
"""Get frame from cache and update LRU"""
if frame_number in self.frame_cache:
if frame_number in self.cache_access_order:
self.cache_access_order.remove(frame_number)
self.cache_access_order.append(frame_number)
return self.frame_cache[frame_number].copy()
return None
def get_frame(self, frame_number): def get_frame(self, frame_number):
"""Get frame at specific index - always accurate""" """Get frame at specific index - always accurate"""
# Clamp frame number to valid range # Clamp frame number to valid range
frame_number = max(0, min(frame_number, self.total_frames - 1)) frame_number = max(0, min(frame_number, self.total_frames - 1))
# Check cache first # Optimize for sequential reading (next frame)
cached_frame = self._get_from_cache(frame_number) if frame_number == self.current_frame + 1:
if cached_frame is not None: ret, frame = self.cap.read()
self.current_frame = frame_number else:
return cached_frame # Seek for non-sequential access
# Not in cache, seek to frame and read
self.cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number) self.cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
ret, frame = self.cap.read() ret, frame = self.cap.read()
if ret: if ret:
self._add_to_cache(frame_number, frame)
self.current_frame = frame_number self.current_frame = frame_number
return frame return frame
else: else:
@@ -461,9 +431,9 @@ class ProjectView:
class VideoEditor: class VideoEditor:
# Configuration constants # Configuration constants
BASE_FRAME_DELAY_MS = 16 # ~60 FPS TARGET_FPS = 80 # Target FPS for speed calculations
SPEED_INCREMENT = 0.2 SPEED_INCREMENT = 0.1
MIN_PLAYBACK_SPEED = 0.1 MIN_PLAYBACK_SPEED = 0.05
MAX_PLAYBACK_SPEED = 10.0 MAX_PLAYBACK_SPEED = 10.0
# Seek multiplier configuration # Seek multiplier configuration
@@ -986,7 +956,17 @@ class VideoEditor:
def calculate_frame_delay(self) -> int: def calculate_frame_delay(self) -> int:
"""Calculate frame delay in milliseconds based on playback speed""" """Calculate frame delay in milliseconds based on playback speed"""
delay_ms = int(self.BASE_FRAME_DELAY_MS / self.playback_speed) # Round to 2 decimals to handle floating point precision issues
speed = round(self.playback_speed, 2)
print(f"Playback speed: {speed}")
if speed >= 1.0:
# Speed >= 1: maximum FPS (no delay)
return 1
else:
# Speed < 1: scale FPS based on speed
# Formula: fps = TARGET_FPS * speed, so delay = 1000 / fps
target_fps = self.TARGET_FPS * speed
delay_ms = int(1000 / target_fps)
return max(1, delay_ms) return max(1, delay_ms)
def seek_video(self, frames_delta: int): def seek_video(self, frames_delta: int):
@@ -1169,9 +1149,8 @@ class VideoEditor:
if not self.is_playing: if not self.is_playing:
return True return True
# Calculate how many frames to advance based on speed # Always advance by 1 frame - speed is controlled by delay timing
frames_to_advance = max(1, int(self.playback_speed)) new_frame = self.current_frame + 1
new_frame = self.current_frame + frames_to_advance
# Handle marker looping bounds # Handle marker looping bounds
if self.looping_between_markers and self.cut_start_frame is not None and self.cut_end_frame is not None: if self.looping_between_markers and self.cut_start_frame is not None and self.cut_end_frame is not None:
@@ -2049,7 +2028,7 @@ class VideoEditor:
line_to_draw = ("prev_next", prev_result, next_result) line_to_draw = ("prev_next", prev_result, next_result)
if line_to_draw: if line_to_draw:
line_type, (frame1, pts1), (frame2, pts2) = line_to_draw line_type, (_, pts1), (_, pts2) = line_to_draw
# Draw lines between corresponding tracking points # Draw lines between corresponding tracking points
for i, (px1, py1) in enumerate(pts1): for i, (px1, py1) in enumerate(pts1):
@@ -2201,7 +2180,7 @@ class VideoEditor:
best_snap_point = None best_snap_point = None
# Check all tracking points from all frames for point snapping # Check all tracking points from all frames for point snapping
for frame_num, points in self.tracking_points.items(): for _, points in self.tracking_points.items():
for (px, py) in points: for (px, py) in points:
sxp, syp = self._map_rotated_to_screen(px, py) sxp, syp = self._map_rotated_to_screen(px, py)
distance = ((sxp - x) ** 2 + (syp - y) ** 2) ** 0.5 distance = ((sxp - x) ** 2 + (syp - y) ** 2) ** 0.5
@@ -2227,7 +2206,7 @@ class VideoEditor:
print(f"DEBUG: Checking prev->next line") print(f"DEBUG: Checking prev->next line")
if line_to_check: if line_to_check:
line_type, (frame1, pts1), (frame2, pts2) = line_to_check line_type, (_, pts1), (_, pts2) = line_to_check
# Check each corresponding pair of points # Check each corresponding pair of points
for j in range(min(len(pts1), len(pts2))): for j in range(min(len(pts1), len(pts2))):
@@ -3048,10 +3027,15 @@ class VideoEditor:
# Use calculated frame delay for proper playback speed # Use calculated frame delay for proper playback speed
delay_ms = self.calculate_frame_delay() delay_ms = self.calculate_frame_delay()
else: else:
# Use minimal delay when not playing for responsive UI # Use non-blocking wait for immediate responsiveness when not playing
delay_ms = 1 delay_ms = 0
# Auto advance frame when playing (videos only)
if self.is_playing and not self.is_image_mode:
self.advance_frame()
# Key capture with appropriate delay # Key capture with appropriate delay
print(f"Delay ms: {delay_ms}")
key = cv2.waitKey(delay_ms) & 0xFF key = cv2.waitKey(delay_ms) & 0xFF
# Route keys based on window focus # Route keys based on window focus
@@ -3305,10 +3289,6 @@ class VideoEditor:
print(f"Contracted crop from left by {self.crop_size_step}px") print(f"Contracted crop from left by {self.crop_size_step}px")
# Auto advance frame when playing (videos only)
if self.is_playing and not self.is_image_mode:
self.advance_frame()
self.save_state() self.save_state()
self.cleanup_render_thread() self.cleanup_render_thread()
if hasattr(self, 'cap') and self.cap: if hasattr(self, 'cap') and self.cap: