Skip to content

Commit b365307

Browse files
committed
move shareablecontent cache to desktop
1 parent ad5497e commit b365307

File tree

12 files changed

+126
-185
lines changed

12 files changed

+126
-185
lines changed

apps/desktop/src-tauri/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ impl ScreenCapturePrewarmer {
393393
}
394394

395395
let warm_start = std::time::Instant::now();
396-
let result = scap_targets::prewarm_shareable_content().await;
396+
let result = crate::platform::prewarm_shareable_content().await;
397397

398398
let mut state = self.state.lock().await;
399399
match result {

apps/desktop/src-tauri/src/platform/macos/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
// use objc::{class, msg_send, sel, sel_impl};
1212

1313
pub mod delegates;
14+
mod sc_shareable_content;
15+
16+
pub use sc_shareable_content::*;
1417

1518
pub fn set_window_level(window: tauri::Window, level: objc2_app_kit::NSWindowLevel) {
1619
let c_window = window.clone();

crates/scap-targets/src/platform/macos/cache.rs renamed to apps/desktop/src-tauri/src/platform/macos/sc_shareable_content.rs

Lines changed: 33 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1+
use cidre::{arc, ns, sc};
2+
use core_graphics::{display::CGDirectDisplayID, window::CGWindowID};
3+
use std::sync::Arc;
14
use std::{
25
collections::HashMap,
36
sync::{OnceLock, RwLock},
47
time::Instant,
58
};
6-
7-
use cidre::{arc, ns, sc};
8-
use core_graphics::{display::CGDirectDisplayID, window::CGWindowID};
9-
use std::sync::Arc;
109
use tokio::sync::{Mutex, Notify};
1110
use tracing::{debug, info, trace};
1211

@@ -30,7 +29,7 @@ fn state() -> &'static CacheState {
3029
STATE.get_or_init(CacheState::default)
3130
}
3231

33-
pub(super) async fn prewarm_shareable_content() -> Result<(), arc::R<ns::Error>> {
32+
pub async fn prewarm_shareable_content() -> Result<(), arc::R<ns::Error>> {
3433
if state().cache.read().unwrap().is_some() {
3534
trace!("ScreenCaptureKit shareable content already warmed");
3635
return Ok(());
@@ -61,6 +60,35 @@ pub(super) async fn prewarm_shareable_content() -> Result<(), arc::R<ns::Error>>
6160
.expect("ScreenCaptureKit warmup task missing result")
6261
}
6362

63+
pub async fn get_shareable_content()
64+
-> Result<Option<arc::R<sc::ShareableContent>>, arc::R<ns::Error>> {
65+
let lookup_start = Instant::now();
66+
67+
if let Some(content) = state()
68+
.cache
69+
.read()
70+
.unwrap()
71+
.as_ref()
72+
.map(|v| v.content.retained())
73+
{
74+
trace!(
75+
elapsed_ms = lookup_start.elapsed().as_micros() as f64 / 1000.0,
76+
"Resolved ScreenCaptureKit from warmed cache"
77+
);
78+
return Ok(Some(content));
79+
}
80+
81+
prewarm_shareable_content().await?;
82+
83+
let content = state().cache.read().unwrap();
84+
trace!(
85+
elapsed_ms = lookup_start.elapsed().as_micros() as f64 / 1000.0,
86+
cache_hit = content.is_some(),
87+
"Resolved ScreenCaptureKit after cache populate"
88+
);
89+
Ok(content.as_ref().map(|v| v.content.retained()))
90+
}
91+
6492
async fn run_warmup(task: WarmupTask) {
6593
let result = async {
6694
let warm_start = Instant::now();
@@ -97,80 +125,6 @@ async fn run_warmup(task: WarmupTask) {
97125
}
98126
}
99127

100-
pub(super) async fn get_display(
101-
id: CGDirectDisplayID,
102-
) -> Result<Option<arc::R<sc::Display>>, arc::R<ns::Error>> {
103-
let lookup_start = Instant::now();
104-
105-
if let Some(display) = state()
106-
.cache
107-
.read()
108-
.unwrap()
109-
.as_ref()
110-
.and_then(|cache| cache.display(id))
111-
{
112-
trace!(
113-
display_id = id,
114-
elapsed_ms = lookup_start.elapsed().as_micros() as f64 / 1000.0,
115-
"Resolved ScreenCaptureKit display from warmed cache"
116-
);
117-
return Ok(Some(display));
118-
}
119-
120-
prewarm_shareable_content().await?;
121-
122-
let result = state()
123-
.cache
124-
.read()
125-
.unwrap()
126-
.as_ref()
127-
.and_then(|cache| cache.display(id));
128-
trace!(
129-
display_id = id,
130-
elapsed_ms = lookup_start.elapsed().as_micros() as f64 / 1000.0,
131-
cache_hit = result.is_some(),
132-
"Resolved ScreenCaptureKit display after cache populate"
133-
);
134-
Ok(result)
135-
}
136-
137-
pub(super) async fn get_window(
138-
id: CGWindowID,
139-
) -> Result<Option<arc::R<sc::Window>>, arc::R<ns::Error>> {
140-
let lookup_start = Instant::now();
141-
142-
if let Some(window) = state()
143-
.cache
144-
.read()
145-
.unwrap()
146-
.as_ref()
147-
.and_then(|cache| cache.window(id))
148-
{
149-
trace!(
150-
window_id = id,
151-
elapsed_ms = lookup_start.elapsed().as_micros() as f64 / 1000.0,
152-
"Resolved ScreenCaptureKit window from warmed cache"
153-
);
154-
return Ok(Some(window));
155-
}
156-
157-
prewarm_shareable_content().await?;
158-
159-
let result = state()
160-
.cache
161-
.read()
162-
.unwrap()
163-
.as_ref()
164-
.and_then(|cache| cache.window(id));
165-
trace!(
166-
window_id = id,
167-
elapsed_ms = lookup_start.elapsed().as_micros() as f64 / 1000.0,
168-
cache_hit = result.is_some(),
169-
"Resolved ScreenCaptureKit window after cache populate"
170-
);
171-
Ok(result)
172-
}
173-
174128
#[derive(Debug)]
175129
struct ShareableContentCache {
176130
#[allow(dead_code)]

apps/desktop/src-tauri/src/recording.rs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,12 @@ pub async fn start_recording(
379379
Err(e) => return Err(e.to_string()),
380380
};
381381

382+
#[cfg(target_os = "macos")]
383+
let shareable_content = crate::platform::get_shareable_content()
384+
.await
385+
.map_err(|e| format!("GetShareableContent: {e}"))?
386+
.ok_or_else(|| format!("GetShareableContent/NotAvailable"))?;
387+
382388
let (actor, actor_done_rx) = match inputs.mode {
383389
RecordingMode::Studio => {
384390
let mut builder = studio_recording::Actor::builder(
@@ -400,10 +406,16 @@ pub async fn start_recording(
400406
builder = builder.with_mic_feed(mic_feed);
401407
}
402408

403-
let (handle, actor_done_rx) = builder.build().await.map_err(|e| {
404-
error!("Failed to spawn studio recording actor: {e}");
405-
e.to_string()
406-
})?;
409+
let (handle, actor_done_rx) = builder
410+
.build(
411+
#[cfg(target_os = "macos")]
412+
shareable_content,
413+
)
414+
.await
415+
.map_err(|e| {
416+
error!("Failed to spawn studio recording actor: {e}");
417+
e.to_string()
418+
})?;
407419

408420
(
409421
InProgressRecording::Studio {
@@ -430,10 +442,16 @@ pub async fn start_recording(
430442
builder = builder.with_mic_feed(mic_feed);
431443
}
432444

433-
let (handle, actor_done_rx) = builder.build().await.map_err(|e| {
434-
error!("Failed to spawn studio recording actor: {e}");
435-
e.to_string()
436-
})?;
445+
let (handle, actor_done_rx) = builder
446+
.build(
447+
#[cfg(target_os = "macos")]
448+
shareable_content,
449+
)
450+
.await
451+
.map_err(|e| {
452+
error!("Failed to spawn studio recording actor: {e}");
453+
e.to_string()
454+
})?;
437455

438456
(
439457
InProgressRecording::Instant {

crates/recording/src/capture_pipeline.rs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -784,24 +784,10 @@ pub async fn create_screen_capture(
784784
audio_tx: Option<Sender<(ffmpeg::frame::Audio, Timestamp)>>,
785785
start_time: SystemTime,
786786
#[cfg(windows)] d3d_device: ::windows::Win32::Graphics::Direct3D11::ID3D11Device,
787+
#[cfg(target_os = "macos")] shareable_content: cidre::arc::R<cidre::sc::ShareableContent>,
787788
) -> Result<ScreenCaptureReturn<ScreenCaptureMethod>, RecordingError> {
788789
let (video_tx, video_rx) = flume::bounded(16);
789790

790-
#[cfg(target_os = "macos")]
791-
{
792-
let warm_start = std::time::Instant::now();
793-
match scap_targets::prewarm_shareable_content().await {
794-
Ok(()) => tracing::trace!(
795-
elapsed_ms = warm_start.elapsed().as_micros() as f64 / 1000.0,
796-
"ScreenCaptureKit cache ensured before capture"
797-
),
798-
Err(error) => tracing::warn!(
799-
error = %error,
800-
"ScreenCaptureKit prewarm failed before capture"
801-
),
802-
}
803-
}
804-
805791
ScreenCaptureSource::<ScreenCaptureMethod>::init(
806792
capture_target,
807793
force_show_cursor,
@@ -812,6 +798,8 @@ pub async fn create_screen_capture(
812798
tokio::runtime::Handle::current(),
813799
#[cfg(windows)]
814800
d3d_device,
801+
#[cfg(target_os = "macos")]
802+
shareable_content,
815803
)
816804
.await
817805
.map(|v| (v, video_rx))

crates/recording/src/instant_recording.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ impl ActorBuilder {
185185

186186
pub async fn build(
187187
self,
188+
#[cfg(target_os = "macos")] shareable_content: cidre::arc::R<cidre::sc::ShareableContent>,
188189
) -> Result<(ActorHandle, oneshot::Receiver<Result<(), String>>), RecordingError> {
189190
spawn_instant_recording_actor(
190191
self.output_path,
@@ -193,6 +194,8 @@ impl ActorBuilder {
193194
capture_system_audio: self.system_audio,
194195
mic_feed: self.mic_feed,
195196
camera_feed: None,
197+
#[cfg(target_os = "macos")]
198+
shareable_content,
196199
},
197200
)
198201
.await
@@ -238,6 +241,7 @@ pub async fn spawn_instant_recording_actor(
238241
start_time,
239242
#[cfg(windows)]
240243
d3d_device,
244+
inputs.shareable_content.retained(),
241245
)
242246
.await?;
243247

crates/recording/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub struct RecordingBaseInputs {
4545
pub capture_system_audio: bool,
4646
pub mic_feed: Option<Arc<MicrophoneFeedLock>>,
4747
pub camera_feed: Option<Arc<CameraFeedLock>>,
48+
pub shareable_content: cidre::arc::R<cidre::sc::ShareableContent>,
4849
}
4950

5051
#[derive(specta::Type, Serialize, Deserialize, Clone, Debug)]

crates/recording/src/sources/screen_capture/macos.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,12 @@ impl PipelineSourceTask for ScreenCaptureSource<CMSampleBufferCapture> {
137137
let display = Display::from_id(&config.display)
138138
.ok_or_else(|| SourceError::NoDisplay(config.display))?;
139139

140+
let content = sc::ShareableContent::current().await
141+
.map_err(|e| SourceError::CreateActor(e))?;
142+
140143
let content_filter = display
141144
.raw_handle()
142-
.as_content_filter()
145+
.as_content_filter(&content)
143146
.await
144147
.ok_or_else(|| SourceError::AsContentFilter)?;
145148

crates/recording/src/sources/screen_capture/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ pub struct ScreenCaptureSource<TCaptureFormat: ScreenCaptureFormat> {
199199
_phantom: std::marker::PhantomData<TCaptureFormat>,
200200
#[cfg(windows)]
201201
d3d_device: ::windows::Win32::Graphics::Direct3D11::ID3D11Device,
202+
#[cfg(target_os = "macos")]
203+
shareable_content: cidre::arc::R<cidre::sc::ShareableContent>,
202204
}
203205

204206
impl<T: ScreenCaptureFormat> std::fmt::Debug for ScreenCaptureSource<T> {
@@ -239,6 +241,8 @@ impl<TCaptureFormat: ScreenCaptureFormat> Clone for ScreenCaptureSource<TCapture
239241
_phantom: std::marker::PhantomData,
240242
#[cfg(windows)]
241243
d3d_device: self.d3d_device.clone(),
244+
#[cfg(target_os = "macos")]
245+
shareable_content: self.shareable_content.clone(),
242246
}
243247
}
244248
}
@@ -281,6 +285,7 @@ impl<TCaptureFormat: ScreenCaptureFormat> ScreenCaptureSource<TCaptureFormat> {
281285
start_time: SystemTime,
282286
tokio_handle: tokio::runtime::Handle,
283287
#[cfg(windows)] d3d_device: ::windows::Win32::Graphics::Direct3D11::ID3D11Device,
288+
#[cfg(target_os = "macos")] shareable_content: cidre::arc::R<cidre::sc::ShareableContent>,
284289
) -> Result<Self, ScreenCaptureInitError> {
285290
cap_fail::fail!("ScreenCaptureSource::init");
286291

@@ -407,6 +412,8 @@ impl<TCaptureFormat: ScreenCaptureFormat> ScreenCaptureSource<TCaptureFormat> {
407412
_phantom: std::marker::PhantomData,
408413
#[cfg(windows)]
409414
d3d_device,
415+
#[cfg(target_os = "macos")]
416+
shareable_content: shareable_content.retained(),
410417
})
411418
}
412419

0 commit comments

Comments
 (0)