Bare `pixelpass` now opens a dialoguer-driven Host/View menu instead of
going straight to host mode. Host path copies the ticket to the system
clipboard via arboard with silent print-only fallback. View path
prompts for the ticket, then after the local listener binds prompts
mpv-vs-VLC and spawns it detached (setsid + null stdio) so the player
survives pixelpass exiting.
Headless invocations (`pixelpass <ticket>`, `pixelpass --repair`)
unchanged. Per spec at ~/Documents/pixelpass-interactive-mode-spec.md.
Symptom: cross-machine streams drifted ~6-9 seconds per minute. mpv
showed continuous "Audio device underrun detected" with video packets
piling up in its demuxer queue while audio queue stayed at 0.
Root causes:
- pipewiresrc captured at the host monitor's refresh rate (180Hz),
not at the configured framerate. The encoder + mux produced 6x more
frames per wallclock second than mpv could consume at realtime,
burying audio packet density in mpv's demuxer queue.
- `--profile=low-latency` sets `audio-buffer=0`, which is too
aggressive — any sub-millisecond network jitter immediately
starves the audio device. Underruns slowed mpv's audio clock, and
with `video-sync=audio` (also from low-latency profile) video
followed.
Fixes:
- `videorate ! video/x-raw,framerate=<fps>/1` after pipewiresrc to
cap input to the requested rate deterministically.
- mpv command grows `--audio-buffer=0.2 --demuxer-max-bytes=2M
--demuxer-readahead-secs=0.5`: small audio buffer to absorb network
jitter, demuxer cap to prevent runaway buildup.
A leaky-queues attempt landed and was reverted in the same commit —
it removed backpressure without addressing the root cause and made
things worse.
Verified cross-machine 6m51s: drift held at ~1s floor, zero audio
underruns, perfect A-V sync.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Implements the Wayland host pipeline from plan §4.5:
ashpd ScreenCast portal
-> CreateSession + SelectSources + Start + OpenPipeWireRemote
-> (pipewire fd, node_id, width, height)
gst-launch-1.0 pipewiresrc fd=N path=NODE_ID ! videoconvert
! video/x-raw,format=NV12 ! fdsink fd=1
ffmpeg
-f rawvideo -pix_fmt nv12 -video_size WxH -i pipe:0
-f pulse -i default
-c:v libx264 -preset ultrafast -tune zerolatency
-c:a aac -f mpegts -listen 1 http://127.0.0.1:<rand>
Phase 1 ships software x264 per plan §7; VAAPI is Phase 2.
src/host/wayland.rs is the new module. capture.rs becomes a thin
dispatcher with a CaptureHandle enum (Wayland today, X11 next).
host/mod.rs swaps the 150ms sleep for a poll-until-listener-ready
helper, and calls handle.shutdown().await for an orderly SIGTERM /
1s grace / SIGKILL teardown. The Drop impl is the panic backstop.
The pipewire fd handoff clears CLOEXEC before gst-launch spawn and
closes the parent's copy of the raw fd after the child has it.
Also deletes the empty src/host/tunnel.rs and src/viewer/tunnel.rs
placeholder files — the generic bridge in common/tunnel.rs is doing
the work, and there's no host- or viewer-specific tunnel concern
worth a module yet.