From e2d442a0dd3cd57d4fa7bc2738f0df36950d24f6 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Thu, 9 Oct 2025 19:54:12 +0800 Subject: [PATCH 1/3] hold camera and mic locks while recording is active --- apps/desktop/src-tauri/src/recording.rs | 57 ++++++++++++----------- crates/recording/src/instant_recording.rs | 2 +- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/apps/desktop/src-tauri/src/recording.rs b/apps/desktop/src-tauri/src/recording.rs index bff295fd21..61ceb0415c 100644 --- a/apps/desktop/src-tauri/src/recording.rs +++ b/apps/desktop/src-tauri/src/recording.rs @@ -8,6 +8,8 @@ use cap_project::{ cursor::CursorEvents, }; use cap_recording::PipelineDoneError; +use cap_recording::feeds::camera::CameraFeedLock; +use cap_recording::feeds::microphone::MicrophoneFeedLock; use cap_recording::{ RecordingError, RecordingMode, feeds::{camera, microphone}, @@ -53,20 +55,24 @@ use crate::{ windows::{CapWindowId, ShowCapWindow}, }; +pub struct InProgressRecordingCommon { + pub target_name: String, + pub inputs: StartRecordingInputs, + pub recording_dir: PathBuf, + pub camera_feed: Option>, + pub mic_feed: Option>, +} + pub enum InProgressRecording { Instant { - target_name: String, handle: instant_recording::ActorHandle, progressive_upload: InstantMultipartUpload, video_upload_info: VideoUploadInfo, - inputs: StartRecordingInputs, - recording_dir: PathBuf, + common: InProgressRecordingCommon, }, Studio { - target_name: String, handle: studio_recording::ActorHandle, - inputs: StartRecordingInputs, - recording_dir: PathBuf, + common: InProgressRecordingCommon, }, } @@ -80,8 +86,8 @@ impl InProgressRecording { pub fn inputs(&self) -> &StartRecordingInputs { match self { - Self::Instant { inputs, .. } => inputs, - Self::Studio { inputs, .. } => inputs, + Self::Instant { common, .. } => &common.inputs, + Self::Studio { common, .. } => &common.inputs, } } @@ -101,8 +107,8 @@ impl InProgressRecording { pub fn recording_dir(&self) -> &PathBuf { match self { - Self::Instant { recording_dir, .. } => recording_dir, - Self::Studio { recording_dir, .. } => recording_dir, + Self::Instant { common, .. } => &common.recording_dir, + Self::Studio { common, .. } => &common.recording_dir, } } @@ -112,21 +118,17 @@ impl InProgressRecording { handle, progressive_upload, video_upload_info, - target_name, + common, .. } => CompletedRecording::Instant { recording: handle.stop().await?, progressive_upload, video_upload_info, - target_name, + target_name: common.target_name, }, - Self::Studio { - handle, - target_name, - .. - } => CompletedRecording::Studio { + Self::Studio { handle, common, .. } => CompletedRecording::Studio { recording: handle.stop().await?, - target_name, + target_name: common.target_name, }, }) } @@ -449,6 +451,14 @@ pub async fn start_recording( .map_err(|e| format!("GetShareableContent: {e}"))? .ok_or_else(|| format!("GetShareableContent/NotAvailable"))?; + let common = InProgressRecordingCommon { + target_name, + inputs: inputs.clone(), + recording_dir: recording_dir.clone(), + camera_feed: camera_feed.clone(), + mic_feed: mic_feed.clone(), + }; + let actor = match inputs.mode { RecordingMode::Studio => { let mut builder = studio_recording::Actor::builder( @@ -481,12 +491,7 @@ pub async fn start_recording( e.to_string() })?; - InProgressRecording::Studio { - handle, - target_name, - inputs, - recording_dir: recording_dir.clone(), - } + InProgressRecording::Studio { handle, common } } RecordingMode::Instant => { let Some(video_upload_info) = video_upload_info.clone() else { @@ -526,9 +531,7 @@ pub async fn start_recording( handle, progressive_upload, video_upload_info, - target_name, - inputs, - recording_dir: recording_dir.clone(), + common, } } }; diff --git a/crates/recording/src/instant_recording.rs b/crates/recording/src/instant_recording.rs index 0a1ddc57cc..400dd6a27d 100644 --- a/crates/recording/src/instant_recording.rs +++ b/crates/recording/src/instant_recording.rs @@ -1,7 +1,7 @@ use crate::{ RecordingBaseInputs, capture_pipeline::{MakeCapturePipeline, ScreenCaptureMethod, Stop, create_screen_capture}, - feeds::microphone::MicrophoneFeedLock, + feeds::{camera::CameraFeedLock, microphone::MicrophoneFeedLock}, output_pipeline::{self, OutputPipeline}, sources::screen_capture::{ScreenCaptureConfig, ScreenCaptureTarget}, }; From 1f6b8cb5f9aecf1dbe2c0ea94815c5065a58f768 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Thu, 9 Oct 2025 20:59:49 +0800 Subject: [PATCH 2/3] hold locks inside sources --- apps/desktop/src-tauri/src/recording.rs | 4 ---- crates/recording/src/sources/camera.rs | 6 +++--- crates/recording/src/sources/microphone.rs | 10 +++++----- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/apps/desktop/src-tauri/src/recording.rs b/apps/desktop/src-tauri/src/recording.rs index 61ceb0415c..559c181d5c 100644 --- a/apps/desktop/src-tauri/src/recording.rs +++ b/apps/desktop/src-tauri/src/recording.rs @@ -59,8 +59,6 @@ pub struct InProgressRecordingCommon { pub target_name: String, pub inputs: StartRecordingInputs, pub recording_dir: PathBuf, - pub camera_feed: Option>, - pub mic_feed: Option>, } pub enum InProgressRecording { @@ -455,8 +453,6 @@ pub async fn start_recording( target_name, inputs: inputs.clone(), recording_dir: recording_dir.clone(), - camera_feed: camera_feed.clone(), - mic_feed: mic_feed.clone(), }; let actor = match inputs.mode { diff --git a/crates/recording/src/sources/camera.rs b/crates/recording/src/sources/camera.rs index 3ef911f63b..48daae8e37 100644 --- a/crates/recording/src/sources/camera.rs +++ b/crates/recording/src/sources/camera.rs @@ -15,7 +15,7 @@ impl VideoSource for Camera { type Frame = FFmpegVideoFrame; async fn setup( - config: Self::Config, + feed_lock: Self::Config, mut video_tx: mpsc::Sender, _: &mut SetupCtx, ) -> anyhow::Result @@ -24,7 +24,7 @@ impl VideoSource for Camera { { let (tx, rx) = flume::bounded(8); - config + feed_lock .ask(camera::AddSender(tx)) .await .map_err(|e| anyhow!("Failed to add camera sender: {e}"))?; @@ -35,7 +35,7 @@ impl VideoSource for Camera { } }); - Ok(Self(config)) + Ok(Self(feed_lock)) } fn video_info(&self) -> VideoInfo { diff --git a/crates/recording/src/sources/microphone.rs b/crates/recording/src/sources/microphone.rs index 36a6fad636..752476661b 100644 --- a/crates/recording/src/sources/microphone.rs +++ b/crates/recording/src/sources/microphone.rs @@ -7,13 +7,13 @@ use cap_media_info::AudioInfo; use futures::{SinkExt, channel::mpsc}; use std::sync::Arc; -pub struct Microphone(AudioInfo); +pub struct Microphone(AudioInfo, Arc); impl AudioSource for Microphone { type Config = Arc; fn setup( - config: Self::Config, + feed_lock: Self::Config, mut audio_tx: mpsc::Sender, _: &mut crate::SetupCtx, ) -> impl Future> + 'static @@ -21,10 +21,10 @@ impl AudioSource for Microphone { Self: Sized, { async move { - let audio_info = config.audio_info(); + let audio_info = feed_lock.audio_info(); let (tx, rx) = flume::bounded(8); - config + feed_lock .ask(microphone::AddSender(tx)) .await .map_err(|e| anyhow!("Failed to add camera sender: {e}"))?; @@ -40,7 +40,7 @@ impl AudioSource for Microphone { } }); - Ok(Self(audio_info)) + Ok(Self(audio_info, feed_lock)) } } From 7007c9c95a30f1226ef8b585289e4ab5cd9fe653 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Thu, 9 Oct 2025 21:03:03 +0800 Subject: [PATCH 3/3] hold camera feed on instant mode --- apps/desktop/src-tauri/src/recording.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/desktop/src-tauri/src/recording.rs b/apps/desktop/src-tauri/src/recording.rs index 559c181d5c..8db9417449 100644 --- a/apps/desktop/src-tauri/src/recording.rs +++ b/apps/desktop/src-tauri/src/recording.rs @@ -67,6 +67,8 @@ pub enum InProgressRecording { progressive_upload: InstantMultipartUpload, video_upload_info: VideoUploadInfo, common: InProgressRecordingCommon, + // camera isn't used as part of recording pipeline so we hold lock here + camera_feed: Option>, }, Studio { handle: studio_recording::ActorHandle, @@ -528,6 +530,7 @@ pub async fn start_recording( progressive_upload, video_upload_info, common, + camera_feed, } } };