feat(runner): Container detection and handling, video bit-depth flags and script updates (#303)

## Description

Works in apptainer now.. podman is still the goat since apptainer needs
docker treatment and even more..

- Added container detection so podman can be used to it's fullest, the
non-sane ones are handled separately..
- Added video bit-depth option, cuz AV1 and 10-bit encoding go well
together.
- Some other package updates to nestri-server.
- General tidying up of scripts to make multi-container-engine handling
less of a pain.
- Updated old wireplumber lua script to new json format.

Further changes:

- Removed unused debug arg from nestri-server.
- Moved configs to config file folder rather than keeping them in
containerfile.
- Improved audio configs, moved some into wireplumber to keep things
tidy.
- Bit better arg handling in nestri-server.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Optional 10‑bit video support and auto‑launch of an app after display
setup.

* **Changes**
* Standardized runtime/user env to NESTRI_* with updated home/cache
paths and explicit LANG; password generation now logged.
* Improved container/GPU detection and startup logging; reduced blanket
root usage during startup; SSH setup surfaced.
* WirePlumber/PipeWire moved to JSON configs; low‑latency clock and
loopback audio policies added; audio capture defaults to PipeWire.
  
* **Chores**
* GStreamer/libp2p dependency upgrades and Rust toolchain pinned; NVIDIA
driver capability exposed.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
This commit is contained in:
Kristian Ollikainen
2025-09-24 20:08:04 +03:00
committed by GitHub
parent aba0bc3be1
commit 590fe5e196
26 changed files with 1508 additions and 1804 deletions

View File

@@ -1,5 +1,5 @@
use crate::args::encoding_args::RateControl;
use crate::gpu::{GPUInfo, get_gpu_by_card_path, get_gpus_by_vendor, get_nvidia_gpu_by_cuda_id};
use crate::gpu::{GPUInfo, GPUVendor, get_gpu_by_card_path, get_gpus_by_vendor};
use clap::ValueEnum;
use gstreamer::prelude::*;
use std::error::Error;
@@ -148,7 +148,7 @@ impl VideoEncoderInfo {
pub fn apply_parameters(&self, element: &gstreamer::Element, verbose: bool) {
for (key, value) in &self.parameters {
if element.has_property(key, None) {
if element.has_property(key) {
if verbose {
tracing::debug!("Setting property {} to {}", key, value);
}
@@ -273,7 +273,7 @@ pub fn encoder_gop_params(encoder: &VideoEncoderInfo, gop_size: u32) -> VideoEnc
pub fn encoder_low_latency_params(
encoder: &VideoEncoderInfo,
rate_control: &RateControl,
_rate_control: &RateControl,
framerate: u32,
) -> VideoEncoderInfo {
// 2 second GOP size, maybe lower to 1 second for fast recovery, if needed?
@@ -375,9 +375,9 @@ pub fn get_compatible_encoders(gpus: &Vec<GPUInfo>) -> Vec<VideoEncoderInfo> {
match api {
EncoderAPI::QSV | EncoderAPI::VAAPI => {
// Safe property access with panic protection, gstreamer-rs is fun
let path = if element.has_property("device-path", None) {
let path = if element.has_property("device-path") {
Some(element.property::<String>("device-path"))
} else if element.has_property("device", None) {
} else if element.has_property("device") {
Some(element.property::<String>("device"))
} else {
None
@@ -385,15 +385,46 @@ pub fn get_compatible_encoders(gpus: &Vec<GPUInfo>) -> Vec<VideoEncoderInfo> {
path.and_then(|p| get_gpu_by_card_path(&gpus, &p))
}
EncoderAPI::NVENC if element.has_property("cuda-device-id", None) => {
let cuda_id = element.property::<u32>("cuda-device-id");
get_nvidia_gpu_by_cuda_id(&gpus, cuda_id as usize)
EncoderAPI::NVENC => {
if encoder_name.contains("device") {
// Parse by element name's index (i.e. "nvh264device{N}enc")
let re = regex::Regex::new(r"device(\d+)").unwrap();
if let Some(caps) = re.captures(encoder_name.as_str()) {
if let Some(m) = caps.get(1) {
if let Ok(id) = m.as_str().parse::<usize>() {
return get_gpus_by_vendor(&gpus, GPUVendor::NVIDIA)
.get(id)
.cloned();
}
}
}
None
} else if element.has_property("cuda-device-id") {
let device_id =
match element.property_value("cuda-device-id").get::<i32>() {
Ok(v) if v >= 0 => Some(v as usize),
_ => None,
};
// We'll just treat cuda-device-id as an index
device_id.and_then(|id| {
get_gpus_by_vendor(&gpus, GPUVendor::NVIDIA)
.get(id)
.cloned()
})
} else {
None
}
}
EncoderAPI::AMF if element.has_property("device", None) => {
let device_id = element.property::<u32>("device");
get_gpus_by_vendor(&gpus, "amd")
.get(device_id as usize)
.cloned()
EncoderAPI::AMF if element.has_property("device") => {
let device_id = match element.property_value("device").get::<u32>() {
Ok(v) => Some(v as usize),
Err(_) => None,
};
device_id.and_then(|id| {
get_gpus_by_vendor(&gpus, GPUVendor::AMD).get(id).cloned()
})
}
_ => None,
}