Skip to content

Conversation

@Brendonovich
Copy link
Contributor

@Brendonovich Brendonovich commented Aug 26, 2025

Summary by CodeRabbit

  • Performance Improvements
    • More reliable multi-threaded handling of Windows system audio during screen capture, improving responsiveness.
  • Bug Fixes
    • Reduces rare freezes or deadlocks when recording system audio on Windows.
  • Improvements
    • Clearer error reporting when system audio capture encounters issues.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 26, 2025

Walkthrough

Adds an unsafe impl Send for WindowsAudioCapture and changes the SourceError::StartCapturingAudio variant from SendError<audio::StartCapturing, cpal::PlayStreamError> to String, updating error propagation to convert inner errors to to_string() in crates/recording/src/sources/screen_capture/windows.rs.

Changes

Cohort / File(s) Summary
Audio concurrency & error shape
crates/recording/src/sources/screen_capture/windows.rs
Added unsafe impl Send for WindowsAudioCapture inside pub mod audio. Modified SourceError::StartCapturingAudio variant type from SendError<audio::StartCapturing, cpal::PlayStreamError> to String and updated error propagation to call to_string() on inner errors when constructing the variant.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I’m a rabbit in the code, ears tuned to thread,
I hop unsafe where sound is led,
Errors now speak in plain-string song,
Packets travel—soft and strong,
Hooray for threads that carry on! 🐇

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-windows

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1cb811d and 9ca07fd.

📒 Files selected for processing (1)
  • crates/recording/src/sources/screen_capture/windows.rs (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)

Comment on lines +470 to +471
unsafe impl Send for WindowsAudioCapture {}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Unsafe Send for WindowsAudioCapture is likely unsound without proof; kameo/tokio may move the actor across threads

The actor holding WindowsAudioCapture can be polled on different Tokio worker threads (multi-thread runtime), which requires the actor state to be Send. By adding unsafe impl Send, we assert that moving WindowsAudioCapture (and thus its scap_cpal::Capturer) between threads is sound. That’s a high bar: CPAL on Windows (WASAPI/COM) often involves thread-affine resources; if scap_cpal::Capturer or anything it retains is !Send, this unsafely sidesteps the compiler and can lead to UB/data races.

Action items:

  • Either justify this unsafe impl with concrete invariants (and references to upstream trait impls) or avoid needing it.

Safer alternatives:

  • Prefer running this actor on a single-threaded executor or a LocalSet so it doesn’t need Send (e.g., a “local actor” variant if kameo supports it).
  • Offload the scap_cpal::Capturer to a dedicated OS thread and communicate with it via channels; keep the actor itself Send by only storing channel ends in the actor.
  • If it’s actually safe: document the invariants and add compile-time checks (see below). Even better, make scap_cpal::Capturer itself Send in its crate with a justified unsafe impl rather than layering the promise here.

Minimal immediate improvement (if you keep this):

  • Add a rigorous SAFETY comment that enumerates why moving across threads is sound, and what invariants must be upheld.

Proposed inline change (documentation only):

-    unsafe impl Send for WindowsAudioCapture {}
+    // SAFETY:
+    // - Explain why moving WindowsAudioCapture across threads is sound.
+    // - Demonstrate that all fields are Send or otherwise thread-agnostic.
+    // - State that methods are only called from one thread at a time (actor's mailbox),
+    //   and that underlying CPAL/WASAPI handles tolerate cross-thread moves.
+    unsafe impl Send for WindowsAudioCapture {}

If kameo supports a non-Send actor path, consider removing the impl instead:

-    unsafe impl Send for WindowsAudioCapture {}
+    // NOTE: Keep WindowsAudioCapture !Send and run this actor on a LocalSet/single-thread runtime.
+    // See follow-up in PR discussion for wiring.

Verification steps I recommend running in the repo to reduce guesswork:

And confirm upstream CPAL’s guarantees for the exact version in Cargo.lock:

If CPAL Stream is not Send for the version in use, this unsafe impl is a correctness bug and should be reverted in favor of a local actor/thread confinement approach.


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "=== Locate scap_cpal::Capturer definition ==="
rg -nP --type=rust -C3 '^\s*(pub\s+)?struct\s+Capturer\b' || true

echo
echo "=== Check for existing Send/Sync impls on Capturer ==="
rg -nP --type=rust -C2 'impl\s+(unsafe\s+)?(Send|Sync)\s+for\s+Capturer' || true

echo
echo "=== What CPAL version are we on? ==="
rg -n 'name = "cpal"' Cargo.lock -n -C1 || true

echo
echo "=== Usage: where is WindowsAudioCapture spawned? (confirm multithread runtime) ==="
rg -nP --type=rust -C3 'WindowsAudioCapture::spawn\(|spawn\(\s*WindowsAudioCapture' || true

Length of output: 3397


🌐 Web query:

Does cpal::<version from Cargo.lock>::Stream implement Send on Windows (WASAPI)? Cite source lines from the CPAL docs or code.

Error during web search


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "=== Locate WindowsAudioCapture struct ==="
rg -nP --type=rust 'pub struct WindowsAudioCapture' -C5 crates/recording/src/sources/screen_capture/windows.rs || true
rg -nP --type=rust 'struct WindowsAudioCapture' -C5 crates/recording/src/sources/screen_capture/windows.rs || true

echo
echo "=== Inspect WindowsAudioCapture fields for Capturer usage ==="
rg -nP --type=rust 'WindowsAudioCapture\s*{?[^;]*' -C5 crates/recording/src/sources/screen_capture/windows.rs || true

Length of output: 2895


🌐 Web query:

Does CPAL 0.15.3's Stream implement Send on Windows (WASAPI)? Please cite source code or docs.

Error during web search


Unsound unsafe impl Send for WindowsAudioCapture – Action Required

The unsafe impl Send for WindowsAudioCapture {} on lines 470–471 asserts that its sole field, scap_cpal::Capturer, is safe to move across threads. However:

  • The Capturer struct in crates/scap-cpal/src/lib.rs is defined as:
    pub struct Capturer {
        stream: Stream,
        config: StreamConfig,
        _output_device: cpal::Device,
    }
    and there are no Send implementations for it or its inner cpal::Stream on Windows. By default, CPAL’s WASAPI stream is not Send due to COM objects being thread-affine and requiring strict thread ownership.
  • Overriding this with an unsafe impl without a documented safety contract opens the door to UB and data races when Tokio’s multi-thread scheduler moves the actor between worker threads.

Next steps – choose one:

Remove the unsafe impl Send and confine the actor to a single-threaded context:

- unsafe impl Send for WindowsAudioCapture {}
+ // NOTE: WindowsAudioCapture is !Send. Run this actor on a Tokio LocalSet
+ //       or single-thread runtime to maintain COM thread-affinity.

This ensures correct WASAPI usage without unsound thread-crossing.

Justify and document the safety if truly safe to send:

- unsafe impl Send for WindowsAudioCapture {}
+ // SAFETY: Moving WindowsAudioCapture across threads is sound because:
+ // - `scap_cpal::Capturer` only holds thread-agnostic handles
+ //   (cpal::Device and StreamConfig implement Send/Sync, and
+ //    the underlying WASAPI COM objects are marshaled if moved).
+ // - All public methods synchronize access via the actor mailbox,
+ //   guaranteeing no concurrent calls.
+ unsafe impl Send for WindowsAudioCapture {}

And consider moving the Send impl upstream into the scap-cpal crate where the invariants can be centrally audited.

Without one of these fixes, the code unsafely sidesteps Rust’s guarantees. Please address as a critical fix.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
crates/recording/src/sources/screen_capture/windows.rs (1)

472-473: Unsafe Send for WindowsAudioCapture is likely unsound; avoid asserting cross-thread safety without proof

WindowsAudioCapture holds scap_cpal::Capturer. For WASAPI/CPAL-backed streams, thread affinity is common; forcing Send can cause UB/races if the actor is moved across threads. Unless you can prove every field is Send and cross-thread moves are safe, this unsafe impl must be removed or the actor confined to a single thread.

Immediate options:

  • Remove the impl and run the actor on a single-threaded executor/LocalSet or dedicated OS thread.
  • If you must keep it, document invariants with a rigorous SAFETY comment and add compile-time assertions for the fields.

Apply this diff to revert and add guidance:

-    unsafe impl Send for WindowsAudioCapture {}
+    // NOTE: WindowsAudioCapture is intentionally !Send. CPAL/WASAPI resources can be
+    // thread-affine. Run this actor on a LocalSet or a dedicated thread to preserve
+    // affinity and avoid UB from cross-thread moves.
+    // If you later decide to make this Send, add a rigorous SAFETY section explaining
+    // why all fields are Send and why moving across threads is sound.

Optionally, if you can prove safety, at minimum annotate with invariants:

-    unsafe impl Send for WindowsAudioCapture {}
+    // SAFETY:
+    // - Explain why scap_cpal::Capturer (and its internals) are safe to move across threads.
+    // - State that the actor model ensures no concurrent method calls.
+    // - Provide links/versions of CPAL guaranteeing Send on Windows.
+    unsafe impl Send for WindowsAudioCapture {}
#!/usr/bin/env bash
set -euo pipefail

echo "=== Locate scap_cpal::Capturer definition and its Send/Sync impls ==="
rg -nP --type=rust -C3 '^\s*(pub\s+)?struct\s+Capturer\b' || true
rg -nP --type=rust -C2 'impl\s+(unsafe\s+)?(Send|Sync)\s+for\s+Capturer\b' || true

echo
echo "=== Check CPAL types used by Capturer for Send/Sync ==="
rg -nP --type=rust -C3 '\bcpal::(Stream|Device|StreamConfig)\b' || true
rg -n 'name = "cpal"' Cargo.lock -n -C1 || true

echo
echo "=== Where is WindowsAudioCapture spawned/run (confirm threading context) ==="
rg -nP --type=rust -C3 'WindowsAudioCapture::spawn\(|spawn\(\s*WindowsAudioCapture' || true
🧹 Nitpick comments (2)
crates/recording/src/sources/screen_capture/windows.rs (2)

200-202: Downgrading StartCapturingAudio to String loses error structure and consistency with StartCapturingVideo

Switching from a typed error to String makes downstream handling and diagnostics worse and is inconsistent with StartCapturingVideo, which remains strongly typed. Prefer preserving the error type or at least keep the error chain.

Two viable approaches:

  1. Keep the typed send error (consistent with video):
-    #[error("StartCapturingAudio/{0}")]
-    StartCapturingAudio(
-        String, /* SendError<audio::StartCapturing, cpal::PlayStreamError> */
-    ),
+    #[error("StartCapturingAudio/{0}")]
+    StartCapturingAudio(
+        kameo::error::SendError<audio::StartCapturing, cpal::PlayStreamError>
+    ),
  1. If trait bounds force erasure, preserve the error chain:
-    #[error("StartCapturingAudio/{0}")]
-    StartCapturingAudio(String),
+    #[error("StartCapturingAudio")]
+    StartCapturingAudio {
+        #[source]
+        cause: Box<dyn std::error::Error + Send + Sync>,
+    },

This keeps std::error::Error::source() intact for better logs/telemetry.


294-294: Preserve error context when mapping the ask() failure

Converting to plain string drops variant info (e.g., mailbox closed vs. actor gone). Map to a structured error (or keep typed).

If adopting approach (1) above:

-                        .map_err(|v| SourceError::StartCapturingAudio(v.to_string()))?;
+                        .map_err(SourceError::StartCapturingAudio)?;

If adopting approach (2) above:

-                        .map_err(|v| SourceError::StartCapturingAudio(v.to_string()))?;
+                        .map_err(|e| SourceError::StartCapturingAudio { cause: Box::new(e) })?;

At minimum, enrich the string to aid debugging:

-                        .map_err(|v| SourceError::StartCapturingAudio(v.to_string()))?;
+                        .map_err(|e| SourceError::StartCapturingAudio(format!("ask(StartCapturing) send error: {e}")))?;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9ca07fd and 6f71671.

📒 Files selected for processing (1)
  • crates/recording/src/sources/screen_capture/windows.rs (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)

@Brendonovich Brendonovich merged commit 9d18f6e into main Aug 26, 2025
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants