Detect missing pipewiresrc element before host startup

`gst-launch-1.0` and `pipewiresrc` ship in separate packages on most
distros (Arch: `gst-plugin-pipewire`, Debian: `gstreamer1.0-pipewire`,
Fedora/openSUSE: `pipewire-gstreamer`). Having the gst binary present
was no guarantee the Wayland capture pipeline would actually work —
without the plugin gst would bail at runtime with `no element
"pipewiresrc"`, which then cascaded into ffmpeg seeing EOF on its
stdin and exiting before its HTTP listener bound, then the host
hitting "Connection refused" against its own port. Confusing.

Now `deps::check_host_binaries` probes `gst-inspect-1.0 --exists
pipewiresrc` on Wayland and fails early with a per-distro install
hint pointing at the right package.
This commit is contained in:
2026-05-15 15:34:28 -04:00
parent 3a551c2287
commit c028e39aba
+43 -11
View File
@@ -1,5 +1,6 @@
use anyhow::{Result, bail}; use anyhow::{Result, bail};
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command;
use crate::common::display::DisplayServer; use crate::common::display::DisplayServer;
@@ -7,6 +8,8 @@ pub fn check_host_binaries(display: DisplayServer) -> Result<()> {
require("ffmpeg")?; require("ffmpeg")?;
if display == DisplayServer::Wayland { if display == DisplayServer::Wayland {
require("gst-launch-1.0")?; require("gst-launch-1.0")?;
require("gst-inspect-1.0")?;
require_gst_element("pipewiresrc")?;
} }
Ok(()) Ok(())
} }
@@ -14,13 +17,25 @@ pub fn check_host_binaries(display: DisplayServer) -> Result<()> {
fn require(bin: &str) -> Result<PathBuf> { fn require(bin: &str) -> Result<PathBuf> {
match which(bin) { match which(bin) {
Some(p) => Ok(p), Some(p) => Ok(p),
None => { None => bail!("`{bin}` not found on PATH.\n{}", install_hint_for_bin(bin)),
let hint = install_hint(bin);
bail!("`{bin}` not found on PATH.\n{hint}");
}
} }
} }
fn require_gst_element(name: &str) -> Result<()> {
let ok = Command::new("gst-inspect-1.0")
.args(["--exists", name])
.status()
.map(|s| s.success())
.unwrap_or(false);
if !ok {
bail!(
"GStreamer element `{name}` not available.\n{}",
install_hint_for_gst_element(name)
);
}
Ok(())
}
fn which(bin: &str) -> Option<PathBuf> { fn which(bin: &str) -> Option<PathBuf> {
let path = std::env::var_os("PATH")?; let path = std::env::var_os("PATH")?;
for dir in std::env::split_paths(&path) { for dir in std::env::split_paths(&path) {
@@ -32,19 +47,37 @@ fn which(bin: &str) -> Option<PathBuf> {
None None
} }
fn install_hint(bin: &str) -> String { fn install_hint_for_bin(bin: &str) -> String {
let distro = detect_distro(); let distro = detect_distro();
let pkg = match bin { let pkg = match bin {
"ffmpeg" => "ffmpeg", "ffmpeg" => "ffmpeg",
"gst-launch-1.0" => match distro.as_deref() { "gst-launch-1.0" | "gst-inspect-1.0" => match distro.as_deref() {
Some("arch" | "cachyos" | "manjaro" | "endeavouros") => "gst-plugins-base gst-plugins-good", Some("arch" | "cachyos" | "manjaro" | "endeavouros") => "gstreamer gst-plugins-base",
Some("debian" | "ubuntu" | "pop" | "linuxmint") => "gstreamer1.0-tools gstreamer1.0-plugins-good", Some("debian" | "ubuntu" | "pop" | "linuxmint") => "gstreamer1.0-tools",
Some("fedora" | "nobara") => "gstreamer1-plugins-good", Some("fedora" | "nobara") => "gstreamer1 gstreamer1-plugins-base-tools",
_ => "gstreamer (and tools / good-plugins)", _ => "gstreamer + tools",
}, },
_ => bin, _ => bin,
}; };
install_command(&distro, pkg)
}
fn install_hint_for_gst_element(name: &str) -> String {
let distro = detect_distro();
let pkg = match name {
"pipewiresrc" => match distro.as_deref() {
Some("arch" | "cachyos" | "manjaro" | "endeavouros") => "gst-plugin-pipewire",
Some("debian" | "ubuntu" | "pop" | "linuxmint") => "gstreamer1.0-pipewire",
Some("fedora" | "nobara") => "pipewire-gstreamer",
Some("opensuse" | "opensuse-tumbleweed" | "opensuse-leap") => "pipewire-gstreamer",
_ => "the GStreamer PipeWire plugin",
},
_ => name,
};
install_command(&distro, pkg)
}
fn install_command(distro: &Option<String>, pkg: &str) -> String {
let cmd = match distro.as_deref() { let cmd = match distro.as_deref() {
Some("arch" | "cachyos" | "manjaro" | "endeavouros") => format!("sudo pacman -S {pkg}"), Some("arch" | "cachyos" | "manjaro" | "endeavouros") => format!("sudo pacman -S {pkg}"),
Some("debian" | "ubuntu" | "pop" | "linuxmint") => format!("sudo apt install {pkg}"), Some("debian" | "ubuntu" | "pop" | "linuxmint") => format!("sudo apt install {pkg}"),
@@ -52,7 +85,6 @@ fn install_hint(bin: &str) -> String {
Some("opensuse" | "opensuse-tumbleweed" | "opensuse-leap") => format!("sudo zypper install {pkg}"), Some("opensuse" | "opensuse-tumbleweed" | "opensuse-leap") => format!("sudo zypper install {pkg}"),
_ => format!("install the `{pkg}` package via your distro's package manager"), _ => format!("install the `{pkg}` package via your distro's package manager"),
}; };
format!("Install hint: {cmd}") format!("Install hint: {cmd}")
} }