Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8a1d966
gallium/va: add the termux-va wire protocol mirror and daemon client
lfdevs Aug 29, 2026
4dadca5
gallium/va: add the termux-va bridge screen/pipe wrappers and codec
lfdevs Aug 29, 2026
4eaddf6
gallium/va: wire the termux-va bridge into driver init and the build
lfdevs Aug 29, 2026
893122f
ci, docs: build the branch tar.gz with the VA bridge and document it
lfdevs Aug 29, 2026
d13026c
gallium/va: create the termux-va bridge vscreen with GPU backend sele…
lfdevs Aug 29, 2026
b7b6c39
gallium/va: stabilize the Termux VA bridge and software fallback
lfdevs Aug 30, 2026
928a794
docs/va: document tested Termux VA backends
lfdevs Aug 31, 2026
3f29ee1
gallium/va: fix termux-va frame export and playback
lfdevs Aug 31, 2026
76bf87c
gallium/va: harden termux-va KGSL frame handoff
lfdevs Sep 4, 2026
fd908f1
gallium/va: fix KGSL H.264 frame handoff
lfdevs Sep 5, 2026
78ca01e
gallium/va: fix High-profile H.264 CSD synthesis
lfdevs Sep 5, 2026
70f0adc
gallium/va: stabilize H.264 PPS reference defaults
lfdevs Sep 5, 2026
4bdff71
gallium/va: add HEVC Main decode bridge
lfdevs Sep 5, 2026
04e4034
gallium/va: preserve HEVC SPS RPS count
lfdevs Sep 5, 2026
aac1f60
gallium/va: add AV1 decode bridge
lfdevs Sep 6, 2026
90fd3e3
gallium/va: fix AV1 surface reuse and frame handoff
lfdevs Sep 6, 2026
d8ee332
gallium/va: infer H.264 PPS reference defaults
lfdevs Sep 6, 2026
cc65e46
gallium/va: support DRM-less KGSL containers
lfdevs Sep 6, 2026
c46c7bb
gallium/va: fix KGSL video decode in DRM-less PRoot containers
lfdevs Sep 8, 2026
a5e3b8f
termux-va: add Wayland DRM shim for KGSL PRoot
lfdevs Sep 9, 2026
8123986
termux-va: allow disabling AV1 profile advertisement
lfdevs Sep 22, 2026
7d0e2f8
termux-va: handle hidden VP9 reference frames
lfdevs Sep 22, 2026
b977b6d
gallium/va: stabilize synthesized H.264 PPS defaults
lfdevs Sep 22, 2026
ff8eebc
gallium/va: add per-codec decode disable switches
lfdevs Sep 22, 2026
d723bb0
gallium/va: use stable H.264 PPS reference defaults
lfdevs Sep 22, 2026
54919b6
termux-va: handle GBM DRM capability probes on KGSL
lfdevs Sep 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 37 additions & 9 deletions .github/workflows/build-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ jobs:
--prefix=/usr \
-Dplatforms=x11,wayland \
-Dgallium-drivers=freedreno,zink,virgl,llvmpipe \
-Dgallium-va=disabled \
-Dgallium-va=enabled \
-Dtermux-va-bridge=enabled \
-Dtermux-va-wayland-shim=enabled \
-Dvideo-codecs=all \
-Dgallium-mediafoundation=disabled \
-Dvulkan-drivers=freedreno \
-Dvulkan-layers= \
Expand Down Expand Up @@ -165,7 +168,10 @@ jobs:
--prefix=/usr \
-Dplatforms=x11,wayland \
-Dgallium-drivers=freedreno,zink,virgl,llvmpipe \
-Dgallium-va=disabled \
-Dgallium-va=enabled \
-Dtermux-va-bridge=enabled \
-Dtermux-va-wayland-shim=enabled \
-Dvideo-codecs=all \
-Dgallium-mediafoundation=disabled \
-Dvulkan-drivers=freedreno \
-Dvulkan-layers= \
Expand Down Expand Up @@ -271,7 +277,10 @@ jobs:
--prefix=/usr \
-Dplatforms=x11,wayland \
-Dgallium-drivers=freedreno,zink,virgl,llvmpipe \
-Dgallium-va=disabled \
-Dgallium-va=enabled \
-Dtermux-va-bridge=enabled \
-Dtermux-va-wayland-shim=enabled \
-Dvideo-codecs=all \
-Dgallium-mediafoundation=disabled \
-Dvulkan-drivers=freedreno \
-Dvulkan-layers= \
Expand Down Expand Up @@ -375,7 +384,10 @@ jobs:
--prefix=/usr \
-Dplatforms=x11,wayland \
-Dgallium-drivers=freedreno,zink,virgl,llvmpipe \
-Dgallium-va=disabled \
-Dgallium-va=enabled \
-Dtermux-va-bridge=enabled \
-Dtermux-va-wayland-shim=enabled \
-Dvideo-codecs=all \
-Dgallium-mediafoundation=disabled \
-Dvulkan-drivers=freedreno \
-Dvulkan-layers= \
Expand Down Expand Up @@ -494,7 +506,10 @@ jobs:
--prefix=/usr \
-Dplatforms=x11,wayland \
-Dgallium-drivers=freedreno,zink,virgl,llvmpipe \
-Dgallium-va=disabled \
-Dgallium-va=enabled \
-Dtermux-va-bridge=enabled \
-Dtermux-va-wayland-shim=enabled \
-Dvideo-codecs=all \
-Dgallium-mediafoundation=disabled \
-Dvulkan-drivers=freedreno \
-Dvulkan-layers= \
Expand Down Expand Up @@ -598,7 +613,10 @@ jobs:
--prefix=/usr \
-Dplatforms=x11,wayland \
-Dgallium-drivers=freedreno,zink,virgl,llvmpipe \
-Dgallium-va=disabled \
-Dgallium-va=enabled \
-Dtermux-va-bridge=enabled \
-Dtermux-va-wayland-shim=enabled \
-Dvideo-codecs=all \
-Dgallium-mediafoundation=disabled \
-Dvulkan-drivers=freedreno \
-Dvulkan-layers= \
Expand Down Expand Up @@ -702,7 +720,10 @@ jobs:
--prefix=/usr \
-Dplatforms=x11,wayland \
-Dgallium-drivers=freedreno,zink,virgl,llvmpipe \
-Dgallium-va=disabled \
-Dgallium-va=enabled \
-Dtermux-va-bridge=enabled \
-Dtermux-va-wayland-shim=enabled \
-Dvideo-codecs=all \
-Dgallium-mediafoundation=disabled \
-Dvulkan-drivers=freedreno \
-Dvulkan-layers= \
Expand Down Expand Up @@ -775,6 +796,7 @@ jobs:
python3-Mako python3-packaging python3-yaml python3-ply \
libffi-devel \
libzstd-devel \
libva-devel \
glslang

- name: Cache ccache
Expand Down Expand Up @@ -818,7 +840,10 @@ jobs:
--prefix=/usr \
-Dplatforms=x11,wayland \
-Dgallium-drivers=freedreno,zink,virgl,llvmpipe \
-Dgallium-va=disabled \
-Dgallium-va=enabled \
-Dtermux-va-bridge=enabled \
-Dtermux-va-wayland-shim=enabled \
-Dvideo-codecs=all \
-Dgallium-mediafoundation=disabled \
-Dvulkan-drivers=freedreno \
-Dvulkan-layers= \
Expand Down Expand Up @@ -923,7 +948,10 @@ jobs:
--prefix=/usr \
-Dplatforms=x11,wayland \
-Dgallium-drivers=freedreno,zink,virgl,llvmpipe \
-Dgallium-va=disabled \
-Dgallium-va=enabled \
-Dtermux-va-bridge=enabled \
-Dtermux-va-wayland-shim=enabled \
-Dvideo-codecs=all \
-Dgallium-mediafoundation=disabled \
-Dvulkan-drivers=freedreno \
-Dvulkan-layers= \
Expand Down
102 changes: 102 additions & 0 deletions docs/envvars.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1404,6 +1404,108 @@ VA-API environment variables

enable MPEG4 for VA-API, disabled by default.

termux-va bridge environment variables
--------------------------------------

The termux-va bridge forwards VA-API video decoding over a Unix socket to
the termux-va daemon running in Termux (Android MediaCodec hardware
decode). See :doc:`termux-va`.

.. envvar:: TERMUX_VA_BRIDGE

``1``/``true`` forces the bridge on, ``0``/``false`` forces it off.
When unset, the bridge activates automatically if ``TERMUX_VA_SOCKET``
or ``TERMUX_VA_SOCKET_DIR`` is set, or if the default endpoint exists
as a socket.

.. envvar:: TERMUX_VA_SOCKET

Full path of the daemon's Unix socket file, overriding the default
``/tmp/termux-va/termux-va.sock`` (the container-side view of the
Termux ``$TMPDIR/termux-va/`` shared-tmp directory).

.. envvar:: TERMUX_VA_SOCKET_DIR

Directory containing the socket; ``termux-va.sock`` is appended.
Takes effect when ``TERMUX_VA_SOCKET`` is unset. The same two
variables are understood by the daemon itself, so one setting covers
both ends.

.. envvar:: TERMUX_VA_GPU_BACKEND

selects how the bridge creates the underlying screen that hosts the
decode surfaces:

- ``auto`` (default): try the stock loader and fall back to llvmpipe. The KGSL alias is not attempted automatically.
- ``kgsl``: force the KGSL Freedreno alias. GPU submission uses
``/dev/kgsl-3d0`` while the handed fd remains the control/identity fd.
EGL and GLX loader selection follows this value, so
``MESA_LOADER_DRIVER_OVERRIDE=kgsl`` and ``FD_FORCE_KGSL=1`` are not
required separately.
- ``drm``: use stock loader selection only.
- ``sw``: use llvmpipe only; no GPU is needed for the CPU frame-copy paths.

.. envvar:: TERMUX_VA_DISABLE_AVC

set to ``1``, ``true`` or ``on`` to hide the H.264/AVC profiles from
libva. ``TERMUX_VA_DISABLE_H264`` is accepted as a compatibility alias.

.. envvar:: TERMUX_VA_DISABLE_HEVC

set to ``1``, ``true`` or ``on`` to hide the HEVC Main profile from libva.

.. envvar:: TERMUX_VA_DISABLE_VP9

set to ``1``, ``true`` or ``on`` to hide the VP9 Profile 0 profile from
libva.

.. envvar:: TERMUX_VA_DISABLE_AV1

set to ``1``, ``true`` or ``on`` to hide the AV1 Main profile from libva.
This is useful when Android exposes only a software MediaCodec component
for AV1, so applications can use their native software decoder instead.

.. envvar:: DMD_WANT_SHM

set to ``0`` to disable the memfd shared-memory frame transport
(zero-copy) and always receive frames inline on the socket. The
``DMD_`` prefix is kept for compatibility with the upstream protocol
tooling.

.. envvar:: TERMUX_VA_PIPELINE_DEPTH

sets the bridge's normal pending-picture depth to a value from 2 to 32
(default 6).
When shared-memory transport is enabled, the value is clamped to the
daemon's ``SHM_SLOTS`` limit.

.. envvar:: DMD_VA_CPU_COPY

controls how staged frames are copied into bridge surfaces. On the KGSL
backend CPU-mapped writes are enabled by default to make the cache handoff
to a separate Vulkan/KGSL importer explicit. Set to ``0``, ``false`` or
``off`` to retain the asynchronous Gallium ``texture_subdata`` path; set
to any other non-empty value to force CPU copies.

.. envvar:: DMD_VA_CONTIGUOUS_DMABUF

controls the NV12 surface layout exported to consumers. Set to ``1``,
``true`` or ``on`` to place both planes in one dma-buf, or to ``0`` (or any
other value) to retain separate plane objects. When unset, the bridge
automatically selects the single-object layout for a KGSL-only container
that has no DRM render node; this is required by Chromium's current native
pixmap importer. ``TERMUX_VA_CONTIGUOUS_DMABUF`` is an alias.

.. envvar:: DMD_VA_LOG

set to ``1`` to enable the bridge's daemon-client logging on stderr.

.. envvar:: LIBVA_DRIVER_NAME

set to ``termuxva`` to make libva load the bridge through the
``termuxva_drv_video.so`` megadriver symlink (recommended; automatic
driver discovery does not know the bridge).

VC4 driver environment variables
--------------------------------

Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ Linux, FreeBSD, and other operating systems.
isaspec
rusticl
android
termux-va
macos
Linux Kernel Drivers <https://www.kernel.org/doc/html/latest/gpu/>

Expand Down
139 changes: 139 additions & 0 deletions docs/termux-va.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
termux-va bridge
================

The termux-va bridge forwards VA-API video decoding from this Mesa build
(the container side) to the `termux-va` daemon running in Termux on the
Android host, which decodes with the Android MediaCodec API in hardware
and returns NV12 frames. Applications inside a Linux container that
shares Termux's tmp directory (``proot-distro ... --shared-tmp``) get
hardware decoding through the standard VA-API without any modification.

The porting model follows anland-termux: a Termux daemon, a Unix socket
placed in the shared tmp directory, and a bridge on the container side.
The daemon lives in the `termux-va` repository; the wire protocol is
byte-compatible with droidspaces-media-decode protocol v3.

Supported codecs: H.264 (Constrained Baseline / Main / High), HEVC Main, VP9 Profile 0, and AV1 Main, outputting NV12 progressive frames. Profiles are advertised to libva through the underlying screen; encode and other codecs are not provided.

Building
--------

Build with ``-Dgallium-va=enabled -Dtermux-va-bridge=enabled`` and at
least one of ``h264dec``, ``h265dec``, ``vp9dec`` in ``video-codecs``
(for example ``-Dvideo-codecs=all``). The megadriver is additionally
exposed as ``termuxva_drv_video.so`` so libva can select it with
``LIBVA_DRIVER_NAME=termuxva``. Native Wayland Chromium support also
requires ``-Dtermux-va-wayland-shim=enabled``; this installs the
process-local DRM compatibility shim described below.

Activation
----------

The bridge is runtime-gated; a Mesa build with the bridge behaves exactly
like an unmodified one until activation:

- ``TERMUX_VA_BRIDGE=1`` forces the bridge on, ``0`` forces it off.
- Unset: the bridge activates when ``TERMUX_VA_SOCKET`` /
``TERMUX_VA_SOCKET_DIR`` is set, or when the default endpoint exists as
a socket.

When the bridge is active but the daemon is unreachable, driver init
fails cleanly and applications fall back to software decoding.

The bridge can selectively hide hardware decode profiles from libva. Set
``TERMUX_VA_DISABLE_AVC=1`` (or the compatibility alias
``TERMUX_VA_DISABLE_H264=1``) to disable H.264/AVC, or set
``TERMUX_VA_DISABLE_HEVC=1``, ``TERMUX_VA_DISABLE_VP9=1``, or
``TERMUX_VA_DISABLE_AV1=1`` for the corresponding codec. The values ``1``,
``true``, and ``on`` enable a switch. A hidden profile is not advertised by
VA-API, so applications can use their native software decoder instead of
sending that format through the bridge. This is useful when Android's
MediaCodec exposes only a software component for a format, or when a codec
needs to be disabled for compatibility testing.

Socket location
---------------

Default endpoint (container view): ``/tmp/termux-va/termux-va.sock`` -
the same directory as the Termux daemon's ``$TMPDIR/termux-va/`` through
the shared tmp mount. Both ends understand ``TERMUX_VA_SOCKET`` (full
socket path) and ``TERMUX_VA_SOCKET_DIR`` (directory), so one setting
covers the daemon and the bridge; Android system properties are accepted
as a fallback through Mesa's ``os_get_option``.

Underlying screen
-----------------

The decode surfaces live on a screen created by the bridge before the
frontend asks for one. ``TERMUX_VA_GPU_BACKEND`` selects how:

``auto`` (default) tries the stock loader first and falls back to llvmpipe. It does not try the KGSL alias automatically because environments that expose a display DRM node may not have a usable Vulkan or stock DRM path. ``kgsl`` explicitly selects the fork's KGSL Freedreno alias: GPU submission goes to ``/dev/kgsl-3d0`` while the handed fd stays the control/identity fd. The same selection is propagated to Mesa's EGL and GLX loaders, so callers do not need to add ``MESA_LOADER_DRIVER_OVERRIDE=kgsl`` or ``FD_FORCE_KGSL=1``. ``sw`` forces llvmpipe for setups without GPU access; the VA decode paths used by vainfo and ffmpeg work without a GPU. ``drm`` selects the stock loader only.

PRoot containers
----------------

A PRoot container can expose ``/dev/kgsl-3d0`` while exposing no usable DRM
render node. With ``TERMUX_VA_GPU_BACKEND=kgsl`` the bridge opens KGSL itself
when the display backend supplies no fd. It also uses one linear dma-buf for
both NV12 planes when no DRM render node is present, because Chromium's native
pixmap importer currently accepts only one dma-buf for this format. Override
this choice with ``DMD_VA_CONTIGUOUS_DMABUF`` or
``TERMUX_VA_CONTIGUOUS_DMABUF`` when needed.

Chromium's native Wayland Ozone backend normally requires a DRM render node
for its GPU process, independently of VA-API. For a DRM-less PRoot desktop,
enable ``-Dtermux-va-wayland-shim=enabled`` and load the installed
``libtva_drm_shim_wayland.so`` into Chromium with ``LD_PRELOAD``. The shim
maps Chromium's DRM discovery calls to ``/dev/kgsl-3d0``; it does not replace
the Mesa KGSL backend or create a DRM device for other applications. Launch
Chromium with the native Wayland platform, ``--render-node-override=/dev/kgsl-3d0``
and ``--hardware-video-device-path=/dev/kgsl-3d0``:

.. code-block:: sh

export WAYLAND_DISPLAY=wayland-0
export XDG_RUNTIME_DIR=/run/user/$(id -u)
export LIBVA_DRIVER_NAME=termuxva
export TERMUX_VA_BRIDGE=1
export TERMUX_VA_GPU_BACKEND=kgsl
export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libtva_drm_shim_wayland.so
google-chrome --ozone-platform=wayland --no-sandbox --use-gl=angle \
--use-angle=gles --render-node-override=/dev/kgsl-3d0 \
--hardware-video-device-path=/dev/kgsl-3d0 \
--enable-features=VaapiIgnoreDriverChecks,AcceleratedVideoDecoder,AcceleratedVideoDecodeLinuxGL,AcceleratedVideoDecodeLinuxZeroCopyGL

The standard libva DRM backend also rejects a KGSL fd; use this Mesa build's
bridge driver or an equivalent KGSL-aware compatibility layer.

Data path
---------

- vaRenderPicture: the frontend parses the VA buffers and hands the
bridge slice data that already carries H.264/HEVC start codes
(parameter sets arrive as slice data buffers).
- vaEndPicture: the bridge sends the complete access unit as one daemon
input unit and associates one pending fence with the picture. The normal
pending depth defaults to 6 and may grow while the decoder reorders output;
SHM mode clamps it to 8 to stay within the daemon's slot pool.
- vaSyncSurface: the bridge waits for the frame tagged with the
picture's unit index, stages it, and copies the visible (cropped)
region into the surface's plane resources, honoring the decoder's
stride/slice-height geometry (Venus aligns buffers to 128x32).

Frames come back inline on the socket or zero-copy through a memfd slot
pool handed over via SCM_RIGHTS (disable with ``DMD_WANT_SHM=0``).
``vaDeriveImage`` is not available (plane resources are separate
textures); CPU consumers can use ``vaGetImage``.

Troubleshooting
---------------

- vainfo shows no profiles: the daemon is not running, or the consumer
environment lacks ``LIBVA_DRIVER_NAME=termuxva`` / bridge activation.
- "endpoint inode mismatch": the socket path resolves to a stale socket
(a single socket FILE was bind-mounted and the daemon restarted).
Mount the socket DIRECTORY instead - the daemon replaces the socket
file on every start, only the directory inode is stable.
- Black frames after a seek: a drain was triggered; should not happen in
steady playback - reproduce with ``DMD_VA_LOG=1`` and the daemon's
``-v`` log.
Loading