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:
+39
-32
@@ -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;
|
||||
|
||||
|
||||
+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. */
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user