How to Run FFmpeg and Parse Progress in Python

Running FFmpeg within Python allows you to automate video processing tasks, but tracking the progress of these operations is crucial for user experience. This article demonstrates how to use Python’s built-in subprocess module to execute FFmpeg commands and parse its real-time output to calculate and display the conversion progress.

The Approach: FFmpeg -progress Flag

While you can parse FFmpeg’s standard error stream (stderr), its layout can change and is difficult to parse reliably. The most robust method is to use FFmpeg’s -progress option. By passing -progress pipe:1, FFmpeg writes easy-to-parse key-value pairs directly to standard output (stdout), which your Python script can read line-by-line.

Python Implementation

The following complete script first retrieves the total duration of the input video using ffprobe (part of the FFmpeg suite), then runs the conversion command and calculates the percentage completion in real-time.

import subprocess
import os

def get_video_duration(input_file):
    """Retrieve the total duration of the video in seconds using ffprobe."""
    cmd = [
        'ffprobe', 
        '-v', 'error', 
        '-show_entries', 'format=duration', 
        '-of', 'default=noprint_wrappers=1:nokey=1', 
        input_file
    ]
    try:
        result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True)
        return float(result.stdout.strip())
    except (subprocess.CalledProcessError, ValueError):
        raise RuntimeError(f"Could not retrieve duration for file: {input_file}")

def convert_video_with_progress(input_file, output_file):
    """Convert video using FFmpeg and print real-time progress to the console."""
    if not os.path.exists(input_file):
        print(f"Error: Input file '{input_file}' does not exist.")
        return

    # Get total duration to calculate percentage
    total_duration = get_video_duration(input_file)
    print(f"Total Duration: {total_duration:.2f} seconds")

    # Command with -progress pipe:1 to output progress metrics to stdout
    cmd = [
        'ffmpeg', '-y',
        '-i', input_file,
        '-progress', 'pipe:1',
        '-nostats',
        output_file
    ]

    # Start the FFmpeg process
    process = subprocess.Popen(
        cmd, 
        stdout=subprocess.PIPE, 
        stderr=subprocess.DEVNULL, 
        text=True,
        bufsize=1
    )

    # Read progress output line by line
    for line in process.stdout:
        if 'out_time_us' in line:
            # out_time_us provides the current processed time in microseconds
            try:
                microseconds = int(line.split('=')[1].strip())
                current_time = microseconds / 1_000_000.0
                percentage = (current_time / total_duration) * 100
                
                # Cap percentage at 100% (metadata processing can sometimes cause slight overflow)
                percentage = min(percentage, 100.0)
                
                print(f"Progress: {percentage:.2f}%", end='\r')
            except ValueError:
                continue

    process.wait()
    
    if process.returncode == 0:
        print("\nConversion completed successfully!")
    else:
        print(f"\nConversion failed with exit code {process.returncode}")

# Example Usage
if __name__ == "__main__":
    convert_video_with_progress("input.mp4", "output.mkv")

How the Code Works

  1. Duration Detection: The get_video_duration function calls ffprobe to query the format metadata and returns the video duration as a float in seconds.
  2. Subprocess Initialization: subprocess.Popen starts the FFmpeg process asynchronously. Setting stdout=subprocess.PIPE redirects the progress information to Python, while stderr=subprocess.DEVNULL suppresses the default diagnostic output. bufsize=1 enables line-buffering to ensure real-time updates.
  3. Progress Parsing: FFmpeg regularly outputs status lines. The script looks specifically for out_time_us (output time in microseconds). By dividing this value by 1,000,000, the script gets the current elapsed playback time in seconds.
  4. Percentage Calculation: The elapsed time is divided by the total duration and multiplied by 100 to yield the current completion percentage, which is printed in place using the carriage return (\r) character.