Compare commits
2 Commits
vim-syntax
...
fix-rust-l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f67815df05 | ||
|
|
f2ca21ae44 |
@@ -42,10 +42,10 @@ serde_derive.workspace = true
|
|||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(any())'.dependencies]
|
||||||
livekit_client_macos = { workspace = true }
|
livekit_client_macos = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "macos"))'.dependencies]
|
[target.'cfg(all())'.dependencies]
|
||||||
livekit_client = { workspace = true }
|
livekit_client = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
pub mod call_settings;
|
pub mod call_settings;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(any())]
|
||||||
mod macos;
|
mod macos;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(any())]
|
||||||
pub use macos::*;
|
pub use macos::*;
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(all())]
|
||||||
mod cross_platform;
|
mod cross_platform;
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(all())]
|
||||||
pub use cross_platform::*;
|
pub use cross_platform::*;
|
||||||
|
|||||||
@@ -1441,7 +1441,7 @@ impl Room {
|
|||||||
let sources = sources.await??;
|
let sources = sources.await??;
|
||||||
let source = sources.first().ok_or_else(|| anyhow!("no display found"))?;
|
let source = sources.first().ok_or_else(|| anyhow!("no display found"))?;
|
||||||
|
|
||||||
let (track, stream) = capture_local_video_track(&**source).await?;
|
let (track, stream) = capture_local_video_track(&**source, None).await?;
|
||||||
|
|
||||||
let publication = participant
|
let publication = participant
|
||||||
.publish_track(
|
.publish_track(
|
||||||
|
|||||||
@@ -2078,17 +2078,7 @@ async fn test_mute_deafen(
|
|||||||
audio_tracks_playing: participant
|
audio_tracks_playing: participant
|
||||||
.audio_tracks
|
.audio_tracks
|
||||||
.values()
|
.values()
|
||||||
.map({
|
.map(|(track, _)| track.rtc_track().enabled())
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
{
|
|
||||||
|track| track.is_playing()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
{
|
|
||||||
|(track, _)| track.rtc_track().enabled()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect(),
|
.collect(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ pub trait PlatformDisplay: Send + Sync + Debug {
|
|||||||
/// A source of on-screen video content that can be captured.
|
/// A source of on-screen video content that can be captured.
|
||||||
pub trait ScreenCaptureSource {
|
pub trait ScreenCaptureSource {
|
||||||
/// Returns the video resolution of this source.
|
/// Returns the video resolution of this source.
|
||||||
fn resolution(&self) -> Result<Size<Pixels>>;
|
fn resolution(&self) -> Size<DevicePixels>;
|
||||||
|
|
||||||
/// Start capture video from this source, invoking the given callback
|
/// Start capture video from this source, invoking the given callback
|
||||||
/// with each frame.
|
/// with each frame.
|
||||||
@@ -253,6 +253,7 @@ pub trait ScreenCaptureSource {
|
|||||||
pub trait ScreenCaptureStream {}
|
pub trait ScreenCaptureStream {}
|
||||||
|
|
||||||
/// A frame of video captured from a screen.
|
/// A frame of video captured from a screen.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct ScreenCaptureFrame(pub PlatformScreenCaptureFrame);
|
pub struct ScreenCaptureFrame(pub PlatformScreenCaptureFrame);
|
||||||
|
|
||||||
/// An opaque identifier for a hardware display
|
/// An opaque identifier for a hardware display
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
platform::{ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream},
|
platform::{ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream},
|
||||||
px, size, Pixels, Size,
|
size, DevicePixels, Size,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use block::ConcreteBlock;
|
use block::ConcreteBlock;
|
||||||
@@ -9,6 +9,10 @@ use cocoa::{
|
|||||||
foundation::NSArray,
|
foundation::NSArray,
|
||||||
};
|
};
|
||||||
use core_foundation::base::TCFType;
|
use core_foundation::base::TCFType;
|
||||||
|
use core_graphics::display::{
|
||||||
|
CGDirectDisplayID, CGDisplayCopyDisplayMode, CGDisplayModeGetPixelHeight,
|
||||||
|
CGDisplayModeGetPixelWidth, CGDisplayModeRelease,
|
||||||
|
};
|
||||||
use ctor::ctor;
|
use ctor::ctor;
|
||||||
use futures::channel::oneshot;
|
use futures::channel::oneshot;
|
||||||
use media::core_media::{CMSampleBuffer, CMSampleBufferRef};
|
use media::core_media::{CMSampleBuffer, CMSampleBufferRef};
|
||||||
@@ -25,6 +29,7 @@ use std::{cell::RefCell, ffi::c_void, mem, ptr, rc::Rc};
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MacScreenCaptureSource {
|
pub struct MacScreenCaptureSource {
|
||||||
sc_display: id,
|
sc_display: id,
|
||||||
|
size: Size<DevicePixels>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MacScreenCaptureStream {
|
pub struct MacScreenCaptureStream {
|
||||||
@@ -43,12 +48,8 @@ const FRAME_CALLBACK_IVAR: &str = "frame_callback";
|
|||||||
const SCStreamOutputTypeScreen: NSInteger = 0;
|
const SCStreamOutputTypeScreen: NSInteger = 0;
|
||||||
|
|
||||||
impl ScreenCaptureSource for MacScreenCaptureSource {
|
impl ScreenCaptureSource for MacScreenCaptureSource {
|
||||||
fn resolution(&self) -> Result<Size<Pixels>> {
|
fn resolution(&self) -> Size<DevicePixels> {
|
||||||
unsafe {
|
self.size
|
||||||
let width: i64 = msg_send![self.sc_display, width];
|
|
||||||
let height: i64 = msg_send![self.sc_display, height];
|
|
||||||
Ok(size(px(width as f32), px(height as f32)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stream(
|
fn stream(
|
||||||
@@ -61,13 +62,21 @@ impl ScreenCaptureSource for MacScreenCaptureSource {
|
|||||||
let configuration: id = msg_send![class!(SCStreamConfiguration), alloc];
|
let configuration: id = msg_send![class!(SCStreamConfiguration), alloc];
|
||||||
let delegate: id = msg_send![DELEGATE_CLASS, alloc];
|
let delegate: id = msg_send![DELEGATE_CLASS, alloc];
|
||||||
let output: id = msg_send![OUTPUT_CLASS, alloc];
|
let output: id = msg_send![OUTPUT_CLASS, alloc];
|
||||||
|
|
||||||
let excluded_windows = NSArray::array(nil);
|
let excluded_windows = NSArray::array(nil);
|
||||||
let filter: id = msg_send![filter, initWithDisplay:self.sc_display excludingWindows:excluded_windows];
|
let filter: id = msg_send![filter, initWithDisplay:self.sc_display excludingWindows:excluded_windows];
|
||||||
let configuration: id = msg_send![configuration, init];
|
let configuration: id = msg_send![configuration, init];
|
||||||
let delegate: id = msg_send![delegate, init];
|
let delegate: id = msg_send![delegate, init];
|
||||||
let output: id = msg_send![output, init];
|
let output: id = msg_send![output, init];
|
||||||
|
|
||||||
|
// ASCII for '420f': https://developer.apple.com/documentation/screencapturekit/scstreamconfiguration/pixelformat?language=objc
|
||||||
|
let format = u32::from_be_bytes([52u8, 50u8, 48u8, 102u8]);
|
||||||
|
|
||||||
|
let _: () = msg_send![configuration, setShowsCursor:YES];
|
||||||
|
let _: () = msg_send![configuration, setWidth:self.size.width];
|
||||||
|
let _: () = msg_send![configuration, setHeight:self.size.height];
|
||||||
|
let _: () = msg_send![configuration, setPixelFormat:format];
|
||||||
|
let _: () = msg_send![configuration, setQueueDepth:5i32];
|
||||||
|
|
||||||
output.as_mut().unwrap().set_ivar(
|
output.as_mut().unwrap().set_ivar(
|
||||||
FRAME_CALLBACK_IVAR,
|
FRAME_CALLBACK_IVAR,
|
||||||
Box::into_raw(Box::new(frame_callback)) as *mut c_void,
|
Box::into_raw(Box::new(frame_callback)) as *mut c_void,
|
||||||
@@ -94,6 +103,7 @@ impl ScreenCaptureSource for MacScreenCaptureSource {
|
|||||||
sc_stream: stream,
|
sc_stream: stream,
|
||||||
sc_stream_output: output,
|
sc_stream_output: output,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Box::new(stream) as Box<dyn ScreenCaptureStream>)
|
Ok(Box::new(stream) as Box<dyn ScreenCaptureStream>)
|
||||||
} else {
|
} else {
|
||||||
let message: id = msg_send![error, localizedDescription];
|
let message: id = msg_send![error, localizedDescription];
|
||||||
@@ -159,8 +169,16 @@ pub(crate) fn get_sources() -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptur
|
|||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
for i in 0..displays.count() {
|
for i in 0..displays.count() {
|
||||||
let display = displays.objectAtIndex(i);
|
let display = displays.objectAtIndex(i);
|
||||||
|
let display: id = msg_send![display, retain];
|
||||||
|
let display_id: CGDirectDisplayID = msg_send![display, displayID];
|
||||||
|
let display_mode_ref = CGDisplayCopyDisplayMode(display_id);
|
||||||
|
let width = CGDisplayModeGetPixelWidth(display_mode_ref);
|
||||||
|
let height = CGDisplayModeGetPixelHeight(display_mode_ref);
|
||||||
|
CGDisplayModeRelease(display_mode_ref);
|
||||||
|
|
||||||
let source = MacScreenCaptureSource {
|
let source = MacScreenCaptureSource {
|
||||||
sc_display: msg_send![display, retain],
|
sc_display: display,
|
||||||
|
size: size(DevicePixels(width as i32), DevicePixels(height as i32)),
|
||||||
};
|
};
|
||||||
result.push(Box::new(source) as Box<dyn ScreenCaptureSource>);
|
result.push(Box::new(source) as Box<dyn ScreenCaptureSource>);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
px, size, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, ForegroundExecutor,
|
size, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DevicePixels,
|
||||||
Keymap, Platform, PlatformDisplay, PlatformTextSystem, ScreenCaptureFrame, ScreenCaptureSource,
|
ForegroundExecutor, Keymap, Platform, PlatformDisplay, PlatformTextSystem, ScreenCaptureFrame,
|
||||||
ScreenCaptureStream, Task, TestDisplay, TestWindow, WindowAppearance, WindowParams,
|
ScreenCaptureSource, ScreenCaptureStream, Task, TestDisplay, TestWindow, WindowAppearance,
|
||||||
|
WindowParams,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::VecDeque;
|
use collections::VecDeque;
|
||||||
@@ -46,8 +47,8 @@ pub struct TestScreenCaptureSource {}
|
|||||||
pub struct TestScreenCaptureStream {}
|
pub struct TestScreenCaptureStream {}
|
||||||
|
|
||||||
impl ScreenCaptureSource for TestScreenCaptureSource {
|
impl ScreenCaptureSource for TestScreenCaptureSource {
|
||||||
fn resolution(&self) -> Result<crate::Size<crate::Pixels>> {
|
fn resolution(&self) -> crate::Size<crate::DevicePixels> {
|
||||||
Ok(size(px(1.), px(1.)))
|
size(DevicePixels(1), DevicePixels(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stream(
|
fn stream(
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ livekit.workspace = true
|
|||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
core-foundation.workspace = true
|
core-foundation.workspace = true
|
||||||
coreaudio-rs = "0.12.1"
|
coreaudio-rs = "0.12.1"
|
||||||
|
media.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
collections = { workspace = true, features = ["test-support"] }
|
collections = { workspace = true, features = ["test-support"] }
|
||||||
|
|||||||
@@ -3,11 +3,12 @@
|
|||||||
// it causes compile errors.
|
// it causes compile errors.
|
||||||
#![cfg_attr(target_os = "macos", allow(unused_imports))]
|
#![cfg_attr(target_os = "macos", allow(unused_imports))]
|
||||||
|
|
||||||
|
use futures::StreamExt;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, bounds, div, point,
|
actions, bounds, div, point,
|
||||||
prelude::{FluentBuilder as _, IntoElement},
|
prelude::{FluentBuilder as _, IntoElement},
|
||||||
px, rgb, size, AsyncAppContext, Bounds, InteractiveElement, KeyBinding, Menu, MenuItem,
|
px, rgb, size, AsyncAppContext, Bounds, InteractiveElement, KeyBinding, Menu, MenuItem,
|
||||||
ParentElement, Pixels, Render, ScreenCaptureStream, SharedString,
|
ParentElement, Pixels, Render, ScreenCaptureFrame, ScreenCaptureStream, SharedString,
|
||||||
StatefulInteractiveElement as _, Styled, Task, View, ViewContext, VisualContext, WindowBounds,
|
StatefulInteractiveElement as _, Styled, Task, View, ViewContext, VisualContext, WindowBounds,
|
||||||
WindowHandle, WindowOptions,
|
WindowHandle, WindowOptions,
|
||||||
};
|
};
|
||||||
@@ -22,6 +23,7 @@ use livekit_client::{
|
|||||||
track::{LocalTrack, RemoteTrack, RemoteVideoTrack, TrackSource},
|
track::{LocalTrack, RemoteTrack, RemoteVideoTrack, TrackSource},
|
||||||
AudioStream, RemoteVideoTrackView, Room, RoomEvent, RoomOptions,
|
AudioStream, RemoteVideoTrackView, Room, RoomEvent, RoomOptions,
|
||||||
};
|
};
|
||||||
|
use media::core_video::CVImageBuffer;
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
use postage::stream::Stream;
|
use postage::stream::Stream;
|
||||||
|
|
||||||
@@ -108,6 +110,7 @@ struct LivekitWindow {
|
|||||||
screen_share_track: Option<LocalTrackPublication>,
|
screen_share_track: Option<LocalTrackPublication>,
|
||||||
microphone_stream: Option<AudioStream>,
|
microphone_stream: Option<AudioStream>,
|
||||||
screen_share_stream: Option<Box<dyn ScreenCaptureStream>>,
|
screen_share_stream: Option<Box<dyn ScreenCaptureStream>>,
|
||||||
|
latest_self_frame: Option<ScreenCaptureFrame>,
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
remote_participants: Vec<(ParticipantIdentity, ParticipantState)>,
|
remote_participants: Vec<(ParticipantIdentity, ParticipantState)>,
|
||||||
_events_task: Task<()>,
|
_events_task: Task<()>,
|
||||||
@@ -156,6 +159,7 @@ impl LivekitWindow {
|
|||||||
microphone_stream: None,
|
microphone_stream: None,
|
||||||
screen_share_track: None,
|
screen_share_track: None,
|
||||||
screen_share_stream: None,
|
screen_share_stream: None,
|
||||||
|
latest_self_frame: None,
|
||||||
remote_participants: Vec::new(),
|
remote_participants: Vec::new(),
|
||||||
_events_task,
|
_events_task,
|
||||||
}
|
}
|
||||||
@@ -312,7 +316,25 @@ impl LivekitWindow {
|
|||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
let sources = sources.await.unwrap()?;
|
let sources = sources.await.unwrap()?;
|
||||||
let source = sources.into_iter().next().unwrap();
|
let source = sources.into_iter().next().unwrap();
|
||||||
let (track, stream) = capture_local_video_track(&*source).await?;
|
|
||||||
|
let (self_stream_tx, mut self_stream_rx) = futures::channel::mpsc::unbounded();
|
||||||
|
let (track, stream) =
|
||||||
|
capture_local_video_track(&*source, Some(self_stream_tx)).await?;
|
||||||
|
|
||||||
|
cx.spawn({
|
||||||
|
let this = this.clone();
|
||||||
|
|mut cx| async move {
|
||||||
|
while let Some(frame) = self_stream_rx.next().await {
|
||||||
|
this.update(&mut cx, |this, cx| {
|
||||||
|
this.latest_self_frame = Some(frame);
|
||||||
|
cx.notify();
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
let publication = participant
|
let publication = participant
|
||||||
.publish_track(
|
.publish_track(
|
||||||
LocalTrack::Video(track),
|
LocalTrack::Video(track),
|
||||||
@@ -394,6 +416,11 @@ impl Render for LivekitWindow {
|
|||||||
.on_click(cx.listener(|this, _, cx| this.toggle_screen_share(cx))),
|
.on_click(cx.listener(|this, _, cx| this.toggle_screen_share(cx))),
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
|
.children(
|
||||||
|
self.latest_self_frame
|
||||||
|
.as_ref()
|
||||||
|
.map(|frame| gpui::surface(frame.0.clone()).size_full()),
|
||||||
|
)
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.id("remote-participants")
|
.id("remote-participants")
|
||||||
@@ -403,7 +430,7 @@ impl Render for LivekitWindow {
|
|||||||
.flex_grow()
|
.flex_grow()
|
||||||
.children(self.remote_participants.iter().map(|(identity, state)| {
|
.children(self.remote_participants.iter().map(|(identity, state)| {
|
||||||
div()
|
div()
|
||||||
.h(px(300.0))
|
.size_full()
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.m_2()
|
.m_2()
|
||||||
|
|||||||
@@ -142,8 +142,9 @@ pub fn init(
|
|||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
pub async fn capture_local_video_track(
|
pub async fn capture_local_video_track(
|
||||||
capture_source: &dyn ScreenCaptureSource,
|
capture_source: &dyn ScreenCaptureSource,
|
||||||
|
show_capture: Option<futures::channel::mpsc::UnboundedSender<ScreenCaptureFrame>>,
|
||||||
) -> Result<(track::LocalVideoTrack, Box<dyn ScreenCaptureStream>)> {
|
) -> Result<(track::LocalVideoTrack, Box<dyn ScreenCaptureStream>)> {
|
||||||
let resolution = capture_source.resolution()?;
|
let resolution = capture_source.resolution();
|
||||||
let track_source = NativeVideoSource::new(VideoResolution {
|
let track_source = NativeVideoSource::new(VideoResolution {
|
||||||
width: resolution.width.0 as u32,
|
width: resolution.width.0 as u32,
|
||||||
height: resolution.height.0 as u32,
|
height: resolution.height.0 as u32,
|
||||||
@@ -153,6 +154,10 @@ pub async fn capture_local_video_track(
|
|||||||
.stream({
|
.stream({
|
||||||
let track_source = track_source.clone();
|
let track_source = track_source.clone();
|
||||||
Box::new(move |frame| {
|
Box::new(move |frame| {
|
||||||
|
if let Some(show_capture) = show_capture.as_ref() {
|
||||||
|
show_capture.unbounded_send(frame.clone()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(buffer) = video_frame_buffer_to_webrtc(frame) {
|
if let Some(buffer) = video_frame_buffer_to_webrtc(frame) {
|
||||||
track_source.capture_frame(&VideoFrame {
|
track_source.capture_frame(&VideoFrame {
|
||||||
rotation: VideoRotation::VideoRotation0,
|
rotation: VideoRotation::VideoRotation0,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(any())]
|
||||||
mod macos;
|
mod macos;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(any())]
|
||||||
pub use macos::*;
|
pub use macos::*;
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(all())]
|
||||||
mod cross_platform;
|
mod cross_platform;
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(all())]
|
||||||
pub use cross_platform::*;
|
pub use cross_platform::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user