From 051b682101f7868358c7a2f2f15a43a1746264d1 Mon Sep 17 00:00:00 2001 From: jb-alvarado Date: Thu, 24 Mar 2022 17:21:38 +0100 Subject: [PATCH] validate ffmpeg --- Cargo.lock | 18 +++++++++ Cargo.toml | 1 + src/main.rs | 4 +- src/utils/config.rs | 4 +- src/utils/mod.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 115 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 374ae3c1..4b1adebb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -132,6 +132,12 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "fastrand" version = "1.7.0" @@ -164,6 +170,7 @@ dependencies = [ "simplelog", "tokio", "walkdir", + "which", ] [[package]] @@ -1096,6 +1103,17 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "which" +version = "4.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +dependencies = [ + "either", + "lazy_static", + "libc", +] + [[package]] name = "winapi" version = "0.2.8" diff --git a/Cargo.toml b/Cargo.toml index df757c7f..40be7f5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ shlex = "1.1" simplelog = { version = "^0.11.2", features = ["paris"] } tokio = { version = "1.16.1", features = ["rt-multi-thread"] } walkdir = "2" +which = "4.2.5" [target.x86_64-unknown-linux-musl.dependencies] openssl = { version = "0.10", features = ["vendored"] } diff --git a/src/main.rs b/src/main.rs index b3b91c29..058c295d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,7 @@ use simplelog::*; use tokio::runtime::Runtime; use crate::output::play; -use crate::utils::{init_config, init_logging}; +use crate::utils::{init_config, init_logging, validate_ffmpeg}; fn main() { init_config(); @@ -21,5 +21,7 @@ fn main() { let logging = init_logging(rt_handle.clone()); CombinedLogger::init(logging).unwrap(); + validate_ffmpeg(); + play(rt_handle); } diff --git a/src/utils/config.rs b/src/utils/config.rs index d116afc3..c0dd36f6 100644 --- a/src/utils/config.rs +++ b/src/utils/config.rs @@ -138,8 +138,8 @@ impl GlobalConfig { Ok(file) => file, Err(err) => { println!( - "'{:?}' doesn't exists!\n{}\n\nSystem error: {err}", - config_path, "Put 'ffplayout.yml' in '/etc/playout/' or beside the executable!" + "{:?} doesn't exists!\n{}\n\nSystem error: {err}", + config_path, "Put \"ffplayout.yml\" in \"/etc/playout/\" or beside the executable!" ); process::exit(0x0100); } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index eb3a85aa..54259b3e 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -6,12 +6,15 @@ use std::{ fs::metadata, io::{BufRead, BufReader, Error}, path::Path, - process::ChildStderr, + process::exit, + process::{ChildStderr, Command, Stdio}, time, time::UNIX_EPOCH, }; +use regex::Regex; use simplelog::*; +use which::which; mod arg_parse; mod config; @@ -345,3 +348,90 @@ pub async fn stderr_reader(std_errors: ChildStderr, suffix: String) -> Result<() Ok(()) } + +fn is_in_system(name: &str) { + // Check whether name is on PATH and marked as executable + + if which(name).is_err() { + error!("{} not found on system!", name); + exit(0x0100); + } +} + +fn ffmpeg_libs_and_filter() -> (Vec, Vec) { + let mut libs: Vec = vec![]; + let mut filters: Vec = vec![]; + let re: Regex = Regex::new(r"^( ?) [TSC.]+").unwrap(); + + let mut ff_proc = match Command::new("ffmpeg") + .arg("-filters") + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + { + Err(e) => { + error!("couldn't spawn ffmpeg process: {}", e); + exit(0x0100); + } + Ok(proc) => proc, + }; + + let err_buffer = BufReader::new(ff_proc.stderr.take().unwrap()); + let out_buffer = BufReader::new(ff_proc.stdout.take().unwrap()); + + for line in err_buffer.lines() { + if let Ok(line) = line { + if line.contains("configuration:") { + let configs = line.split_whitespace(); + + for config in configs { + if config.contains("--enable-lib") { + libs.push(config.replace("--enable-", "")); + } + } + } + } + } + + for line in out_buffer.lines() { + if let Ok(line) = line { + if let Some(_) = re.captures(line.as_str()) { + let filter_line = line.split_whitespace(); + + filters.push(filter_line.collect::>()[1].to_string()); + } + } + } + + (libs, filters) +} +pub fn validate_ffmpeg() { + let config = GlobalConfig::global(); + + is_in_system("ffmpeg"); + is_in_system("ffprobe"); + + if config.out.mode == "desktop" { + is_in_system("ffplay"); + } + + let (libs, filters) = ffmpeg_libs_and_filter(); + + if !libs.contains(&"libx264".to_string()) { + error!("ffmpeg contains no libx264!"); + exit(0x0100); + } + + if !libs.contains(&"libfdk-aac".to_string()) { + warn!("ffmpeg contains no libfdk-aac! Can't use high quality aac encoder..."); + } + + if !filters.contains(&"tpad".to_string()) { + error!("ffmpeg contains no tpad filter!"); + exit(0x0100); + } + + if !filters.contains(&"zmq".to_string()) { + warn!("ffmpeg contains no zmq filter! Text messages will not work..."); + } +}