6e4d30bfa9
VLC's MPEG-TS demuxer (libts_plugin.so) ships in a separate package on Arch / CachyOS (vlc-plugin-dvb). Without it, VLC silently falls back to the PS demuxer and misidentifies our H.264 stream — the symptom is a green screen. mpv doesn't share this dependency. - README: list vlc-plugin-dvb under requirements, replace the "green screen, not yet diagnosed" gotcha with the diagnosis. - interactive.rs: when the user picks VLC, check for /usr/lib/vlc/plugins/demux/libts_plugin.so and print a warning to stderr if it's missing. Soft warning, not a hard error — VLC still spawns so the user can confirm the symptom for themselves. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
171 lines
6.5 KiB
Markdown
171 lines
6.5 KiB
Markdown
# pixelpass
|
|
|
|
P2P screen sharing CLI for Linux. Single binary, hole-punched over
|
|
[iroh](https://www.iroh.computer/) — no port forwarding, no signup, no
|
|
server-side accounts. Hardware-encoded H.264 + AAC audio, viewed in
|
|
mpv or VLC.
|
|
|
|
Built for people who just want to show their screen to a friend
|
|
without spinning up a Discord call or fighting with NAT.
|
|
|
|
## Status
|
|
|
|
**v0.1.0** — verified end-to-end on the public internet (LTE relay path,
|
|
~2s latency, real carrier-grade NAT) as of 2026-05-20.
|
|
|
|
Working:
|
|
- Wayland capture via the screencast portal (KDE Plasma 6 confirmed; other
|
|
Wayland compositors with the portal should work but are untested)
|
|
- VAAPI H.264 encode in GStreamer (RDNA3 confirmed; other VAAPI-capable
|
|
GPUs should work)
|
|
- Audio capture of the default sink's monitor
|
|
- iroh QUIC bi-stream tunnel, direct-UDP and relay paths both verified
|
|
- Interactive Host/View menu with clipboard auto-copy and mpv/VLC picker
|
|
- Headless mode for scripts (`pixelpass <ticket>`)
|
|
|
|
Not yet working:
|
|
- X11 capture (stubbed, returns an error)
|
|
- Per-app audio routing (`--app <name>` is a flag stub)
|
|
- Multi-viewer (single viewer per host by design right now)
|
|
- `--repair` (PipeWire orphan cleanup) is a stub
|
|
|
|
## Quick start
|
|
|
|
### Interactive (recommended)
|
|
|
|
```sh
|
|
pixelpass
|
|
```
|
|
|
|
On the host machine: pick "Host", share a monitor via the portal dialog,
|
|
the ticket lands on your clipboard. Send it to your viewer however you
|
|
like (chat, email, paste in a note).
|
|
|
|
On the viewer machine: run `pixelpass`, pick "View", paste the ticket,
|
|
pick mpv or VLC. The player launches detached and the stream starts.
|
|
|
|
### Headless
|
|
|
|
```sh
|
|
# host: prints a ticket on stdout, waits for a peer
|
|
pixelpass
|
|
|
|
# viewer: skips the menu
|
|
pixelpass <ticket>
|
|
# then run the printed mpv command in another terminal
|
|
```
|
|
|
|
## Requirements
|
|
|
|
- Linux (Wayland session for now; X11 stubbed)
|
|
- A VAAPI-capable GPU and the right driver:
|
|
- AMD: `libva-mesa-driver`
|
|
- Intel: `intel-media-driver` (modern iGPUs) or `intel-vaapi-driver` (older)
|
|
- NVIDIA: `libva-nvidia-driver` (untested)
|
|
- `vainfo` from `libva-utils` should list at least one H.264 entrypoint
|
|
- GStreamer with these plugin packages installed:
|
|
- `gstreamer`, `gst-plugins-base`, `gst-plugins-good`, `gst-plugins-bad`,
|
|
`gst-plugins-ugly`, `gst-libav`, `gst-plugin-va`, `gst-plugin-pipewire`
|
|
- A player: `mpv` (recommended) or `vlc`
|
|
- If you use VLC: `vlc-plugin-dvb` must also be installed. Arch / CachyOS
|
|
ship VLC's MPEG-TS demuxer (`libts_plugin.so`) in that separate
|
|
package, and without it VLC misidentifies the H.264 stream and shows
|
|
a green screen. mpv is unaffected.
|
|
- PipeWire (for screencast portal + audio capture)
|
|
|
|
On Arch / CachyOS:
|
|
|
|
```sh
|
|
sudo pacman -S gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad \
|
|
gst-plugins-ugly gst-libav gst-plugin-va gst-plugin-pipewire \
|
|
libva-utils mpv
|
|
# plus your GPU's VAAPI driver
|
|
# plus, if you want to use VLC instead of mpv:
|
|
sudo pacman -S vlc vlc-plugin-dvb
|
|
```
|
|
|
|
If the viewer is running on battery, set the CPU governor to performance
|
|
or balanced — power-saver can choke even hardware-decoded 1080p H.264.
|
|
|
|
## Build
|
|
|
|
```sh
|
|
cargo build --release
|
|
./target/release/pixelpass --help
|
|
```
|
|
|
|
`rustc` 1.95+ / edition 2024.
|
|
|
|
## How it works
|
|
|
|
```
|
|
Host Viewer
|
|
──── ──────
|
|
Wayland portal (ashpd) ──> PipeWire fd
|
|
│
|
|
▼
|
|
gst-launch: pipewiresrc -> videorate -> vah264enc ->
|
|
h264parse -> mpegtsmux
|
|
(audio: pulsesrc <sink>.monitor ->
|
|
avenc_aac -> aacparse ─┘)
|
|
│ stdout
|
|
▼
|
|
tokio HTTP server (in-process, ~30 lines)
|
|
│
|
|
▼
|
|
iroh QUIC bi-stream (ALPN pixelpass/0) ◄══════════►
|
|
│
|
|
▼
|
|
tokio TcpListener
|
|
on 127.0.0.1:rand
|
|
│
|
|
▼
|
|
mpv / VLC HTTP client
|
|
```
|
|
|
|
The viewer's player connects to a localhost HTTP server, which is
|
|
just one end of the iroh tunnel. The host's HTTP server sits on the
|
|
other end and streams GStreamer's stdout (an MPEG-TS containing
|
|
hardware-encoded H.264 + AAC) through with no demux or remux.
|
|
|
|
iroh handles NAT traversal: direct UDP if hole-punching succeeds,
|
|
relay path otherwise. Both have been verified end-to-end.
|
|
|
|
## Why these choices
|
|
|
|
- **iroh over Holesail / dumbpipe / Tailscale**: single Rust dep, no Node
|
|
runtime, no signup, no daemon — fits the "one self-contained binary"
|
|
goal.
|
|
- **GStreamer for capture/encode, not ffmpeg**: stride/format pitfalls
|
|
when bridging raw video between processes; one in-process pipeline
|
|
sidesteps them.
|
|
- **In-process Rust HTTP server, not ffmpeg-as-server**: ffmpeg's
|
|
`-listen 1` is one-shot and probe-budget-sensitive; the Rust task is
|
|
pure passthrough with no codec assumptions.
|
|
- **MPEG-TS over fragmented MP4**: every player on Linux handles it
|
|
out of the box. AV1-in-MPEG-TS was tried and is unworkable through
|
|
libavformat — if AV1 ever comes back, it has to ride a different
|
|
container.
|
|
- **VAAPI H.264 over x264**: ~5% of one CPU core instead of ~50% on
|
|
the host's hardware.
|
|
|
|
## Known limitations and gotchas
|
|
|
|
- **VLC needs `vlc-plugin-dvb`** to demux MPEG-TS on Arch / CachyOS. The
|
|
symptom of missing it is a green screen — VLC falls back to the PS
|
|
demuxer and misidentifies the H.264 stream. pixelpass warns at
|
|
player-launch time if the plugin isn't on disk. mpv doesn't need it.
|
|
- **Audio echo** if the host plays the stream through speakers and
|
|
captures system audio — expected, the mic / monitor picks up the
|
|
playback. Headphones bypass it.
|
|
- **Single viewer per host** by design right now. Restarting the player
|
|
against the same URL fails with "connection refused"; restart the
|
|
viewer too.
|
|
- **VAAPI driver must be package-tracked**, not an orphaned `.so` on
|
|
disk. mpv's `--hwdec=auto` silently falls back to software decode
|
|
otherwise, which then chokes on a low-power viewer.
|
|
|
|
## License
|
|
|
|
MIT OR Apache-2.0, your pick.
|