Skip to content

Commit 4a7985b

Browse files
Merge pull request #1374 from CapSoftware/recording-actor-fixes
fix: auto-recover desktop mic feed and ScreenCaptureKit stream failures
2 parents 3bbf641 + 45fefed commit 4a7985b

4 files changed

Lines changed: 429 additions & 196 deletions

File tree

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

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ use cap_recording::{
5050
use cap_rendering::{ProjectRecordingsMeta, RenderedFrame};
5151
use clipboard_rs::common::RustImage;
5252
use clipboard_rs::{Clipboard, ClipboardContext};
53+
use cpal::StreamError;
5354
use editor_window::{EditorInstances, WindowEditorInstance};
5455
use ffmpeg::ffi::AV_TIME_BASE;
5556
use general_settings::GeneralSettingsStore;
@@ -109,6 +110,8 @@ pub struct App {
109110
recording_state: RecordingState,
110111
recording_logging_handle: LoggingHandle,
111112
mic_feed: ActorRef<feeds::microphone::MicrophoneFeed>,
113+
mic_meter_sender: flume::Sender<microphone::MicrophoneSamples>,
114+
selected_mic_label: Option<String>,
112115
camera_feed: ActorRef<feeds::camera::CameraFeed>,
113116
server_url: String,
114117
logs_dir: PathBuf,
@@ -168,6 +171,32 @@ impl App {
168171
}
169172
}
170173

174+
async fn restart_mic_feed(&mut self) -> Result<(), String> {
175+
info!("Restarting microphone feed after actor shutdown");
176+
177+
let (error_tx, error_rx) = flume::bounded(1);
178+
let mic_feed = MicrophoneFeed::spawn(MicrophoneFeed::new(error_tx));
179+
180+
spawn_mic_error_logger(error_rx);
181+
182+
mic_feed
183+
.ask(microphone::AddSender(self.mic_meter_sender.clone()))
184+
.await
185+
.map_err(|e| e.to_string())?;
186+
187+
if let Some(label) = self.selected_mic_label.clone() {
188+
let ready = mic_feed
189+
.ask(microphone::SetInput { label })
190+
.await
191+
.map_err(|e| e.to_string())?;
192+
ready.await.map_err(|e| e.to_string())?;
193+
}
194+
195+
self.mic_feed = mic_feed;
196+
197+
Ok(())
198+
}
199+
171200
async fn add_recording_logging_handle(&mut self, path: &PathBuf) -> Result<(), String> {
172201
let logfile =
173202
std::fs::File::create(path).map_err(|e| format!("Failed to create logfile: {e}"))?;
@@ -208,8 +237,9 @@ impl App {
208237
#[instrument(skip(state))]
209238
async fn set_mic_input(state: MutableState<'_, App>, label: Option<String>) -> Result<(), String> {
210239
let mic_feed = state.read().await.mic_feed.clone();
240+
let desired_label = label.clone();
211241

212-
match label {
242+
match desired_label.as_ref() {
213243
None => {
214244
mic_feed
215245
.ask(microphone::RemoveInput)
@@ -218,14 +248,21 @@ async fn set_mic_input(state: MutableState<'_, App>, label: Option<String>) -> R
218248
}
219249
Some(label) => {
220250
mic_feed
221-
.ask(feeds::microphone::SetInput { label })
251+
.ask(feeds::microphone::SetInput {
252+
label: label.clone(),
253+
})
222254
.await
223255
.map_err(|e| e.to_string())?
224256
.await
225257
.map_err(|e| e.to_string())?;
226258
}
227259
}
228260

261+
{
262+
let mut app = state.write().await;
263+
app.selected_mic_label = desired_label;
264+
}
265+
229266
Ok(())
230267
}
231268

@@ -271,6 +308,16 @@ async fn set_camera_input(
271308
Ok(())
272309
}
273310

311+
fn spawn_mic_error_logger(error_rx: flume::Receiver<StreamError>) {
312+
tokio::spawn(async move {
313+
let Ok(err) = error_rx.recv_async().await else {
314+
return;
315+
};
316+
317+
error!("Mic feed actor error: {err}");
318+
});
319+
}
320+
274321
#[derive(specta::Type, Serialize, tauri_specta::Event, Clone)]
275322
pub struct RecordingOptionsChanged;
276323

@@ -1946,6 +1993,7 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {
19461993
let (camera_tx, camera_ws_port, _shutdown) = camera_legacy::create_camera_preview_ws().await;
19471994

19481995
let (mic_samples_tx, mic_samples_rx) = flume::bounded(8);
1996+
let mic_meter_sender = mic_samples_tx.clone();
19491997

19501998
let camera_feed = CameraFeed::spawn(CameraFeed::default());
19511999
let _ = camera_feed.ask(feeds::camera::AddSender(camera_tx)).await;
@@ -1955,18 +2003,14 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {
19552003

19562004
let mic_feed = MicrophoneFeed::spawn(MicrophoneFeed::new(error_tx));
19572005

1958-
// TODO: make this part of a global actor one day
1959-
tokio::spawn(async move {
1960-
let Ok(err) = error_rx.recv_async().await else {
1961-
return;
1962-
};
1963-
1964-
error!("Mic feed actor error: {err}");
1965-
});
2006+
spawn_mic_error_logger(error_rx);
19662007

1967-
let _ = mic_feed
2008+
if let Err(err) = mic_feed
19682009
.ask(feeds::microphone::AddSender(mic_samples_tx))
1969-
.await;
2010+
.await
2011+
{
2012+
error!("Failed to attach audio meter sender: {err}");
2013+
}
19702014

19712015
mic_feed
19722016
};
@@ -2136,6 +2180,8 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {
21362180
recording_state: RecordingState::None,
21372181
recording_logging_handle,
21382182
mic_feed,
2183+
mic_meter_sender,
2184+
selected_mic_label: None,
21392185
camera_feed,
21402186
server_url,
21412187
logs_dir: logs_dir.clone(),

0 commit comments

Comments
 (0)