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