diff --git a/croppa/main.py b/croppa/main.py index 900dd11..760c4e6 100644 --- a/croppa/main.py +++ b/croppa/main.py @@ -1632,6 +1632,10 @@ class VideoEditor: else: output_width = int(crop_width * self.zoom_factor) output_height = int(crop_height * self.zoom_factor) + + # Ensure dimensions are divisible by 2 for H.264 encoding + output_width = output_width - (output_width % 2) + output_height = output_height - (output_height % 2) # Send progress update self.render_progress_queue.put(("progress", "Setting up FFmpeg encoder...", 0.1, 0.0)) @@ -1845,44 +1849,43 @@ class VideoEditor: def _render_with_ffmpeg_pipe(self, output_path: str, start_frame: int, end_frame: int, output_width: int, output_height: int): """Render video with transformations""" try: + # Test FFmpeg with a simple command first 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" + test_result = subprocess.run(['ffmpeg', '-version'], capture_output=True, text=True, timeout=10) + if test_result.returncode != 0: + print(f"FFmpeg test failed with return code {test_result.returncode}") + print(f"FFmpeg stderr: {test_result.stderr}") + error_msg = "FFmpeg is not working properly" + self.render_progress_queue.put(("error", error_msg, 1.0, 0.0)) + return False + except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired) as e: + error_msg = f"FFmpeg not found or not working: {e}" print(error_msg) self.render_progress_queue.put(("error", error_msg, 1.0, 0.0)) return False self.render_progress_queue.put(("progress", "Starting encoder...", 0.0, 0.0)) - ffmpeg_cmd = [ - '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 - ] - + import tempfile import os temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.raw') temp_file.close() - ffmpeg_cmd[ffmpeg_cmd.index('-i') + 1] = temp_file.name - self.ffmpeg_process = subprocess.Popen( - ffmpeg_cmd, - stderr=subprocess.PIPE, - stdout=subprocess.DEVNULL, - creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0 - ) - + # Use a simpler, more Windows-compatible FFmpeg command + ffmpeg_cmd = [ + 'ffmpeg', '-y', + '-f', 'rawvideo', + '-s', f'{output_width}x{output_height}', + '-pix_fmt', 'bgr24', + '-r', str(self.fps), + '-i', temp_file.name, + '-c:v', 'libx264', + '-preset', 'fast', + '-crf', '18', + '-pix_fmt', 'yuv420p', + output_path + ] self.temp_file_name = temp_file.name render_cap = cv2.VideoCapture(str(self.video_path)) @@ -1927,11 +1930,27 @@ class VideoEditor: self.render_progress_queue.put(("progress", "Encoding...", 0.9, 0.0)) - stderr = self.ffmpeg_process.communicate()[1] - return_code = self.ffmpeg_process.returncode - self.ffmpeg_process = None + # Use subprocess.run() with timeout for better Windows reliability + result = subprocess.run( + ffmpeg_cmd, + capture_output=True, + text=True, + timeout=300, # 5 minute timeout + creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0 + ) - if hasattr(self, 'temp_file_name') and os.path.exists(self.temp_file_name): + return_code = result.returncode + stdout = result.stdout + stderr = result.stderr + + # Debug output + print(f"FFmpeg return code: {return_code}") + if stdout: + print(f"FFmpeg stdout: {stdout}") + if stderr: + print(f"FFmpeg stderr: {stderr}") + + if os.path.exists(self.temp_file_name): try: os.unlink(self.temp_file_name) except OSError: @@ -1944,7 +1963,7 @@ class VideoEditor: print(f"Successfully rendered {frames_written} frames (avg {avg_fps:.1f} FPS)") return True else: - error_details = stderr.decode() if stderr else "No error details available" + error_details = stderr if stderr else "No error details available" print(f"Encoding failed with return code {return_code}") print(f"Error: {error_details}") self.render_progress_queue.put(("error", f"Encoding failed: {error_details}", 1.0, 0.0))