# 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.c` — **5409 lines** - `gsth265parser.h` — **2440 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 `). 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()`; `__builtin_unreachable()` for not_reached | | `gboolean`, `gchar`, `gint`, `guint`, `gint8/16/32/64`, `guint8/16/32/64` | C99 `` + `` (`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: ```c #include ... 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 `` 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): ```c /* 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 build` → `sudo install -m644 build/src/v4l2_request_drv_video.so /usr/lib/dri/` → re-run ffmpeg. 4. **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) 5. **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. 6. **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).