diff --git a/crates/camera/src/macos.rs b/crates/camera/src/macos.rs index fece38486f1..128e0f2e395 100644 --- a/crates/camera/src/macos.rs +++ b/crates/camera/src/macos.rs @@ -24,38 +24,40 @@ pub(super) fn list_cameras_impl() -> impl Iterator { impl CameraInfo { pub(super) fn formats_impl(&self) -> Option> { - let device = find_device(self)?; - - let mut ret = vec![]; - - for format in device.formats().iter() { - let desc = format.format_desc(); - let width = desc.dims().width as u32; - let height = desc.dims().height as u32; - let pixel_format = fourcc_display(desc.media_sub_type()); - - for fr_range in format.video_supported_frame_rate_ranges().iter() { - // SAFETY: trust me bro it crashes on intel mac otherwise - let fr_range = unsafe { - &*(fr_range as *const av::capture::device::FrameRateRange) - .cast::() - }; - - let min = unsafe { fr_range.minFrameDuration() }; - - ret.push(Format { - native: format.retained(), - info: FormatInfo { - width, - height, - frame_rate: min.timescale as f32 / min.value as f32, - }, - pixel_format: Some(pixel_format.clone()), - }) + objc::ar_pool(|| { + let device = find_device(self)?; + + let mut ret = vec![]; + + for format in device.formats().iter() { + let desc = format.format_desc(); + let width = desc.dims().width as u32; + let height = desc.dims().height as u32; + let pixel_format = fourcc_display(desc.media_sub_type()); + + for fr_range in format.video_supported_frame_rate_ranges().iter() { + // SAFETY: trust me bro it crashes on intel mac otherwise + let fr_range = unsafe { + &*(fr_range as *const av::capture::device::FrameRateRange) + .cast::() + }; + + let min = unsafe { fr_range.minFrameDuration() }; + + ret.push(Format { + native: format.retained(), + info: FormatInfo { + width, + height, + frame_rate: min.timescale as f32 / min.value as f32, + }, + pixel_format: Some(pixel_format.clone()), + }) + } } - } - Some(ret) + Some(ret) + }) } } @@ -156,7 +158,7 @@ pub(super) fn start_capturing_impl( let input = av::capture::DeviceInput::with_device(&device).map_err(AVFoundationError::Static)?; - let queue = dispatch::Queue::new(); + let queue = dispatch::Queue::serial_with_ar_pool(); let mut missing_image_bufs: u64 = 0; let delegate = CallbackOutputDelegate::with(CallbackOutputDelegateInner::new(Box::new(move |data| { diff --git a/crates/recording/src/output_pipeline/macos.rs b/crates/recording/src/output_pipeline/macos.rs index 0fd2e282a9b..7dba784546d 100644 --- a/crates/recording/src/output_pipeline/macos.rs +++ b/crates/recording/src/output_pipeline/macos.rs @@ -457,65 +457,85 @@ impl Muxer for AVFoundationMp4Muxer { let mut retry_count = 0; const MAX_RETRIES: u32 = 100; - loop { - let queue_result = { - let mut encoder = match encoder_clone.lock() { - Ok(e) => e, - Err(_) => { - let message = "MP4 encoder mutex poisoned".to_string(); - set_fatal_error(&video_fatal_error, message.clone()); - return Err(anyhow!(message)); - } + let mut step_result = Ok(None); + cidre::objc::ar_pool(|| { + loop { + let queue_result = { + let mut encoder = match encoder_clone.lock() { + Ok(e) => e, + Err(_) => { + let message = "MP4 encoder mutex poisoned".to_string(); + set_fatal_error(&video_fatal_error, message.clone()); + step_result = Err(anyhow!(message)); + return; + } + }; + encoder.queue_video_frame(sample_buf.clone(), timestamp) }; - encoder.queue_video_frame(sample_buf.clone(), timestamp) - }; - match queue_result { - Ok(()) => break, - Err(QueueFrameError::NotReadyForMore) => { - retry_count += 1; - if retry_count >= MAX_RETRIES { - encoder_busy_count += 1; - video_busy_thread.store( - encoder_busy_count, - std::sync::atomic::Ordering::Relaxed, - ); - if encoder_busy_count <= 5 || encoder_busy_count.is_multiple_of(100) { - debug!( + match queue_result { + Ok(()) => { + step_result = Ok(None); + break; + } + Err(QueueFrameError::NotReadyForMore) => { + retry_count += 1; + if retry_count >= MAX_RETRIES { + encoder_busy_count += 1; + video_busy_thread.store( encoder_busy_count, - "MP4 encoder busy, frame queued after max retries" + std::sync::atomic::Ordering::Relaxed, ); + if encoder_busy_count <= 5 || encoder_busy_count.is_multiple_of(100) { + debug!( + encoder_busy_count, + "MP4 encoder busy, frame queued after max retries" + ); + } + step_result = Ok(None); + break; } + std::thread::sleep(Duration::from_micros(500)); + } + Err(QueueFrameError::WriterFailed(err)) => { + let total = video_count_thread + .load(std::sync::atomic::Ordering::Relaxed); + let message = format!( + "Failed to encode video frame: WriterFailed/{err:?} \ + (frame #{total}, ts={timestamp:?})" + ); + set_fatal_error(&video_fatal_error, message.clone()); + step_result = Err(anyhow!(message)); + return; + } + Err(QueueFrameError::Failed) => { + let total = video_count_thread + .load(std::sync::atomic::Ordering::Relaxed); + let message = format!( + "Failed to encode video frame: Failed \ + (frame #{total}, ts={timestamp:?})" + ); + set_fatal_error(&video_fatal_error, message.clone()); + step_result = Err(anyhow!(message)); + return; + } + Err(QueueFrameError::Finished) => { + step_result = Ok(Some(())); + return; + } + Err(e) => { + warn!("Failed to encode video frame: {e}"); + step_result = Ok(None); break; } - std::thread::sleep(Duration::from_micros(500)); - } - Err(QueueFrameError::WriterFailed(err)) => { - let total = video_count_thread - .load(std::sync::atomic::Ordering::Relaxed); - let message = format!( - "Failed to encode video frame: WriterFailed/{err:?} \ - (frame #{total}, ts={timestamp:?})" - ); - set_fatal_error(&video_fatal_error, message.clone()); - return Err(anyhow!(message)); - } - Err(QueueFrameError::Failed) => { - let total = video_count_thread - .load(std::sync::atomic::Ordering::Relaxed); - let message = format!( - "Failed to encode video frame: Failed \ - (frame #{total}, ts={timestamp:?})" - ); - set_fatal_error(&video_fatal_error, message.clone()); - return Err(anyhow!(message)); - } - Err(QueueFrameError::Finished) => return Ok(()), - Err(e) => { - warn!("Failed to encode video frame: {e}"); - break; } } + }); + + match step_result { + Ok(Some(())) => return Ok(()), + Ok(None) => {} + Err(err) => return Err(err), } video_count_thread.fetch_add(1, std::sync::atomic::Ordering::Relaxed); diff --git a/crates/recording/src/output_pipeline/macos_fragmented_m4s.rs b/crates/recording/src/output_pipeline/macos_fragmented_m4s.rs index a0390e30141..53b158a31fc 100644 --- a/crates/recording/src/output_pipeline/macos_fragmented_m4s.rs +++ b/crates/recording/src/output_pipeline/macos_fragmented_m4s.rs @@ -370,100 +370,109 @@ impl MacOSFragmentedM4SMuxer { continue; } - if let Some(frame_pool) = frame_pool.as_mut() { - let convert_start = std::time::Instant::now(); - let frame = frame_pool.get_frame(); - let fill_result = fill_frame_from_sample_buf(&sample_buf, frame); - let convert_elapsed_ms = convert_start.elapsed().as_millis(); - - if convert_elapsed_ms > slow_threshold_ms { - slow_convert_count += 1; - if slow_convert_count <= 5 || slow_convert_count.is_multiple_of(100) { - debug!( - elapsed_ms = convert_elapsed_ms, - count = slow_convert_count, - "fill_frame_from_sample_buf exceeded {}ms threshold", - slow_threshold_ms - ); + let mut step_result = Ok(()); + cidre::objc::ar_pool(|| { + if let Some(frame_pool) = frame_pool.as_mut() { + let convert_start = std::time::Instant::now(); + let frame = frame_pool.get_frame(); + let fill_result = fill_frame_from_sample_buf(&sample_buf, frame); + let convert_elapsed_ms = convert_start.elapsed().as_millis(); + + if convert_elapsed_ms > slow_threshold_ms { + slow_convert_count += 1; + if slow_convert_count <= 5 || slow_convert_count.is_multiple_of(100) { + debug!( + elapsed_ms = convert_elapsed_ms, + count = slow_convert_count, + "fill_frame_from_sample_buf exceeded {}ms threshold", + slow_threshold_ms + ); + } } - } - match fill_result { - Ok(()) => { - let encode_start = std::time::Instant::now(); - let owned_frame = frame_pool.take_frame(); + match fill_result { + Ok(()) => { + let encode_start = std::time::Instant::now(); + let owned_frame = frame_pool.take_frame(); - match encoder_clone.lock() { - Ok(mut encoder) => { - if let Err(e) = encoder.queue_frame(owned_frame, timestamp) { - warn!("Failed to encode frame: {e}"); + match encoder_clone.lock() { + Ok(mut encoder) => { + if let Err(e) = encoder.queue_frame(owned_frame, timestamp) { + warn!("Failed to encode frame: {e}"); + } + } + Err(_) => { + error!("Encoder mutex poisoned - encoder thread likely panicked, stopping"); + step_result = Err(anyhow!("Encoder mutex poisoned - all subsequent frames would be lost")); + return; } } - Err(_) => { - error!("Encoder mutex poisoned - encoder thread likely panicked, stopping"); - return Err(anyhow!("Encoder mutex poisoned - all subsequent frames would be lost")); - } - } - let encode_elapsed_ms = encode_start.elapsed().as_millis(); - - if encode_elapsed_ms > slow_threshold_ms { - slow_encode_count += 1; - if slow_encode_count <= 5 - || slow_encode_count.is_multiple_of(100) - { - debug!( - elapsed_ms = encode_elapsed_ms, - count = slow_encode_count, - "encoder.queue_frame exceeded {}ms threshold", - slow_threshold_ms - ); + let encode_elapsed_ms = encode_start.elapsed().as_millis(); + + if encode_elapsed_ms > slow_threshold_ms { + slow_encode_count += 1; + if slow_encode_count <= 5 + || slow_encode_count.is_multiple_of(100) + { + debug!( + elapsed_ms = encode_elapsed_ms, + count = slow_encode_count, + "encoder.queue_frame exceeded {}ms threshold", + slow_threshold_ms + ); + } } } + Err(e) => { + warn!("Failed to convert frame: {e:?}"); + } } - Err(e) => { - warn!("Failed to convert frame: {e:?}"); - } - } - } else { - let encode_start = std::time::Instant::now(); - - let queued = match sample_buf.image_buf() { - Some(image_buf) => { - let ptr = (std::ptr::from_ref(image_buf) - as *const std::ffi::c_void) - .cast_mut(); - match encoder_clone.lock() { - Ok(mut encoder) => { - unsafe { encoder.queue_hw_pixel_buffer(ptr, timestamp) } - .map_err(|e| anyhow!("{e}")) - } - Err(_) => { - error!("Encoder mutex poisoned - encoder thread likely panicked, stopping"); - return Err(anyhow!("Encoder mutex poisoned - all subsequent frames would be lost")); + } else { + let encode_start = std::time::Instant::now(); + + let queued = match sample_buf.image_buf() { + Some(image_buf) => { + let ptr = (std::ptr::from_ref(image_buf) + as *const std::ffi::c_void) + .cast_mut(); + match encoder_clone.lock() { + Ok(mut encoder) => { + unsafe { encoder.queue_hw_pixel_buffer(ptr, timestamp) } + .map_err(|e| anyhow!("{e}")) + } + Err(_) => { + error!("Encoder mutex poisoned - encoder thread likely panicked, stopping"); + step_result = Err(anyhow!("Encoder mutex poisoned - all subsequent frames would be lost")); + return; + } } } + None => Err(anyhow!("sample buffer has no image buffer")), + }; + + if let Err(e) = queued { + warn!("Failed to encode frame: {e}"); } - None => Err(anyhow!("sample buffer has no image buffer")), - }; - if let Err(e) = queued { - warn!("Failed to encode frame: {e}"); - } + let encode_elapsed_ms = encode_start.elapsed().as_millis(); - let encode_elapsed_ms = encode_start.elapsed().as_millis(); - - if encode_elapsed_ms > slow_threshold_ms { - slow_encode_count += 1; - if slow_encode_count <= 5 || slow_encode_count.is_multiple_of(100) { - debug!( - elapsed_ms = encode_elapsed_ms, - count = slow_encode_count, - "encoder.queue_hw_pixel_buffer exceeded {}ms threshold", - slow_threshold_ms - ); + if encode_elapsed_ms > slow_threshold_ms { + slow_encode_count += 1; + if slow_encode_count <= 5 || slow_encode_count.is_multiple_of(100) { + debug!( + elapsed_ms = encode_elapsed_ms, + count = slow_encode_count, + "encoder.queue_hw_pixel_buffer exceeded {}ms threshold", + slow_threshold_ms + ); + } } } + }); + + if let Err(err) = step_result { + return Err(err); } total_frames += 1; diff --git a/crates/recording/src/output_pipeline/oop_fragmented_m4s.rs b/crates/recording/src/output_pipeline/oop_fragmented_m4s.rs index 9afbb643412..46d8c892472 100644 --- a/crates/recording/src/output_pipeline/oop_fragmented_m4s.rs +++ b/crates/recording/src/output_pipeline/oop_fragmented_m4s.rs @@ -411,7 +411,10 @@ impl OutOfProcessFragmentedM4SMuxer { let convert_start = std::time::Instant::now(); let frame = frame_pool.get_frame(); - let fill_result = fill_frame_from_sample_buf(&sample_buf, frame); + let mut fill_result = Ok(()); + cidre::objc::ar_pool(|| { + fill_result = fill_frame_from_sample_buf(&sample_buf, frame); + }); let convert_elapsed_ms = convert_start.elapsed().as_millis(); if convert_elapsed_ms > slow_threshold_ms {