Skip to content

Speed up screencap via gzip-compressed framebuffer with host-side PNG encoding - #375

Open
Komzpa wants to merge 7 commits into
JeffLIrion:masterfrom
Komzpa:fix/framebuffer-screencap-20261015-v2
Open

Komzpa wants to merge 7 commits into
JeffLIrion:masterfrom
Komzpa:fix/framebuffer-screencap-20261015-v2

Conversation

@Komzpa

@Komzpa Komzpa commented Oct 15, 2026 •

Copy link
Copy Markdown

On Android TVs with slow native PNG encoding, adb_screencap() can time out before returning an image. A tested Android projector took 16.83 seconds to produce native PNG, compared with 2.24 seconds for gzip-compressed raw capture in the device-side comparison.

Capture the raw framebuffer through gzip/base64 and encode PNG on the host, inside the existing synchronous and asynchronous adb_screencap() APIs. The async decoder runs in an executor. Support RGBA, RGBX, RGB888, RGB565 and BGRA framebuffers with 12- or 16-byte headers; retain native capture when the tools or pixel format are unsupported. Corrupt or truncated image data returns None without disconnecting a healthy ADB transport; connection errors still propagate.

Validation: 254 tests passed, 1 skipped. Coverage includes pixel formats, headers, malformed gzip/framebuffers, native fallback, async execution and transport errors. A read-only capture on the projector returned a 1920×1080 PNG, with decoded pixels identical to the raw framebuffer. End-to-end capture took 9.12 seconds and host encoding took 0.34 seconds; this does not establish that every capture fits Home Assistant's polling interval.

… encoding

Capture the raw Android framebuffer gzip-compressed over ADB and encode the PNG on the host with the standard library. The device no longer spends seconds encoding PNG itself. Falls back to screencap -p when gzip or base64 is unavailable or the pixel format is unsupported. Corrupt or truncated data raises ScreencapError.
The new framebuffer screencap calibration and ADBPythonSync.shell_binary
paths were uncovered, dropping total coverage on the PR branch below the
master baseline. Add tests for pinned-method fallbacks, calibration with
no valid candidate, big-endian RGB565 expansion, and shell_binary.
…e lint matrix

The requirement was a bare `black`, so each CI Python resolved a different
release (23.3.0 on 3.7 ... 26.10.1 on 3.10-3.12) and the newest normalizes
androidtv/exceptions.py's module docstring value, which the repo's own
formatting cannot absorb without a literal change. 23.3.0 is the newest
release that still supports the maintained Python 3.7 leg and it leaves
package, tests, and setup.py clean as authored.
On Python 3.7 unittest.mock.AsyncMock is unavailable, so the fallback
AsyncMock subclass returned the coroutine from __call__ without
awaiting it. Await the result when side_effect is a coroutine function,
matching the 3.8+ contract so async test patches resolve correctly.

Document the 3.7 semantics in venv_requirements.txt. No runtime code
changed; dependencies unchanged.
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.

1 participant