diff options
Diffstat (limited to 'src/ffmpeg.rs')
| -rw-r--r-- | src/ffmpeg.rs | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/src/ffmpeg.rs b/src/ffmpeg.rs new file mode 100644 index 0000000..fbfdc94 --- /dev/null +++ b/src/ffmpeg.rs @@ -0,0 +1,150 @@ +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<String> { + 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<u32>, + pub fps: f64, + pub alpha: bool, + pub codec_tag: String, +} + +pub fn probe_video(ff_args: &[String], max_frames: Option<u32>) -> 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, + } +} |
