Repository navigation
Conversation
lfdevs
force-pushed
the
test/add-va-bridge
branch
2 times, most recently
from
September 1, 2026 05:36
bb69993 to
3f29ee1
Compare
lfdevs
force-pushed
the
test/add-va-bridge
branch
from
September 9, 2026 03:51
76697c0 to
a5e3b8f
Compare
lfdevs
force-pushed
the
test/add-va-bridge
branch
from
October 2, 2026 16:33
54919b6 to
09e2cbe
Compare
tva_protocol.h is the cmp-verified mirror of the daemon's
common/tva_protocol.h (repository lfdevs/termux-va); the two copies must
stay byte-identical (scripts/check-mirror.sh in the daemon repo).
tva_client.{h,c} is a port of droidspaces-media-decode's
vaapi-driver/src/dmd_client.{c,h} (Apache-2.0, relicensed GPL-3.0 with
the modification notice in the file headers). Changes vs upstream:
Unix-socket-only transport (TCP removed, wire values unchanged),
protocol constants from the mirror header, symbols renamed dmd_* ->
tva_*, and the default endpoint resolves in container view
(TERMUX_VA_SOCKET > TERMUX_VA_SOCKET_DIR > shared-tmp paths, i.e.
/tmp/termux-va/termux-va.sock) through Mesa's os_get_option so Android
system properties work as a fallback.
Kept faithful: the v3 handshake with the endpoint dev/ino reconciliation,
the v2 downgrade retry, the SHM pickup through the abstract socket with
SCM_RIGHTS, the format-block parsing with CAP_FRAME_PTS, non-blocking I/O
with bounded waits, MSG_NOSIGNAL and CLOEXEC everywhere.
The bridge delegates VA-API bitstream decode to the termux-va daemon (MediaCodec hardware decode in Termux) over the Unix socket described by tva_client.h, and runs entirely on the application thread - no bridge-owned threads touch pipe_context. - tva_screen wraps the real screen: get_video_param / is_video_format_supported advertise the bridge codec set (H.264 Baseline/Main/High, HEVC Main, VP9 Profile0, NV12 only, 96x96..8192x4320, progressive) while everything else forwards. - tva_pipe wraps the multimedia context: create_video_codec hands decode to the bridge codec, and create_video_buffer(_with_modifiers) falls back to the generic vl_video_buffer_create helper because the underlying drivers have no video path. - The codec accumulates the frontend's slice data (which already carries H.264/HEVC start codes; parameter sets arrive as slice data buffers), splits it into Annex B units in end_frame (parameter sets excluded from the unit index exactly like the daemon), and pairs the returned frame with the picture by the daemon's unit index through a pending ring whose depth is capped by the daemon's SHM slot pool. - fence_wait pumps frames into staging buffers and copies the visible (cropped) region of the NV12 buffer into the surface plane resources on the caller's thread, honoring the decoder stride/slice_height geometry from the format block. Derived in part from droidspaces-media-decode's vaapi-driver (Apache-2.0): the capability table, pipeline-depth model and unit classification; the modification notice per GPL-3.0 section 5 is in the file header.
- VA_DRIVER_INIT_FUNC wraps the multimedia context with the bridge right after it is created (activation is runtime-gated by the TERMUX_VA_* environment variables) and appends a marker to the vendor string so vainfo shows when the bridge is active. - New meson feature option 'termux-va-bridge' (default auto): the bridge sources are compiled into libva_st unless disabled, and the option also relaxes the VA state tracker's gallium-driver whitelist so a freedreno-only build can enable gallium-va for the bridge. - targets/dri exposes the megadriver as termuxva_drv_video.so so libva can dlopen it via LIBVA_DRIVER_NAME=termuxva. Runtime activation summary: TERMUX_VA_BRIDGE=1 forces the bridge on, =0 forces it off; with the variable unset the bridge activates when TERMUX_VA_SOCKET/TERMUX_VA_SOCKET_DIR is set or the default endpoint /tmp/termux-va/termux-va.sock exists. A failed bridge activation returns a VA error so applications fall back to software decoding instead of crashing.
build-check.yml: the debian-trixie job now runs on pushes to test/add-va-bridge and workflow_dispatch (the other distro jobs stay pull_request-only; their clone step only knows the PR head ref), installs libva-dev, and enables the VA bridge in the packaged tar.gz: -Dgallium-va=enabled -Dtermux-va-bridge=enabled -Dvideo-codecs=all. The clone step falls back to the pushed branch when no PR ref exists. docs: add docs/termux-va.rst (building, activation, socket location, data path, troubleshooting) to the documentation index and document the bridge's environment variables (TERMUX_VA_BRIDGE, TERMUX_VA_SOCKET, TERMUX_VA_SOCKET_DIR, DMD_WANT_SHM, DMD_VA_LOG, LIBVA_DRIVER_NAME) in docs/envvars.rst.
…ction The stock loader path cannot create a screen on the kgsl stack: Xiaomi/ DroidSpaces kernels report the display controller's DRM node as "msm_drm" (no pipe_loader descriptor matches, so kmsro/zink fallbacks engage and fail - "ZINK: failed to choose pdev" - because the kgsl stack has no usable Vulkan device), and even a node reported as "msm" drives no GPU there. The VA frontend's vscreen creation failed before the bridge's codec ever came into play. tva_bridge_vscreen_create() now creates the underlying screen with a TERMUX_VA_GPU_BACKEND selection: - auto (default): stock loader selection, then the fork's "kgsl" freedreno alias, then llvmpipe over the null sw winsys. - kgsl: force the "kgsl" alias - pipe_loader re-points driver_name at the fork-registered descriptor whose device layer redirects GPU submission to /dev/kgsl-3d0 while keeping the handed fd as the control/identity fd (freedreno_device.c); FD_FORCE_KGSL=1 is set when unset, matching the EGL path (MESA_LOADER_DRIVER_OVERRIDE=kgsl). - drm: stock selection only. sw: llvmpipe only. The frontend's DRM/Wayland vscreen creation calls the bridge when it is active; the fd stays owned by libva (pipe_loader dups internally).
Install Termux VA capability and codec hooks directly on the real Gallium screen and multimedia context instead of creating memcpy-based wrapper objects that can be misinterpreted by native drivers. Move frame reception to a dedicated reader thread, track pending frames by input-unit identity, and keep surface copies on the application thread. Add reversible drain and explicit frame-release handling for inline and SHM transport. Support TERMUX_VA_GPU_BACKEND=sw through a null winsys and llvmpipe so the bridge can operate without KGSL, DRM, or Vulkan access. Initialize and release the GLSL type singleton in the Freedreno screen lifetime to prevent shader compilation from using a null GLSL linear allocation context.
Align the Termux VA documentation and bridge comments with the tested backend selection. The auto mode uses the stock DRM loader and falls back to llvmpipe, while KGSL is selected explicitly and sw forces llvmpipe. Document that the current Mesa bridge advertises H.264 and VP9 Profile 0 only, while HEVC parsing and CSD synthesis remain incomplete.
Synchronize the bridge reader and fence lifecycle, retain pending surface resources, and make teardown cancel the socket worker safely. Export KGSL bridge surfaces as linear DMA-BUF-backed resources with complete object metadata. The bridge now owns video capability queries, initializes KGSL DMA-BUF allocation before screen creation, and avoids advertising unsupported external timeline semaphore FDs to Vulkan clients on KGSL. This lets FFmpeg and ffplay consume exported VA surfaces while preserving the existing Unix-socket protocol.
Keep complete H.264/HEVC access units together, retain pending resources until frame copies complete, and make CPU/Gallium uploads explicit for KGSL. Synchronize imported DMA-BUFs before Turnip use, add cache and barrier diagnostics, and document the pipeline and copy controls.
Synchronize imported dma-bufs across KGSL producer and consumer contexts, invalidate shared NV12 texture state, and retain pending VA frames until MediaCodec output arrives. Improve the lowered NV12 import path and synthesize H.264 parameter sets without an incorrect zero-reorder restriction while restoring reference defaults from the decoder DPB.
Preserve the VA H.264 profile when rebuilding SPS headers and emit the High-profile SPS and PPS fields required by MediaCodec. Keep zero PPS reference defaults for High streams while retaining the DPB fallback for the existing Main-profile path.
Use the active reference-list sizes reported by VA slice parameters when synthesizing Main-profile PPS headers. Keep the largest observed values in the codec so CSD updates converge during the first GOP instead of being regenerated for every frame.
Advertise HEVC Main through the termux-va bridge and synthesize the VPS, SPS, and PPS parameter sets required by MediaCodec from VA-API picture descriptors. Forward HEVC slice data unchanged and cache parameter sets so Chrome and FFmpeg can use the hardware decoder. Reject parameter sets whose reference-picture-set contents are not exposed by VA-API instead of sending malformed CSD.
Keep the short-term reference picture set count when synthesizing HEVC SPS headers. Qualcomm's decoder requires a non-zero SPS RPS count even when FFmpeg supplies each slice's RPS inline, while VA-API does not expose the set contents, so emit empty placeholders and reject slices that refer to unavailable SPS RPS data.
Advertise AV1 Main and route it through the termux-va codec. Reconstruct complete AV1 temporal units from VA-API picture and tile descriptors, including sequence headers, frame OBUs, tile groups, hidden reference frames, and refresh masks required by MediaCodec. Add shared H.264/HEVC bitstream helpers and AV1 OBU writers, and include the new sources in the VA frontend build.
Handle hidden AV1 pictures as output frames or synthesize show_existing_frame OBUs so MediaCodec returns reference surfaces needed by the VA bridge. Track per-picture pending entries and wait for recycled fences before detaching them, preventing late frames from overwriting surfaces reused by Chromium. Copy decoded linear resources directly into exported DMA-BUFs from the reader thread when possible, retain the Gallium transfer fallback, and avoid unnecessary flushes for CPU copies. Add configurable AV1 output modes, pending limits, fence wait timing, and tracing for diagnostics.
VAAPI does not expose H.264 PPS default reference-list counts. Infer them from effective slice parameters when synthesizing CSD, while keeping learned High-profile defaults stable across subsequent slices. This prevents Qualcomm MediaCodec from stalling on High@L4 streams after the initial IDR frames.
Open /dev/kgsl-3d0 when a VA display has no DRM fd and route screen creation through the KGSL Freedreno alias. Automatically allocate linear NV12 surfaces from a single dma-buf in KGSL-only containers, while preserving per-plane offsets for PRIME exports. Treat shared plane resources as one exported object when PRoot cannot compare file descriptions. Document the PRoot XWayland requirements and the contiguous dma-buf override.
Support Chromium hardware video decode in PRoot containers that expose /dev/kgsl-3d0 but no usable DRM render node. - Synchronize KGSL dma-buf cache ownership and imported resource handling. - Stabilize VA bridge staging, fence retirement, surface teardown, and VPP synchronization. - Support linear NV12 exports and planar DRI/GBM imports used by Chromium. - Make Freedreno compositor paths compatible with KGSL. - Propagate TERMUX_VA_GPU_BACKEND=kgsl through the loader, X11 DRI3, EGL, and GLX paths. - Document the DRM-less PRoot configuration and add bridge diagnostics.
Add the optional termux-va-wayland-shim Meson feature and build libtva_drm_shim_wayland.so for Chromium's native Wayland backend in DRM-less KGSL PRoot containers. The shim redirects Chromium's DRM discovery and version probes to /dev/kgsl-3d0 without replacing Mesa's KGSL backend. Enable the target in build-check workflows and document the required LD_PRELOAD launch configuration.
Add TERMUX_VA_DISABLE_AV1 to suppress the AV1 Main profile while keeping H.264, HEVC, and VP9 available through the bridge. This lets devices without a hardware AV1 MediaCodec decoder fall back to the application's native software decoder, avoiding unnecessary bridge transport and presentation overhead.
Continue submitting hidden VP9 pictures to MediaCodec so they can update the decoder reference state, but do not reserve output fences for them. Chromium splits VP9 superframes into hidden and displayed pictures, while MediaCodec produces no output buffer for hidden pictures. Avoid leaving permanent holes in the pending queue that eventually cause vaEndPicture to fail.
Use the fixed DPB size reported by VA-API when synthesizing H.264 PPS reference-list defaults. VA-API does not preserve num_ref_idx_active_override_flag, so per-picture reference counts cannot be promoted to PPS defaults. Keeping the synthetic PPS stable prevents Qualcomm MediaCodec from resetting mid-stream and exhausting the pending output queue.
Add TERMUX_VA_DISABLE_AVC, TERMUX_VA_DISABLE_HEVC, and TERMUX_VA_DISABLE_VP9 alongside the existing AV1 switch. Accept TERMUX_VA_DISABLE_H264 as an AVC compatibility alias and share the boolean option parsing across all codec switches. Disabled profiles are hidden from libva so applications can select their native software decoders when a MediaCodec implementation is unavailable, software-only, or being excluded for compatibility testing. Document the switches and their accepted values in the termux-va bridge documentation.
Do not derive synthesized H.264 PPS active-list defaults from the VA-API DPB size. VA-API does not preserve the PPS default-reference fields or the slice override flag, and Bilibili and Chromium streams can advertise a DPB larger than their active L0 list. Use a stable two-reference L0 and zero-reference L1 default, bounded by the configured DPB, so slices that rely on the original PPS defaults remain valid without changing parameter sets mid-stream. This prevents Qualcomm MediaCodec from rejecting the stream and exhausting the pending output queue.
GBM issues DRM_IOCTL_GET_CAP while probing render devices, but the KGSL character device exposed in PRoot is not a DRM node and returns ENOTTY. Emulate the minimal capability set needed by the native-pixmap path: monotonic timestamps and PRIME import/export, while leaving dumb-buffer and modifier support disabled. Add opt-in ioctl tracing via TERMUX_VA_DRM_SHIM_LOG to diagnose DRM/GBM initialization failures without changing default logging.
Recognize explicitly opened /dev/mali0 descriptors and provide the minimal libdrm metadata needed for VA-API display initialization. Keep Mali out of global DRM device enumeration so it is not selected as the graphics render node, while preserving the existing KGSL path.
Advertise AVC Constrained Baseline/Main/High and HEVC Main encode profiles through the Termux VA bridge, with NV12 input and the device-supported dimension limits. Map encode profiles to the new termux-va protocol IDs. Copy NV12 planes into packed encoder frames, receive encoded access units synchronously, and write them into VA bitstream buffers with feedback sizes. Keep encoder sessions separate from the decoder reader-thread and shared-memory paths. Mirror the updated protocol definitions and preserve the existing decoder lifecycle synchronization.
PRoot environments without DRM render nodes were implicitly using non-blocking VA surface synchronization. When FFmpeg combined VAAPI decode, VPP scaling, and hardware encoding, VPP could sample a decoder surface before termux-va had copied the frame, producing green initial frames. Make non-blocking synchronization opt-in through DMD_VA_SURFACE_NO_WAIT, and preserve explicit waiting for termux-va fences consumed by the encoder.
Capture the VA-API encoder target bitrate and frame rate from H.264 and HEVC picture descriptors, then pass them to the termux-va daemon through the encoder handshake. Create encoder sessions lazily after the first picture parameters are available, ensuring that -b:v settings are applied before MediaCodec is configured. Extend the client configuration and keep the shared protocol header synchronized with the termux-va repository.
The llvmpipe null winsys cannot allocate display targets when PIPE_BIND_SHARED and PIPE_BIND_LINEAR are requested. This caused Termux VA surface creation to fail in Pixel PRoot containers using TERMUX_VA_GPU_BACKEND=sw. Detect the explicit software backend and preserve the original resource binds in that case. Keep shared linear resources for hardware-backed backends, where they are required for DMA-BUF export.
Advertise VAProfileVP9Profile0 with VAEntrypointEncSlice and map VP9 encoding requests to the termux-va VP9 encoder protocol ID. Preserve VP9 bitrate and frame-rate parameters, retain the VA coded-buffer handle, and allocate a staging resource so encoded packets remain available through VAMapBuffer.
Export the requested Freedreno resource plane instead of reusing the luma resource for chroma. Align KGSL NV12 plane pitches consistently and round subsampled dimensions up for small capability-probe allocations. Configure encoder sessions from the actual source buffer dimensions rather than macroblock-aligned context dimensions. Forward explicit AVC Baseline, Main and High profile requests using the mirrored protocol extensions. Drain pending AVC and HEVC decoder output during context destruction and complete retained frame copies on the application thread within a shared timeout. Preserve reader-thread restrictions and teardown lock ordering so exported frames contain the final decoded pixels. Document Chromium encoding requirements and frame-rate limitations.
lfdevs
force-pushed
the
test/add-va-bridge
branch
from
October 8, 2026 07:49
c7c0264 to
7573b8c
Compare
A resource's backing storage can change while its sampler view remains bound. Looking up texture state by the unchanged view ID can then reuse stale descriptors and trigger the view_rsc_seqno assertion, aborting XWayland during VLC playback. Update bound sampler views before constructing the cache key. The existing helper invalidates stale entries and refreshes descriptors, while retaining its fast path when the resource sequence is unchanged. Also index the supplied sampler-view array from zero rather than adding the destination binding offset, consistent with fd_set_sampler_views().
Resolve the EGL device from the KGSL render descriptor before applying the existing software fallback for KGSL and Zink displays. This allows the Wayland DRM shim to expose /dev/kgsl-3d0 as an accelerated EGL device, so applications such as Firefox can report an accelerated renderer and enable VA-API hardware video decoding. Keep the new lookup guarded by HAVE_LIBDRM and retain the existing software-device behavior when no matching DRM device is available.
VA-API does not expose several AV1 sequence-level fields required when reconstructing a bitstream for Android MediaCodec. Keep the generated sequence header self-consistent by disabling loop restoration and omitting qm_v when separate_uv_delta_q is forced to zero. Emit allow_warped_motion for every non-intra, non-error-resilient frame as specified by AV1. This field is independent of is_motion_mode_switchable; omitting it shifted the tile-group header in multi-tile frames. Tested on a Pixel 6 Pro Debian PRoot container with FFmpeg and termux-va: all eight AV1 samples from 256x128 through 3840x1920 decoded 240 frames successfully. Hardware NV12 frame checksums for multi-tile samples matched software dav1d decoding.
Pixel MediaCodec can retain the final AV1 showable frame until an EOS signal. Since VA-API has no explicit decoder flush callback, the bridge could leave the final temporal unit pending indefinitely and make FFmpeg wait forever. Flush the terminal AV1 unit after a configurable idle period, send a serialized reversible drain request, and serialize bridge socket writes. Also release the context mutex before blocking fence waits to avoid a driver/context lock inversion in surface and buffer synchronization.
Pixel C2 AV1 streams expose incomplete sequence and frame syntax through the VA-API descriptors. Reconstruct the missing screen-content, restoration, initial-display-delay, skip-mode, and warped-motion fields from the available descriptor state and tested Pixel stream variants. Carry restoration state through the bridge, correct color/chroma and loop restoration bit layout, restore reduced transforms for affected 1080p base frames, and serialize hidden-frame show_existing units to preserve reference and display order.
The MediaCodec bridge could submit access units before synthesized parameter sets and reference state were complete. On Pixel 6 Pro, this caused H.264 streams with larger reference lists to stall and could reorder or drop HEVC frames. Parse and retain HEVC short-term RPS indices, reconstruct reference picture sets, and track temporal sublayers while generating codec configuration data. Queue initial H.264 and HEVC access units until the reference state is known, then flush them with ordered sequence numbers, retained surfaces, and fences. Also derive H.264 SPS/PPS defaults from the VA descriptors and release queued resources during teardown.
The AV1 bridge always encoded non-final tile sizes as two-byte values, although the frame header may require up to four bytes. Large Pixel C2 frames could therefore become misaligned after the first tile. Derive the tile-size width from the largest non-final tile and use it consistently in the frame header and tile group. Also avoid treating the BT.709 matrix coefficient as a universal Pixel warped-motion marker, while retaining the validated 1080p compatibility path.
Android MediaCodec decoders may retain the final reordered H.264 or HEVC picture after the last access unit is submitted. Since VA-API has no decoder flush callback, the terminal fence could wait indefinitely during conversion teardown. When the newest pending AVC or HEVC unit remains idle, request a reversible daemon drain to release the delayed picture. Keep the idle timeout configurable through DMD_REORDER_DRAIN_IDLE_MS, defaulting to five seconds. Avoid draining older pending units while decoding is still progressing, and fail the terminal fence after a second timeout if the decoder still produces no matching output.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Tip
See also: Termux:VA
Summary
Based on DroidSpaces Media Decode Daemon by Re-s, this branch adds a termux-va bridge to the VA frontend: VA-API video decoding is forwarded over a Unix socket to the
termux-vadaemon running in Termux, 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 — ffmpeg, Firefox and Chrome work unmodified.This follows the porting model established by anland-termux: a Termux daemon, a Unix socket placed in the shared tmp directory, and a bridge on the container side. The wire protocol is byte-compatible with droidspaces-media-decode protocol v3 (
HELLO_MAGIC 0x444D4400), which lets us reuse its protocol regression tooling.The daemon itself lives in the companion repository
lfdevs/termux-va(NDK r29, 29.0.14206865, same pin as anland-termux); it listens on$TMPDIR/termux-va/termux-va.sock, which appears as/tmp/termux-va/termux-va.sockinside the container.