#![forbid(unsafe_code)] use gstreamer as gst; /// Pick the client-side H.264 decoder in a predictable preference order. /// /// Inputs: none, though operators may override the choice with /// `LESAVKA_H264_DECODER=` or bias automatic fallback order with /// `LESAVKA_H264_DECODER_PREFERENCE=hardware|software`. /// Outputs: the chosen decoder element name, or `decodebin` as a last-resort /// fallback when no explicit decoder is present. /// Why: Lesavka should use GPU decode on NVIDIA/VAAPI/V4L2-capable clients /// when possible, while keeping an explicit CPU route for open-source driver /// comparisons and driver debugging. #[must_use] pub fn pick_h264_decoder() -> String { if let Ok(raw) = std::env::var("LESAVKA_H264_DECODER") { let name = raw.trim(); if name.eq_ignore_ascii_case("decodebin") { return "decodebin".to_string(); } if !name.is_empty() && buildable_decoder(name) { return name.to_string(); } } for name in h264_decoder_preference_order() { if buildable_decoder(name) { return name.to_string(); } } "decodebin".to_string() } /// Return automatic H.264 decoder candidates in selection order. /// /// Inputs: `LESAVKA_H264_DECODER_PREFERENCE`, if set. Output: ordered decoder /// element names. Why: tests and diagnostics need to prove both proprietary /// NVIDIA and open-source VAAPI/V4L2 routes stay available before CPU fallback. #[must_use] pub fn h264_decoder_preference_order() -> Vec<&'static str> { const HARDWARE: &[&str] = &[ "nvh264dec", "nvh264sldec", "vah264dec", "vaapih264dec", "v4l2h264dec", "v4l2slh264dec", ]; const SOFTWARE: &[&str] = &["avdec_h264", "openh264dec"]; let prefer_software = std::env::var("LESAVKA_H264_DECODER_PREFERENCE") .ok() .map(|value| { matches!( value.trim().to_ascii_lowercase().as_str(), "software" | "sw" | "cpu" ) }) .unwrap_or(false); let mut candidates = Vec::with_capacity(HARDWARE.len() + SOFTWARE.len()); if prefer_software { candidates.extend_from_slice(SOFTWARE); candidates.extend_from_slice(HARDWARE); } else { candidates.extend_from_slice(HARDWARE); candidates.extend_from_slice(SOFTWARE); } candidates } fn buildable_decoder(name: &str) -> bool { #[cfg(coverage)] if std::env::var("TEST_FAIL_GST_INIT").is_ok() { return false; } if gst::init().is_err() { return false; } #[cfg(coverage)] if std::env::var("TEST_DISABLE_H264_DECODER_FACTORY").is_ok() { return false; } gst::ElementFactory::find(name).is_some() && gst::ElementFactory::make(name).build().is_ok() }