Parse FFmpeg Progress Output via Socket or File

Monitoring the progress of an FFmpeg command is essential for providing real-time feedback in applications. This article explains how to direct FFmpeg’s progress metrics to a local file or socket using the -progress flag, details the structure of the generated key-value pair output, and demonstrates how to parse this data efficiently in your code.

Directing FFmpeg Progress Output

FFmpeg provides a built-in -progress option that directs real-time status information to a specified destination. You can write this data to a local file or stream it over a network protocol like TCP or UDP.

To write progress to a local file:

ffmpeg -i input.mp4 -progress progress.txt output.mp4

To stream progress to a local TCP socket:

ffmpeg -i input.mp4 -progress tcp://127.0.0.1:5555 output.mp4

Using a socket is generally preferred for live applications, as it avoids continuous disk I/O and allows your application to read the stream in real-time.

Understanding the Progress Log Format

FFmpeg outputs progress information as newline-separated key=value pairs. A single update block consists of several lines of metrics and always ends with the progress key.

An example of a single progress block looks like this:

frame=143
fps=29.97
stream_0_0_q=28.0
bitrate= 1054.3kbits/s
total_size=1048576
out_time_us=4766667
out_time_ms=4766667
out_time=00:00:04.766667
dup_frames=0
drop_frames=0
speed=1.02x
progress=continue

Key Metrics to Parse:

  • frame: The total number of frames processed so far.
  • fps: The current processing speed in frames per second.
  • out_time_us: The current playback position of the output file in microseconds. Divide this by 1000000 to get seconds.
  • speed: The encoding speed relative to realtime (e.g., 2.0x means encoding is happening at twice the speed of playback).
  • progress: Indicates the status of the process. This will be continue during encoding and end when the process completes.

How to Parse the Output

To parse this data, your application needs to read the incoming stream line-by-line, split each line by the = character, and store the keys and values. When the parser encounters progress=continue or progress=end, it signifies that a complete frame update block has been received and can be processed.

Here is a practical Python example demonstrating how to start a local TCP server, receive progress from FFmpeg, and parse the data:

import socket

def start_progress_server(host='127.0.0.1', port=5555):
    # Create and bind the TCP socket
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind((host, port))
    server_socket.listen(1)
    print(f"Listening for FFmpeg progress on {host}:{port}...")

    conn, addr = server_socket.accept()
    print(f"Connected by {addr}")

    buffer = ""
    current_metrics = {}

    try:
        while True:
            data = conn.recv(1024)
            if not data:
                break
            
            # Append received bytes decoded to string to the buffer
            buffer += data.decode('utf-8')
            
            # Process complete lines
            while "\n" in buffer:
                line, buffer = buffer.split("\n", 1)
                line = line.strip()
                
                if not line:
                    continue
                
                # Split key and value
                if "=" in line:
                    key, value = line.split("=", 1)
                    current_metrics[key.strip()] = value.strip()
                    
                    # When 'progress' is received, the current block is complete
                    if key.strip() == "progress":
                        handle_progress_update(current_metrics)
                        if value.strip() == "end":
                            return
    finally:
        conn.close()
        server_socket.close()

def handle_progress_update(metrics):
    # Extract specific values for display or calculation
    fps = metrics.get('fps', 'N/A')
    speed = metrics.get('speed', 'N/A')
    
    # out_time_us can be used to calculate percentage if total duration is known
    out_time_raw = metrics.get('out_time_us', '0')
    seconds = int(out_time_raw) / 1000000 if out_time_raw.isdigit() else 0
    
    print(f"Progress: Time: {seconds:.2f}s | FPS: {fps} | Speed: {speed}")

if __name__ == "__main__":
    start_progress_server()

Calculating Completion Percentage

The FFmpeg progress stream does not inherently know the total length of the input file, so it cannot output a completion percentage directly. To calculate percentage progress:

  1. Get the total duration of the input file beforehand using ffprobe:

    ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4
  2. Compute the percentage in your parser by comparing the parsed out_time_us (converted to seconds) against the total duration: \[\text{Percentage} = \left( \frac{\text{Current Seconds}}{\text{Total Duration}} \right) \times 100\]