diff --git a/src/config.c b/src/config.c index 0ff19ea..7fea711 100644 --- a/src/config.c +++ b/src/config.c @@ -88,6 +88,16 @@ VAStatus RequestCreateConfig(VADriverContextP context, VAProfile profile, return VA_STATUS_ERROR_UNSUPPORTED_PROFILE; } + /* + * iter38: retarget the active V4L2 device to whichever physical + * decoder (rkvdec or hantro-vpu on RK3399) serves this codec profile. + * Safe no-op when the right device is already active. When a switch + * is needed, output/capture pools and the video_format cache are + * torn down so the next RequestCreateContext rebuilds them on the + * new device. + */ + (void)request_switch_device_for_profile(driver_data, profile); + if (attributes_count > V4L2_REQUEST_MAX_CONFIG_ATTRIBUTES) attributes_count = V4L2_REQUEST_MAX_CONFIG_ATTRIBUTES; @@ -139,6 +149,30 @@ VAStatus RequestDestroyConfig(VADriverContextP context, VAConfigID config_id) return VA_STATUS_SUCCESS; } +/* + * iter38: check whether `fmt` is supported on any of the open V4L2 device + * fds (active + the two alt fds tracked since iter38 multi-device probe). + * Tries both VIDEO_OUTPUT and VIDEO_OUTPUT_MPLANE. + */ +static bool any_fd_supports_output_format(struct request_data *driver_data, + unsigned int fmt) +{ + int fds[3] = { + driver_data->video_fd, + driver_data->video_fd_rkvdec, + driver_data->video_fd_hantro, + }; + int i; + for (i = 0; i < 3; i++) { + if (fds[i] < 0) continue; + if (v4l2_find_format(fds[i], V4L2_BUF_TYPE_VIDEO_OUTPUT, fmt)) + return true; + if (v4l2_find_format(fds[i], V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, fmt)) + return true; + } + return false; +} + VAStatus RequestQueryConfigProfiles(VADriverContextP context, VAProfile *profiles, int *profiles_count) { @@ -146,23 +180,13 @@ VAStatus RequestQueryConfigProfiles(VADriverContextP context, unsigned int index = 0; bool found; - found = v4l2_find_format(driver_data->video_fd, - V4L2_BUF_TYPE_VIDEO_OUTPUT, - V4L2_PIX_FMT_MPEG2_SLICE) || - v4l2_find_format(driver_data->video_fd, - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - V4L2_PIX_FMT_MPEG2_SLICE); + found = any_fd_supports_output_format(driver_data, V4L2_PIX_FMT_MPEG2_SLICE); if (found && index < (V4L2_REQUEST_MAX_CONFIG_ATTRIBUTES - 2)) { profiles[index++] = VAProfileMPEG2Simple; profiles[index++] = VAProfileMPEG2Main; } - found = v4l2_find_format(driver_data->video_fd, - V4L2_BUF_TYPE_VIDEO_OUTPUT, - V4L2_PIX_FMT_H264_SLICE) || - v4l2_find_format(driver_data->video_fd, - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - V4L2_PIX_FMT_H264_SLICE); + found = any_fd_supports_output_format(driver_data, V4L2_PIX_FMT_H264_SLICE); if (found && index < (V4L2_REQUEST_MAX_CONFIG_ATTRIBUTES - 5)) { profiles[index++] = VAProfileH264Main; profiles[index++] = VAProfileH264High; @@ -171,32 +195,15 @@ VAStatus RequestQueryConfigProfiles(VADriverContextP context, profiles[index++] = VAProfileH264StereoHigh; } - found = v4l2_find_format(driver_data->video_fd, - V4L2_BUF_TYPE_VIDEO_OUTPUT, - V4L2_PIX_FMT_HEVC_SLICE) || - v4l2_find_format(driver_data->video_fd, - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - V4L2_PIX_FMT_HEVC_SLICE); + found = any_fd_supports_output_format(driver_data, V4L2_PIX_FMT_HEVC_SLICE); if (found && index < (V4L2_REQUEST_MAX_CONFIG_ATTRIBUTES - 1)) profiles[index++] = VAProfileHEVCMain; - /* fresnel-fourier iter3: VP8 enumeration on hantro-vpu-dec */ - found = v4l2_find_format(driver_data->video_fd, - V4L2_BUF_TYPE_VIDEO_OUTPUT, - V4L2_PIX_FMT_VP8_FRAME) || - v4l2_find_format(driver_data->video_fd, - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - V4L2_PIX_FMT_VP8_FRAME); + found = any_fd_supports_output_format(driver_data, V4L2_PIX_FMT_VP8_FRAME); if (found && index < (V4L2_REQUEST_MAX_CONFIG_ATTRIBUTES - 1)) profiles[index++] = VAProfileVP8Version0_3; - /* fresnel-fourier iter4: VP9 enumeration on rkvdec */ - found = v4l2_find_format(driver_data->video_fd, - V4L2_BUF_TYPE_VIDEO_OUTPUT, - V4L2_PIX_FMT_VP9_FRAME) || - v4l2_find_format(driver_data->video_fd, - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - V4L2_PIX_FMT_VP9_FRAME); + found = any_fd_supports_output_format(driver_data, V4L2_PIX_FMT_VP9_FRAME); if (found && index < (V4L2_REQUEST_MAX_CONFIG_ATTRIBUTES - 1)) profiles[index++] = VAProfileVP9Profile0; diff --git a/src/request.c b/src/request.c index d8f26d5..0e1661b 100644 --- a/src/request.c +++ b/src/request.c @@ -25,10 +25,12 @@ */ #include "buffer.h" +#include "cap_pool.h" #include "config.h" #include "context.h" #include "image.h" #include "picture.h" +#include "request_pool.h" #include "subpicture.h" #include "surface.h" @@ -274,56 +276,168 @@ out: * guards against encoder-only devices that happen to share the same driver * name (e.g. hantro-vpu encoder vs decoder inside one /dev/mediaN). */ -static int find_codec_device(char *video_out, size_t video_out_sz, - char *media_out, size_t media_out_sz) +/* + * iter38: locate a /dev/mediaN whose driver name matches `want_driver` + * AND exposes at least one MEDIA_ENT_F_PROC_VIDEO_DECODER entity (rules + * out encoder-only devices sharing the same driver name). Resolves the + * matching /dev/videoM via topology graph walk. + * + * `want_driver`: + * - non-NULL → match only that exact driver name + * - NULL → match any name in known_decoder_drivers[] + */ +static int find_decoder_device_by_driver(const char *want_driver, + char *video_out, size_t video_out_sz, + char *media_out, size_t media_out_sz) { struct media_device_info info; char path[32]; const char * const *kd; - int fd, i, pass; + int fd, i; - for (pass = 0; pass < 2; pass++) { - for (i = 0; i < 16; i++) { - bool match; + for (i = 0; i < 16; i++) { + bool match; - snprintf(path, sizeof path, "/dev/media%d", i); - fd = open(path, O_RDWR | O_NONBLOCK); - if (fd < 0) - continue; - memset(&info, 0, sizeof info); - if (ioctl(fd, MEDIA_IOC_DEVICE_INFO, &info) != 0) { - close(fd); - continue; - } - if (pass == 0) { - /* Pass 1: rkvdec only. */ - match = (strcmp(info.driver, "rkvdec") == 0); - } else { - /* Pass 2: any known decoder driver. */ - match = false; - for (kd = known_decoder_drivers; *kd; kd++) { - if (strcmp(info.driver, *kd) == 0) { - match = true; - break; - } + snprintf(path, sizeof path, "/dev/media%d", i); + fd = open(path, O_RDWR | O_NONBLOCK); + if (fd < 0) + continue; + memset(&info, 0, sizeof info); + if (ioctl(fd, MEDIA_IOC_DEVICE_INFO, &info) != 0) { + close(fd); + continue; + } + if (want_driver != NULL) { + match = (strcmp(info.driver, want_driver) == 0); + } else { + match = false; + for (kd = known_decoder_drivers; *kd; kd++) { + if (strcmp(info.driver, *kd) == 0) { + match = true; + break; } } - if (!match) { - close(fd); - continue; - } - if (find_decoder_video_node_via_topology( - fd, video_out, video_out_sz) == 0) { - snprintf(media_out, media_out_sz, "%s", path); - close(fd); - return 0; - } - close(fd); } + if (!match) { + close(fd); + continue; + } + if (find_decoder_video_node_via_topology( + fd, video_out, video_out_sz) == 0) { + snprintf(media_out, media_out_sz, "%s", path); + close(fd); + return 0; + } + close(fd); } return -1; } +static int find_codec_device(char *video_out, size_t video_out_sz, + char *media_out, size_t media_out_sz) +{ + if (find_decoder_device_by_driver("rkvdec", + video_out, video_out_sz, + media_out, media_out_sz) == 0) + return 0; + return find_decoder_device_by_driver(NULL, + video_out, video_out_sz, + media_out, media_out_sz); +} + +/* + * iter38: profile → which physical decoder device should serve it on + * RK3399. Returns 'r' for rkvdec, 'h' for hantro, '?' for unknown. + * + * This is RK3399-shaped knowledge — a more general impl would interrogate + * each open device's supported OUTPUT formats. For the campaign-scope + * five codecs, the mapping is stable and explicit. + */ +char request_device_kind_for_profile(VAProfile profile); +char request_device_kind_for_profile(VAProfile profile) +{ + switch (profile) { + case VAProfileH264Main: + case VAProfileH264High: + case VAProfileH264ConstrainedBaseline: + case VAProfileH264MultiviewHigh: + case VAProfileH264StereoHigh: + case VAProfileHEVCMain: + case VAProfileVP9Profile0: + return 'r'; + case VAProfileMPEG2Simple: + case VAProfileMPEG2Main: + case VAProfileVP8Version0_3: + return 'h'; + default: + return '?'; + } +} + +/* + * iter38: retarget driver_data->{video,media}_fd to the device kind + * required by `profile`. If a switch is needed, tear down any per-device + * pool state so the next RequestCreateContext rebuilds it against the + * new device. Returns 0 on success, -1 if the required device wasn't + * probed (e.g. trying VP8 on a system without hantro). + * + * Safe to call repeatedly with the same profile: if the active fd + * already matches, the function is a no-op. + */ +int request_switch_device_for_profile(struct request_data *driver_data, + VAProfile profile); +int request_switch_device_for_profile(struct request_data *driver_data, + VAProfile profile) +{ + char kind = request_device_kind_for_profile(profile); + int target_video, target_media; + + if (kind == 'r') { + target_video = driver_data->video_fd_rkvdec; + target_media = driver_data->media_fd_rkvdec; + } else if (kind == 'h') { + target_video = driver_data->video_fd_hantro; + target_media = driver_data->media_fd_hantro; + } else { + return -1; + } + + /* Either side never probed (e.g. env-override single-device init, + * or this kind isn't present on the running kernel) → tolerate by + * staying on whatever's already active. RequestCreateConfig still + * accepted the profile via the format probe, so the active fd + * supports it. */ + if (target_video < 0 || target_media < 0) + return 0; + + if (driver_data->video_fd == target_video && + driver_data->media_fd == target_media) + return 0; /* already active, nothing to do */ + + /* + * Tear down any per-device pool state. cap_pool needs capture_type, + * which comes from video_format. Both rkvdec and hantro use + * V4L2_PIX_FMT_NV12 MPLANE on RK3399 (verified Phase 0 inventory) + * so the MPLANE form is always right here. + */ + if (driver_data->capture_pool.initialized) { + cap_pool_destroy(&driver_data->capture_pool, + driver_data->video_fd, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + } + if (driver_data->output_pool.initialized) + request_pool_destroy(&driver_data->output_pool); + + /* video_format is a static-ref pointer; re-probe on next + * CreateContext since the new device's format menu may differ. */ + driver_data->video_format = NULL; + driver_data->fmt_valid = false; + + driver_data->video_fd = target_video; + driver_data->media_fd = target_media; + return 0; +} + /* Set default visibility for the init function only. */ VAStatus __attribute__((visibility("default"))) VA_DRIVER_INIT_FUNC(VADriverContextP context); @@ -467,6 +581,68 @@ VAStatus VA_DRIVER_INIT_FUNC(VADriverContextP context) driver_data->video_fd = video_fd; driver_data->media_fd = media_fd; + driver_data->video_fd_rkvdec = -1; + driver_data->media_fd_rkvdec = -1; + driver_data->video_fd_hantro = -1; + driver_data->media_fd_hantro = -1; + + /* + * iter38: probe BOTH rkvdec and hantro-vpu so a single libva session + * can serve all 5 codecs. Tag the primary fd (already opened above) + * by inspecting which driver the media_fd is on, then probe the OTHER + * driver and open its fds if present. RequestCreateConfig retargets + * driver_data->{video,media}_fd to the right pair per profile. + * + * Skip the alt-probe when the user provided explicit + * LIBVA_V4L2_REQUEST_VIDEO_PATH / MEDIA_PATH overrides — they signal + * a specific single device intent. + */ + if (!getenv("LIBVA_V4L2_REQUEST_VIDEO_PATH") && + !getenv("LIBVA_V4L2_REQUEST_MEDIA_PATH")) { + struct media_device_info info; + const char *primary_driver = NULL; + const char *alt_driver = NULL; + + memset(&info, 0, sizeof info); + if (ioctl(media_fd, MEDIA_IOC_DEVICE_INFO, &info) == 0) { + if (strcmp(info.driver, "rkvdec") == 0) { + primary_driver = "rkvdec"; + alt_driver = "hantro-vpu"; + driver_data->video_fd_rkvdec = video_fd; + driver_data->media_fd_rkvdec = media_fd; + } else if (strcmp(info.driver, "hantro-vpu") == 0) { + primary_driver = "hantro-vpu"; + alt_driver = "rkvdec"; + driver_data->video_fd_hantro = video_fd; + driver_data->media_fd_hantro = media_fd; + } + } + + if (alt_driver != NULL) { + static char alt_video[32], alt_media[32]; + if (find_decoder_device_by_driver(alt_driver, + alt_video, sizeof alt_video, + alt_media, sizeof alt_media) == 0) { + 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) { + if (strcmp(alt_driver, "rkvdec") == 0) { + driver_data->video_fd_rkvdec = alt_v; + driver_data->media_fd_rkvdec = alt_m; + } else { + driver_data->video_fd_hantro = alt_v; + driver_data->media_fd_hantro = alt_m; + } + 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); + } + } + } + (void)primary_driver; + } status = VA_STATUS_SUCCESS; goto complete; @@ -501,8 +677,27 @@ VAStatus RequestTerminate(VADriverContextP context) */ request_pool_destroy(&driver_data->output_pool); - close(driver_data->video_fd); - close(driver_data->media_fd); + /* + * iter38: close both probed device pairs. video_fd / media_fd above + * are ACTIVE pointers into one of these pairs; close the underlying + * fds explicitly. Each may be -1 if its device wasn't found. + */ + if (driver_data->video_fd_rkvdec >= 0) + close(driver_data->video_fd_rkvdec); + if (driver_data->media_fd_rkvdec >= 0) + close(driver_data->media_fd_rkvdec); + if (driver_data->video_fd_hantro >= 0) + close(driver_data->video_fd_hantro); + if (driver_data->media_fd_hantro >= 0) + close(driver_data->media_fd_hantro); + /* 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) { + if (driver_data->video_fd >= 0) + close(driver_data->video_fd); + if (driver_data->media_fd >= 0) + close(driver_data->media_fd); + } /* Cleanup leftover buffers. */ diff --git a/src/request.h b/src/request.h index 37e6d49..02305a9 100644 --- a/src/request.h +++ b/src/request.h @@ -56,6 +56,27 @@ struct request_data { int video_fd; int media_fd; + /* + * iter38: multi-device probe. RK3399 has two V4L2 stateless decoders: + * - rkvdec → H264 / HEVC / VP9 + * - hantro-vpu (rk3399-vpu-dec) → MPEG-2 / VP8 + * At VA_DRIVER_INIT we probe both, open their fds, and store them + * here. driver_data->video_fd / media_fd above are the "active" fds + * (point at one of the pairs below). RequestCreateConfig retargets + * them based on the profile's required device. Pools and video_format + * are torn down at retarget time so the next CreateContext rebuilds + * them against the right device. + * + * -1 means that device kind isn't present on this kernel boot. + * Honours LIBVA_V4L2_REQUEST_VIDEO_PATH / MEDIA_PATH explicit + * overrides — when those are set, only the single requested device + * is opened and the alt fds stay -1. + */ + int video_fd_rkvdec; + int media_fd_rkvdec; + int video_fd_hantro; + int media_fd_hantro; + struct video_format *video_format; /* @@ -117,4 +138,12 @@ struct request_data { VAStatus VA_DRIVER_INIT_FUNC(VADriverContextP context); VAStatus RequestTerminate(VADriverContextP context); +/* + * iter38: retarget driver_data->{video,media}_fd to the device required by + * `profile`. Returns 0 on success, -1 on profile not mappable to any kind. + * Defined in request.c. + */ +int request_switch_device_for_profile(struct request_data *driver_data, + VAProfile profile); + #endif