For reliable, hardware-accelerated raspberry pi video playback on the Pi 5 running Bookworm OS, use mpv with the drm (Direct Rendering Manager) video output driver, controlled via Python and gpiozero. This bypasses the Wayland/X11 compositor overhead, delivering raw hardware overlay performance essential for 24/7 digital signage and kiosk loops.
The Raspberry Pi Video Playback Decision Matrix
Choosing the right media backend for embedded displays is where most kiosk builds fail. The legacy omxplayer is deprecated on Pi 5, and standard desktop players introduce unacceptable latency and screen-tearing. Use this decision path to select your backend:
| Condition / Requirement | Recommended Backend | Hardware Decode Path |
|---|---|---|
| Need GUI, mouse support, and streaming plugins | Kodi (LibreELEC) | FFmpeg V4L2 |
| Need network streams, complex playlists, and GUI | VLC (cvlc) | MMAL / V4L2 |
| Need headless/DRM kiosk, GPIO control, low latency | mpv (Python bindings) | V4L2-Request / DRM-Prime |
mpv via python-mpv with vo='drm'. It claims the display hardware directly, eliminating the 200-400MB RAM overhead of running a desktop environment.
Hardware Spec Sheet and GPIO Pin Mapping
This build targets the Raspberry Pi 5 (8GB variant). The 8GB model is mandatory for smooth 4K H.265 buffering and prevents out-of-memory crashes when looping high-bitrate local files. Total BOM cost is approximately $165 USD in 2026.
| Component | Exact Variant / Model | Spec Notes |
|---|---|---|
| Compute Board | Raspberry Pi 5 (8GB) | Requires active cooler; unified memory architecture. |
| Display | Waveshare 7" HDMI LCD (H) | 1024x600, IPS, capacitive touch (touch unused here). |
| Storage | SanDisk Extreme PRO 128GB | A2 rating required for fast OS boot and file indexing. |
| Controls | 3x Cherry MX or Sanwa Arcade Buttons | Momentary NO (Normally Open) switches. |
GPIO Pin Mapping Table
We use internal pull-up resistors configured in software, so buttons only need to connect to Ground and the respective GPIO pin. No external resistors are required.
| Function | BCM GPIO Pin | Physical Pin (40-pin Header) | Wiring |
|---|---|---|---|
| Play / Pause | 17 | 11 | Switch between Pin 11 and GND (Pin 9) |
| Next Track | 27 | 13 | Switch between Pin 13 and GND (Pin 14) |
| Exit / Shutdown | 22 | 15 | Switch between Pin 15 and GND (Pin 20) |
Complete Python Playback Controller Code
Before running this, install the dependencies on your Pi 5: sudo apt install libmpv-dev mpv and pip3 install python-mpv gpiozero. This script targets the Pi 5 board variant and assumes your video files are in a /home/pi/videos/ directory.
import mpv
import sys
import os
import glob
from gpiozero import Button
from signal import pause
# --- Pin Definitions (BCM Numbering) ---
PIN_PLAY_PAUSE = 17
PIN_NEXT = 27
PIN_EXIT = 22
VIDEO_DIR = '/home/pi/videos/'
def get_playlist():
"""Fetches all mp4/mkv files in the target directory."""
extensions = ['*.mp4', '*.mkv', '*.avi']
files = []
for ext in extensions:
files.extend(glob.glob(os.path.join(VIDEO_DIR, ext)))
return sorted(files)
def main():
playlist = get_playlist()
if not playlist:
print('Error: No video files found in', VIDEO_DIR)
sys.exit(1)
try:
# Initialize MPV with Direct Rendering Manager (bypasses X11/Wayland)
player = mpv.MPV(
vo='drm',
hwdec='auto',
loop_playlist='inf',
osd_level=0,
input_default_bindings=False,
input_vo_keyboard=False
)
except Exception as e:
print(f'Failed to initialize mpv DRM backend: {e}')
sys.exit(1)
# Load playlist
for video in playlist:
player.loadfile(video, mode='append-play')
# --- GPIO Button Setup ---
# pull_up=None relies on gpiozero default internal pull-ups
btn_play = Button(PIN_PLAY_PAUSE, pull_up=True, bounce_time=0.05)
btn_next = Button(PIN_NEXT, pull_up=True, bounce_time=0.05)
btn_exit = Button(PIN_EXIT, pull_up=True, bounce_time=0.05)
def toggle_pause():
player.pause = not player.pause
print('Paused' if player.pause else 'Playing')
def next_video():
player.playlist_next()
print('Skipped to next track')
def safe_exit():
print('Exit button pressed. Shutting down gracefully...')
player.terminate()
sys.exit(0)
btn_play.when_pressed = toggle_pause
btn_next.when_pressed = next_video
btn_exit.when_pressed = safe_exit
print('Raspberry Pi Video Playback Kiosk Active. Press Exit to quit.')
try:
pause() # Keep script alive to listen for GPIO events
except KeyboardInterrupt:
safe_exit()
if __name__ == '__main__':
main()
Debugging: 'Failed to initialize EGL display' and Hardware Failures
When migrating raspberry pi video playback scripts from Pi 4 to Pi 5, or moving from X11 to Wayland, you will inevitably hit display server errors. The most notorious is:
[vo/gpu] Failed to initialize EGL display
[vo/gpu] Failed to create EGL context!
Error opening/initializing the selected video_out (-vo) device.
Ranked Causes and Fixes
- Cause: Running
vo='gpu'headless (No Compositor). The GPU output requires an active Wayland or X11 session to create an EGL context.
Fix: Changevo='gpu'tovo='drm'in the Python script to render directly to the framebuffer via KMS/DRM. - Cause: Missing Mesa DRM libraries. Pi OS Bookworm Lite strips GUI libraries by default.
Fix: Runsudo apt install libgl1-mesa-dri libegl1. - Cause: User lacks
/dev/dripermissions. Thedrmbackend requires direct hardware access.
Fix: Add your user to the render group:sudo usermod -aG render,video $USERand reboot.
The First 3 Things to Check When Playback Fails
If the script runs but the screen remains black or stutters, execute these diagnostic steps in order:
- Verify KMS Display Detection: Run
kmsprintin the terminal. If your Waveshare display doesn't show up as a connected connector, check the HDMI cable and ensuredtoverlay=vc4-kms-v3dis active in/boot/firmware/config.txt. - Check Hardware Decode Status: Run
mpv --hwdec=auto test_video.mp4manually. Watch the terminal output forUsing hardware decoding (drmprime). If it falls back to software decoding, your CPU will throttle at 100% on 1080p60 content. - Inspect Power Supply Throttling: The Pi 5 requires a 27W USB-C PD supply. Run
vcgencmd get_throttled. If it returns anything other than0x0, the Pi is throttling the GPU clock, causing frame drops.
Extending and Simplifying the Build
Depending on your deployment environment, you may need to scale this architecture up or strip it down.
How to Simplify (The Zero-Code Route)
If you do not need physical GPIO buttons and just want a monitor to turn on and loop a single video forever, delete the Python script entirely. Instead, use systemd to launch mpv directly. Create a service file at /etc/systemd/system/kiosk.service:
[Service]
ExecStart=/usr/bin/mpv --vo=drm --hwdec=auto --loop-file=inf /home/pi/videos/loop.mp4
Restart=always
User=pi
This removes Python overhead, reduces boot-to-video time to under 8 seconds, and eliminates gpiozero dependency conflicts.
How to Extend (Networked Signage)
To convert this into a networked digital signage node, integrate MQTT via the Paho library. Subscribe to an MQTT topic like signage/node_01/cmd. When a 'next' or 'download' payload arrives, trigger the player.playlist_next() method or initiate an rsync pull to update the local /home/pi/videos/ directory dynamically without rebooting the Pi. For comprehensive mpv configuration flags, refer to the official manual to fine-tune cache sizes for high-bitrate 4K streams over SMB shares.
Final Recommendation: Stick to the Pi 5 8GB, use the drm video output, and rely on python-mpv for control. It is the most robust, crash-resistant pipeline for embedded raspberry pi video playback available in 2026.






