Skip to content

Show a real-time progress bar for FFmpeg exports in Python

When an FFmpeg export takes more than a second or two, a script that just sits there silently is hard to trust — is it hung, or actually working? ffmpeg-studio lets you pass a progress_callback into run() so you can surface live progress in a CLI, a log file, or, as shown here, a tqdm progress bar — instead of leaving users staring at a blank terminal during long exports.

Example

example/progress_bar.py
"""
Usage of callback function to make progress with tqdm
"""

from functools import partial
from ffmpeg import VideoFile, export
from tqdm import tqdm

# Total duration in seconds at export
duration = 5

# make subclip with from 0 to duration
clip = VideoFile(r"video.mp4").subclip(0, duration)


# this function will be called everytime
# it must take a atleast one or last arg (stats) of dictionary you can add as many arg before it
# duration is required to calculate the progress but you can skip it
# and indicate that process it runing and just print the stats
# all raised exception in this function will be ignored
# it must not update the stats dictionary
def update_progress(duration: float, pbar: tqdm, stats: dict):
    out_time_ms = stats.get("out_time_ms")
    if out_time_ms is None:
        return

    current_time = out_time_ms / 1_000_000
    pbar.n = min(current_time, duration)
    pbar.update(0)


# Create tqdm progress bar
pbar = tqdm(total=duration, unit="s", desc="Processing", leave=True)

# here we are setting args for callback stats like
# update_progress(duration, pbar)
# last arg will be added during runtime call
progress_callback = partial(update_progress, duration, pbar)

export(clip, path="out.mp4").run(progress_callback=progress_callback, progress_period=1)

pbar.close()

How it works

  • update_progress is the callback FFmpeg invokes repeatedly during export. It must accept stats argument, which ffmpeg-studio passes in on every call.
  • duration is the expected length (in seconds) of whatever you're exporting — used calculate a percentage. Here it's also passed to subclip(0, duration) to trim the clip, you can use know export length as duration.
  • functools.partial pre-binds duration and pbar to update_progress, since FFmpeg only ever supplies stats at call time.
  • stats["out_time_ms"] is the current output timestamp in microseconds despite the name — dividing by 1_000_000 converts it to seconds.
  • progress_period=1 controls how often (in seconds) the callback fires; lower it for smoother updates or raise it to reduce overhead.
  • Exceptions raised inside the callback are swallowed by ffmpeg-studio, and the callback should treat stats as read-only.