request: route VP9/AV1/H.264 to daedalus_v4l2 on Pi 5 mixed deploy

LIBVA-1 — when both rpi-hevc-dec and daedalus_v4l2 are loaded, finish
the per-codec dispatch so HEVC goes to rpi-hevc-dec (existing 'p'
override) and VP9 / AV1 / H.264 go to the daedalus daemon ('d').

Before this change the multi-device-probe accepted only ONE driver
plus a fixed alt slot (rkvdec↔hantro-vpu); on a Pi 5 with both decoders
the find_codec_device() walk preferred rpi-hevc-dec by known_decoder_
drivers[] order and never opened daedalus_v4l2, so VP9/AV1/H.264 frames
hit rpi-hevc-dec's S_FMT and failed.

Changes:

  - request.c multi-device-probe: when primary = rpi-hevc-dec, alt =
    daedalus_v4l2 (when HAVE_DAEDALUS_V4L2 is on); symmetric handling
    in the daedalus_v4l2 primary branch so alt = rpi-hevc-dec.  This
    preserves the iter40 fallback (no daedalus → alt = NULL) when the
    build option is off.

  - request.c alt-driver opening block: generalized from the iter38
    rkvdec/hantro pair to also dispatch into video_fd_rpi_hevc_dec and
    video_fd_daedalus slots.  Defensive close on unknown alt-driver
    name (shouldn't happen — primary_driver branches gate the choices —
    but keeps the slot tally clean if a future driver name is added
    above without wiring up the dispatch here).

  - request_switch_device_for_profile: added 'd' kind handler +
    profile override block.  When daedalus is open, VP9 / AV1 / H.264*
    route to it.  HEVC stays on rpi-hevc-dec via the existing 'p'
    override.  AV1 'a' kind (RK3588 vpu981) wins ONLY if vpu981 was
    probed, so the override only fires on hosts where vpu981 stayed
    -1 (i.e. Pi 5).

  - RequestTerminate: close the daedalus_v4l2 fd pair on teardown
    (was leaking — caught while reviewing the alt-driver expansion).

Build: meson + ninja clean on boltzmann (only pre-existing GStreamer
H265 parser noise).  Behaviour on RK3399/3588 boxes unchanged — the
new branches are gated by HAVE_DAEDALUS_V4L2 *and* video_fd_daedalus
≥ 0, both of which stay false in those deployments.

Companion to daedalus-v4l2 481279c (Phase 8.13 systemd unit) and
marfrit-packages noether/daedalus-v4l2-kernel-6.18-compat branch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 10:41:18 +02:00
parent de27e95571
commit 6173a8da8e
+94 -16
View File
@@ -461,6 +461,44 @@ int request_switch_device_for_profile(struct request_data *driver_data,
kind = 'p'; kind = 'p';
} }
#ifdef HAVE_DAEDALUS_V4L2
/*
* LIBVA-1: VP9/AV1/H.264 → daedalus_v4l2 when the daemon-backed
* decoder fd is open. Pi 5 has no rkvdec (those profiles map to
* 'r' by default → video_fd_rkvdec = -1 → "stay on whatever's
* active" fallback would put H.264 frames on rpi-hevc-dec's fd
* and S_FMT would fail). Re-route to the daedalus daemon instead.
*
* HEVC stays on 'p' (rpi-hevc-dec is HEVC-only — daedalus would
* accept it via FFmpeg, but rpi-hevc-dec has the GPU-backed
* hardware path so it's the right choice on this SoC).
*
* AV1 'a' kind (RK3588 vpu981) wins ONLY if vpu981 was probed.
* On a Pi 5 the vpu981 slot stays -1, so we still route AV1 to
* daedalus here. Check video_fd_vpu981 to preserve the RK3588
* priority for that case.
*/
if (driver_data->video_fd_daedalus >= 0 &&
driver_data->media_fd_daedalus >= 0) {
switch (profile) {
case VAProfileH264Main:
case VAProfileH264High:
case VAProfileH264ConstrainedBaseline:
case VAProfileH264MultiviewHigh:
case VAProfileH264StereoHigh:
case VAProfileVP9Profile0:
kind = 'd';
break;
case VAProfileAV1Profile0:
if (driver_data->video_fd_vpu981 < 0)
kind = 'd';
break;
default:
break;
}
}
#endif
if (kind == 'r') { if (kind == 'r') {
target_video = driver_data->video_fd_rkvdec; target_video = driver_data->video_fd_rkvdec;
target_media = driver_data->media_fd_rkvdec; target_media = driver_data->media_fd_rkvdec;
@@ -473,6 +511,11 @@ int request_switch_device_for_profile(struct request_data *driver_data,
} else if (kind == 'a') { } else if (kind == 'a') {
target_video = driver_data->video_fd_vpu981; target_video = driver_data->video_fd_vpu981;
target_media = driver_data->media_fd_vpu981; target_media = driver_data->media_fd_vpu981;
#ifdef HAVE_DAEDALUS_V4L2
} else if (kind == 'd') {
target_video = driver_data->video_fd_daedalus;
target_media = driver_data->media_fd_daedalus;
#endif
} else { } else {
return -1; return -1;
} }
@@ -697,26 +740,32 @@ VAStatus VA_DRIVER_INIT_FUNC(VADriverContextP context)
driver_data->video_fd_hantro = video_fd; driver_data->video_fd_hantro = video_fd;
driver_data->media_fd_hantro = media_fd; driver_data->media_fd_hantro = media_fd;
} else if (strcmp(info.driver, "rpi-hevc-dec") == 0) { } else if (strcmp(info.driver, "rpi-hevc-dec") == 0) {
/* iter40: Pi 5 / CM5 — sole decoder is rpi-hevc-dec. /* iter40 + LIBVA-1: Pi 5 / CM5. rpi-hevc-dec is
* No alt driver to probe; the rkvdec / hantro slots * HEVC-only. If daedalus_v4l2 is ALSO loaded (Pi 5
* stay -1 and HEVC routes to 'p' via * mixed deployment — out-of-tree daemon-backed
* request_device_kind_for_profile. */ * decoder for VP9/AV1/H264), pick it up as the alt
* so VP9/AV1/H264 have somewhere to land. */
primary_driver = "rpi-hevc-dec"; primary_driver = "rpi-hevc-dec";
#ifdef HAVE_DAEDALUS_V4L2
alt_driver = "daedalus_v4l2";
#else
alt_driver = NULL; alt_driver = NULL;
#endif
driver_data->video_fd_rpi_hevc_dec = video_fd; driver_data->video_fd_rpi_hevc_dec = video_fd;
driver_data->media_fd_rpi_hevc_dec = media_fd; driver_data->media_fd_rpi_hevc_dec = media_fd;
#ifdef HAVE_DAEDALUS_V4L2 #ifdef HAVE_DAEDALUS_V4L2
} else if (strcmp(info.driver, "daedalus_v4l2") == 0) { } else if (strcmp(info.driver, "daedalus_v4l2") == 0) {
/* phase 8.10: Pi 5 daemon-backed decoder. Sole /* phase 8.10 + LIBVA-1: Pi 5 daemon-backed decoder.
* V4L2 stateless slot on this kernel; VP9 / AV1 / * VP9 / AV1 / H.264 route through it via the 'd'
* H.264 all route through it. Other slots stay -1. * kind below. On a mixed-driver box where
* * rpi-hevc-dec is ALSO loaded, pick it up as the
* On a mixed-driver box (daedalus loaded ALONGSIDE * alt so HEVC has somewhere to land too — find_
* rpi-hevc-dec) HEVC would prefer rpi-hevc-dec via * codec_device's known_decoder_drivers[] order
* the existing 'p' override; VP9/AV1/H264 prefer * normally puts rpi-hevc-dec first (we hit the
* daedalus_v4l2 since rpi-hevc-dec is HEVC-only. */ * other branch in practice), but symmetric handling
* keeps us correct if probe order ever flips. */
primary_driver = "daedalus_v4l2"; primary_driver = "daedalus_v4l2";
alt_driver = NULL; alt_driver = "rpi-hevc-dec";
driver_data->video_fd_daedalus = video_fd; driver_data->video_fd_daedalus = video_fd;
driver_data->media_fd_daedalus = media_fd; driver_data->media_fd_daedalus = media_fd;
#endif #endif
@@ -731,15 +780,38 @@ VAStatus VA_DRIVER_INIT_FUNC(VADriverContextP context)
int alt_v = open(alt_video, O_RDWR | O_NONBLOCK); int alt_v = open(alt_video, O_RDWR | O_NONBLOCK);
int alt_m = (alt_v >= 0) ? open(alt_media, O_RDWR | O_NONBLOCK) : -1; int alt_m = (alt_v >= 0) ? open(alt_media, O_RDWR | O_NONBLOCK) : -1;
if (alt_v >= 0 && alt_m >= 0) { if (alt_v >= 0 && alt_m >= 0) {
/* Dispatch into the matching per-driver slot.
* iter38 only had rkvdec/hantro pairs; iter40 +
* LIBVA-1 extended this to rpi-hevc-dec and
* daedalus_v4l2 for the Pi 5 mixed-decoder
* deployment. */
if (strcmp(alt_driver, "rkvdec") == 0) { if (strcmp(alt_driver, "rkvdec") == 0) {
driver_data->video_fd_rkvdec = alt_v; driver_data->video_fd_rkvdec = alt_v;
driver_data->media_fd_rkvdec = alt_m; driver_data->media_fd_rkvdec = alt_m;
} else { } else if (strcmp(alt_driver, "hantro-vpu") == 0) {
driver_data->video_fd_hantro = alt_v; driver_data->video_fd_hantro = alt_v;
driver_data->media_fd_hantro = alt_m; driver_data->media_fd_hantro = alt_m;
} else if (strcmp(alt_driver, "rpi-hevc-dec") == 0) {
driver_data->video_fd_rpi_hevc_dec = alt_v;
driver_data->media_fd_rpi_hevc_dec = alt_m;
#ifdef HAVE_DAEDALUS_V4L2
} else if (strcmp(alt_driver, "daedalus_v4l2") == 0) {
driver_data->video_fd_daedalus = alt_v;
driver_data->media_fd_daedalus = alt_m;
#endif
} else {
/* Shouldn't happen — primary_driver branches
* above only set alt_driver to one of the
* names handled here. Close and move on. */
close(alt_v);
close(alt_m);
alt_v = -1;
alt_m = -1;
}
if (alt_v >= 0) {
request_log("iter38: also opened %s decoder at %s + %s\n",
alt_driver, alt_video, alt_media);
} }
request_log("iter38: also opened %s decoder at %s + %s\n",
alt_driver, alt_video, alt_media);
} else { } else {
if (alt_v >= 0) close(alt_v); if (alt_v >= 0) close(alt_v);
if (alt_m >= 0) close(alt_m); if (alt_m >= 0) close(alt_m);
@@ -890,6 +962,12 @@ VAStatus RequestTerminate(VADriverContextP context)
close(driver_data->video_fd_vpu981); close(driver_data->video_fd_vpu981);
if (driver_data->media_fd_vpu981 >= 0) if (driver_data->media_fd_vpu981 >= 0)
close(driver_data->media_fd_vpu981); close(driver_data->media_fd_vpu981);
#ifdef HAVE_DAEDALUS_V4L2
if (driver_data->video_fd_daedalus >= 0)
close(driver_data->video_fd_daedalus);
if (driver_data->media_fd_daedalus >= 0)
close(driver_data->media_fd_daedalus);
#endif
/* Fall back to direct close if neither alt fd captured the active /* Fall back to direct close if neither alt fd captured the active
* pair (env-override path). */ * pair (env-override path). */
if (driver_data->video_fd_rkvdec < 0 && driver_data->video_fd_hantro < 0) { if (driver_data->video_fd_rkvdec < 0 && driver_data->video_fd_hantro < 0) {