Files
pixelpass/README.md
T
mollusk 74b4101d4f vlc-plugin-ffmpeg: extend docs + runtime check
The previous vlc-plugin-dvb diagnosis was incomplete. On a laptop with
only vlc-plugin-dvb installed, VLC reads the MPEG-TS container, sees
the H.264 stream type in the PMT, then errors "Codec h264 ... is not
supported" because libavcodec_plugin.so is also a split package and
also wasn't pulled in by the base `vlc` install.

Installing vlc-plugin-ffmpeg (which pulls ffmpeg4.4 as a compat dep)
on the laptop made VLC play pixelpass cleanly via Intel iHD hardware
decode.

- README: list both plugin packages under requirements; rewrite the
  known-limitations line.
- interactive.rs: extend the launch-time check to also probe for
  libavcodec_plugin.so; combine both into one warning that lists
  every missing piece and the single pacman invocation to fix.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 06:21:32 -04:00

177 lines
6.9 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, two split plugin packages are also needed on Arch-family
distros — the base `vlc` package does not pull them in:
- `vlc-plugin-dvb` — provides the MPEG-TS demuxer (`libts_plugin.so`).
Without it, VLC can't parse the container.
- `vlc-plugin-ffmpeg` — provides the H.264 decoder
(`libavcodec_plugin.so`). Without it, VLC parses the container,
identifies the codec as H.264, then errors with
`Codec h264 ... is not supported`.
mpv ships its own decoder stack and doesn't share either dependency.
- PipeWire (for screencast portal + audio capture)
On Arch / CachyOS / EndeavourOS:
```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 vlc-plugin-ffmpeg
```
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` and `vlc-plugin-ffmpeg`** on Arch-family
distros — the base `vlc` package doesn't pull these in, and missing
either one breaks playback (the first kills the demuxer, the second
kills the H.264 decoder). pixelpass warns at player-launch time if
either plugin isn't on disk. mpv doesn't share these dependencies.
- **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.