Files
ampere-kernel-decoders/phase2_situation_iter2.md
marfrit 5541e01d26 iter2 phase2: vendoring spec concrete + dependency chain re-verified
Fetched gsth265parser.c (5409 lines) + .h (2440 lines) from GStreamer
main mirror. Plus GstBitReader (~600 LOC vendored separately) as
the parser's only non-self-contained dependency.

License LGPL v2.1+ (Intel + Sreerenj Balachandran), preserves verbatim
in vendored copies; compatible with backend COPYING.LGPL. Add README
note listing the two vendored files.

GLib adaptation mapped to 6 mechanical replacements:
  GArray/g_array_* -> plain C dynamic arrays (count+ptr)
  g_malloc/g_free  -> libc malloc/free
  g_clear_pointer  -> inline free+NULL
  g_assert         -> propagate parser-failure-code (NOT abort!)
  gboolean/gint/etc -> stdbool/stdint
  GST_DEBUG_*      -> backend's request_log/error_log

Vendor the FULL parser unchanged per upstream-alignment rule; dead
code (PPS/slice/SEI parsing we don't strictly need) is acceptable
to preserve upstream-bug-fix-sync simplicity.

Header strategy concretized: new src/hevc-ctrls/v4l2-hevc-ext-controls.h
~50 lines with verbatim kernel UAPI defs; runtime probe via
VIDIOC_QUERYCTRL at backend init, stored per-driver_data, gated by
both kernel-supports + active driver-kind is vdpu381/383.

Build system impact: 2-line Makefile.am addition, no autoconf, no
pkg-config changes. Compile time uptick acceptable.

New constraints (6 total):
  1. Vendored LGPL header verbatim preservation + README note
  2. Hand-build install path (carries from iter1)
  3. Reboot needed after HEVC OOPS recovery (iter2 test cycle)
  5. Replace g_assert with error propagation, not abort
  6. Parser interpretation may differ from kernel even with same spec

Ready for Phase 3 (mostly inherited from iter1 + iter2 phase 0).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 08:42:07 +00:00

8.0 KiB

Phase 2 — iter2 situation analysis (HEVC backend EXT_SPS_*_RPS extension)

Context reset 2026-05-16 evening. iter2 substrate from Phase 0 still holds (no time has elapsed since Phase 0 close). Re-verifies the dependency chain end-to-end now that Phase 1 picked the architecture (vendor GStreamer parser) + a header strategy (runtime-optional probe, no #ifndef shim).

Vendored-parser characterization

Fetched 2026-05-16 from GStreamer main mirror (github.com/GStreamer/gstreamer @ subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/):

  • gsth265parser.c5409 lines
  • gsth265parser.h2440 lines
  • Total: 7849 lines

Coverage: whole H.265 parser — NAL header, SPS, PPS, slice header, SEI messages, scaling lists, VUI. Our iter2 only needs NAL-header (to locate SPS NAL) + SPS parsing (specifically short_term_ref_pic_set + long-term RPS), but per the upstream-alignment-over-speed rule we vendor the full parser unchanged to preserve future bug-fix sync simplicity. Dead code in the vendored portion is acceptable.

License: LGPL v2.1-or-later (Copyright (C) 2012 Intel Corporation, Copyright (C) 2013 Sreerenj Balachandran <sreerenj.balachandran@intel.com>). Compatible with backend's existing COPYING.LGPL. The vendored file keeps its LGPL header verbatim; backend's MIT-licensed files remain MIT.

GLib dependencies to adapt (mechanical replacement; no logic changes):

GLib API Backend replacement
GArray + g_array_* (heavily used for variable-length SPS structures) plain C dynamic arrays — pair of size_t count + heap-allocated pointer; helper macros if it cleans up the call sites
g_malloc, g_free, g_slice_* libc malloc / free
g_clear_pointer inline equivalent (free(p); p=NULL)
g_assert, g_assert_not_reached <assert.h> assert(); __builtin_unreachable() for not_reached
gboolean, gchar, gint, guint, gint8/16/32/64, guint8/16/32/64 C99 <stdbool.h> + <stdint.h> (bool, int8_t, etc.)
GST_DEBUG_CATEGORY / GST_DEBUG/_WARNING/_ERROR backend's existing request_log / error_log macros (or strip to bare fprintf(stderr, …) if cleaner)

Additional dependency uncovered during fetch: GstBitReader from gst-base/gstbitreader.h. The H.265 parser pulls it via:

#include <gst/base/gstbitreader.h>
...
GstBitReader br;
gst_bit_reader_init (&br, data, nalu->size - nalu->offset);
gst_bit_reader_skip_unchecked (&br, 1);
nalu->type = gst_bit_reader_get_bits_uint8_unchecked (&br, 6);

This is a separate vendor target — GStreamer's subprojects/gstreamer/gst/parse/gstbitreader.c + .h (roughly ~400 lines, plain bit-reading utility). Same LGPL header. Vendoring shape is identical: drop GLib types, replace g_assert etc.

Alternative: the backend already has src/h264_slice_header.c which does H.264 slice-header bit-reading — check whether it has a reusable bit-reader the H.265 vendor could use instead of pulling in GStreamer's gstbitreader. Reduces vendored-code surface IF the existing reader's API is compatible enough. Phase 4 detail.

Build-system impact

  • Backend's Makefile.am lists each source file explicitly. Adding src/gsth265parser/{gsth265parser.c, gstbitreader.c} is a 2-line addition. No autoconf changes (no new external deps; LGPL vendored code is self-contained C).
  • The pkg-config line stays as-is (no new packages required).
  • The build now includes ~7-8k lines of vendored parser code, ~600 lines of vendored bit-reader. Compile time impact: minor (these are mostly straight-line bitstream parsing, not heavy template / metaprogramming). The build-time uptick on ampere is acceptable — N=1 test shows ninja -C build typically completes in <30s currently.

Header strategy concrete shape

src/hevc-ctrls/v4l2-hevc-ext-controls.h (new file, ~50 lines):

  • Defines V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS and _LT_RPS constants (the exact integer literals from kernel v4l2-controls.h 7.0).
  • Defines struct v4l2_ctrl_hevc_ext_sps_st_rps and _lt_rps (exact verbatim from kernel UAPI 7.0).
  • Defines the V4L2_HEVC_EXT_SPS_*_RPS_FLAG_* flag constants.
  • Includes <linux/types.h> for __u8/16/32 (which the host already has from 6.19 headers).
  • Has a comment block citing the upstream UAPI source (kernel commit / version where these landed).

Backend includes this header from src/h265.c (and from the vendored parser's call sites). When the host eventually bumps linux-api-headers to 7.0+, this internal header is dead but harmless — the kernel's v4l2-controls.h will have the same constants and the C preprocessor / linker won't notice.

Runtime probe at backend init (in src/context.c or a new helper):

/* gracefully detect whether the running kernel knows these controls */
static bool probe_hevc_ext_sps_controls(int video_fd) {
    struct v4l2_queryctrl q = {.id = V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS};
    if (ioctl(video_fd, VIDIOC_QUERYCTRL, &q) < 0) return false;
    q = (struct v4l2_queryctrl){.id = V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS};
    if (ioctl(video_fd, VIDIOC_QUERYCTRL, &q) < 0) return false;
    return true;
}

Stored on driver_data per-device-fd. h265_set_controls populates+submits the new controls only when driver_data->has_hevc_ext_sps_rps is true AND the active driver-kind is one of rkvdec-vdpu381 / rkvdec-vdpu383 (gated per feedback_per_driver_kludge_gating). For RK3399 rkvdec, the controls are skipped entirely (kernel doesn't have them, control submission would NAK).

Re-verified Phase 0 substrate (post-Phase-1)

  • ampere reachable, uptime since SDDM auto-login setup at ~10:36; XDG_RUNTIME_DIR=/run/user/1001.
  • Backend /usr/lib/dri/v4l2_request_drv_video.so md5 still 0c9a7efaab… (hand-built 7ac934e, the iter1 substrate).
  • Source clip ~/measurements/encoded/bbb_60s_720p.hevc.mp4 intact (5.3 MB, iter1 encoded May 16 08:20).
  • Kernel 7.0.0-rc3-devices+ running; no new dmesg lines since iter1 close (no HEVC attempts in iter2 Phase 0/1, so no new wedge).

Constraints (new in iter2 vs ampere-fourier iter1)

  1. Vendored LGPL code must keep its license header verbatim. Adding a top-level README.md note listing gsth265parser.c + gstbitreader.c as LGPL-licensed third-party components.
  2. Backend rebuild after any vendoring change requires re-install to /usr/lib/dri/. The hand-build location is unowned-but-pacman-claims-ownership per Phase 0 carryover. Test cycle is: edit → ninja -C buildsudo install -m644 build/src/v4l2_request_drv_video.so /usr/lib/dri/ → re-run ffmpeg.
  3. HEVC failure recovery requires reboot (per ampere-fourier iter1 Phase 0 — the m2m wedge cascades). Iter2 Phase 6 test cycles must budget ~30 s per reboot if any iteration triggers the OOPS again.

Known failure modes (iter2-specific additions)

  1. Vendored parser's g_assert triggering at runtime — once GLib's assert is replaced with libc assert, a parser-detected malformed bitstream may abort the whole process instead of returning a parse error. Mitigation: replace g_assert with parser-returning-failure-code in the vendored adaptation, not with assert. Spec-violating inputs should propagate up, not crash the backend.
  2. GStreamer parser may produce different RPS data than kernel expects — even with the same H.265 spec, there's room for subtle interpretation. Phase 7 C3 (frame 0 byte-identical) is the contract; if it fails, the vendored parser's output differs from what rkvdec_hevc_assemble_hw_rps expects, and bisection against GStreamer's gst-launch test output (or FFmpeg WIP) is needed.

Phase 2 close

Vendoring spec concrete: 5409 + 2440 + ~600 lines (gsth265parser + gstbitreader), GLib-to-libc adaptation mapped to 6 mechanical replacements, build system impact minor, header shim file shape decided, runtime probe sketched. Six new constraints/failure-modes catalogued.

Ready for Phase 3 baseline (which for iter2 is mostly inherited from ampere-fourier iter1 + iter2 Phase 0's existing HEVC OOPS reproducer).