Jump to content

wl2kodi

From Hugo's wiki

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-recorder and curl.
  • 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.

Tip Put the receiver on Ethernet and turn on the TV's Game Mode. Those two beat every option inside the tool.

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