Pipe RGB Frames from Python to FFmpeg on Windows
This article explains how to efficiently stream raw RGB video frames
generated in a Python script directly into FFmpeg on a Windows operating
system using subprocesses. By leveraging Python’s
subprocess module, you can pipe raw image data directly
into FFmpeg’s standard input (stdin), avoiding the need to
write temporary image files to your disk and significantly improving
video encoding performance.
Prerequisites
To follow this guide, you must have the following installed on your
Windows machine: 1. Python 3.x (with numpy
installed for frame generation). 2. FFmpeg (ensuring
the ffmpeg executable is added to your Windows System
PATH).
The Python Implementation
To pipe frames to FFmpeg, you need to launch the FFmpeg process from
Python, configure it to accept raw video from stdin, and
then write the raw pixel bytes of each frame sequentially into the
process’s write pipe.
Here is a complete, ready-to-run script that generates a 5-second synthetic video:
import subprocess
import numpy as np
def main():
# Define video properties
width = 1920
height = 1080
fps = 30
duration_seconds = 5
total_frames = fps * duration_seconds
# Define the FFmpeg command
# We use a list of arguments for clean execution on Windows
ffmpeg_cmd = [
'ffmpeg',
'-y', # Overwrite output file if it exists
'-f', 'rawvideo', # Tell FFmpeg the input is raw video
'-vcodec', 'rawvideo',
'-pix_fmt', 'rgb24', # Pixel format of the raw input
'-s', f'{width}x{height}', # Resolution of the input
'-r', str(fps), # Framerate of the input
'-i', '-', # '-' tells FFmpeg to read from stdin
'-c:v', 'libx264', # Encode output using H.264
'-pix_fmt', 'yuv420p', # Output pixel format for maximum compatibility
'-crf', '23', # Constant Rate Factor (quality scale)
'output.mp4' # Output filename
]
# Start the FFmpeg subprocess
# stdin=subprocess.PIPE allows us to write bytes directly to the process
process = subprocess.Popen(ffmpeg_cmd, stdin=subprocess.PIPE)
print("Streaming frames to FFmpeg...")
try:
for frame_idx in range(total_frames):
# Generate a dummy RGB frame (moving color gradient)
# A 3D numpy array of shape (height, width, 3) representing RGB channels
frame = np.zeros((height, width, 3), dtype=np.uint8)
color_shift = int((frame_idx / total_frames) * 255)
# Fill channels with some dynamic patterns
frame[:, :, 0] = color_shift # Red channel
frame[:, :, 1] = (color_shift + 100) % 256 # Green channel
# Convert the numpy array to raw bytes and write to FFmpeg's stdin
process.stdin.write(frame.tobytes())
except IOError as e:
print(f"Pipe broken or FFmpeg closed unexpectedly: {e}")
finally:
# Close the stdin pipe to signal End-Of-File (EOF) to FFmpeg
if process.stdin:
process.stdin.close()
# Wait for FFmpeg to finish writing the output file
process.wait()
print("Video encoding complete.")
if __name__ == '__main__':
main()Key Parameters Explained
-f rawvideoand-pix_fmt rgb24: These tell FFmpeg not to look for file headers (like PNG or JPEG) and instead expect a continuous stream of raw 24-bit RGB bytes (8 bits per channel: Red, Green, Blue).-s 1920x1080: Because raw video has no header metadata, you must explicitly tell FFmpeg the resolution of each incoming frame so it knows where to split the byte stream.-i -: The hyphen tells FFmpeg that the input file is coming from standard input (stdin) instead of a physical file on the disk.frame.tobytes(): In Python, you must convert your image data (typically a NumPy array) into raw, contiguous bytes before writing it to thestdinbuffer.process.stdin.close(): Closing the stream is crucial. It tells FFmpeg that no more frames are coming, allowing it to finalize the.mp4container and exit cleanly.