FFmpegProgressBar
NAME
FFmpegProgressBar - Add a progress bar to ffmpeg commands
DESCRIPTION
Provides a progress bar for ffmpeg encoding operations. Uses Terminal::Spinners for rendering and detects duration automatically via ffprobe or by parsing -t/-to/-ss arguments.
Supports graceful shutdown via 'q' or Ctrl+C (Unix), preserving partial output.
SUBROUTINES
sub ffmpeg-progress(*@args, *%opts) is export
Convenience wrapper. Equivalent to:
FfmpegProgress.new(|%opts).run(@args);
Valid %opts keys (any FfmpegProgress attribute):
duration - Numeric duration in seconds
ffmpeg - Str path to ffmpeg binary (default: 'ffmpeg')
ffprobe - Str path to ffprobe binary (default: 'ffprobe')
bar-type - Str 'bar', 'hash', 'hash-dash', or 'equals'
show-frames - Bool show frame count
show-bitrate - Bool show bitrate
no-color - Bool disable colors
verbose - Bool show detected duration and command
Note: Extra keys in %opts are silently ignored. Use named parameters
with the module for validation, e.g. FfmpegProgress.new(:verbose).
Example:
ffmpeg-progress('-i', 'input.mp4', '-c:v', 'libx264', 'output.mp4');
ATTRIBUTES
$!ffmpeg - Path to ffmpeg binary (default: "ffmpeg")
$!ffprobe - Path to ffprobe binary (default: "ffprobe")
$!duration - Manually set duration in seconds (default: 0, auto-detected)
$!verbose - Show detected duration and executed command
$.show-frames - Show current frame and encoding fps
$.show-bitrate - Show output bitrate
$.stderr-lines - Number of stderr lines to show on success (default: '', 20 lines if --stderr used)
$!bar-type - Progress bar style: bar, hash, hash-dash, or equals
$.output-throttle - Minimum time between output updates (default: 0.1 seconds)
$!no-color - Disable colored output
$.dry-run - Validate args without running ffmpeg
$.eta-percent-threshold - Percent threshold for ETA calculation (default: 5)
$.eta-time-threshold - Time threshold (seconds) for ETA calculation (default: 4)
EncodeSkipped - Output file(s) already exist and -n was passed; ffmpeg did nothing
METHODS
method new(...) - Create FfmpegProgress instance
method run(*@args --> Int) - Run ffmpeg with progress bar, returns exit code
sub ffmpeg-progress(*@args --> Int) - Convenience function wrapper
method color(Str $name) - Get ANSI color code by name (reset, green, red, yellow, cyan, bold)
method bar-length() - Returns appropriate bar length for terminal width
method get-duration(Str $file) - Get video duration via ffprobe
method get-frame-count(Str $file) - Get video frame count via ffprobe
method quit(Bool :$force = False) - Quit the running ffmpeg process. Use :force for immediate kill.
Progress Tracking API
FFmpegProgressBar provides a progress API for external code to monitor encoding:
method get-progress(--> ProgressSnapshot) - Get current progress as an immutable snapshot. Returns a ProgressSnapshot object with: percent, elapsed, fps, speed, frame-count, total-frames, bitrate, status, is-stalled, last-error, eta.
method progress-supply(--> Supply) - Get a Supply that emits ProgressSnapshot updates. Use this for reactive/async progress monitoring.
method watch-progress(Numeric :$interval = 0.1, Bool :$emit = False --> Supply) - Simple progress iteration. By default yields percent (0-100). Use :emit to yield full ProgressSnapshot instead. Recommended for most use cases - no manual promise management needed.
Example: Using watch-progress() (Simplest)
use FFmpegProgressBar;
my $p = FfmpegProgress.new(:quiet); # Suppress progress bar, use API only
my $promise = start { $p.run('-i', 'input.mp4', 'output.mp4') };
for $p.watch-progress(:interval(0.5)) -> $percent {
say "Progress: {$percent.fmt('%.1f')}%";
$p.quit() if $percent >= 50;
}
await $promise;
say $p.get-progress().summary;
Quitting early based on progress:
for $p.watch-progress(:emit) -> $snap {
$p.quit() if $snap.percent >= 50;
$p.quit() if $snap.is-stalled;
}
Alternative: get-progress() (One-off)
For one-off reads:
my $snap = $p.get-progress();
say $snap.percent;
say $snap.is-complete;
ProgressSnapshot Methods
method summary() - Human-readable one-line summary (e.g., "75.0% [500/1000 25.0fps] @ 2.5Mbps ETA: 3s")
method remaining-seconds(--> Numeric) - Estimated seconds remaining
method completion-time(--> DateTime) - Estimated DateTime when encoding will finish
method is-complete(--> Bool) - True if encoding finished successfully
method is-failed(--> Bool) - True if encoding failed or was aborted
Example: External Progress Monitoring with Quit
This example shows how to monitor progress externally and quit early based on progress:
use FFmpegProgressBar;
my $p = FfmpegProgress.new;
# Start encoding in background
my $promise = start {
$p.run('-i', 'input.mp4', '-c:v', 'libx264', 'output.mp4');
};
# Poll progress until quit condition or completion
loop {
last if $promise.status ~~ Kept | Broken;
my $snap = $p.get-progress();
say "Progress: {$snap.percent.fmt('%.1f')}% - {$snap.status}";
# Quit at 50% if some external condition is met
if $snap.percent >= 50 && $snap.percent < 75 { # e.g. quit between 50-75%
say "Quitting at 50%!";
$p.quit();
last;
}
if $snap.is-complete || $snap.is-failed {
last;
}
sleep 0.1;
}
await $promise;
my $final = $p.get-progress();
say "Final: {$final.summary}";
say "Final status: {$final.status}";
For reactive monitoring using Supply:
use FFmpegProgressBar;
my $p = FfmpegProgress.new;
# Start encoding and subscribe to progress updates
my $promise = start {
$p.run('-i', 'input.mp4', 'output.mp4');
};
$p.progress-supply.tap: -> $snap {
say "Progress: {$snap.percent.fmt('%.1f')}% - ETA: {$snap.eta // '?'}s";
};
await $promise;
method run-pass1(Array $args --> Int) - Run first pass of two-pass encoding
method detect-duration(Numeric $duration, Numeric $output-duration, Numeric $seek-start, Bool $has-shortest, @input-groups, $mapped-inputs, Numeric $output-seek = 0) - Detect effective duration from multiple sources (considers -map and -ss/-t/-to to filter relevant inputs)
SIGNAL HANDLING
Unix/Linux/macOS: First Ctrl+C sends 'q' (graceful), second force-kills ffmpeg, third exits program. During two-pass pass 1, Ctrl+C aborts the analysis pass.
Windows: Press 'q' for graceful shutdown. Ctrl+C force kills ffmpeg immediately.
TWO-PASS ENCODING
When -pass 2 is detected in the arguments, FFmpegProgressBar automatically runs the first (analysis) pass before showing the progress bar for the second (encoding) pass. Signal handling works during both passes.
COMMAND-LINE USAGE
The module installs the ffpb command for CLI usage:
ffpb -i input.mp4 output.mp4
ffpb -V -F -B -i input.mp4 output.mp4
ffpb -S hash -D 60 -i input.mp4 output.mp4
ffpb -M /usr/bin/ffmpeg -P /usr/bin/ffprobe -i input.mp4 output.mp4
ffpb -E -i input.mp4 output.mp4
Use ffpb --help for options, or ffpb --man for the full manual.
CLI OPTIONS
-h, --help - Show help message
-V, --verbose - Show detected duration and executed command
-F, --frames - Show current frame and encoding fps
-B, --bitrate - Show output bitrate
-S, --style=style - Set progress bar style (bar, hash, hash-dash, equals)
-D, --duration=seconds - Set duration in seconds (skip auto-detection)
-M, --ffmpeg=path - Path to ffmpeg binary
-P, --ffprobe=path - Path to ffprobe binary
-E, --stderr[=N|all] - Show ffmpeg stderr on successful completion. Without value: last 20 lines. With value: N lines or 'all'.
--version - Show version information
--man - Show full manual page
CONFIG FILES
Config files are checked in the following order:
Unix/Linux/macOS
Windows
Each line should contain one option (like CLI arguments). Lines
starting with # are treated as comments.
SEE ALSO
FFmpegProgressBar::Parser, FFmpegProgressBar::Formatter, FFmpegProgressBar::Probe, FFmpegProgressBar::Terminal