Spaces:
Runtime error
Runtime error
| import ffmpeg | |
| def change_aspect_ratio(video_path: str, output_video_path: str, target_aspect_ratio: str, | |
| resize_mode: str = 'pad', padding_color: str = 'black') -> str: | |
| """Changes the aspect ratio of a video, using padding or cropping. | |
| Args listed in PRD. | |
| Returns: | |
| A status message indicating success or failure. | |
| """ | |
| try: | |
| probe = ffmpeg.probe(video_path) | |
| video_stream_info = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None) | |
| if not video_stream_info: | |
| return "Error: No video stream found in the input file." | |
| original_width = int(video_stream_info['width']) | |
| original_height = int(video_stream_info['height']) | |
| num, den = map(int, target_aspect_ratio.split(':')) | |
| target_ar_val = num / den | |
| original_ar_val = original_width / original_height | |
| vf_filter = "" | |
| # Attempt to copy codecs if the operation doesn't strictly require re-encoding video stream | |
| # This is mostly for padding. Cropping implies re-encoding the video stream. | |
| codec_to_use = None | |
| if resize_mode == 'pad': | |
| if abs(original_ar_val - target_ar_val) < 1e-4: | |
| try: | |
| ffmpeg.input(video_path).output(output_video_path, c='copy').run(capture_stdout=True, capture_stderr=True) | |
| return f"Video aspect ratio already matches. Copied to {output_video_path}." | |
| except ffmpeg.Error: | |
| # If copy fails, just re-encode | |
| ffmpeg.input(video_path).output(output_video_path).run(capture_stdout=True, capture_stderr=True) | |
| return f"Video aspect ratio already matches. Re-encoded to {output_video_path}." | |
| if original_ar_val > target_ar_val: | |
| final_w = int(original_height * target_ar_val) | |
| final_h = original_height | |
| vf_filter = f"scale={final_w}:{final_h}:force_original_aspect_ratio=decrease,pad={final_w}:{final_h}:(ow-iw)/2:(oh-ih)/2:{padding_color}" | |
| else: | |
| final_w = original_width | |
| final_h = int(original_width / target_ar_val) | |
| vf_filter = f"scale={final_w}:{final_h}:force_original_aspect_ratio=decrease,pad={final_w}:{final_h}:(ow-iw)/2:(oh-ih)/2:{padding_color}" | |
| codec_to_use = 'copy' # Try to copy for padding, audio will be copied too | |
| elif resize_mode == 'crop': | |
| if abs(original_ar_val - target_ar_val) < 1e-4: | |
| try: | |
| ffmpeg.input(video_path).output(output_video_path, c='copy').run(capture_stdout=True, capture_stderr=True) | |
| return f"Video aspect ratio already matches. Copied to {output_video_path}." | |
| except ffmpeg.Error: | |
| ffmpeg.input(video_path).output(output_video_path).run(capture_stdout=True, capture_stderr=True) | |
| return f"Video aspect ratio already matches. Re-encoded to {output_video_path}." | |
| if original_ar_val > target_ar_val: | |
| new_width = int(original_height * target_ar_val) | |
| vf_filter = f"crop={new_width}:{original_height}:(iw-{new_width})/2:0" | |
| else: | |
| new_height = int(original_width / target_ar_val) | |
| vf_filter = f"crop={original_width}:{new_height}:0:(ih-{new_height})/2" | |
| else: | |
| return f"Error: Invalid resize_mode '{resize_mode}'. Must be 'pad' or 'crop'." | |
| try: | |
| # Try with specified video filter and copying audio codec | |
| ffmpeg.input(video_path).output(output_video_path, vf=vf_filter, acodec='copy').run(capture_stdout=True, capture_stderr=True) | |
| return f"Video aspect ratio changed (audio copy) to {target_aspect_ratio} using {resize_mode}. Saved to {output_video_path}" | |
| except ffmpeg.Error as e_acopy: | |
| # Fallback to re-encoding audio if audio copy failed | |
| try: | |
| ffmpeg.input(video_path).output(output_video_path, vf=vf_filter).run(capture_stdout=True, capture_stderr=True) | |
| return f"Video aspect ratio changed (audio re-encoded) to {target_aspect_ratio} using {resize_mode}. Saved to {output_video_path}" | |
| except ffmpeg.Error as e_recode_all: | |
| err_acopy_msg = e_acopy.stderr.decode('utf8') if e_acopy.stderr else str(e_acopy) | |
| err_recode_msg = e_recode_all.stderr.decode('utf8') if e_recode_all.stderr else str(e_recode_all) | |
| return f"Error changing aspect ratio. Audio copy attempt failed: {err_acopy_msg}. Full re-encode attempt also failed: {err_recode_msg}." | |
| except ffmpeg.Error as e: | |
| error_message = e.stderr.decode('utf8') if e.stderr else str(e) | |
| return f"Error changing aspect ratio: {error_message}" | |
| except FileNotFoundError: | |
| return f"Error: Input video file not found at {video_path}" | |
| except ValueError: | |
| return f"Error: Invalid target_aspect_ratio format. Expected 'num:den' (e.g., '16:9')." | |
| except Exception as e: | |
| return f"An unexpected error occurred: {str(e)}" | |
| # Example usage | |
| if __name__ == "__main__": | |
| video_path = r"videos/e4cf148d-a419-4d69-9bf3-dff5f64e17c0.mp4" # Replace with your input video path | |
| output_video_path = "output_video.mp4" # Replace with your desired output path | |
| target_aspect_ratio = "9:16" # Desired aspect ratio | |
| resize_mode = "pad" # or "crop" | |
| padding_color = "black" # Color for padding if using pad mode | |
| result = change_aspect_ratio(video_path, output_video_path, target_aspect_ratio, resize_mode, padding_color) | |
| print(result) # Output the result of the operation |