2026-07-06 01:45:57 +00:00
|
|
|
[Unit]
|
Drive both HDMI outputs via X kiosk + single mirrored mpv
The Pi 4's two HDMI connectors share one vc4 DRM card, and DRM master is
exclusive per card -- so the old per-connector-mpv design could never light
the second screen (the mirror mpv died with "Failed to acquire DRM master:
Permission denied"). Replace it with a minimal X server started by the
mediapi-mpv unit via xinit: X is the single DRM master, xrandr --same-as
clones the first output onto every other connected HDMI, and one fullscreen
mpv renders to both. Uses hwdec=v4l2m2m-copy (Pi HW decoder, ~1/3 the CPU of
software) and carries audio + the app's sole IPC socket.
- new scripts/mediapi-session.sh (X client: waits for connectors, mirrors,
execs mpv); rewritten mediapi-mpv unit (xinit on VT7)
- delete scripts/start-mpv.py and all mirror-socket/broadcast code in
player.py, config.py, __init__.py -- a single mpv means one socket
- install.sh installs xserver-xorg-core/xinit/x11-xserver-utils, writes
/etc/X11/Xwrapper.config, adds the user to input,tty
- README + troubleshooting updated
Verified from a cold boot on the Pi: both pixelvalve CRTCs scan the same
framebuffer; app healthy; HW decode engaged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 00:59:05 +00:00
|
|
|
Description=mediapi player (X kiosk: one mpv mirrored across all HDMI outputs)
|
|
|
|
|
# Wait until udev has settled so the DRM/HDMI devices (/dev/dri/*) exist before
|
|
|
|
|
# X tries to grab them. Without this a cold-boot race can leave X unable to find
|
|
|
|
|
# a screen. (systemd-udev-settle is deprecated but still the simplest reliable
|
|
|
|
|
# gate here; the session script additionally waits for connectors to appear.)
|
2026-07-06 02:19:52 +00:00
|
|
|
After=local-fs.target systemd-udev-settle.service
|
|
|
|
|
Wants=systemd-udev-settle.service
|
2026-07-06 01:45:57 +00:00
|
|
|
|
|
|
|
|
[Service]
|
|
|
|
|
Type=simple
|
Extract config into .env; add deploy.sh with overlay guard + rollback
- Move user-specific config (login, media roots, port, AP ssid/psk, service
user) into a gitignored .env; add .env.example template.
- config.py reads .env at startup via a tiny zero-dependency parser (works for
both `uv run` and systemd), and fails loudly if required vars are unset.
- load_secret_key falls back to an ephemeral key on a read-only filesystem
instead of crashing.
- systemd units become .template files; deploy.sh renders them with the
.env-derived user/paths.
- deploy.sh: refuses to run on a read-only overlay, git pull --ff-only,
uv sync, ensure secret key, re-apply the AP from .env, install units,
restart, health-check with automatic rollback to the previous commit.
- Scrub literal credentials/ssid out of the README; document .env + deploy.sh.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 02:06:47 +00:00
|
|
|
User=${MEDIAPI_USER}
|
Drive both HDMI outputs via X kiosk + single mirrored mpv
The Pi 4's two HDMI connectors share one vc4 DRM card, and DRM master is
exclusive per card -- so the old per-connector-mpv design could never light
the second screen (the mirror mpv died with "Failed to acquire DRM master:
Permission denied"). Replace it with a minimal X server started by the
mediapi-mpv unit via xinit: X is the single DRM master, xrandr --same-as
clones the first output onto every other connected HDMI, and one fullscreen
mpv renders to both. Uses hwdec=v4l2m2m-copy (Pi HW decoder, ~1/3 the CPU of
software) and carries audio + the app's sole IPC socket.
- new scripts/mediapi-session.sh (X client: waits for connectors, mirrors,
execs mpv); rewritten mediapi-mpv unit (xinit on VT7)
- delete scripts/start-mpv.py and all mirror-socket/broadcast code in
player.py, config.py, __init__.py -- a single mpv means one socket
- install.sh installs xserver-xorg-core/xinit/x11-xserver-utils, writes
/etc/X11/Xwrapper.config, adds the user to input,tty
- README + troubleshooting updated
Verified from a cold boot on the Pi: both pixelvalve CRTCs scan the same
framebuffer; app healthy; HW decode engaged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 00:59:05 +00:00
|
|
|
# video+render: GPU/DRM access. input: evdev under X. tty: own the VT for X.
|
|
|
|
|
SupplementaryGroups=video render input tty
|
|
|
|
|
# A controlling tty so the X server can take over VT 7.
|
|
|
|
|
TTYPath=/dev/tty7
|
|
|
|
|
StandardInput=tty
|
|
|
|
|
StandardOutput=journal
|
|
|
|
|
StandardError=journal
|
2026-07-06 01:45:57 +00:00
|
|
|
RuntimeDirectory=mediapi
|
|
|
|
|
RuntimeDirectoryMode=0770
|
Drive both HDMI outputs via X kiosk + single mirrored mpv
The Pi 4's two HDMI connectors share one vc4 DRM card, and DRM master is
exclusive per card -- so the old per-connector-mpv design could never light
the second screen (the mirror mpv died with "Failed to acquire DRM master:
Permission denied"). Replace it with a minimal X server started by the
mediapi-mpv unit via xinit: X is the single DRM master, xrandr --same-as
clones the first output onto every other connected HDMI, and one fullscreen
mpv renders to both. Uses hwdec=v4l2m2m-copy (Pi HW decoder, ~1/3 the CPU of
software) and carries audio + the app's sole IPC socket.
- new scripts/mediapi-session.sh (X client: waits for connectors, mirrors,
execs mpv); rewritten mediapi-mpv unit (xinit on VT7)
- delete scripts/start-mpv.py and all mirror-socket/broadcast code in
player.py, config.py, __init__.py -- a single mpv means one socket
- install.sh installs xserver-xorg-core/xinit/x11-xserver-utils, writes
/etc/X11/Xwrapper.config, adds the user to input,tty
- README + troubleshooting updated
Verified from a cold boot on the Pi: both pixelvalve CRTCs scan the same
framebuffer; app healthy; HW decode engaged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 00:59:05 +00:00
|
|
|
# xinit starts the X server on VT 7 and runs the session script as its client.
|
|
|
|
|
# The session mirrors the HDMI outputs (single DRM master) and execs one mpv.
|
|
|
|
|
# Non-root X is permitted via /etc/X11/Xwrapper.config (installed by install.sh).
|
|
|
|
|
ExecStart=/usr/bin/xinit ${PROJECT_DIR}/scripts/mediapi-session.sh -- :0 vt7 -nolisten tcp -keeptty
|
2026-07-06 01:45:57 +00:00
|
|
|
Restart=on-failure
|
|
|
|
|
RestartSec=2
|
|
|
|
|
|
|
|
|
|
[Install]
|
|
|
|
|
WantedBy=multi-user.target
|