livekit_client: Screensharing on Niri + NixOS (#52017)

Release Notes:

- Fixed a weird niche interaction between niri and nixos that broke
screensharing

---------

Co-authored-by: Jakub Konka <kubkon@jakubkonka.com>
This commit is contained in:
Cameron Mcloughlin 2026-03-21 06:42:55 +00:00 committed by GitHub
parent b99200fbaf
commit 17e4b492c6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 28 additions and 7 deletions

1
Cargo.lock generated
View file

@ -10004,6 +10004,7 @@ dependencies = [
"tokio",
"ui",
"util",
"webrtc-sys",
"zed-scap",
]

View file

@ -779,6 +779,7 @@ wax = "0.7"
which = "6.0.0"
wasm-bindgen = "0.2.113"
web-time = "1.1.0"
webrtc-sys = "0.3.23"
wgpu = { git = "https://github.com/zed-industries/wgpu.git", branch = "v29" }
windows-core = "0.61"
yawc = "0.2.5"
@ -849,6 +850,7 @@ windows-capture = { git = "https://github.com/zed-industries/windows-capture.git
calloop = { git = "https://github.com/zed-industries/calloop" }
livekit = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "c1209aa155cbf4543383774f884a46ae7e53ee2e" }
libwebrtc = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "c1209aa155cbf4543383774f884a46ae7e53ee2e" }
webrtc-sys = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "c1209aa155cbf4543383774f884a46ae7e53ee2e" }
[profile.dev]
split-debuginfo = "unpacked"

View file

@ -49,6 +49,7 @@ livekit.workspace = true
[target.'cfg(target_os = "linux")'.dependencies]
tokio = { workspace = true, features = ["time"] }
webrtc-sys.workspace = true
[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "windows"))'.dependencies]
scap.workspace = true

View file

@ -14,6 +14,7 @@ use std::sync::{
};
static NEXT_WAYLAND_SHARE_ID: AtomicU64 = AtomicU64::new(1);
const PIPEWIRE_TIMEOUT_S: u64 = 30;
pub struct WaylandScreenCaptureStream {
id: u64,
@ -64,6 +65,17 @@ pub(crate) async fn start_wayland_desktop_capture(
};
use libwebrtc::native::yuv_helper::argb_to_nv12;
use std::time::Duration;
use webrtc_sys::webrtc::ffi as webrtc_ffi;
fn webrtc_log_callback(message: String, severity: webrtc_ffi::LoggingSeverity) {
match severity {
webrtc_ffi::LoggingSeverity::Error => log::error!("[webrtc] {}", message.trim()),
_ => log::debug!("[webrtc] {}", message.trim()),
}
}
let _webrtc_log_sink = webrtc_ffi::new_log_sink(webrtc_log_callback);
log::debug!("Wayland desktop capture: WebRTC internal logging enabled");
let stop_flag = Arc::new(AtomicBool::new(false));
let (mut video_source_tx, mut video_source_rx) = mpsc::channel::<NativeVideoSource>(1);
@ -79,7 +91,6 @@ pub(crate) async fn start_wayland_desktop_capture(
})?;
let permanent_error = Arc::new(AtomicBool::new(false));
let stop_cb = stop_flag.clone();
let permanent_error_cb = permanent_error.clone();
capturer.start_capture(None, {
@ -136,6 +147,8 @@ pub(crate) async fn start_wayland_desktop_capture(
}
});
log::info!("Wayland desktop capture: starting capture loop");
let stop = stop_flag.clone();
let tokio_task = gpui_tokio::Tokio::spawn(cx, async move {
loop {
@ -162,10 +175,11 @@ pub(crate) async fn start_wayland_desktop_capture(
let executor = cx.background_executor().clone();
let video_source = video_source_rx
.next()
.with_timeout(Duration::from_secs(15), &executor)
.with_timeout(Duration::from_secs(PIPEWIRE_TIMEOUT_S), &executor)
.await
.map_err(|_| {
stop_flag.store(true, Ordering::Relaxed);
log::error!("Wayland desktop capture timed out.");
anyhow::anyhow!(
"Screen sharing timed out waiting for the first frame. \
Check that xdg-desktop-portal and PipeWire are running, \

View file

@ -7,12 +7,14 @@ fn main() {
// Add rpaths for libraries that webrtc-sys dlopens at runtime.
// This is mostly required for hosts with non-standard SO installation
// locations such as NixOS.
let dlopened_libs = ["libva", "libva-drm"];
let dlopened_libs = ["libva", "libva-drm", "egl"];
let mut rpath_dirs = std::collections::BTreeSet::new();
for lib in &dlopened_libs {
if let Some(libdir) = pkg_config::get_variable(lib, "libdir").ok() {
rpath_dirs.insert(libdir);
} else {
eprintln!("zed build.rs: {lib} not found in pkg-config's path");
}
}

View file

@ -77,7 +77,6 @@ let
builtins.elem firstComp topLevelIncludes;
craneLib = crane.overrideToolchain rustToolchain;
gpu-lib = if withGLES then libglvnd else vulkan-loader;
commonArgs =
let
zedCargoLock = builtins.fromTOML (builtins.readFile ../crates/zed/Cargo.toml);
@ -179,7 +178,8 @@ let
libva
libxkbcommon
wayland
gpu-lib
libglvnd
vulkan-loader
xorg.libX11
xorg.libxcb
libdrm
@ -236,7 +236,8 @@ let
# about them that's special is that they're manually dlopened at runtime
NIX_LDFLAGS = lib.optionalString stdenv'.hostPlatform.isLinux "-rpath ${
lib.makeLibraryPath [
gpu-lib
libglvnd
vulkan-loader
wayland
libva
]
@ -245,7 +246,7 @@ let
NIX_OUTPATH_USED_AS_RANDOM_SEED = "norebuilds";
};
# prevent nix from removing the "unused" wayland/gpu-lib rpaths
# prevent nix from removing the "unused" wayland rpaths
dontPatchELF = stdenv'.hostPlatform.isLinux;
# TODO: try craneLib.cargoNextest separate output