The ffmpeg_graceful function runs FFmpeg with both output monitoring and graceful shutdown support. On cancellation, it sends q (FFmpeg’s built-in quit command) to stdin, giving the process up to 5 seconds to exit cleanly before falling back to SIGKILL. This ensures FFmpeg can properly finalize output files.
From the source code at libffmpeg/src/ffmpeg/graceful.rs:45-88, here’s how graceful shutdown works:
1
Process starts normally
FFmpeg begins execution with a separate process token:
// Different token for the processlet process_token = CancellationToken::new();let exit_token = CancellationToken::new();
2
User requests cancellation
When the main cancellation token is triggered, the shutdown handler activates.
3
Send quit command
The handler sends q to FFmpeg’s stdin:
// Send quit commandclient.send("q").await;
4
Wait with timeout
Give FFmpeg up to 5 seconds to exit cleanly:
match tokio::time::timeout( Duration::from_secs(5), exit_token.cancelled()).await { Ok(()) => { // Process exited cleanly } Err(_timeout) => { // Timeout: force kill tracing::warn!( "ffmpeg process did not respond to quit command, sending SIGKILL" ); process_token.cancel(); }}
5
Fallback to SIGKILL
If FFmpeg doesn’t exit within 5 seconds, it’s killed forcefully.
This timeout is generally sufficient for FFmpeg to:
Finish encoding the current frame
Write file headers and trailers
Close file handles
The 5-second timeout is not configurable in the current API. If you need a different timeout, consider forking the function or using the standard ffmpeg function with manual shutdown logic.
From libffmpeg/src/ffmpeg/graceful.rs:11-115, the function creates a background task that monitors for cancellation:
let shutdown_handle = { let span = tracing::info_span!(parent: span, "ffmpeg_graceful::shutdown_handle"); let client = client.clone(); let process_token = process_token.clone(); let exit_token = exit_token.clone(); let kill_token = cancellation_token.child_token(); tokio::spawn(async move { // Wait for kill token or exit tokio::select! { () = exit_token.cancelled() => { // Process exited naturally return }, () = kill_token.cancelled() => { // Continue with shutdown } } // Send quit client.send("q").await; // Wait with timeout match tokio::time::timeout( Duration::from_secs(5), exit_token.cancelled() ).await { Ok(()) => {} Err(_timeout) => { tracing::warn!( "ffmpeg process did not respond to quit command, sending SIGKILL" ); process_token.cancel(); } } }.instrument(span))};
The graceful shutdown mechanism uses FFmpeg’s built-in -stdin support. If you disable stdin (e.g., with -nostdin), graceful shutdown won’t work, and the process will be killed immediately.