Windows screen sharing (#34223)

Release Notes:

- N/A

---------

Co-authored-by: localcc <work@localcc.cc>
Co-authored-by: Peter Tripp <petertripp@gmail.com>
This commit is contained in:
Max Brunsfeld 2025-07-10 14:02:00 -07:00 committed by GitHub
parent f82fdaa0a4
commit 7588280915
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 57 additions and 51 deletions

View file

@ -150,6 +150,9 @@ metal.workspace = true
[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))'.dependencies]
pathfinder_geometry = "0.5"
[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "windows"))'.dependencies]
scap = { workspace = true, optional = true }
[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
# Always used
flume = "0.11"
@ -168,7 +171,6 @@ cosmic-text = { version = "0.14.0", optional = true }
font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "5474cfad4b719a72ec8ed2cb7327b2b01fd10568", features = [
"source-fontconfig-dlopen",
], optional = true }
scap = { workspace = true, optional = true }
calloop = { version = "0.13.0" }
filedescriptor = { version = "0.8.2", optional = true }

View file

@ -26,8 +26,13 @@ mod windows;
#[cfg(all(
feature = "screen-capture",
any(target_os = "linux", target_os = "freebsd"),
any(feature = "wayland", feature = "x11"),
any(
target_os = "windows",
all(
any(target_os = "linux", target_os = "freebsd"),
any(feature = "wayland", feature = "x11"),
)
)
))]
pub(crate) mod scap_screen_capture;

View file

@ -26,4 +26,7 @@ pub(crate) use wrapper::*;
pub(crate) use windows::Win32::Foundation::HWND;
#[cfg(feature = "screen-capture")]
pub(crate) type PlatformScreenCaptureFrame = scap::frame::Frame;
#[cfg(not(feature = "screen-capture"))]
pub(crate) type PlatformScreenCaptureFrame = ();

View file

@ -434,16 +434,14 @@ impl Platform for WindowsPlatform {
#[cfg(feature = "screen-capture")]
fn is_screen_capture_supported(&self) -> bool {
false
true
}
#[cfg(feature = "screen-capture")]
fn screen_capture_sources(
&self,
) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>> {
let (mut tx, rx) = oneshot::channel();
tx.send(Err(anyhow!("screen capture not implemented"))).ok();
rx
crate::platform::scap_screen_capture::scap_screen_sources(&self.foreground_executor)
}
fn active_window(&self) -> Option<AnyWindowHandle> {

View file

@ -25,7 +25,7 @@ async-trait.workspace = true
collections.workspace = true
cpal.workspace = true
futures.workspace = true
gpui = { workspace = true, features = ["screen-capture", "x11", "wayland"] }
gpui = { workspace = true, features = ["screen-capture", "x11", "wayland", "windows-manifest"] }
gpui_tokio.workspace = true
http_client_tls.workspace = true
image.workspace = true
@ -45,7 +45,7 @@ livekit = { rev = "d2eade7a6b15d6dbdb38ba12a1ff7bf07fcebba4", git = "https://git
"__rustls-tls"
] }
[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "windows"))'.dependencies]
scap.workspace = true
[target.'cfg(target_os = "macos")'.dependencies]

View file

@ -3,36 +3,16 @@ use collections::HashMap;
mod remote_video_track_view;
pub use remote_video_track_view::{RemoteVideoTrackView, RemoteVideoTrackViewEvent};
#[cfg(not(any(
test,
feature = "test-support",
any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd")
)))]
#[cfg(not(any(test, feature = "test-support", target_os = "freebsd")))]
mod livekit_client;
#[cfg(not(any(
test,
feature = "test-support",
any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd")
)))]
#[cfg(not(any(test, feature = "test-support", target_os = "freebsd")))]
pub use livekit_client::*;
#[cfg(any(
test,
feature = "test-support",
any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd")
))]
#[cfg(any(test, feature = "test-support", target_os = "freebsd"))]
mod mock_client;
#[cfg(any(
test,
feature = "test-support",
any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd")
))]
#[cfg(any(test, feature = "test-support", target_os = "freebsd"))]
pub mod test;
#[cfg(any(
test,
feature = "test-support",
any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd")
))]
#[cfg(any(test, feature = "test-support", target_os = "freebsd"))]
pub use mock_client::*;
#[derive(Debug, Clone)]

View file

@ -585,10 +585,10 @@ fn video_frame_buffer_from_webrtc(buffer: Box<dyn VideoBuffer>) -> Option<Remote
if start_ptr.is_null() {
return None;
}
let bgra_frame_slice = std::slice::from_raw_parts_mut(start_ptr, byte_len);
let argb_frame_slice = std::slice::from_raw_parts_mut(start_ptr, byte_len);
buffer.to_argb(
VideoFormatType::ARGB, // For some reason, this displays correctly while RGBA (the correct format) does not
bgra_frame_slice,
VideoFormatType::ARGB,
argb_frame_slice,
stride,
width as i32,
height as i32,
@ -596,12 +596,13 @@ fn video_frame_buffer_from_webrtc(buffer: Box<dyn VideoBuffer>) -> Option<Remote
Vec::from_raw_parts(start_ptr, byte_len, byte_len)
};
// TODO: Unclear why providing argb_image to RgbaImage works properly.
let image = RgbaImage::from_raw(width, height, argb_image)
.with_context(|| "Bug: not enough bytes allocated for image.")
.log_err()?;
Some(Arc::new(RenderImage::new(SmallVec::from_elem(
Frame::new(
RgbaImage::from_raw(width, height, argb_image)
.with_context(|| "Bug: not enough bytes allocated for image.")
.log_err()?,
),
Frame::new(image),
1,
))))
}
@ -617,9 +618,9 @@ fn video_frame_buffer_to_webrtc(frame: ScreenCaptureFrame) -> Option<impl AsRef<
}
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
#[cfg(not(target_os = "macos"))]
fn video_frame_buffer_to_webrtc(frame: ScreenCaptureFrame) -> Option<impl AsRef<dyn VideoBuffer>> {
use libwebrtc::native::yuv_helper::argb_to_nv12;
use libwebrtc::native::yuv_helper::{abgr_to_nv12, argb_to_nv12};
use livekit::webrtc::prelude::NV12Buffer;
match frame.0 {
scap::frame::Frame::BGRx(frame) => {
@ -638,6 +639,22 @@ fn video_frame_buffer_to_webrtc(frame: ScreenCaptureFrame) -> Option<impl AsRef<
);
Some(buffer)
}
scap::frame::Frame::RGBx(frame) => {
let mut buffer = NV12Buffer::new(frame.width as u32, frame.height as u32);
let (stride_y, stride_uv) = buffer.strides();
let (data_y, data_uv) = buffer.data_mut();
abgr_to_nv12(
&frame.data,
frame.width as u32 * 4,
data_y,
stride_y,
data_uv,
stride_uv,
frame.width,
frame.height,
);
Some(buffer)
}
scap::frame::Frame::YUVFrame(yuvframe) => {
let mut buffer = NV12Buffer::with_strides(
yuvframe.width as u32,
@ -659,11 +676,6 @@ fn video_frame_buffer_to_webrtc(frame: ScreenCaptureFrame) -> Option<impl AsRef<
}
}
#[cfg(target_os = "windows")]
fn video_frame_buffer_to_webrtc(_frame: ScreenCaptureFrame) -> Option<impl AsRef<dyn VideoBuffer>> {
None as Option<Box<dyn VideoBuffer>>
}
trait DeviceChangeListenerApi: Stream<Item = ()> + Sized {
fn new(input: bool) -> Result<Self>;
}