File size: 3,688 Bytes
c6abe34 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | """
A module for reading and writing video files.
This module provides utility functions to load video frames into memory and save
processed frames back to video files, with support for common video formats.
"""
import cv2
import os
def read_video(video_path):
"""
Read all frames from a video file into memory.
Args:
video_path (str): Path to the input video file.
Returns:
list: List of video frames as numpy arrays.
"""
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
cap.release()
return []
frames = []
try:
while True:
ret, frame = cap.read()
if not ret:
break
frames.append(frame)
finally:
cap.release()
return frames
def save_video(output_video_frames, output_video_path):
"""
Save a sequence of frames as a video file.
Creates necessary directories if they don't exist and selects appropriate codec
based on the output file extension (.mp4, .avi, etc.).
Args:
output_video_frames (list): List of frames to save.
output_video_path (str): Path where the video should be saved.
"""
if not output_video_frames:
return
# If folder doesn't exist, create it
output_dir = os.path.dirname(output_video_path)
if output_dir and not os.path.exists(output_dir):
os.makedirs(output_dir)
# Determine codec based on file extension
extension = os.path.splitext(output_video_path)[1].lower()
# Track if we used a legacy codec that needs transcoding for browser support
needs_transcoding = False
if extension == '.mp4':
# Primary choice: mp4v (widely supported software codec)
# We then transcode to H.264 (avc1) using FFmpeg for the browser.
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
needs_transcoding = True
else:
# Default to XVID for AVI or other formats
fourcc = cv2.VideoWriter_fourcc(*'XVID')
height, width = output_video_frames[0].shape[:2]
out = cv2.VideoWriter(output_video_path, fourcc, 24, (width, height))
# Final fallback if even mp4v fails (unlikely)
if not out.isOpened():
print(f"⚠️ VideoWriter failed with {fourcc}. Trying alternative...")
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter(output_video_path, fourcc, 24, (width, height))
needs_transcoding = True
if not out.isOpened():
print(f"❌ CRITICAL: Failed to open VideoWriter with path: {output_video_path}")
return
for frame in output_video_frames:
out.write(frame)
out.release()
# Transcode to H.264 if we used a legacy codec and have ffmpeg available
if needs_transcoding:
try:
temp_path = output_video_path + ".temp.mp4"
os.rename(output_video_path, temp_path)
# Using libx264 ensures maximum browser compatibility
import subprocess
cmd = [
'ffmpeg', '-y', '-i', temp_path,
'-c:v', 'libx264', '-pix_fmt', 'yuv420p',
'-preset', 'ultrafast', '-crf', '23',
output_video_path
]
subprocess.run(cmd, capture_output=True, check=True)
os.remove(temp_path)
print(f"✅ Transcoded video to H.264 for browser compatibility: {output_video_path}")
except Exception as e:
print(f"⚠️ Failed to transcode video with FFmpeg: {e}")
if os.path.exists(temp_path) and not os.path.exists(output_video_path):
os.rename(temp_path, output_video_path) |