Skip to content

fix(viewer): pi2/pi3 video via GStreamer HW pipeline (replace VLC) - #2972

Merged
vpetersson merged 1 commit into
masterfrom
fix/pi3-video-ffmpeg-fbdev
Jun 2, 2026
Merged

fix(viewer): pi2/pi3 video via GStreamer HW pipeline (replace VLC)#2972
vpetersson merged 1 commit into
masterfrom
fix/pi3-video-ffmpeg-fbdev

Conversation

@vpetersson

@vpetersson vpetersson commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Issue Fixed

pi2/pi3 (Qt5 linuxfb) video plays black fleet-wide. Reproduced + root-caused on a Pi 3B+ (across every OS version, so not a recent regression in itself): Showing asset … (video) is logged but nothing reaches the screen. Images/webpages are fine; pi4/pi5/x86 are fine (Qt6 in-process QtMultimedia).

Root cause

  • These boards used to play video with VLC's Broadcom --vout=mmal_vout (HW decode, HW scale, HW color-convert, HW scanout — no DRM master). The Bookworm upgrade (Upgrade containers to use Bookworm #1980) dropped mmal and nothing replaced the vout.
  • With no --vout, VLC falls back to outputs that can't drive a bare linuxfb console: the rpidistro drm_vout wants a compositor DRM lease (Failed to get xlease — there's no X/Wayland), and the KMS/master vouts need DRM master, which a uid-1000 viewer can't acquire (the viewer's python process already holds card0EBUSY/EPERM). mpv --vo=drm hits the same EBUSY.

Fix

Replace VLCMediaPlayer with GstFbdevMediaPlayer for the linuxfb boards (pi1/pi2/pi3): spawn gst-launch-1.0 playbin with a fully-hardware video sink that reaches /dev/fb0 without any DRM master / compositor:

playbin → v4l2h264dec (bcm2835 codec, HW decode)
        → v4l2convert (bcm2835 ISP, HW scale + YUV→fb-format)
        → fbdevsink /dev/fb0

This drives the same VPU + ISP silicon mmal used — decode, scale and color-convert all stay in hardware; only the final framebuffer write is a CPU memcpy. playbin gives container-agnostic demux, auto-plugs the HW decoder (v4l2h264dec at PRIMARY rank), and degrades gracefully when a clip has no audio track. Rotation rides videoflip, inserted only when the panel is actually rotated.

Why not ffmpeg → fbdev

An interim FFmpegFbdevMediaPlayer (ffmpeg HW-decode → CPU convert → -f fbdev) was built and then abandoned after benchmarking on the Pi 3: HW decode worked (~60 fps), but doing the YUV→RGB convert + scale on the ARM CPU via swscale managed only ~6 fps to rgb565 (no NEON rgb565 path) and ~28 fps to bgra — and any real scaling collapsed it further. v4l2convert moves that work back onto the ISP, which is what the ~40 fps, 0-drop benchmark above measures. (An earlier "decode hang" scare turned out to be a malformed trimmed test clip that didn't start on an IDR — the strict HW decoder waits for a random-access point; well-formed clips decode fine.)

Description

  • New GstFbdevMediaPlayer: builds the gst-launch playbin argv, loops the on-screen slot by re-launching on EOS (breaks on error), and kills by process group so no gst-launch orphan keeps the framebuffer. Reads fb geometry/format from /sys/class/graphics/fb0 to pin the v4l2convert output caps.
  • MediaPlayerProxy routes pi1/pi2/pi3 → GstFbdevMediaPlayer; everything else (incl. forced pi4-64/arm64) stays on MPVMediaPlayer.
  • Image: add gstreamer1.0-{tools,plugins-base,plugins-good,plugins-bad} to the Qt5 viewer; drop the now-unused vlc apt package + python-vlc dep (+ its mypy override).
  • Docs (docs/board-enablement.md), the upload-gate comment in processing.py, and C++/.pro comments updated.

On-device validation

Run on a live fleet Pi 3 (HDMI attached). This surfaced — and the PR fixes — two bugs that the headless benchmark could not (it used file:// URIs and fakesink audio):

  1. Bare-path URI — local assets store a bare absolute path, but playbin's uri needs a scheme; _as_gst_uri() now wraps bare paths as file://.
  2. Missing alsasink — Debian ships the ALSA sink in gstreamer1.0-alsa (not -plugins-base); without it the audio-sink=alsasink … fails pipeline construction and black-screens all video. Added to the image deps.

After both fixes the full pipeline renders correctly on real hardware.

Checklist

  • I have performed a self-review of my own code.
  • New and existing unit tests pass locally (962 passed) and lint/format are clean.
  • End-to-end validated on a live balena Pi 3 (HDMI connected, 1920×1080 @16bpp rgb565): the full pipeline (v4l2h264decv4l2convert→RGB16 → fbdevsink + alsasink HDMI audio) constructs and writes motion video to the real /dev/fb0 with zero pipeline errors (verified by capturing the framebuffer across frames). HW decode + scale + convert benchmarked at ~40 fps / 0 drops.
  • x86 — N/A (linuxfb-only path; x86 uses QtMultimedia).
  • Documentation updated (docs/board-enablement.md).

🤖 Generated with Claude Code

@vpetersson
vpetersson requested a review from a team as a code owner June 2, 2026 08:42
@vpetersson vpetersson self-assigned this Jun 2, 2026
@vpetersson
vpetersson requested a review from Copilot June 2, 2026 08:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR restores video playback on Qt5/linuxfb Raspberry Pi boards by replacing the prior VLC-based path with an ffmpeg-based player that hardware-decodes via V4L2 M2M and renders directly to /dev/fb0 (fbdev), avoiding DRM-master/compositor requirements that cause black video on Pi 2/3 post-Bookworm.

Changes:

  • Add FFmpegFbdevMediaPlayer (ffprobe codec selection → optional <codec>_v4l2m2m decode → scale/pad/format/transpose → -f fbdev).
  • Route linuxfb Pi devices to the new ffmpeg player via MediaPlayerProxy and update unit tests accordingly.
  • Update docs and Qt-side comments to reflect the new linuxfb video path.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/anthias_viewer/media_player.py Introduces FFmpegFbdevMediaPlayer and routes Pi linuxfb playback to it.
tests/test_media_player.py Replaces VLC tests with ffmpeg/fbdev player tests + proxy routing assertions.
docs/board-enablement.md Documents the Pi 2/3 ffmpeg→fbdev playback strategy and rationale.
src/anthias_webview/src/view.h Updates comments describing how Qt5 boards play video.
src/anthias_webview/src/view.cpp Updates comments describing how Qt5 boards play video.
src/anthias_webview/src/mainwindow.h Updates comments about Qt5 boards bypassing Qt video slots.
src/anthias_webview/AnthiasViewer.pro Updates build comments describing the Qt5 video path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/anthias_viewer/media_player.py Outdated
Comment thread src/anthias_viewer/media_player.py Outdated
Comment thread src/anthias_viewer/media_player.py
@vpetersson
vpetersson force-pushed the fix/pi3-video-ffmpeg-fbdev branch from a7a6b16 to 010c723 Compare June 2, 2026 11:19
@vpetersson vpetersson changed the title fix(viewer): pi2/pi3 video via ffmpeg HW-decode → fbdev (replace VLC) fix(viewer): pi2/pi3 video via GStreamer HW pipeline (replace VLC) Jun 2, 2026
@vpetersson
vpetersson requested a review from Copilot June 2, 2026 11:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 11 changed files in this pull request and generated 3 comments.

Comment thread src/anthias_viewer/media_player.py Outdated
Comment thread src/anthias_viewer/media_player.py
Comment thread tests/test_media_player.py Outdated
@vpetersson
vpetersson force-pushed the fix/pi3-video-ffmpeg-fbdev branch 3 times, most recently from 81f1d4d to 514e62a Compare June 2, 2026 11:56
@vpetersson
vpetersson requested a review from Copilot June 2, 2026 11:57
@vpetersson
vpetersson force-pushed the fix/pi3-video-ffmpeg-fbdev branch from 514e62a to 6dc3dbf Compare June 2, 2026 12:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 11 changed files in this pull request and generated 6 comments.

Comment thread src/anthias_webview/src/view.h Outdated
Comment thread src/anthias_webview/src/view.cpp Outdated
Comment thread src/anthias_webview/src/mainwindow.h
Comment thread src/anthias_webview/AnthiasViewer.pro Outdated
Comment thread tools/image_builder/utils.py Outdated
Comment thread docs/board-enablement.md
- Replace VLCMediaPlayer with GstFbdevMediaPlayer: spawn `gst-launch-1.0
  playbin` with a fully-hardware video sink — v4l2h264dec (bcm2835 codec)
  decodes, v4l2convert (bcm2835 ISP) HW-scales + converts YUV to the
  framebuffer format, fbdevsink paints /dev/fb0. No DRM master / X /
  Wayland needed, which a bare uid-1000 viewer with no compositor cannot
  acquire.
- Restores hardware video on the Qt5 linuxfb boards after #1980 dropped
  the Broadcom mmal_vout; drives the same VPU + ISP silicon mmal used.
  Measured on a Pi 3: 1080p30 -> rgb565 at ~40 fps, zero dropped frames.
  (An interim ffmpeg -> fbdev approach was abandoned: HW decode worked
  but CPU YUV->rgb565 convert via swscale managed only ~6 fps — no NEON
  path — and CPU scaling is unaccelerated. v4l2convert moves that to the
  ISP.)
- playbin gives container-agnostic demux, auto-plugged HW decoder, and
  graceful optional-audio. Loop the slot via re-launch-on-EOS; kill by
  process group so no gst-launch orphan keeps the framebuffer.
- Rotation rides `videoflip`, inserted only when the panel is rotated.
- Add gstreamer1.0-{tools,plugins-base,plugins-good,plugins-bad} to the
  Qt5 viewer image; drop the now-unused vlc apt package + python-vlc dep
  (+ its mypy override). Sync the upload-gate comment in processing.py.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@vpetersson
vpetersson force-pushed the fix/pi3-video-ffmpeg-fbdev branch from 6dc3dbf to 11bc78f Compare June 2, 2026 12:10
@sonarqubecloud

sonarqubecloud Bot commented Jun 2, 2026

Copy link
Copy Markdown

@vpetersson
vpetersson merged commit 9481495 into master Jun 2, 2026
10 checks passed
@vpetersson vpetersson mentioned this pull request Jun 2, 2026
5 tasks
vpetersson added a commit that referenced this pull request Jun 2, 2026
- CalVer (YYYY.0M.MICRO); still June 2026, micro 0 -> 1
- Ships the QML VideoOutput presentation path (#2975), the pi2/pi3
  GStreamer HW video pipeline (#2972), and the x86 WLR_DRM_NO_ATOMIC
  display-freeze fix (#2978)
- Also picks up Pi 4 eglfs rotation (#2971, #2973), the armv7 viewer
  spawn retry (#2969), the Writeback-connector headless guard (#2968),
  and viewer log cleanups (#2977, #2979)

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants