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>
This commit is contained in:
2026-05-16 08:42:07 +00:00
parent eefc378d93
commit 5541e01d26
+97
View File
@@ -0,0 +1,97 @@
# 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 <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:
```c
#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):
```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).