Initial commit: Go driver for the Lian Li Galahad II LCD
Streams H.264 to the Galahad II LCD via USB (gousb) with: - ffmpeg CLI transcoding (no fragile FFmpeg C-ABI bindings) - pure-Go Annex-B access-unit splitting - framerate pacing with graceful shutdown and USB reconnect - TOML config, cobra CLI, structured slog logging, unit tests
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
# galahad2lcd
|
||||
|
||||
A modern Go driver for the **Lian Li Galahad II LCD** cooler display. It
|
||||
transcodes a video or GIF to H.264 and streams it over USB to the 480×480
|
||||
LCD, running as a systemd service.
|
||||
|
||||
This is a clean-room rewrite of the archived
|
||||
[GalahadII_LCD_Linux](https://github.com/H4rk3nz0/GalahadII_LCD_Linux)
|
||||
project. It keeps the exact wire protocol but drops the fragile FFmpeg C-ABI
|
||||
dependency in favour of the system `ffmpeg` binary, adds structured logging,
|
||||
automatic USB reconnection and a testable, layered architecture.
|
||||
|
||||
## Features
|
||||
|
||||
- `daemon` — transcodes media to H.264 and streams it in a loop
|
||||
- `set` — change the input/rotation/speed and restart the service
|
||||
- `list` — enumerate USB devices to diagnose connectivity
|
||||
- `version` — print the build version
|
||||
- Automatic reconnect if the USB link is lost (e.g. after suspend)
|
||||
- Graceful shutdown on SIGINT/SIGTERM (`systemctl stop` works cleanly)
|
||||
- Structured logs (`log/slog`) captured by journald
|
||||
- Rotation, playback speed and framerate overrides via config or flags
|
||||
|
||||
## Requirements
|
||||
|
||||
- Go 1.22+ (to build)
|
||||
- FFmpeg + FFprobe (any recent version, e.g. `sudo pacman -S ffmpeg`)
|
||||
- libusb (transitive build dependency of `github.com/google/gousb`)
|
||||
- systemd
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
sudo ./packaging/install.sh /path/to/media.gif
|
||||
```
|
||||
|
||||
The script builds the binary, installs the systemd unit and udev rule, writes
|
||||
an initial config and starts the service. If no media file is given, configure
|
||||
it afterwards:
|
||||
|
||||
```sh
|
||||
sudo galahad2lcd set --input /path/to/media.gif --rotate 0
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Settings live in `/etc/galahad2lcd.toml`:
|
||||
|
||||
```toml
|
||||
[display]
|
||||
input = "/home/user/media.gif"
|
||||
rotate = 0 # 0, 90, 180, 270 (clockwise)
|
||||
speed = 1.0 # >1 slower, <1 faster
|
||||
|
||||
[usb]
|
||||
vendor_id = 0x0416
|
||||
product_id = 0x7395
|
||||
|
||||
[stream]
|
||||
fps = 0 # 0 = auto-detect from the source
|
||||
cache_path = "/tmp/galahad_cache.h264"
|
||||
```
|
||||
|
||||
The `set` command updates this file and restarts the service:
|
||||
|
||||
```sh
|
||||
sudo galahad2lcd set --input /home/user/media.gif --rotate 90 --speed 1.2
|
||||
sudo galahad2lcd set --fps 15 --no-restart # apply later
|
||||
```
|
||||
|
||||
CLI flags also override the config for one-off runs:
|
||||
|
||||
```sh
|
||||
sudo galahad2lcd daemon --input /home/user/media.gif --rotate 180
|
||||
```
|
||||
|
||||
## Diagnostics
|
||||
|
||||
```sh
|
||||
galahad2lcd list # find the LCD on the USB bus (needs udev rule or root)
|
||||
journalctl -u galahad2lcd -f # live daemon logs
|
||||
```
|
||||
|
||||
The bundled udev rule (`packaging/99-galahad2lcd.rules`) grants the
|
||||
logged-in session user access to the device so `list` works without root.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
cmd/galahad2lcd entry point (composition root)
|
||||
internal/cli cobra commands: daemon, set, list, version
|
||||
internal/config TOML configuration model, load/save/validate
|
||||
internal/transcoder ffmpeg/ffprobe wrapper, filter graph, FPS detection
|
||||
internal/h264 Annex-B stream → access-unit splitting (pure Go)
|
||||
internal/stream frame-rate pacing loop (testable, device-agnostic)
|
||||
internal/device Device interface + gousb (libusb) implementation
|
||||
internal/protocol USB framing (pure functions, golden-tested)
|
||||
internal/service systemd unit management
|
||||
```
|
||||
|
||||
Layers depend only downward. `stream` talks to a `device.Device` interface,
|
||||
so the transport can be swapped or faked in tests without touching the rest
|
||||
of the pipeline.
|
||||
|
||||
## Development
|
||||
|
||||
```sh
|
||||
make build # build ./bin/galahad2lcd
|
||||
make test # unit + integration tests (integration needs ffmpeg)
|
||||
make vet # go vet
|
||||
make fmt # gofmt
|
||||
make install # copy binary to /usr/local/bin
|
||||
```
|
||||
|
||||
## Uninstall
|
||||
|
||||
```sh
|
||||
sudo ./packaging/uninstall.sh
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
Reference in New Issue
Block a user