From 2fb4691e49f09d329923c8c4024c9ded237d5d23 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Wed, 1 Oct 2025 18:11:40 +0800 Subject: [PATCH 1/7] move windows capturer onto dedicated thread to avoid marshalling errors --- .../src/sources/screen_capture/windows.rs | 77 ++++++++++++++----- 1 file changed, 56 insertions(+), 21 deletions(-) diff --git a/crates/recording/src/sources/screen_capture/windows.rs b/crates/recording/src/sources/screen_capture/windows.rs index d152635a7..896d49f50 100644 --- a/crates/recording/src/sources/screen_capture/windows.rs +++ b/crates/recording/src/sources/screen_capture/windows.rs @@ -6,6 +6,7 @@ use ::windows::{ use cap_fail::fail_err; use cap_timestamp::{PerformanceCounterTimestamp, Timestamps}; use cpal::traits::{DeviceTrait, HostTrait}; +use futures::channel::oneshot; use kameo::prelude::*; use scap_ffmpeg::*; use std::{ @@ -368,7 +369,7 @@ impl PipelineSourceTask for ScreenCaptureSource { #[derive(Actor)] struct ScreenCaptureActor { - capture_handle: Option, + stop_tx: Option>>, error_tx: Sender<()>, d3d_device: ID3D11Device, } @@ -424,30 +425,59 @@ impl Message for ScreenCaptureActor { let error_tx = self.error_tx.clone(); - let mut capture_handle = scap_direct3d::Capturer::new( - msg.target, - msg.settings, - move |frame| { - let _ = msg.frame_handler.tell(NewFrame(frame)).try_send(); + let (ready_tx, ready_rx) = oneshot::channel(); - Ok(()) - }, - move || { - let _ = error_tx.send(()); + let (stop_tx, stop_rx) = std::sync::mpsc::sync_channel(1); - Ok(()) - }, - Some(self.d3d_device.clone()), - ) - .map_err(StartCapturingError::CreateCapturer)?; + std::thread::spawn(|| { + cap_mediafoundation_utils::thread_init(); - capture_handle - .start() - .map_err(StartCapturingError::StartCapturer)?; + let res = (|| { + let mut res = scap_direct3d::Capturer::new( + msg.target, + msg.settings, + move |frame| { + let _ = msg.frame_handler.tell(NewFrame(frame)).try_send(); + + Ok(()) + }, + move || { + let _ = error_tx.send(()); + + Ok(()) + }, + Some(self.d3d_device.clone()), + ) + .map_err(StartCapturingError::CreateCapturer)?; + + capture_handle + .start() + .map_err(StartCapturingError::StartCapturer); + })(); + + let capturer = match res { + Ok(capturer) => { + ready_tx.send(Ok(())); + capturer + } + Err(e) => { + ready_tx.send(Err(e)); + return; + } + }; + + let stop_channel = stop_rx.recv(); + + let res = capturer.stop(); + + if let Ok(stop_channel) = stop_channel { + stop_channel.send(res); + } + }); info!("Capturer started"); - self.capture_handle = Some(capture_handle); + self.stop_tx = Some(stop_tx); Ok(()) } @@ -461,14 +491,19 @@ impl Message for ScreenCaptureActor { _: StopCapturing, _: &mut Context, ) -> Self::Reply { - let Some(mut capturer) = self.capture_handle.take() else { + let Some(stop_tx) = self.stop_tx.take() else { return Err(StopCapturingError::NotCapturing); }; - if let Err(e) = capturer.stop() { + let (done_tx, done_rx) = oneshot::channel(); + if let Err(e) = stop_tx.send(done_tx) { error!("Silently failed to stop Windows capturer: {}", e); } + if let Ok(res) = done_rx.await { + res?; + } + info!("stopped windows capturer"); Ok(()) From 753825882c01e179cee0cbfd23f1dfe306c30a85 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Thu, 2 Oct 2025 08:39:33 +0800 Subject: [PATCH 2/7] run scap-direct3d capturer on dedicated thread --- crates/recording/examples/recording-cli.rs | 2 +- .../src/sources/screen_capture/windows.rs | 35 +++++++++++-------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/crates/recording/examples/recording-cli.rs b/crates/recording/examples/recording-cli.rs index 5e35f42d5..54c6c0e36 100644 --- a/crates/recording/examples/recording-cli.rs +++ b/crates/recording/examples/recording-cli.rs @@ -66,7 +66,7 @@ pub async fn main() { id: Display::primary().id(), }, ) - // .with_system_audio(true) + .with_system_audio(true) // .with_camera_feed(std::sync::Arc::new( // camera_feed.ask(feeds::camera::Lock).await.unwrap(), // )) diff --git a/crates/recording/src/sources/screen_capture/windows.rs b/crates/recording/src/sources/screen_capture/windows.rs index 896d49f50..ed6f3d0cf 100644 --- a/crates/recording/src/sources/screen_capture/windows.rs +++ b/crates/recording/src/sources/screen_capture/windows.rs @@ -8,6 +8,7 @@ use cap_timestamp::{PerformanceCounterTimestamp, Timestamps}; use cpal::traits::{DeviceTrait, HostTrait}; use futures::channel::oneshot; use kameo::prelude::*; +use scap_direct3d::StopCapturerError; use scap_ffmpeg::*; use std::{ collections::VecDeque, @@ -369,7 +370,7 @@ impl PipelineSourceTask for ScreenCaptureSource { #[derive(Actor)] struct ScreenCaptureActor { - stop_tx: Option>>, + stop_tx: Option>>>, error_tx: Sender<()>, d3d_device: ID3D11Device, } @@ -377,7 +378,7 @@ struct ScreenCaptureActor { impl ScreenCaptureActor { pub fn new(error_tx: Sender<()>, d3d_device: ID3D11Device) -> Self { Self { - capture_handle: None, + stop_tx: None, error_tx, d3d_device, } @@ -412,7 +413,7 @@ impl Message for ScreenCaptureActor { msg: StartCapturing, _: &mut Context, ) -> Self::Reply { - if self.capture_handle.is_some() { + if self.stop_tx.is_some() { return Err(StartCapturingError::AlreadyCapturing); } @@ -427,13 +428,15 @@ impl Message for ScreenCaptureActor { let (ready_tx, ready_rx) = oneshot::channel(); - let (stop_tx, stop_rx) = std::sync::mpsc::sync_channel(1); + let (stop_tx, stop_rx) = + std::sync::mpsc::sync_channel::>>(1); - std::thread::spawn(|| { + let d3d_device = self.d3d_device.clone(); + std::thread::spawn(move || { cap_mediafoundation_utils::thread_init(); let res = (|| { - let mut res = scap_direct3d::Capturer::new( + let mut capture_handle = scap_direct3d::Capturer::new( msg.target, msg.settings, move |frame| { @@ -446,22 +449,24 @@ impl Message for ScreenCaptureActor { Ok(()) }, - Some(self.d3d_device.clone()), + Some(d3d_device), ) .map_err(StartCapturingError::CreateCapturer)?; capture_handle .start() - .map_err(StartCapturingError::StartCapturer); + .map_err(StartCapturingError::StartCapturer)?; + + Ok::<_, StartCapturingError>(capture_handle) })(); - let capturer = match res { + let mut capturer = match res { Ok(capturer) => { - ready_tx.send(Ok(())); + let _ = ready_tx.send(Ok(())); capturer } Err(e) => { - ready_tx.send(Err(e)); + let _ = ready_tx.send(Err(e)); return; } }; @@ -471,7 +476,7 @@ impl Message for ScreenCaptureActor { let res = capturer.stop(); if let Ok(stop_channel) = stop_channel { - stop_channel.send(res); + let _ = stop_channel.send(res); } }); @@ -484,7 +489,7 @@ impl Message for ScreenCaptureActor { } impl Message for ScreenCaptureActor { - type Reply = Result<(), StopCapturingError>; + type Reply = Result<(), String>; async fn handle( &mut self, @@ -492,7 +497,7 @@ impl Message for ScreenCaptureActor { _: &mut Context, ) -> Self::Reply { let Some(stop_tx) = self.stop_tx.take() else { - return Err(StopCapturingError::NotCapturing); + return Err("Not Capturing".to_string()); }; let (done_tx, done_rx) = oneshot::channel(); @@ -501,7 +506,7 @@ impl Message for ScreenCaptureActor { } if let Ok(res) = done_rx.await { - res?; + res.map_err(|e| e.to_string())?; } info!("stopped windows capturer"); From eb69b3b49c569cc600d772268efdf93404b9cf54 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Thu, 2 Oct 2025 19:24:02 +0800 Subject: [PATCH 3/7] await ready_rx --- crates/recording/src/sources/screen_capture/windows.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/recording/src/sources/screen_capture/windows.rs b/crates/recording/src/sources/screen_capture/windows.rs index ed6f3d0cf..9560aa957 100644 --- a/crates/recording/src/sources/screen_capture/windows.rs +++ b/crates/recording/src/sources/screen_capture/windows.rs @@ -480,6 +480,10 @@ impl Message for ScreenCaptureActor { } }); + if let Ok(res) = ready_rx.await { + res?; + } + info!("Capturer started"); self.stop_tx = Some(stop_tx); From bc8a0d7efd84dd1537496f6b14753a8f653593cb Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Thu, 2 Oct 2025 19:25:29 +0800 Subject: [PATCH 4/7] ci --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8226689de..827c80e27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -97,6 +97,7 @@ jobs: uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.settings.target }} + components: clippy - name: Rust cache uses: swatinem/rust-cache@v2 From 1c4d9088a2d207e3dd1ed60b3bf626fb1f0409d1 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Thu, 2 Oct 2025 19:52:45 +0800 Subject: [PATCH 5/7] handle error better --- .../recording/src/sources/screen_capture/windows.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/recording/src/sources/screen_capture/windows.rs b/crates/recording/src/sources/screen_capture/windows.rs index 9560aa957..c3a406d9d 100644 --- a/crates/recording/src/sources/screen_capture/windows.rs +++ b/crates/recording/src/sources/screen_capture/windows.rs @@ -480,12 +480,19 @@ impl Message for ScreenCaptureActor { } }); - if let Ok(res) = ready_rx.await { - res?; + match ready_rx.await { + Ok(res) => res?, + Err(_) => { + return Err(StartCapturingError::StartCapturer( + ::windows::core::Error::new( + ::windows::core::HRESULT(0x80004005u32 as i32), + "Capturer thread dropped ready channel".into(), + ), + )); + } } info!("Capturer started"); - self.stop_tx = Some(stop_tx); Ok(()) From 3a09ae21dda3ad390bfdaa2768f8e068bcaca026 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Thu, 2 Oct 2025 21:25:46 +0800 Subject: [PATCH 6/7] bruh --- crates/recording/src/sources/screen_capture/windows.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/recording/src/sources/screen_capture/windows.rs b/crates/recording/src/sources/screen_capture/windows.rs index c3a406d9d..8b1ecc9b9 100644 --- a/crates/recording/src/sources/screen_capture/windows.rs +++ b/crates/recording/src/sources/screen_capture/windows.rs @@ -486,7 +486,7 @@ impl Message for ScreenCaptureActor { return Err(StartCapturingError::StartCapturer( ::windows::core::Error::new( ::windows::core::HRESULT(0x80004005u32 as i32), - "Capturer thread dropped ready channel".into(), + "Capturer thread dropped ready channel", ), )); } From dbb8f7b16c62542ee4979ecedaf57c171eeb84e1 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Thu, 2 Oct 2025 22:11:35 +0800 Subject: [PATCH 7/7] Apply suggestion from @coderabbitai[bot] Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- crates/recording/src/sources/screen_capture/windows.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/recording/src/sources/screen_capture/windows.rs b/crates/recording/src/sources/screen_capture/windows.rs index 8b1ecc9b9..819cec845 100644 --- a/crates/recording/src/sources/screen_capture/windows.rs +++ b/crates/recording/src/sources/screen_capture/windows.rs @@ -516,8 +516,9 @@ impl Message for ScreenCaptureActor { error!("Silently failed to stop Windows capturer: {}", e); } - if let Ok(res) = done_rx.await { - res.map_err(|e| e.to_string())?; + match done_rx.await { + Ok(res) => res.map_err(|e| e.to_string())?, + Err(_) => return Err("Capturer thread dropped stop channel".to_string()), } info!("stopped windows capturer");