lesavka/client/src/video_support.rs

88 lines
2.8 KiB
Rust
Raw Normal View History

#![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=<element>` 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()
}