diff --git a/crates/recording/src/output_pipeline/core.rs b/crates/recording/src/output_pipeline/core.rs index b60777eec9..767e588aa9 100644 --- a/crates/recording/src/output_pipeline/core.rs +++ b/crates/recording/src/output_pipeline/core.rs @@ -138,19 +138,26 @@ impl TaskPool { )); } - pub fn spawn_thread(&mut self, name: &'static str, cb: impl FnOnce() + Send + 'static) { + pub fn spawn_thread( + &mut self, + name: &'static str, + cb: impl FnOnce() -> anyhow::Result<()> + Send + 'static, + ) { let span = error_span!("", task = name); let (done_tx, done_rx) = oneshot::channel(); std::thread::spawn(move || { let _guard = span.enter(); trace!("Task started"); - cb(); - let _ = done_tx.send(()); + let _ = done_tx.send(cb()); info!("Task finished"); }); self.0.push(( name, - tokio::spawn(done_rx.map_err(|_| anyhow!("Cancelled"))), + tokio::spawn( + done_rx + .map_err(|_| anyhow!("Cancelled")) + .map(|v| v.and_then(|v| v)), + ), )); } } @@ -481,7 +488,10 @@ async fn configure_audio( setup_ctx.tasks().spawn_thread("audio-mixer", { let stop_flag = stop_flag.clone(); - move || audio_mixer.run(audio_tx, ready_tx, stop_flag) + move || { + audio_mixer.run(audio_tx, ready_tx, stop_flag); + Ok(()) + } }); let _ = ready_rx .await diff --git a/crates/recording/src/output_pipeline/win.rs b/crates/recording/src/output_pipeline/win.rs index 4845f5a541..30cd843b3d 100644 --- a/crates/recording/src/output_pipeline/win.rs +++ b/crates/recording/src/output_pipeline/win.rs @@ -1,6 +1,5 @@ use crate::{AudioFrame, AudioMuxer, Muxer, TaskPool, VideoMuxer, screen_capture}; -use anyhow::Context; -use anyhow::anyhow; +use anyhow::{Context, anyhow}; use cap_enc_ffmpeg::AACEncoder; use cap_media_info::{AudioInfo, VideoInfo}; use futures::channel::oneshot; @@ -58,7 +57,7 @@ impl Muxer for WindowsMuxer { .transpose()?; let output = Arc::new(Mutex::new(output)); - let (ready_tx, ready_rx) = oneshot::channel(); + let (ready_tx, ready_rx) = oneshot::channel::>(); { let output = output.clone(); @@ -115,14 +114,14 @@ impl Muxer for WindowsMuxer { Ok(encoder) => { if ready_tx.send(Ok(())).is_err() { error!("Failed to send ready signal - receiver dropped"); - return; + return Ok(()); } encoder } Err(e) => { - error!("Encoder setup failed: {}", e); - let _ = ready_tx.send(Err(e)); - return; + error!("Encoder setup failed: {:#}", e); + let _ = ready_tx.send(Err(anyhow!("{e}"))); + return Err(anyhow!("{e}")); } }; @@ -157,7 +156,7 @@ impl Muxer for WindowsMuxer { Ok(()) }, ) - .unwrap(); + .context("run native encoder") } either::Right(mut encoder) => { while let Ok(Some((frame, time))) = video_rx.recv() { @@ -173,20 +172,17 @@ impl Muxer for WindowsMuxer { use scap_ffmpeg::AsFFmpeg; - if let Err(e) = - frame - .as_ffmpeg() - .context("frame as_ffmpeg") - .and_then(|frame| { - encoder - .queue_frame(frame, time, &mut output) - .context("queue_frame") - }) - { - error!("{e}"); - return; - } + frame + .as_ffmpeg() + .context("frame as_ffmpeg") + .and_then(|frame| { + encoder + .queue_frame(frame, time, &mut output) + .context("queue_frame") + })?; } + + Ok(()) } } }); diff --git a/crates/recording/src/sources/screen_capture/windows.rs b/crates/recording/src/sources/screen_capture/windows.rs index f856304da5..d14eb84f6b 100644 --- a/crates/recording/src/sources/screen_capture/windows.rs +++ b/crates/recording/src/sources/screen_capture/windows.rs @@ -197,15 +197,13 @@ impl output_pipeline::VideoSource for VideoSource { } Err(e) => { error!("Failed to create GraphicsCaptureItem on capture thread: {}", e); - let _ = error_tx.send(anyhow!("Failed to create GraphicsCaptureItem: {}", e)); - return; + return Err(anyhow!("Failed to create GraphicsCaptureItem: {}", e)); } } } None => { error!("Display not found for ID: {:?}", display_id); - let _ = error_tx.send(anyhow!("Display not found for ID: {:?}", display_id)); - return; + return Err(anyhow!("Display not found for ID: {:?}", display_id)); } }; @@ -246,15 +244,13 @@ impl output_pipeline::VideoSource for VideoSource { } Err(e) => { error!("Failed to create D3D capturer: {}", e); - let _ = error_tx.send(e.into()); - return; + return Err(e.into()); } }; let Ok(VideoControl::Start(reply)) = ctrl_rx.recv() else { error!("Failed to receive Start control message - channel disconnected"); - let _ = error_tx.send(anyhow!("Control channel disconnected before Start")); - return; + return Err(anyhow!("Control channel disconnected before Start")); }; tokio_rt.spawn( @@ -279,19 +275,21 @@ impl output_pipeline::VideoSource for VideoSource { } if reply.send(start_result).is_err() { error!("Failed to send start result - receiver dropped"); - return; + return Ok(()); } let Ok(VideoControl::Stop(reply)) = ctrl_rx.recv() else { trace!("Failed to receive Stop control message - channel disconnected (expected during shutdown)"); - return; + return Ok(()); }; if reply.send(capturer.stop().map_err(Into::into)).is_err() { - return; + return Ok(()); } - drop(drop_guard) + drop(drop_guard); + + Ok(()) }); ctx.tasks().spawn("d3d-capture", async move { diff --git a/crates/scap-direct3d/src/lib.rs b/crates/scap-direct3d/src/lib.rs index e69b36c927..17c9f1b5bc 100644 --- a/crates/scap-direct3d/src/lib.rs +++ b/crates/scap-direct3d/src/lib.rs @@ -384,6 +384,12 @@ impl Capturer { } } +impl Drop for Capturer { + fn drop(&mut self) { + let _ = self.stop(); + } +} + pub struct Frame { width: u32, height: u32,