The PartialProgress struct accumulates FFmpeg’s -progress pipe:1 output line by line and produces structured Progress snapshots. This enables real-time progress tracking, percentage calculation, and user feedback during encoding operations.
This example is adapted from libffmpeg/examples/transcode_with_progress.rs:36-74:
use libffmpeg::ffmpeg::ffmpeg;use libffmpeg::ffmpeg::progress::PartialProgress;use libffmpeg::libcmd::{CommandMonitor, CommandMonitorMessage};use tokio_util::sync::CancellationToken;use std::time::Duration;#[tokio::main]async fn main() -> anyhow::Result<()> { let input = "input.mp4"; let output = "output.mp4"; // Get video duration for percentage calculation let cancellation_token = CancellationToken::new(); let video_duration = libffmpeg::util::get_duration( input, cancellation_token.child_token() ).await?; let total_seconds = video_duration.as_secs() as f64; // Create monitor let monitor = CommandMonitor::with_capacity(100); // Spawn monitoring task let monitor_task = { let mut client = monitor.client.clone(); tokio::spawn(async move { let mut progress = PartialProgress::default(); while let Some(Some(message)) = client.recv().await { match message { CommandMonitorMessage::Stdout { line } => { // Try to parse as progress line if !progress.with_line(&line) { // Not a progress line, print it println!("[OUT] {}", line); continue; } // Check if progress block is complete if let Some(update) = progress.finish() { let percent = 100.0 * update.out_time.as_secs_f64() / total_seconds; let elapsed = humantime::format_duration(update.out_time); let total = humantime::format_duration(Duration::from_secs_f64(total_seconds)); println!( "Progress: {:.2}% ({} / {}) | Frame: {} | Bitrate: {} | FPS: {:.1} | Speed: {:.1}x", percent, elapsed, total, update.frame, format_bitrate(update.bitrate), update.fps, update.speed ); } } CommandMonitorMessage::Stderr { line } => { eprintln!("[ERR] {}", line); } } } }) }; // Run FFmpeg with progress output let result = ffmpeg(cancellation_token.child_token(), &monitor.server, |cmd| { cmd.arg("-i").arg(input); cmd.arg("-c:v").arg("libx264"); cmd.arg("-preset").arg("fast"); cmd.arg("-progress").arg("pipe:1"); // IMPORTANT: Send progress to stdout cmd.arg("-y"); cmd.arg(output); }).await?; monitor_task.await?; Ok(())}fn format_bitrate(bitrate: isize) -> String { if bitrate < 1024 { format!("{}bps", bitrate) } else if bitrate < 1024 * 1024 { format!("{:.1}Kbps", bitrate as f64 / 1024.0) } else { format!("{:.1}Mbps", bitrate as f64 / (1024.0 * 1024.0)) }}
Always use -progress pipe:1 in your FFmpeg command to send structured progress output to stdout. Without this flag, you won’t receive parseable progress information.
The Progress struct contains the following fields (from libffmpeg/src/ffmpeg/progress.rs:152-174):
pub struct Progress { /// Number of frames processed so far. pub frame: usize, /// Current encoding speed in frames per second. pub fps: f64, /// Current bitrate in bytes per second. pub bitrate: isize, /// Total output size in bytes. pub total_size: usize, /// Elapsed output time (position in the output stream). pub out_time: Duration, /// Number of duplicated frames. pub dup_frames: usize, /// Number of dropped frames. pub drop_frames: usize, /// Encoding speed as a multiplier of realtime (e.g. 2.0 = 2x realtime). pub speed: f64, /// Whether ffmpeg is still processing or has finished. pub progress: ProgressState,}
To display completion percentage, you need the total video duration:
use libffmpeg::util::get_duration;// Get total durationlet total_duration = get_duration(input, cancellation_token.child_token()).await?;let total_seconds = total_duration.as_secs_f64();// In your monitoring loopif let Some(progress) = partial.finish() { let elapsed_seconds = progress.out_time.as_secs_f64(); let percentage = (elapsed_seconds / total_seconds) * 100.0; println!("Progress: {:.2}%", percentage);}
use std::time::Instant;let start_time = Instant::now();let total_duration = video_duration.as_secs_f64();if let Some(progress) = partial.finish() { let elapsed_secs = start_time.elapsed().as_secs_f64(); let progress_secs = progress.out_time.as_secs_f64(); if progress_secs > 0.0 { let rate = progress_secs / elapsed_secs; // How much video per second let remaining_video = total_duration - progress_secs; let eta_seconds = remaining_video / rate; println!("ETA: {:.0} seconds", eta_seconds); }}