iter38: multi-device probe — single libva session serves all 5 codecs
Probe BOTH rkvdec and hantro-vpu at VA_DRIVER_INIT and keep their
{video,media}_fd pairs in driver_data. RequestQueryConfigProfiles
enumerates the union of supported profiles from all open fds.
RequestCreateConfig retargets driver_data->{video,media}_fd to the
device that serves the requested profile; if a switch is needed
(active fd is wrong), tears down output_pool, capture_pool, video_format
cache, and fmt_valid so the next RequestCreateContext rebuilds them
on the new device.
Profile→device map (RK3399-shaped):
H264 / HEVC / VP9 → rkvdec
MPEG-2 / VP8 → hantro-vpu
Honours LIBVA_V4L2_REQUEST_VIDEO_PATH / MEDIA_PATH explicit overrides
(skips alt-probe when those are set).
Closes the 'libva multi-device probe' open item from iter36/iter37
campaign-close.
This commit is contained in:
+234
-39
@@ -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. */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user