use std::process::Command; /// Options that ffmpeg supports but ffprobe does not. const FFMPEG_ONLY_OPTIONS: &[&str] = &["-stream_loop", "-t"]; const ALPHA_FMTS: &[&str] = &[ "pal8", "argb", "rgba", "abgr", "bgra", "yuva420p", "ya8", "yuva422p", "yuva444p", "yuva420p9be", "yuva420p9le", "yuva422p9be", "yuva422p9le", "yuva444p9be", "yuva444p9le", "yuva420p10be", "yuva420p10le", "yuva422p10be", "yuva422p10le", "yuva444p10be", "yuva444p10le", "yuva420p16be", "yuva420p16le", "yuva422p16be", "yuva422p16le", "yuva444p16be", "yuva444p16le", "rgba64be", "rgba64le", "bgra64be", "bgra64le", "ya16be", "ya16le", "gbrap", "gbrap16be", "gbrap16le", "ayuv64le", "ayuv64be", "gbrap12be", "gbrap12le", "gbrap10be", "gbrap10le", "gbrapf32be", "gbrapf32le", "yuva422p12be", "yuva422p12le", "yuva444p12be", "yuva444p12le", "vuya", "rgbaf16be", "rgbaf16le", "rgbaf32be", "rgbaf32le", "gbrap14be", "gbrap14le", "ayuv", "uyva", "rgba128be", "rgba128le", "gbrapf16be", "gbrapf16le", "yaf32be", "yaf32le", "yaf16be", "yaf16le", "gbrap32be", "gbrap32le", ]; fn filter_probe_args(ff_args: &[String]) -> Vec { let mut out = Vec::new(); let mut skip_next = false; for arg in ff_args { if skip_next { skip_next = false; continue; } if FFMPEG_ONLY_OPTIONS.contains(&arg.as_str()) { skip_next = true; continue; } out.push(arg.clone()); } out } pub struct VideoInfo { pub width: u32, pub height: u32, pub num_frames: Option, pub fps: f64, pub alpha: bool, pub codec_tag: String, } pub fn probe_video(ff_args: &[String], max_frames: Option) -> VideoInfo { let probe_args = filter_probe_args(ff_args); let output = Command::new("ffprobe") .args(["-v", "error"]) .args(["-select_streams", "v:0"]) .args([ "-show_entries", "stream=codec_tag_string,width,height,pix_fmt,r_frame_rate,nb_frames", ]) .args(["-of", "csv=p=0"]) .args(&probe_args) .output() .expect("failed to run ffprobe"); if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); panic!("ffprobe failed: {stderr}"); } let stdout = String::from_utf8_lossy(&output.stdout); let line = stdout.trim(); let parts: Vec<&str> = line.split(',').collect(); if parts.len() != 6 { panic!("unexpected ffprobe output: {line}"); } let codec_tag = parts[0].to_string(); let width: u32 = parts[1].parse().expect("parse width"); let height: u32 = parts[2].parse().expect("parse height"); let alpha = ALPHA_FMTS.contains(&parts[3]); let fps: f64 = if let Some((num, den)) = parts[4].split_once('/') { let n: f64 = num.parse().expect("parse fps numerator"); let d: f64 = den.parse().expect("parse fps denominator"); n / d } else { parts[4].parse().expect("parse fps") }; let num_frames = parts[5].parse().ok().or(max_frames); VideoInfo { width, height, num_frames, fps, alpha, codec_tag, } }