commit 14e3afdd7b262815d6596e4fed0522ddc853d8cc Author: Joey Yakimowich-Payne Date: Thu Jan 29 16:14:32 2026 -0700 Plan diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..4c717d7 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,59 @@ +# Warppipe Plan (C++ libpipewire library) + +- [ ] Milestone 0 — Groundwork and constraints + - [ ] Choose build system: CMake (confirmed). Define minimal library target and example app target. + - [ ] Define public API surface (namespaces, class/struct layout, error model, threading model). + - [ ] Define performance budget and metrics (e.g., 200 create/modify/delete ops in < 1s on typical desktop). + - [ ] Choose identity strategy for ephemeral sources (match rules on application.name, application.process.binary, media.role, node.name, fallback to client properties; avoid serial IDs). + - [ ] Tests to add (non-happy path/edge cases): instructions: create unit tests that fail on missing PipeWire daemon, missing libpipewire-module-link-factory, missing libpipewire-module-metadata, and misconfigured runtime properties (e.g., invalid PW_KEY_MEDIA_CLASS). + - [ ] Performance tests: instructions: add a microbenchmark harness that measures connect→create→destroy of N no-op objects (no links) and asserts subsecond N=200 on a warm PipeWire connection. + +- [ ] Milestone 1 — Core runtime and registry model + - [ ] Implement a WarpContext (pw_main_loop/pw_thread_loop + pw_context + pw_core) with lifecycle and reconnect handling. + - [ ] Implement registry cache for nodes/ports/links with event listeners, and a stable “object identity” resolver (node name, application properties). + - [ ] Expose a query API to list nodes, ports, and identify sinks/sources. + - [ ] Tests to add (non-happy path/edge cases): instructions: simulate registry events where nodes/ports disappear mid-iteration; ensure safe iteration and cleanup; verify reconnection logic after daemon restart. + - [ ] Performance tests: instructions: measure time to ingest registry snapshot of 1000 objects and process 100 add/remove events; assert latency per event and total under subsecond. + +- [ ] Milestone 2 — Virtual sinks and sources + - [ ] Implement virtual sink/source creation via pw_stream_new with PW_KEY_MEDIA_CLASS set to Audio/Sink or Audio/Source and autoconnect flags as needed (see src/pipewire/stream.h). + - [ ] Support “null” behavior (discard) and “loopback” behavior (sink that forwards to target) using stream properties and explicit links. + - [ ] Provide a naming scheme and metadata tags for virtual nodes to ensure stable identification. + - [ ] Tests to add (non-happy path/edge cases): instructions: create sink/source with missing media class and expect validation error; create duplicate node name; attempt to connect when target node is absent. + - [ ] Performance tests: instructions: create/destroy 100 virtual sinks and 100 virtual sources in a tight loop; measure wall time and ensure it stays within the target budget. + +- [ ] Milestone 3 — Link management API + - [ ] Implement link creation via link-factory (load libpipewire-module-link-factory and call pw_core_create_object with link.input.* and link.output.* props; see src/modules/module-link-factory.c, src/examples/internal.c, src/tools/pw-link.c). + - [ ] Support linking by node+port names and by object IDs; add object.linger and link.passive options. + - [ ] Add link deletion and link reconciliation (auto-remove stale links when endpoints vanish). + - [ ] Tests to add (non-happy path/edge cases): instructions: link to non-existent port; link output-to-output or input-to-input; remove node while link is initializing; create two links to same port and validate policy behavior. + - [ ] Performance tests: instructions: create 200 links between existing ports; measure create+destroy time and verify subsecond target where possible. + +- [ ] Milestone 4 — Persistence and “ephemeral source” policy + - [ ] Implement persistence (JSON or TOML) for: virtual nodes, links, and per-app routing rules. Persist on change; load on startup. + - [ ] Implement policy engine: + - [ ] Watch for node/port appearance; apply stored rules to auto-link ephemeral sources to preferred sinks. + - [ ] Store mapping by rule (app identity → target sink/source). Avoid serial IDs; use stable metadata (app/process/role). + - [ ] Allow user override to update rule and persist. + - [ ] Integrate metadata store for defaults and routing hints using libpipewire-module-metadata (see src/modules/module-metadata.c). Track default.audio.sink/source and default.configured.audio.sink/source for stable defaults; use a dedicated warppipe.* metadata namespace to avoid conflicts. + - [ ] Tests to add (non-happy path/edge cases): instructions: rule for app that disappears and reappears under a different PID; verify re-routing; conflicting rules (two matches) resolved deterministically; persistence file corrupted; metadata module not available. + - [ ] Performance tests: instructions: simulate 200 ephemeral sources (connect/disconnect) and measure time to apply routing rules and create links; ensure rule lookup is O(1) or O(log n). + +- [ ] Milestone 5 — Stability, compatibility, and tooling + - [ ] Provide a simple CLI (optional) to inspect nodes, create virtual nodes, link/unlink, and export/import config (useful for manual testing). + - [ ] Add documentation: API usage patterns, threading model, and performance notes. + - [ ] Validate behavior with PipeWire session manager present (WirePlumber) to avoid fighting policy; allow “policy-only” mode (observes and sets metadata without forcing links). + - [ ] Tests to add (non-happy path/edge cases): instructions: run with session manager disabled/enabled and verify no infinite re-link loops; ensure policy mode doesn’t override user defaults unexpectedly. + - [ ] Performance tests: instructions: end-to-end scenario (virtual sink + app connect + reroute + disconnect + reconnect) repeated 200 times; measure median/95th percentile latency. + +## Design notes grounded in PipeWire code/docs + +- Virtual sinks/sources via pw_stream_new and PW_KEY_MEDIA_CLASS (Audio/Sink, Audio/Source): src/pipewire/stream.h. +- Explicit link creation via link-factory and link.input.*/link.output.* properties: src/modules/module-link-factory.c, src/examples/internal.c, src/tools/pw-link.c. +- Metadata store creation and defaults (default.audio.sink/source and configured defaults): src/modules/module-metadata.c; metadata tools in src/tools/pw-metadata.c; pulse-related defaults/metadata in src/modules/module-protocol-pulse/*. +- Official concepts: nodes/ports/links and session manager policy: https://docs.pipewire.org/page_objects_design.html and https://docs.pipewire.org/page_session_manager.html + +## Key behavior for ephemeral sources + +- Store per-app routing rules keyed by stable application metadata (name/process/role) and apply on node appearance. +- When an app reappears, re-link its output port(s) to the saved target sink (instead of default speakers).