Fix rendering again
This commit is contained in:
		
							
								
								
									
										167
									
								
								croppa/main.py
									
									
									
									
									
								
							
							
						
						
									
										167
									
								
								croppa/main.py
									
									
									
									
									
								
							@@ -1626,6 +1626,11 @@ class VideoEditor:
 | 
			
		||||
            # Send progress update
 | 
			
		||||
            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
 | 
			
		||||
            print("Using FFmpeg for encoding with OpenCV transformations...")
 | 
			
		||||
            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()
 | 
			
		||||
                elif update_type == "error":
 | 
			
		||||
                    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":
 | 
			
		||||
                    self.hide_progress_bar()
 | 
			
		||||
                    self.show_feedback_message("Render cancelled")
 | 
			
		||||
@@ -1826,35 +1833,23 @@ class VideoEditor:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
    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:
 | 
			
		||||
            # 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))
 | 
			
		||||
 | 
			
		||||
            # Start FFmpeg process to receive frames via pipe
 | 
			
		||||
            # Use Windows-friendly approach with explicit binary mode
 | 
			
		||||
            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
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
            # 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
 | 
			
		||||
            )
 | 
			
		||||
            # Create temporary raw video file instead of using pipes
 | 
			
		||||
            temp_raw_file = output_path.replace('.mp4', '_temp.raw')
 | 
			
		||||
            print(f"Using temporary file approach: {temp_raw_file}")
 | 
			
		||||
 | 
			
		||||
            # OpenCV for frame reading and transformations
 | 
			
		||||
            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))
 | 
			
		||||
 | 
			
		||||
            for i in range(total_frames):
 | 
			
		||||
                if self.render_cancelled:
 | 
			
		||||
                    self.ffmpeg_process.stdin.close()
 | 
			
		||||
                    self.ffmpeg_process.terminate()
 | 
			
		||||
                    self.ffmpeg_process.wait()
 | 
			
		||||
                    render_cap.release()
 | 
			
		||||
                    self.ffmpeg_process = None
 | 
			
		||||
                    self.render_progress_queue.put(("cancelled", "Render cancelled", 0.0, 0.0))
 | 
			
		||||
                    return False
 | 
			
		||||
            # Write frames to temporary raw file
 | 
			
		||||
            with open(temp_raw_file, 'wb') as raw_file:
 | 
			
		||||
                for i in range(total_frames):
 | 
			
		||||
                    if self.render_cancelled:
 | 
			
		||||
                        render_cap.release()
 | 
			
		||||
                        self.render_progress_queue.put(("cancelled", "Render cancelled", 0.0, 0.0))
 | 
			
		||||
                        return False
 | 
			
		||||
 | 
			
		||||
                ret, frame = render_cap.read()
 | 
			
		||||
                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
 | 
			
		||||
                    ret, frame = render_cap.read()
 | 
			
		||||
                    if not ret:
 | 
			
		||||
                        break
 | 
			
		||||
 | 
			
		||||
                # Update progress with FPS calculation
 | 
			
		||||
                current_time = time.time()
 | 
			
		||||
                progress = 0.1 + (0.8 * (i + 1) / total_frames)
 | 
			
		||||
                
 | 
			
		||||
                # 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
 | 
			
		||||
                    # Apply transformations with OpenCV
 | 
			
		||||
                    processed_frame = self._process_frame_for_render(frame, output_width, output_height)
 | 
			
		||||
                    if processed_frame is not None:
 | 
			
		||||
                        # Debug frame dimensions
 | 
			
		||||
                        if i == 0:  # Only print for first frame
 | 
			
		||||
                            print(f"Processed frame dimensions: {processed_frame.shape[1]}x{processed_frame.shape[0]}")
 | 
			
		||||
                            print(f"Expected dimensions: {output_width}x{output_height}")
 | 
			
		||||
                        
 | 
			
		||||
                        # Write frame to temporary raw file
 | 
			
		||||
                        raw_file.write(processed_frame.tobytes())
 | 
			
		||||
                        frames_written += 1
 | 
			
		||||
 | 
			
		||||
            # Close FFmpeg input and wait for completion
 | 
			
		||||
            self.ffmpeg_process.stdin.close()
 | 
			
		||||
            stderr = self.ffmpeg_process.communicate()[1]
 | 
			
		||||
            return_code = self.ffmpeg_process.returncode
 | 
			
		||||
            self.ffmpeg_process = None
 | 
			
		||||
                    # Update progress with FPS calculation
 | 
			
		||||
                    current_time = time.time()
 | 
			
		||||
                    progress = 0.1 + (0.8 * (i + 1) / total_frames)
 | 
			
		||||
                    
 | 
			
		||||
                    # 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()
 | 
			
		||||
 | 
			
		||||
            # 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:
 | 
			
		||||
                total_time = time.time() - start_time
 | 
			
		||||
                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))
 | 
			
		||||
                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
 | 
			
		||||
            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
 | 
			
		||||
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            error_msg = str(e)
 | 
			
		||||
            # Handle specific Windows pipe errors
 | 
			
		||||
            if "Errno 22" in error_msg or "invalid argument" in error_msg.lower():
 | 
			
		||||
                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"
 | 
			
		||||
            print(f"FFmpeg rendering exception: {error_msg}")
 | 
			
		||||
            print(f"Exception type: {type(e).__name__}")
 | 
			
		||||
            
 | 
			
		||||
            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
 | 
			
		||||
        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):
 | 
			
		||||
        """Main editor loop"""
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user