wl2kodi
wl2kodi mirrors the screen of a wlroots Wayland desktop to Kodi with low latency. The stream is hardware-encoded on the sender's GPU and hardware-decoded on the receiver, so the TV box stays idle.
Why
Most screen-mirroring setups make the receiver do real work. That fails on a Raspberry Pi: it has a capable video decoder but a weak CPU, so anything that misses the hardware decode path turns into a slideshow.
wl2kodi puts every expensive step on the sender. The laptop captures its own framebuffer without a copy, encodes H.264 on its GPU, and sends a plain MPEG-TS stream over UDP. The Pi only does hardware decode straight to the display plane. On a Raspberry Pi 3 receiving 1080p30, the CPU sits around 13%, mostly Kodi's own interface.
┌─────────────── laptop ───────────────┐ ┌──────── Raspberry Pi ────────┐ │ compositor ──DMA-BUF──▶ GPU encoder │ │ V4L2 M2M ──DRM PRIME──▶ TV │ │ (no copy) (H.264) │─UDP─▶│ hardware decode, zero-copy │ └──────────────────────────────────────┘ └──────────────────────────────┘
It is deliberately not a new protocol: H.264 MPEG-TS over UDP is what Kodi already plays for IPTV.
Requirements
- Sender: a wlroots compositor (Hyprland, Sway,
river...), a VAAPI-capable GPU,
wf-recorderandcurl. - Receiver: Kodi with hardware decoding on, and Settings → Services → Control → Allow remote control via HTTP enabled.
# pacman -S wf-recorder curl
Installation
$ git clone https://github.com/hugoocoto/wl2kodi $ install -Dm755 wl2kodi/wl2kodi ~/.local/bin/wl2kodi
It is also available through the pm user repository.
Usage
$ wl2kodi --tv 192.168.1.50
It detects the monitor and its resolution, asks Kodi what the TV runs at, sizes the stream to match, picks a render node, starts encoding and tells the TV to tune in. Ctrl-C stops the encoder and the playback on the TV.
$ wl2kodi --tv 192.168.1.50 --mode fill # crop to the TV's aspect $ wl2kodi --tv 192.168.1.50 --res 720 # lower latency, softer text $ wl2kodi --tv 192.168.1.50 --res 720 --fifo 300 --bitrate 5M # lowest latency $ wl2kodi --tv 192.168.1.50 --dry-run # show what it would do
Every option also works as an environment variable; see
wl2kodi --help.
Latency
Expect 200–500 ms. Kodi does not expose its decode and render queues, so that is a floor. Fine for slides, demos, video and browsing; not for gaming.
How it stays fast
| Decision | Reason |
|---|---|
| DMA-BUF capture | The frame never touches system RAM on the way to the encoder |
| No B-frames | B-frames reference future frames, so they buffer by definition |
low_power=1 |
Uses the GPU's low-latency encode path |
| Keyframe every 0.5 s | A lost packet resolves twice as fast |
| Bounded receive FIFO | ffmpeg's default holds seconds of video; wl2kodi caps it
(--fifo) |
| UDP | No retransmission stalls; a late frame is worse than a lost one |
pkt_size=1316 |
Exactly 7 MPEG-TS packets, so datagrams never fragment |
The macroblock ceiling
H.264 Level 4.1 allows 8192 macroblocks. 1920×1080 is 8160; a 16:10
panel at 1920×1200 is 9000. Over the limit, the receiver silently falls
back to software decoding. wl2kodi always scales on the sender and
refuses to emit more than --max-mb (default 8192).
Troubleshooting
- Nothing appears, but Kodi says OK: check Kodi's log
for
failed to configure renderer, and don't start a stream within ~30 s of restarting Kodi. - wf-recorder is already running:
pkill -x wf-recorder. - Washed-out colors: the encoder emits full range; not handled yet.
Limitations
- Video only, no audio yet.
- wlroots compositors only; GNOME and KDE would need a PipeWire portal.
- H.264 only, one receiver at a time.
See also
- Kodi, setting Kodi up for this and its sibling tools
- Source code (MIT)
- send2kodi, to send a link or a file instead of the screen
- browser2kodi, to send what you are looking at in the browser
- Tools and applications