diff --git a/src/client.rs b/src/client.rs index 72652776a..267595e21 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1541,13 +1541,22 @@ pub struct VideoHandler { decoder: Decoder, pub rgb: ImageRgb, pub texture: ImageTexture, - recorder: Arc>>, record: bool, + record_tx: Option>, + record_send_err_logged: bool, _display: usize, // useful for debug fail_counter: usize, first_frame: bool, } +enum RecordTask { + Frame { + frame: video_frame::Union, + w: usize, + h: usize, + }, +} + impl VideoHandler { #[cfg(feature = "flutter")] pub fn get_adapter_luid() -> Option { @@ -1573,8 +1582,9 @@ impl VideoHandler { decoder: Decoder::new(format, luid), rgb: ImageRgb::new(rgba_format, crate::get_dst_align_rgba()), texture: Default::default(), - recorder: Default::default(), record: false, + record_tx: None, + record_send_err_logged: false, _display, fail_counter: 0, first_frame: true, @@ -1585,7 +1595,7 @@ impl VideoHandler { #[inline] pub fn handle_frame( &mut self, - vf: VideoFrame, + mut vf: VideoFrame, pixelbuffer: &mut bool, chroma: &mut Option, ) -> ResultType { @@ -1593,15 +1603,19 @@ impl VideoHandler { if format != self.decoder.format() { self.reset(Some(format)); } - match &vf.union { - Some(frame) => { - let res = self.decoder.handle_video_frame( - frame, - &mut self.rgb, - &mut self.texture, - pixelbuffer, - chroma, - ); + if vf.union.is_none() { + return Ok(false); + } + let res = { + let frame = vf.union.as_ref().unwrap(); + self.decoder.handle_video_frame( + frame, + &mut self.rgb, + &mut self.texture, + pixelbuffer, + chroma, + ) + }; if res.as_ref().is_ok_and(|x| *x) { self.fail_counter = 0; } else { @@ -1620,19 +1634,34 @@ impl VideoHandler { } self.first_frame = false; if self.record { - self.recorder.lock().unwrap().as_mut().map(|r| { - let (w, h) = if *pixelbuffer { - (self.rgb.w, self.rgb.h) - } else { - (self.texture.w, self.texture.h) - }; - r.write_frame(frame, w, h).ok(); - }); + let (w, h) = if *pixelbuffer { + (self.rgb.w, self.rgb.h) + } else { + (self.texture.w, self.texture.h) + }; + if let Some(tx) = self.record_tx.as_ref() { + use std::sync::mpsc::TrySendError; + if let Some(frame) = vf.union.take() { + match tx.try_send(RecordTask::Frame { frame, w, h }) { + Ok(()) => {} + Err(TrySendError::Full(_task)) => { + // Drop frames if recorder can't keep up (e.g. slow network share). + } + Err(TrySendError::Disconnected(_task)) => { + if !self.record_send_err_logged { + self.record_send_err_logged = true; + log::warn!( + "recording worker disconnected, stop recording frames" + ); + } + self.record = false; + self.record_tx = None; + } + } + } + } } res - } - _ => Ok(false), - } } /// Reset the decoder, change format if it is Some @@ -1653,20 +1682,38 @@ impl VideoHandler { /// Start or stop screen record. pub fn record_screen(&mut self, start: bool, id: String, display_idx: usize, camera: bool) { self.record = false; + self.record_tx = None; + self.record_send_err_logged = false; if start { - self.recorder = Recorder::new(RecorderContext { + let (tx, rx) = std::sync::mpsc::sync_channel::(120); + let ctx = RecorderContext { server: false, id, dir: crate::ui_interface::video_save_directory(false), display_idx, camera, tx: None, - }) - .map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r)))); - } else { - self.recorder = Default::default(); + }; + std::thread::spawn(move || { + let mut recorder = match Recorder::new(ctx) { + Ok(r) => r, + Err(e) => { + log::error!("failed to create recorder: {e}"); + return; + } + }; + while let Ok(task) = rx.recv() { + match task { + RecordTask::Frame { frame, w, h } => { + if let Err(e) = recorder.write_frame(&frame, w, h) { + log::error!("recording write_frame failed: {e}"); + } + } + } + } + }); + self.record_tx = Some(tx); } - self.record = start; } }