From 6173a8da8e5b096309c2cc29b8059f3069edbaa9 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Wed, 20 May 2026 10:41:18 +0200 Subject: [PATCH] request: route VP9/AV1/H.264 to daedalus_v4l2 on Pi 5 mixed deploy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/request.c | 110 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 94 insertions(+), 16 deletions(-) diff --git a/src/request.c b/src/request.c index 295f271..4a700b6 100644 --- a/src/request.c +++ b/src/request.c @@ -461,6 +461,44 @@ int request_switch_device_for_profile(struct request_data *driver_data, 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') { target_video = driver_data->video_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') { target_video = driver_data->video_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 { return -1; } @@ -697,26 +740,32 @@ VAStatus VA_DRIVER_INIT_FUNC(VADriverContextP context) driver_data->video_fd_hantro = video_fd; driver_data->media_fd_hantro = media_fd; } else if (strcmp(info.driver, "rpi-hevc-dec") == 0) { - /* iter40: Pi 5 / CM5 — sole decoder is rpi-hevc-dec. - * No alt driver to probe; the rkvdec / hantro slots - * stay -1 and HEVC routes to 'p' via - * request_device_kind_for_profile. */ + /* iter40 + LIBVA-1: Pi 5 / CM5. rpi-hevc-dec is + * HEVC-only. If daedalus_v4l2 is ALSO loaded (Pi 5 + * mixed deployment — out-of-tree daemon-backed + * decoder for VP9/AV1/H264), pick it up as the alt + * so VP9/AV1/H264 have somewhere to land. */ primary_driver = "rpi-hevc-dec"; +#ifdef HAVE_DAEDALUS_V4L2 + alt_driver = "daedalus_v4l2"; +#else alt_driver = NULL; +#endif driver_data->video_fd_rpi_hevc_dec = video_fd; driver_data->media_fd_rpi_hevc_dec = media_fd; #ifdef HAVE_DAEDALUS_V4L2 } else if (strcmp(info.driver, "daedalus_v4l2") == 0) { - /* phase 8.10: Pi 5 daemon-backed decoder. Sole - * V4L2 stateless slot on this kernel; VP9 / AV1 / - * H.264 all route through it. Other slots stay -1. - * - * On a mixed-driver box (daedalus loaded ALONGSIDE - * rpi-hevc-dec) HEVC would prefer rpi-hevc-dec via - * the existing 'p' override; VP9/AV1/H264 prefer - * daedalus_v4l2 since rpi-hevc-dec is HEVC-only. */ + /* phase 8.10 + LIBVA-1: Pi 5 daemon-backed decoder. + * VP9 / AV1 / H.264 route through it via the 'd' + * kind below. On a mixed-driver box where + * rpi-hevc-dec is ALSO loaded, pick it up as the + * alt so HEVC has somewhere to land too — find_ + * codec_device's known_decoder_drivers[] order + * normally puts rpi-hevc-dec first (we hit the + * other branch in practice), but symmetric handling + * keeps us correct if probe order ever flips. */ primary_driver = "daedalus_v4l2"; - alt_driver = NULL; + alt_driver = "rpi-hevc-dec"; driver_data->video_fd_daedalus = video_fd; driver_data->media_fd_daedalus = media_fd; #endif @@ -731,15 +780,38 @@ VAStatus VA_DRIVER_INIT_FUNC(VADriverContextP context) int alt_v = open(alt_video, O_RDWR | O_NONBLOCK); int alt_m = (alt_v >= 0) ? open(alt_media, O_RDWR | O_NONBLOCK) : -1; 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) { driver_data->video_fd_rkvdec = alt_v; 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->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 { if (alt_v >= 0) close(alt_v); if (alt_m >= 0) close(alt_m); @@ -890,6 +962,12 @@ VAStatus RequestTerminate(VADriverContextP context) close(driver_data->video_fd_vpu981); if (driver_data->media_fd_vpu981 >= 0) 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 * pair (env-override path). */ if (driver_data->video_fd_rkvdec < 0 && driver_data->video_fd_hantro < 0) {