Pipe Python RGB Frames into FFmpeg on macOS
This article explains how to efficiently pipe raw RGB video frames
generated in a Python script directly into FFmpeg on macOS to create a
video file. By using Python’s built-in subprocess module,
you can stream raw byte data from your script straight to FFmpeg’s
standard input, eliminating the need to save temporary image files to
your disk.
Prerequisites
To get started on macOS, you must have FFmpeg installed. The easiest way to install it is via Homebrew. Open your terminal and run:
brew install ffmpegYou will also need numpy to generate sample frame data.
You can install it using pip:
pip install numpyThe Python Implementation
The following Python script generates a sequence of colored frames and pipes them directly into FFmpeg to output an MP4 video.
import subprocess
import numpy as np
# Define video properties
width = 1920
height = 1080
fps = 30
duration_seconds = 5
total_frames = fps * duration_seconds
# Configure the FFmpeg command
# -f rawvideo: Tells FFmpeg the input is raw video data
# -pix_fmt rgb24: Specifies that each pixel has 3 bytes (Red, Green, Blue)
# -s: Sets the frame resolution
# -r: Sets the input frame rate
# -i -: Tells FFmpeg to read the input from stdin (the pipe)
ffmpeg_cmd = [
'ffmpeg',
'-y', # Overwrite output file if it exists
'-f', 'rawvideo',
'-pix_fmt', 'rgb24',
'-s', f'{width}x{height}',
'-r', str(fps),
'-i', '-', # Read from stdin
'-c:v', 'libx264', # Encode using H.264
'-pix_fmt', 'yuv420p', # Ensure compatibility with macOS QuickTime Player
'output.mp4'
]
# Start the FFmpeg process with stdin mapped to a pipe
process = subprocess.Popen(ffmpeg_cmd, stdin=subprocess.PIPE)
print("Piping frames to FFmpeg...")
for frame_idx in range(total_frames):
# Create a dummy RGB frame (moving color gradient)
blue_val = int(255 * (frame_idx / total_frames))
frame = np.zeros((height, width, 3), dtype=np.uint8)
frame[:, :, 0] = 100 # Red channel
frame[:, :, 1] = 150 # Green channel
frame[:, :, 2] = blue_val # Animating Blue channel
# Write the raw bytes of the frame directly to FFmpeg's stdin
process.stdin.write(frame.tobytes())
# Close the stdin pipe and wait for FFmpeg to finish encoding
process.stdin.close()
process.wait()
print("Video encoding complete: output.mp4")How It Works
- FFmpeg Configuration: The
-i -parameter directs FFmpeg to accept input from standard input (stdin). Specifying-f rawvideoand-pix_fmt rgb24is crucial because raw byte streams do not contain header information, so FFmpeg must be explicitly told how to interpret the incoming bytes. - Subprocess Pipe:
subprocess.Popenlaunches FFmpeg as a background process and opens a write-only pipe (stdin=subprocess.PIPE) linked to it. - Writing Bytes: The numpy array
frame.tobytes()converts the 3D RGB array into a flat sequence of bytes. Since the format isrgb24, FFmpeg expects exactlywidth * height * 3bytes per frame. - Closing the Stream: Once all frames are written,
process.stdin.close()signals to FFmpeg that the video stream has ended, allowing FFmpeg to finalize and write the container file (output.mp4).