Monitor FFmpeg Progress Programmatically

Monitoring long-running FFmpeg tasks is crucial for providing real-time feedback in applications. This article explains how to use the FFmpeg -progress option to output real-time status data to a file, pipe, or network socket, and how to programmatically parse this data to calculate the exact percentage of completion.

The -progress Option Syntax

The -progress option tells FFmpeg to write program-readable progress information to a specified URL or stream. The basic syntax is:

ffmpeg -i input.mp4 -progress [target] output.mp4

The [target] can be: * pipe:1 or pipe:2 to output to standard output (stdout) or standard error (stderr). * A local file path (e.g., progress.txt). * A network socket (e.g., tcp://127.0.0.1:5555).

Understanding the Progress Output Format

When FFmpeg runs, it repeatedly writes block of key-value pairs separated by newlines. Each block ends with a progress key. A typical block of output looks like this:

frame=124
fps=30.20
stream_0_0_q=28.0
bitrate=1204.5kbits/s
total_size=1024300
out_time_us=5160000
out_time_ms=5160000
out_time=00:00:05.160000
dup_frames=0
drop_frames=0
speed=1.2x
progress=continue

The most important keys for calculating overall progress are: * out_time_us: The current timestamp of the processed video in microseconds. * progress: Indicates whether the process is ongoing (continue) or finished (end).

Calculating Percentage Programmatically

To calculate the percentage of completion, you must first obtain the total duration of the input file (usually in seconds or microseconds) using a tool like ffprobe.

The formula to calculate the progress percentage is:

\[\text{Progress \%} = \left( \frac{\text{out\_time\_us}}{\text{Total Duration in Microseconds}} \right) \times 100\]

Python Implementation Example

Here is a practical Python script that runs an FFmpeg command, captures the progress output from a pipe, parses the out_time_us value, and prints the percentage in real time.

import subprocess
import re

# 1. Define input parameters (manually set or retrieved via ffprobe)
input_duration_seconds = 120.0  # 2 minutes
total_duration_us = int(input_duration_seconds * 1000000)

# 2. Build the FFmpeg command
# We use pipe:1 to output progress to stdout and -nostats to disable default stderr stats
cmd = [
    'ffmpeg', '-y',
    '-i', 'input.mp4',
    '-progress', 'pipe:1',
    '-nostats',
    'output.mkv'
]

# 3. Start the subprocess
process = subprocess.Popen(
    cmd,
    stdout=subprocess.PIPE,
    stderr=subprocess.DEVNULL, # Suppress standard FFmpeg logs
    text=True
)

# 4. Read the stdout stream line by line
try:
    for line in process.stdout:
        line = line.strip()
        if "out_time_us=" in line:
            try:
                # Extract the microsecond value
                out_time_us = int(line.split('=')[1])
                
                # Calculate and display the percentage
                percentage = (out_time_us / total_duration_us) * 100
                percentage = min(max(percentage, 0.0), 100.0) # Clamp between 0 and 100
                print(f"Progress: {percentage:.2f}%")
            except ValueError:
                pass
        
        if line == "progress=end":
            print("Processing complete!")
finally:
    process.stdout.close()
    process.wait()

Best Practices