Skip to content

Commit 0a1b90c

Browse files
committed
Use libwebrtc's DesktopCapturer for screen capture
This adds support for screen sharing on Wayland (fixes #28754). libwebrtc replaces scap, which aims to be cross platform but was only used on Windows and X11. The X11 support relied on an out-of-tree branch on which the original author closed their own pull request (CapSoftware/scap#124). This also replaces Zed's platform-specific code on macOS. Switching to a single cross platform library simplifies the code by removing the need for Zed to have its own traits for cross platform abstraction.
1 parent 0386f24 commit 0a1b90c

File tree

34 files changed

+1300
-2285
lines changed

34 files changed

+1300
-2285
lines changed

Cargo.lock

Lines changed: 1016 additions & 1036 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ async-recursion = "1.0.0"
462462
async-tar = "0.5.1"
463463
async-task = "4.7"
464464
async-trait = "0.1"
465-
async-tungstenite = "0.31.0"
465+
async-tungstenite = "0.32.0"
466466
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
467467
aws-config = { version = "1.6.1", features = ["behavior-version-latest"] }
468468
aws-credential-types = { version = "1.2.2", features = [
@@ -542,6 +542,10 @@ jupyter-websocket-client = "0.15.0"
542542
libc = "0.2"
543543
libsqlite3-sys = { version = "0.30.1", features = ["bundled"] }
544544
linkify = "0.10.0"
545+
libwebrtc = { branch = "zed", git = "https://github.com/Be-ing/rust-sdks" }
546+
livekit = { branch = "zed", git = "https://github.com/Be-ing/rust-sdks", features = [
547+
"__rustls-tls"
548+
] }
545549
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
546550
lsp-types = { git = "https://github.com/zed-industries/lsp-types", rev = "b71ab4eeb27d9758be8092020a46fe33fbca4e33" }
547551
mach2 = "0.5"
@@ -629,8 +633,6 @@ rust-embed = { version = "8.4", features = ["include-exclude"] }
629633
rustc-hash = "2.1.0"
630634
rustls = { version = "0.23.26" }
631635
rustls-platform-verifier = "0.5.0"
632-
# WARNING: If you change this, you must also publish a new version of zed-scap to crates.io
633-
scap = { git = "https://github.com/zed-industries/scap", rev = "4afea48c3b002197176fb19cd0f9b180dd36eaac", default-features = false, package = "zed-scap", version = "0.0.8-zed" }
634636
schemars = { version = "1.0", features = ["indexmap2"] }
635637
semver = "1.0"
636638
serde = { version = "1.0.221", features = ["derive", "rc"] }
@@ -674,7 +676,7 @@ time = { version = "0.3", features = [
674676
] }
675677
tiny_http = "0.8"
676678
tokio = { version = "1" }
677-
tokio-tungstenite = { version = "0.26", features = ["__rustls-tls"] }
679+
tokio-tungstenite = { version = "0.28", features = ["__rustls-tls"] }
678680
toml = "0.8"
679681
toml_edit = { version = "0.22", default-features = false, features = ["display", "parse", "serde"] }
680682
tower-http = "0.4.4"
@@ -783,7 +785,6 @@ features = [
783785
[patch.crates-io]
784786
notify = { git = "https://github.com/zed-industries/notify.git", rev = "b4588b2e5aee68f4c0e100f140e808cbce7b1419" }
785787
notify-types = { git = "https://github.com/zed-industries/notify.git", rev = "b4588b2e5aee68f4c0e100f140e808cbce7b1419" }
786-
windows-capture = { git = "https://github.com/zed-industries/windows-capture.git", rev = "f0d6c1b6691db75461b732f6d5ff56eed002eeb9" }
787788

788789
[profile.dev]
789790
split-debuginfo = "unpacked"

crates/audio/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@ thiserror.workspace = true
2929
util.workspace = true
3030

3131
[target.'cfg(not(any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd")))'.dependencies]
32-
libwebrtc = { rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d", git = "https://github.com/zed-industries/livekit-rust-sdks" }
32+
libwebrtc = { workspace = true }

crates/call/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ collections.workspace = true
3030
fs.workspace = true
3131
futures.workspace = true
3232
feature_flags.workspace = true
33-
gpui = { workspace = true, features = ["screen-capture"] }
33+
gpui.workspace = true
3434
language.workspace = true
35+
libwebrtc.workspace = true
3536
log.workspace = true
3637
postage.workspace = true
3738
project.workspace = true

crates/call/src/call_impl/room.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ use feature_flags::FeatureFlagAppExt;
1313
use fs::Fs;
1414
use futures::StreamExt;
1515
use gpui::{
16-
App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, FutureExt as _,
17-
ScreenCaptureSource, ScreenCaptureStream, Task, Timeout, WeakEntity,
16+
App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, FutureExt as _, Task, Timeout,
17+
WeakEntity,
1818
};
1919
use gpui_tokio::Tokio;
2020
use language::LanguageRegistry;
21+
use libwebrtc::desktop_capturer::CaptureSource;
2122
use livekit::{LocalTrackPublication, ParticipantIdentity, RoomEvent};
22-
use livekit_client::{self as livekit, AudioStream, TrackSid};
23+
use livekit_client::{self as livekit, AudioStream, ScreenCaptureStreamHandle, TrackSid};
2324
use postage::{sink::Sink, stream::Stream, watch};
2425
use project::Project;
2526
use settings::Settings as _;
@@ -1267,9 +1268,7 @@ impl Room {
12671268

12681269
pub fn shared_screen_id(&self) -> Option<u64> {
12691270
self.live_kit.as_ref().and_then(|lk| match lk.screen_track {
1270-
LocalTrack::Published { ref _stream, .. } => {
1271-
_stream.metadata().ok().map(|meta| meta.id)
1272-
}
1271+
LocalTrack::Published { ref _stream, .. } => Some(_stream.screen_id),
12731272
_ => None,
12741273
})
12751274
}
@@ -1398,7 +1397,7 @@ impl Room {
13981397

13991398
pub fn share_screen(
14001399
&mut self,
1401-
source: Rc<dyn ScreenCaptureSource>,
1400+
source: CaptureSource,
14021401
cx: &mut Context<Self>,
14031402
) -> Task<Result<()>> {
14041403
if self.status.is_offline() {
@@ -1418,7 +1417,7 @@ impl Room {
14181417
};
14191418

14201419
cx.spawn(async move |this, cx| {
1421-
let publication = participant.publish_screenshare_track(&*source, cx).await;
1420+
let publication = participant.publish_screenshare_track(source, cx).await;
14221421

14231422
this.update(cx, |this, cx| {
14241423
let live_kit = this
@@ -1445,7 +1444,7 @@ impl Room {
14451444
} else {
14461445
live_kit.screen_track = LocalTrack::Published {
14471446
track_publication: publication,
1448-
_stream: stream,
1447+
_stream: Box::new(stream),
14491448
};
14501449
cx.notify();
14511450
}
@@ -1644,7 +1643,7 @@ fn spawn_room_connection(
16441643

16451644
struct LiveKitRoom {
16461645
room: Rc<livekit::Room>,
1647-
screen_track: LocalTrack<dyn ScreenCaptureStream>,
1646+
screen_track: LocalTrack<ScreenCaptureStreamHandle>,
16481647
microphone_track: LocalTrack<AudioStream>,
16491648
/// Tracks whether we're currently in a muted state due to auto-mute from deafening or manual mute performed by user.
16501649
muted_by_user: bool,

crates/collab_ui/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ futures.workspace = true
4141
fuzzy.workspace = true
4242
gpui.workspace = true
4343
log.workspace = true
44+
livekit_client.workspace = true
4445
menu.workspace = true
4546
notifications.workspace = true
4647
picker.workspace = true

crates/collab_ui/src/collab_panel.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use gpui::{
1919
Render, SharedString, Styled, Subscription, Task, TextStyle, WeakEntity, Window, actions,
2020
anchored, canvas, deferred, div, fill, list, point, prelude::*, px,
2121
};
22+
use livekit_client::screen_capture_sources;
2223
use menu::{Cancel, Confirm, SecondaryConfirm, SelectNext, SelectPrevious};
2324
use project::{Fs, Project};
2425
use rpc::{
@@ -152,10 +153,8 @@ pub fn init(cx: &mut App) {
152153
if room.is_sharing_screen() {
153154
room.unshare_screen(true, cx).ok();
154155
} else {
155-
let sources = cx.screen_capture_sources();
156-
156+
let sources = screen_capture_sources();
157157
cx.spawn(async move |room, cx| {
158-
let sources = sources.await??;
159158
let first = sources.into_iter().next();
160159
if let Some(first) = first {
161160
room.update(cx, |room, cx| room.share_screen(first, cx))?

crates/gpui/Cargo.toml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,6 @@ x11 = [
7373
"x11-clipboard",
7474
"filedescriptor",
7575
"open",
76-
"scap?/x11",
77-
]
78-
screen-capture = [
79-
"scap",
8076
]
8177
windows-manifest = []
8278

@@ -164,9 +160,6 @@ metal.workspace = true
164160
[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))'.dependencies]
165161
pathfinder_geometry = "0.5"
166162

167-
[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "windows"))'.dependencies]
168-
scap = { workspace = true, optional = true }
169-
170163
[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
171164
# Always used
172165
flume = "0.11"

crates/gpui/src/app.rs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ use crate::{
4040
Keymap, Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform,
4141
PlatformDisplay, PlatformKeyboardLayout, PlatformKeyboardMapper, Point, PromptBuilder,
4242
PromptButton, PromptHandle, PromptLevel, Render, RenderImage, RenderablePromptHandle,
43-
Reservation, ScreenCaptureSource, SharedString, SubscriberSet, Subscription, SvgRenderer, Task,
44-
TextSystem, Window, WindowAppearance, WindowHandle, WindowId, WindowInvalidator,
43+
Reservation, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, Window,
44+
WindowAppearance, WindowHandle, WindowId, WindowInvalidator,
4545
colors::{Colors, GlobalColors},
4646
current_platform, hash, init_app_menus,
4747
};
@@ -1026,18 +1026,6 @@ impl App {
10261026
self.platform.primary_display()
10271027
}
10281028

1029-
/// Returns whether `screen_capture_sources` may work.
1030-
pub fn is_screen_capture_supported(&self) -> bool {
1031-
self.platform.is_screen_capture_supported()
1032-
}
1033-
1034-
/// Returns a list of available screen capture sources.
1035-
pub fn screen_capture_sources(
1036-
&self,
1037-
) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
1038-
self.platform.screen_capture_sources()
1039-
}
1040-
10411029
/// Returns the display with the given ID, if one exists.
10421030
pub fn find_display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
10431031
self.displays()

crates/gpui/src/app/test_context.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ use crate::{
33
BackgroundExecutor, BorrowAppContext, Bounds, Capslock, ClipboardItem, DrawPhase, Drawable,
44
Element, Empty, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Modifiers,
55
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
6-
Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform,
7-
TestScreenCaptureSource, TestWindow, TextSystem, VisualContext, Window, WindowBounds,
8-
WindowHandle, WindowOptions,
6+
Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow,
7+
TextSystem, VisualContext, Window, WindowBounds, WindowHandle, WindowOptions,
98
};
109
use anyhow::{anyhow, bail};
1110
use futures::{Stream, StreamExt, channel::oneshot};
@@ -340,12 +339,6 @@ impl TestAppContext {
340339
rx
341340
}
342341

343-
/// Causes the given sources to be returned if the application queries for screen
344-
/// capture sources.
345-
pub fn set_screen_capture_sources(&self, sources: Vec<TestScreenCaptureSource>) {
346-
self.test_platform.set_screen_capture_sources(sources);
347-
}
348-
349342
/// Returns all windows open in the test.
350343
pub fn windows(&self) -> Vec<AnyWindowHandle> {
351344
self.app.borrow().windows()

0 commit comments

Comments
 (0)