Skip to content

fix(viewer): rotate 180 video in the ISP on the linuxfb boards (Pi 1/2/3)#3207

Merged
vpetersson merged 1 commit into
masterfrom
fix/3198-isp-hwflip-180-linuxfb
Jul 17, 2026
Merged

fix(viewer): rotate 180 video in the ISP on the linuxfb boards (Pi 1/2/3)#3207
vpetersson merged 1 commit into
masterfrom
fix/3198-isp-hwflip-180-linuxfb

Conversation

@vpetersson

Copy link
Copy Markdown
Contributor

Summary

Rotated video on the Qt5 linuxfb boards (Pi 1/2/3) collapses to ~2 fps: the software videoflip rotates every full-resolution frame on a single core. Investigating #3198 on a real Pi 2 turned up two things that reshape it.

First, 180 is just as broken as 90 — the issue only ever measured 90. Second, 180 doesn't need the CPU at all. A horizontal flip plus a vertical flip composes to a 180 rotation, and the bcm2835 ISP exposes both as v4l2 controls on the very v4l2convert pass that already does the aspect-fit scale. Driving them through extra-controls keeps the pipeline fully hardware and restores the full frame rate.

Measurements (real Pi 2, 1080p30, HW decode ceiling 43.7 fps)

Via fpsdisplaysink:

screen_rotation pipeline fps
0 all hardware 27.2
180 software videoflip (before) 2.3
180 ISP hflip+vflip (after) 26.9
90 / 270 software videoflip (unchanged) ~2

The ISP result is not just fast, it's pixel-identical to the software rotation: mean absolute difference 0.00 against a true 180 rotation of the unflipped frame.

Validation

Beyond unit tests, this was validated on the integrated viewer stack on a Pi 2 (not a component harness) — same asset, same service, A/B'd against the shipping player:

build delivered fps @ rotation 180
shipping (videoflip) 0.5
this PR (ISP flip) 7.7 — matching the unrotated reference (8.3)

(That sampler under-reads in absolute terms because it costs CPU on a Pi 2; it's used for A/B only, which is why the absolute numbers come from fpsdisplaysink above.) Framebuffer captures confirm the screen is visibly, correctly rotated. Testbed restored to its original state afterwards.

Why 90/270 are left alone (and documented as unsupported)

The quarter turns need a transpose, and no bcm2835 device exposes a rotate control — v4l2-ctl -l on the ISP lists only horizontal_flip/vertical_flip, and nothing on any other video device. That's the same limit the pi3-64 vc4 DRM plane hits (0/180 + reflect only, forum 6730). Two independent blocks, one silicon limit.

The "scale before flip" idea from the issue is a dead end, and I've dropped it: it only helps the letterboxed case, while the case that actually matters — portrait content on a portrait-mounted panel — rotates to exactly fill the framebuffer, leaving no downscale to exploit. Measured slightly worse: 1.9 vs 2.1 fps.

So 90/270 keep the software videoflip (visually correct, just slow) and docs/board-enablement.md now states plainly that rotated video is unsupported on Pi 1/2/3 — use a Pi 4/5, where the platform layer rotates. Still images and web pages cost nothing per frame and are unaffected.

Test plan

  • pytest tests/ — 1187 passed
  • ruff check . / ruff format --check clean
  • E2E on the integrated stack on a real Pi 2 (fps A/B + framebuffer capture)
  • ISP output verified pixel-exact vs the software rotation

Fixes #3198

🤖 Generated with Claude Code

Rotated video on the Qt5 linuxfb boards (Pi 1/2/3) collapses to ~2 fps
because the software `videoflip` rotates every full-resolution frame on
a single core. Issue #3198 only measured 90, but 180 is just as broken
(measured 2.3 fps on a Pi 2 at 1080p30, vs 27 fps unrotated).

180 does not need the CPU at all: a horizontal flip plus a vertical
flip composes to a 180 rotation, and the bcm2835 ISP exposes both as
v4l2 controls on the same `v4l2convert` pass that already does the
aspect-fit scale. Driving them via `extra-controls` keeps the pipeline
fully hardware and restores the full frame rate.

Measured on a Pi 2 (1080p30, HW decode ceiling 43.7 fps), by
fpsdisplaysink:

  rotation 0   (all hardware)        27.2 fps
  rotation 180 (videoflip, before)    2.3 fps
  rotation 180 (ISP flip, after)     26.9 fps

The ISP output is pixel-identical to the software rotation (mean
absolute difference 0.00 against rot180 of the unflipped frame), and
the integrated viewer stack on a Pi 2 reproduces the same result end to
end: 0.5 -> 7.7 delivered fps at rotation 180, matching the unrotated
reference, with the screen visibly rotated.

90/270 keep the software videoflip and stay slow: they need a
transpose, and no bcm2835 device exposes a rotate control (`v4l2-ctl -l`
on the ISP lists only hflip/vflip) — the same limit the pi3-64 vc4 DRM
plane hits, which offers 0/180 + reflect only. Reordering to
scale-before-flip does not rescue them either: the case that matters,
portrait content on a portrait panel, rotates to exactly fill the
framebuffer, so there is no downscale to exploit ahead of the flip
(measured 1.9 vs 2.1 fps, i.e. slightly worse). Documented as
unsupported instead of pretending otherwise.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

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

Improves video playback performance on Qt5/linuxfb Raspberry Pi 1/2/3 boards when screen_rotation=180 by offloading the rotation to the bcm2835 ISP (via v4l2convert extra-controls) instead of using the CPU-bound videoflip.

Changes:

  • Update the fbdev GStreamer sink pipeline to apply 180° rotation using ISP horizontal_flip + vertical_flip controls (keeping 90°/270° on videoflip).
  • Extend unit tests to validate rotation-specific pipeline composition (90/270 use videoflip, 180 uses ISP controls, 0 uses neither).
  • Document the rotation behavior and performance implications for Pi 1/2/3 in board enablement docs.

Reviewed changes

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

File Description
src/anthias_viewer/gst_fbdev_player.py Adds ISP-driven 180° rotation via v4l2convert extra-controls, keeps 90/270 on videoflip, updates arg choices and docs in-code.
tests/test_gst_fbdev_player.py Updates/expands tests to assert the intended pipeline structure for 0/90/180/270 rotations.
docs/board-enablement.md Documents Pi 1/2/3 rotation limitations and the hardware fast-path for 180°.

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

@vpetersson
vpetersson merged commit d8ecf54 into master Jul 17, 2026
10 checks passed
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.

Rotated video is ~2 fps on Pi 1/2/3 (linuxfb): fix 180 in the ISP, 90/270 have no HW path

2 participants