Build Video Filtergraphs Using ffmpeg-python

This article provides a practical guide on how to build and execute complex video filtergraphs using the ffmpeg-python library. You will learn how to set up your environment, chain multiple video filters together (such as scaling, color adjustments, and overlays), and run the final FFmpeg command directly from Python code.

Getting Started

To use ffmpeg-python, you must have both the FFmpeg command-line tool and the Python wrapper installed on your system.

First, install the Python library via pip:

pip install ffmpeg-python

Ensure that ffmpeg is installed on your operating system and added to your system’s PATH.

Understanding the ffmpeg-python API

The ffmpeg-python library uses a fluent, node-based API to represent FFmpeg filtergraphs. In this paradigm, every input, filter, and output acts as a node in a directed acyclic graph (DAG). You pass streams from one node to the next by chaining method calls.

Building a Basic Filter Chain

A basic filtergraph applies one or more filters sequentially to a single input stream. The example below scales an input video and adjusts its contrast.

import ffmpeg

# Define the input stream
input_stream = ffmpeg.input('input.mp4')

# Chain filters: Scale to 1280x720, then adjust contrast and saturation
filtered_stream = (
    input_stream
    .filter('scale', 1280, 720)
    .filter('eq', contrast=1.2, saturation=1.5)
)

# Define the output and run the pipeline
output_stream = ffmpeg.output(filtered_stream, 'output.mp4')
ffmpeg.run(output_stream)

In this code, .filter() accepts the name of the FFmpeg filter as the first argument, followed by positional or keyword arguments that match FFmpeg’s command-line parameters.

Building Complex Filtergraphs (Overlays and Multi-Inputs)

Filtergraphs often require merging multiple streams. The following example demonstrates how to overlay a logo onto a background video, which requires defining multiple input nodes and combining them.

import ffmpeg

# Define inputs
video_input = ffmpeg.input('background.mp4')
logo_input = ffmpeg.input('logo.png')

# Scale the logo down to 150px wide, preserving aspect ratio
scaled_logo = logo_input.filter('scale', 150, -1)

# Overlay the scaled logo onto the background video at coordinates (x=50, y=50)
overlayed_video = ffmpeg.overlay(video_input, scaled_logo, x=50, y=50)

# Output the combined streams
output = ffmpeg.output(overlayed_video, 'overlay_output.mp4')
ffmpeg.run(output)

Handling Audio and Video Separately

When applying video filters, the audio stream from the original file is often dropped unless explicitly included in the output. To preserve the original audio while filtering the video, you must pass both streams to the output node.

import ffmpeg

input_file = ffmpeg.input('input.mp4')

# Split the input into video and audio streams
video = input_file.video
audio = input_file.audio

# Apply a fade-in effect to the video stream
filtered_video = video.filter('fade', type='in', start_frame=0, nb_frames=30)

# Recombine the filtered video with the untouched original audio
output = ffmpeg.output(filtered_video, audio, 'faded_output.mp4')
ffmpeg.run(output)