Fix rendering again

This commit is contained in:
2025-09-08 20:24:59 +02:00
parent 01ea25168a
commit 6559310adc

View File

@@ -1626,6 +1626,11 @@ class VideoEditor:
# Send progress update # Send progress update
self.render_progress_queue.put(("progress", "Setting up FFmpeg encoder...", 0.1, 0.0)) self.render_progress_queue.put(("progress", "Setting up FFmpeg encoder...", 0.1, 0.0))
# Debug output dimensions
print(f"Output dimensions: {output_width}x{output_height}")
print(f"Zoom factor: {self.zoom_factor}")
print(f"Crop dimensions: {crop_width}x{crop_height}")
# Skip all the OpenCV codec bullshit and go straight to FFmpeg # Skip all the OpenCV codec bullshit and go straight to FFmpeg
print("Using FFmpeg for encoding with OpenCV transformations...") print("Using FFmpeg for encoding with OpenCV transformations...")
return self._render_with_ffmpeg_pipe(output_path, start_frame, end_frame, output_width, output_height) return self._render_with_ffmpeg_pipe(output_path, start_frame, end_frame, output_width, output_height)
@@ -1663,6 +1668,8 @@ class VideoEditor:
self._handle_overwrite_completion() self._handle_overwrite_completion()
elif update_type == "error": elif update_type == "error":
self.update_progress_bar(progress, text, fps) self.update_progress_bar(progress, text, fps)
# Also show error as feedback message for better visibility
self.show_feedback_message(f"ERROR: {text}")
elif update_type == "cancelled": elif update_type == "cancelled":
self.hide_progress_bar() self.hide_progress_bar()
self.show_feedback_message("Render cancelled") self.show_feedback_message("Render cancelled")
@@ -1826,35 +1833,23 @@ class VideoEditor:
return None return None
def _render_with_ffmpeg_pipe(self, output_path: str, start_frame: int, end_frame: int, output_width: int, output_height: int): def _render_with_ffmpeg_pipe(self, output_path: str, start_frame: int, end_frame: int, output_width: int, output_height: int):
"""Hybrid approach: OpenCV transformations + FFmpeg encoding via pipe""" """Hybrid approach: OpenCV transformations + FFmpeg encoding via temporary file"""
temp_raw_file = None
try: try:
# Verify FFmpeg is available
try:
subprocess.run(['ffmpeg', '-version'], capture_output=True, check=True)
except (subprocess.CalledProcessError, FileNotFoundError):
error_msg = "FFmpeg not found - please install FFmpeg and ensure it's in your PATH"
print(error_msg)
self.render_progress_queue.put(("error", error_msg, 1.0, 0.0))
return False
self.render_progress_queue.put(("progress", "Starting FFmpeg encoder...", 0.0, 0.0)) self.render_progress_queue.put(("progress", "Starting FFmpeg encoder...", 0.0, 0.0))
# Start FFmpeg process to receive frames via pipe # Create temporary raw video file instead of using pipes
# Use Windows-friendly approach with explicit binary mode temp_raw_file = output_path.replace('.mp4', '_temp.raw')
ffmpeg_cmd = [ print(f"Using temporary file approach: {temp_raw_file}")
'ffmpeg', '-y', '-v', 'quiet',
'-f', 'rawvideo',
'-vcodec', 'rawvideo',
'-s', f'{output_width}x{output_height}',
'-pix_fmt', 'bgr24',
'-r', str(self.fps),
'-i', '-', # Read from stdin
'-c:v', 'libx264',
'-preset', 'fast',
'-crf', '18',
'-pix_fmt', 'yuv420p',
output_path
]
# Start FFmpeg process with Windows-friendly settings
self.ffmpeg_process = subprocess.Popen(
ffmpeg_cmd,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE,
bufsize=0, # Unbuffered for better pipe performance
universal_newlines=False # Binary mode for Windows
)
# OpenCV for frame reading and transformations # OpenCV for frame reading and transformations
render_cap = cv2.VideoCapture(str(self.video_path)) render_cap = cv2.VideoCapture(str(self.video_path))
@@ -1867,70 +1862,100 @@ class VideoEditor:
self.render_progress_queue.put(("progress", f"Processing {total_frames} frames...", 0.1, 0.0)) self.render_progress_queue.put(("progress", f"Processing {total_frames} frames...", 0.1, 0.0))
for i in range(total_frames): # Write frames to temporary raw file
if self.render_cancelled: with open(temp_raw_file, 'wb') as raw_file:
self.ffmpeg_process.stdin.close() for i in range(total_frames):
self.ffmpeg_process.terminate() if self.render_cancelled:
self.ffmpeg_process.wait() render_cap.release()
render_cap.release() self.render_progress_queue.put(("cancelled", "Render cancelled", 0.0, 0.0))
self.ffmpeg_process = None return False
self.render_progress_queue.put(("cancelled", "Render cancelled", 0.0, 0.0))
return False
ret, frame = render_cap.read() ret, frame = render_cap.read()
if not ret: if not ret:
break
# Apply transformations with OpenCV
processed_frame = self._process_frame_for_render(frame, output_width, output_height)
if processed_frame is not None:
# Write frame to FFmpeg via pipe
try:
self.ffmpeg_process.stdin.write(processed_frame.tobytes())
frames_written += 1
except BrokenPipeError:
# FFmpeg process died
break break
# Update progress with FPS calculation # Apply transformations with OpenCV
current_time = time.time() processed_frame = self._process_frame_for_render(frame, output_width, output_height)
progress = 0.1 + (0.8 * (i + 1) / total_frames) if processed_frame is not None:
# Debug frame dimensions
# Calculate FPS and update progress (throttled) if i == 0: # Only print for first frame
if current_time - last_progress_update > 0.5: print(f"Processed frame dimensions: {processed_frame.shape[1]}x{processed_frame.shape[0]}")
elapsed = current_time - start_time print(f"Expected dimensions: {output_width}x{output_height}")
fps_rate = frames_written / elapsed if elapsed > 0 else 0
self.render_progress_queue.put(("progress", f"Processed {i+1}/{total_frames} frames", progress, fps_rate)) # Write frame to temporary raw file
last_progress_update = current_time raw_file.write(processed_frame.tobytes())
frames_written += 1
# Close FFmpeg input and wait for completion # Update progress with FPS calculation
self.ffmpeg_process.stdin.close() current_time = time.time()
stderr = self.ffmpeg_process.communicate()[1] progress = 0.1 + (0.8 * (i + 1) / total_frames)
return_code = self.ffmpeg_process.returncode
self.ffmpeg_process = None # Calculate FPS and update progress (throttled)
if current_time - last_progress_update > 0.5:
elapsed = current_time - start_time
fps_rate = frames_written / elapsed if elapsed > 0 else 0
self.render_progress_queue.put(("progress", f"Processed {i+1}/{total_frames} frames", progress, fps_rate))
last_progress_update = current_time
render_cap.release() render_cap.release()
# Now encode the raw file with FFmpeg
self.render_progress_queue.put(("progress", "Encoding with FFmpeg...", 0.9, 0.0))
ffmpeg_cmd = [
'ffmpeg', '-y', '-v', 'error',
'-f', 'rawvideo',
'-vcodec', 'rawvideo',
'-s', f'{output_width}x{output_height}',
'-pix_fmt', 'bgr24',
'-r', str(self.fps),
'-i', temp_raw_file,
'-c:v', 'libx264',
'-preset', 'fast',
'-crf', '18',
'-pix_fmt', 'yuv420p',
output_path
]
# Run FFmpeg encoding
result = subprocess.run(ffmpeg_cmd, capture_output=True, text=True)
return_code = result.returncode
stderr = result.stderr
if return_code == 0: if return_code == 0:
total_time = time.time() - start_time total_time = time.time() - start_time
avg_fps = frames_written / total_time if total_time > 0 else 0 avg_fps = frames_written / total_time if total_time > 0 else 0
self.render_progress_queue.put(("complete", f"Rendered {frames_written} frames with FFmpeg", 1.0, avg_fps)) self.render_progress_queue.put(("complete", f"Rendered {frames_written} frames with FFmpeg", 1.0, avg_fps))
print(f"Successfully rendered {frames_written} frames using FFmpeg pipe (avg {avg_fps:.1f} FPS)") print(f"Successfully rendered {frames_written} frames using FFmpeg (avg {avg_fps:.1f} FPS)")
return True return True
else: else:
self.render_progress_queue.put(("error", f"FFmpeg encoding failed: {stderr.decode()}", 1.0, 0.0)) error_details = stderr if stderr else "No error details available"
print(f"FFmpeg encoding failed with return code {return_code}")
print(f"FFmpeg stderr: {error_details}")
self.render_progress_queue.put(("error", f"FFmpeg encoding failed: {error_details}", 1.0, 0.0))
return False return False
except Exception as e: except Exception as e:
error_msg = str(e) error_msg = str(e)
# Handle specific Windows pipe errors print(f"FFmpeg rendering exception: {error_msg}")
if "Errno 22" in error_msg or "invalid argument" in error_msg.lower(): print(f"Exception type: {type(e).__name__}")
error_msg = "Windows pipe error - try using a different output path or restart the application"
elif "BrokenPipeError" in error_msg:
error_msg = "FFmpeg process terminated unexpectedly - check if FFmpeg is installed correctly"
self.render_progress_queue.put(("error", f"FFmpeg pipe rendering failed: {error_msg}", 1.0, 0.0)) # Handle specific errors
if "Errno 22" in error_msg or "invalid argument" in error_msg.lower():
error_msg = "File system error - try using a different output path"
elif "FileNotFoundError" in error_msg or "ffmpeg" in error_msg.lower():
error_msg = "FFmpeg not found - please install FFmpeg and ensure it's in your PATH"
self.render_progress_queue.put(("error", f"FFmpeg rendering failed: {error_msg}", 1.0, 0.0))
return False return False
finally:
# Clean up temporary file
if temp_raw_file and os.path.exists(temp_raw_file):
try:
os.remove(temp_raw_file)
print(f"Cleaned up temporary file: {temp_raw_file}")
except Exception as e:
print(f"Warning: Could not remove temporary file {temp_raw_file}: {e}")
def run(self): def run(self):
"""Main editor loop""" """Main editor loop"""