Skip to content

Commit 0e67b6b

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 84f24e4 commit 0e67b6b

File tree

28 files changed

+1097
-1839
lines changed

28 files changed

+1097
-1839
lines changed

Cargo.lock

Lines changed: 854 additions & 712 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 & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ async-recursion = "1.0.0"
460460
async-tar = "0.5.1"
461461
async-task = "4.7"
462462
async-trait = "0.1"
463-
async-tungstenite = "0.31.0"
463+
async-tungstenite = "0.32.0"
464464
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
465465
aws-config = { version = "1.6.1", features = ["behavior-version-latest"] }
466466
aws-credential-types = { version = "1.2.2", features = [
@@ -539,6 +539,10 @@ jupyter-websocket-client = "0.15.0"
539539
libc = "0.2"
540540
libsqlite3-sys = { version = "0.30.1", features = ["bundled"] }
541541
linkify = "0.10.0"
542+
libwebrtc = { branch = "zed", git = "https://github.com/Be-ing/rust-sdks" }
543+
livekit = { branch = "zed", git = "https://github.com/Be-ing/rust-sdks", features = [
544+
"__rustls-tls"
545+
] }
542546
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
543547
lsp-types = { git = "https://github.com/zed-industries/lsp-types", rev = "b71ab4eeb27d9758be8092020a46fe33fbca4e33" }
544548
mach2 = "0.5"
@@ -670,7 +674,7 @@ time = { version = "0.3", features = [
670674
] }
671675
tiny_http = "0.8"
672676
tokio = { version = "1" }
673-
tokio-tungstenite = { version = "0.26", features = ["__rustls-tls"] }
677+
tokio-tungstenite = { version = "0.28", features = ["__rustls-tls"] }
674678
toml = "0.8"
675679
toml_edit = { version = "0.22", default-features = false, features = ["display", "parse", "serde"] }
676680
tower-http = "0.4.4"

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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ futures.workspace = true
3232
feature_flags.workspace = true
3333
gpui = { workspace = true, features = ["screen-capture"] }
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/src/app.rs

Lines changed: 2 additions & 9 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
};
@@ -1031,13 +1031,6 @@ impl App {
10311031
self.platform.is_screen_capture_supported()
10321032
}
10331033

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-
10411034
/// Returns the display with the given ID, if one exists.
10421035
pub fn find_display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
10431036
self.displays()

crates/gpui/src/platform.rs

Lines changed: 0 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,6 @@ mod test;
2323
#[cfg(target_os = "windows")]
2424
mod windows;
2525

26-
#[cfg(all(
27-
feature = "screen-capture",
28-
any(
29-
target_os = "windows",
30-
all(
31-
any(target_os = "linux", target_os = "freebsd"),
32-
any(feature = "wayland", feature = "x11"),
33-
)
34-
)
35-
))]
36-
pub(crate) mod scap_screen_capture;
37-
3826
use crate::{
3927
Action, AnyWindowHandle, App, AsyncWindowContext, BackgroundExecutor, Bounds,
4028
DEFAULT_WINDOW_SIZE, DevicePixels, DispatchEventResult, Font, FontId, FontMetrics, FontRun,
@@ -189,21 +177,6 @@ pub(crate) trait Platform: 'static {
189177
fn is_screen_capture_supported(&self) -> bool {
190178
false
191179
}
192-
#[cfg(feature = "screen-capture")]
193-
fn screen_capture_sources(&self)
194-
-> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>>;
195-
#[cfg(not(feature = "screen-capture"))]
196-
fn screen_capture_sources(
197-
&self,
198-
) -> oneshot::Receiver<anyhow::Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
199-
let (sources_tx, sources_rx) = oneshot::channel();
200-
sources_tx
201-
.send(Err(anyhow::anyhow!(
202-
"gpui was compiled without the screen-capture feature"
203-
)))
204-
.ok();
205-
sources_rx
206-
}
207180

208181
fn open_window(
209182
&self,
@@ -302,42 +275,6 @@ pub trait PlatformDisplay: Send + Sync + Debug {
302275
}
303276
}
304277

305-
/// Metadata for a given [ScreenCaptureSource]
306-
#[derive(Clone)]
307-
pub struct SourceMetadata {
308-
/// Opaque identifier of this screen.
309-
pub id: u64,
310-
/// Human-readable label for this source.
311-
pub label: Option<SharedString>,
312-
/// Whether this source is the main display.
313-
pub is_main: Option<bool>,
314-
/// Video resolution of this source.
315-
pub resolution: Size<DevicePixels>,
316-
}
317-
318-
/// A source of on-screen video content that can be captured.
319-
pub trait ScreenCaptureSource {
320-
/// Returns metadata for this source.
321-
fn metadata(&self) -> Result<SourceMetadata>;
322-
323-
/// Start capture video from this source, invoking the given callback
324-
/// with each frame.
325-
fn stream(
326-
&self,
327-
foreground_executor: &ForegroundExecutor,
328-
frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
329-
) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>>;
330-
}
331-
332-
/// A video stream captured from a screen.
333-
pub trait ScreenCaptureStream {
334-
/// Returns metadata for this source.
335-
fn metadata(&self) -> Result<SourceMetadata>;
336-
}
337-
338-
/// A frame of video captured from a screen.
339-
pub struct ScreenCaptureFrame(pub PlatformScreenCaptureFrame);
340-
341278
/// An opaque identifier for a hardware display
342279
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
343280
pub struct DisplayId(pub(crate) u32);

crates/gpui/src/platform/linux.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,5 @@ pub(crate) use wayland::*;
2323
#[cfg(feature = "x11")]
2424
pub(crate) use x11::*;
2525

26-
#[cfg(all(feature = "screen-capture", any(feature = "wayland", feature = "x11")))]
27-
pub(crate) type PlatformScreenCaptureFrame = scap::frame::Frame;
28-
#[cfg(not(all(feature = "screen-capture", any(feature = "wayland", feature = "x11"))))]
29-
pub(crate) type PlatformScreenCaptureFrame = ();
30-
3126
#[cfg(feature = "wayland")]
3227
pub use wayland::layer_shell;

0 commit comments

Comments
 (0)