Implement Linux desktop capture parity with process routing and exclusions#19
Conversation
There was a problem hiding this comment.
Pull request overview
This PR formalizes Linux as a supported build/test target for @recappi/sdk while clarifying that Linux support currently focuses on the platform-agnostic audio decoder path (not ShareableContent capture backends).
Changes:
- Add an explicit Linux Rust module to the crate graph and wire Linux into the library’s OS-gated module exports.
- Expand CI to run
cargo teston Linux/macOS/Windows and build/test Linux native bindings (x86_64-unknown-linux-gnu). - Add Rust unit tests covering decoder mono-mixing and resampling behavior, and update docs/tests to reflect platform support boundaries.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/linux/mod.rs |
Introduces an explicit Linux module with documentation about current Linux support scope. |
src/lib.rs |
Adds Linux module gating and re-exports for target_os = "linux". |
src/audio_decoder.rs |
Adds cross-platform Rust unit tests for stereo→mono mixing and resampling behavior. |
README.md |
Documents current platform support matrix and clarifies Linux is decoder-only for now. |
package.json |
Adds x86_64-unknown-linux-gnu to napi.targets for Linux binding builds. |
.github/workflows/CI.yml |
Adds cargo test matrix and Linux build/test coverage for native bindings. |
__test__/index.spec.ts |
Updates AVA wording to reflect “decoder-only platforms” vs platforms with capture backends. |
|
@codex review |
|
@pengx17 can https://github.com/Brooooooklyn/webcodecs-node replace the ffmpeg in tests? |
|
@Brooooooklyn I switched the Linux test fixture away from ffmpeg for helper-side tone generation. The fixture now generates PCM/WAV in Node and uses PulseAudio utilities / a small Node pipe writer for the synthetic sources.\n\nI kept ffmpeg in CI because the runtime under test still shells out to ffmpeg for the real Linux capture path, and I wanted the e2e coverage to stay honest.\n\nVerified locally with |
Context
@recappi/sdkfrom Linux decoder support into a real Linux desktop capture surface that matches the existing macOS / Windows API shape.Collaboration Process
sequenceDiagram participant U as User participant C as Codex participant R as Recappi SDK participant D as Docker Linux Env participant CI as GitHub Actions U->>C: Push Linux support beyond decoder-only behavior C->>R: Move Linux desktop capture into the native Rust/N-API surface U->>C: Reject the hand-maintained JS wrapper approach C->>R: Restore generated package entrypoints and keep Linux logic in Rust U->>C: Push for a real Linux implementation instead of a stub C->>R: Add dedicated-sink Pulse routing for process capture and exclusion-aware global capture U->>C: Review the PR again from scratch C->>R: Tighten failure rollback, capability detection, and CI coverage based on the review findings C->>D: Re-run local and Docker Linux verification C->>CI: Push the updated implementation and trim duplicated bindings jobsDesign Discussion
ShareableContentAPI contract and leaves Linux semantics misleading.tapAudio(processId)API, which attaches to an already-running process.ShareableContentimplementation.Final Approach
src/linux/mod.rs.tapAudio(processId)with dedicated Pulse routing:ffmpegtapGlobalAudio(excludedProcesses)with exclusion-aware routing:tapGlobalAudiogated by generic monitor-capture readinesstapAudioon readable sink-input state plus detectablemodule-null-sink/module-loopbacksupport without mutating the live Pulse graphnode@22bindings jobs from the matrixflowchart LR A[ShareableContent.tapAudio(processId)] --> B[Create dedicated capture sink] B --> C[Move matching sink-inputs into capture sink] C --> D[Loop capture sink back to original source sink] D --> E[Capture monitor with ffmpeg] F[ShareableContent.tapGlobalAudio(excludedProcesses)] --> G[Create exclusion-aware capture sink] G --> H[Move only non-excluded sink-inputs] H --> I[Capture sink monitor plus optional mic] E --> J[Float32 samples via N-API callback] I --> JVerification
cargo fmt --allcorepack yarn lintcargo testcorepack yarn testbash ./scripts/test-linux-docker.sh<!-- Please add any manual verification performed outside this Codex session -->Known Limitations / Follow-up
x86_64-unknown-linux-gnuonly.pactlandffmpegbeing available at runtime.tapAudio(processId)andtapGlobalAudio(excludedProcesses)remain best-effort because they depend on movable Pulse sink-inputs andapplication.process.idmetadata being exposed by the target streams.