feat(main.py): track display updates and optimize video rendering process

This commit is contained in:
2025-09-08 16:27:55 +02:00
parent 4d60526952
commit 9763597af8

View File

@@ -142,6 +142,10 @@ class VideoEditor:
self.render_thread = None
self.render_cancelled = False
self.render_progress_queue = queue.Queue()
# Display optimization - track when redraw is needed
self.display_needs_update = True
self.last_display_state = None
def _get_state_file_path(self) -> Path:
"""Get the state file path for the current media file"""
@@ -507,6 +511,7 @@ class VideoEditor:
)
self.current_frame = target_frame
self.load_current_frame()
self.display_needs_update = True
def seek_video_with_modifier(
@@ -561,9 +566,6 @@ class VideoEditor:
)
self.last_display_update = current_time
def should_update_display(self) -> bool:
"""Check if display should be updated"""
return True
def seek_to_frame(self, frame_number: int):
"""Seek to specific frame"""
@@ -640,7 +642,8 @@ class VideoEditor:
if frame is None:
return None
processed_frame = frame.copy()
# Work in-place when possible to avoid unnecessary copying
processed_frame = frame
# Apply brightness/contrast first (to original frame for best quality)
processed_frame = self.apply_brightness_contrast(processed_frame)
@@ -715,10 +718,12 @@ class VideoEditor:
def adjust_brightness(self, delta: int):
"""Adjust brightness by delta (-100 to 100)"""
self.brightness = max(-100, min(100, self.brightness + delta))
self.display_needs_update = True
def adjust_contrast(self, delta: float):
"""Adjust contrast by delta (0.1 to 3.0)"""
self.contrast = max(0.1, min(3.0, self.contrast + delta))
self.display_needs_update = True
def show_progress_bar(self, text: str = "Processing..."):
"""Show progress bar with given text"""
@@ -727,6 +732,7 @@ class VideoEditor:
self.progress_bar_complete = False
self.progress_bar_complete_time = None
self.progress_bar_text = text
self.display_needs_update = True
def update_progress_bar(self, progress: float, text: str = None, fps: float = None):
"""Update progress bar progress (0.0 to 1.0) and optionally text and FPS"""
@@ -753,6 +759,7 @@ class VideoEditor:
"""Show a feedback message on screen for a few seconds"""
self.feedback_message = message
self.feedback_message_time = time.time()
self.display_needs_update = True
def draw_feedback_message(self, frame):
"""Draw feedback message on frame if visible"""
@@ -1055,9 +1062,28 @@ class VideoEditor:
if self.current_display_frame is None:
return
# Check if display needs update (optimization)
current_state = (
self.current_frame,
self.crop_rect,
self.zoom_factor,
self.rotation_angle,
self.brightness,
self.contrast,
self.display_offset,
self.progress_bar_visible,
self.feedback_message
)
if not self.display_needs_update and current_state == self.last_display_state:
return # Skip redraw if nothing changed
self.last_display_state = current_state
self.display_needs_update = False
# Apply crop, zoom, and rotation transformations for preview
display_frame = self.apply_crop_zoom_and_rotation(
self.current_display_frame.copy()
self.current_display_frame
)
if display_frame is None:
@@ -1577,11 +1603,37 @@ class VideoEditor:
# Send progress update
self.render_progress_queue.put(("progress", "Setting up video writer...", 0.1, 0.0))
# Use mp4v codec (most compatible with MP4)
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
out = cv2.VideoWriter(
output_path, fourcc, self.fps, (output_width, output_height)
)
# Try hardware-accelerated codecs first, fallback to mp4v
codecs_to_try = [
("h264_nvenc", "H264"), # NVIDIA hardware acceleration
("h264_qsv", "H264"), # Intel Quick Sync
("h264", "H264"), # Software H.264
("mp4v", "MP4V") # Fallback
]
out = None
for codec_name, fourcc_code in codecs_to_try:
try:
fourcc = cv2.VideoWriter_fourcc(*fourcc_code)
out = cv2.VideoWriter(
output_path, fourcc, self.fps, (output_width, output_height)
)
if out.isOpened():
print(f"Using {codec_name} codec for rendering")
break
else:
out.release()
out = None
except Exception:
continue
if out is None:
# Final fallback to mp4v
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
out = cv2.VideoWriter(
output_path, fourcc, self.fps, (output_width, output_height)
)
print("Using mp4v codec (fallback)")
if not out.isOpened():
self.render_progress_queue.put(("error", "Could not open video writer!", 1.0, 0.0))
@@ -1805,34 +1857,29 @@ class VideoEditor:
if self.rotation_angle != 0:
frame = self.apply_rotation(frame)
# Apply zoom and resize in one step for efficiency
# Apply zoom and resize directly to final output dimensions
if self.zoom_factor != 1.0:
height, width = frame.shape[:2]
intermediate_width = int(width * self.zoom_factor)
intermediate_height = int(height * self.zoom_factor)
# If zoom results in different dimensions than output, resize directly to output
if (
intermediate_width != output_width
or intermediate_height != output_height
):
# Calculate what the zoomed dimensions would be
zoomed_width = int(width * self.zoom_factor)
zoomed_height = int(height * self.zoom_factor)
# If zoomed dimensions match output, use them; otherwise resize directly to output
if zoomed_width == output_width and zoomed_height == output_height:
frame = cv2.resize(
frame,
(output_width, output_height),
interpolation=cv2.INTER_LINEAR,
frame, (zoomed_width, zoomed_height), interpolation=cv2.INTER_LINEAR
)
else:
# Resize directly to final output dimensions
frame = cv2.resize(
frame,
(intermediate_width, intermediate_height),
interpolation=cv2.INTER_LINEAR,
frame, (output_width, output_height), interpolation=cv2.INTER_LINEAR
)
else:
# No zoom, just resize to output dimensions if needed
if frame.shape[1] != output_width or frame.shape[0] != output_height:
frame = cv2.resize(
frame, (output_width, output_height), interpolation=cv2.INTER_LINEAR
)
# Final size check and resize if needed
if frame.shape[1] != output_width or frame.shape[0] != output_height:
frame = cv2.resize(
frame, (output_width, output_height), interpolation=cv2.INTER_LINEAR
)
return frame
@@ -1913,9 +1960,8 @@ class VideoEditor:
# Update render progress from background thread
self.update_render_progress()
# Only update display if needed and throttled
if self.should_update_display():
self.display_current_frame()
# Update display
self.display_current_frame()
delay = self.calculate_frame_delay() if self.is_playing else 1 # Very short delay for responsive key detection
key = cv2.waitKey(delay) & 0xFF