mirror of
https://gitlab.freedesktop.org/pipewire/pipewire
synced 2024-09-20 16:31:34 +00:00
v4l2: handle multiple /dev/videoX nodes
Associate each PipeWire video source to a video device and let applications probe and open multiple sources.
This commit is contained in:
parent
fbd3885ff1
commit
faab559568
|
@ -57,6 +57,7 @@ PW_LOG_TOPIC_STATIC(v4l2_log_topic, "v4l2");
|
||||||
#define DEFAULT_CARD "PipeWire Camera"
|
#define DEFAULT_CARD "PipeWire Camera"
|
||||||
#define DEFAULT_BUS_INFO "PipeWire"
|
#define DEFAULT_BUS_INFO "PipeWire"
|
||||||
|
|
||||||
|
#define MAX_DEV 32
|
||||||
struct file_map {
|
struct file_map {
|
||||||
void *addr;
|
void *addr;
|
||||||
struct file *file;
|
struct file *file;
|
||||||
|
@ -73,6 +74,7 @@ struct globals {
|
||||||
pthread_mutex_t lock;
|
pthread_mutex_t lock;
|
||||||
struct pw_array fd_maps;
|
struct pw_array fd_maps;
|
||||||
struct pw_array file_maps;
|
struct pw_array file_maps;
|
||||||
|
uint32_t dev_map[MAX_DEV];
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct globals globals;
|
static struct globals globals;
|
||||||
|
@ -93,6 +95,9 @@ struct buffer {
|
||||||
struct file {
|
struct file {
|
||||||
int ref;
|
int ref;
|
||||||
|
|
||||||
|
uint32_t dev_id;
|
||||||
|
uint32_t serial;
|
||||||
|
|
||||||
struct pw_properties *props;
|
struct pw_properties *props;
|
||||||
struct pw_thread_loop *loop;
|
struct pw_thread_loop *loop;
|
||||||
struct pw_loop *l;
|
struct pw_loop *l;
|
||||||
|
@ -300,6 +305,28 @@ static int add_fd_map(int fd, struct file *file)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t find_dev_for_serial(uint32_t serial)
|
||||||
|
{
|
||||||
|
uint32_t i, res = SPA_ID_INVALID;
|
||||||
|
pthread_mutex_lock(&globals.lock);
|
||||||
|
for (i = 0; i < SPA_N_ELEMENTS(globals.dev_map); i++) {
|
||||||
|
if (globals.dev_map[i] == serial) {
|
||||||
|
res = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&globals.lock);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool add_dev_for_serial(uint32_t dev, uint32_t serial)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&globals.lock);
|
||||||
|
globals.dev_map[dev] = serial;
|
||||||
|
pthread_mutex_unlock(&globals.lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* must be called with `globals.lock` held */
|
/* must be called with `globals.lock` held */
|
||||||
static struct fd_map *find_fd_map_unlocked(int fd)
|
static struct fd_map *find_fd_map_unlocked(int fd)
|
||||||
{
|
{
|
||||||
|
@ -584,20 +611,31 @@ static void registry_event_global(void *data, uint32_t id,
|
||||||
const struct global_info *info = NULL;
|
const struct global_info *info = NULL;
|
||||||
struct pw_proxy *proxy;
|
struct pw_proxy *proxy;
|
||||||
const char *str;
|
const char *str;
|
||||||
|
uint32_t serial = SPA_ID_INVALID, dev;
|
||||||
pw_log_debug("got %d %s", id, type);
|
|
||||||
|
|
||||||
if (spa_streq(type, PW_TYPE_INTERFACE_Node)) {
|
if (spa_streq(type, PW_TYPE_INTERFACE_Node)) {
|
||||||
|
|
||||||
if (file->node != NULL)
|
if (file->node != NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (props == NULL ||
|
pw_log_info("got %d %s", id, type);
|
||||||
((str = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS)) == NULL) ||
|
|
||||||
|
if (props == NULL)
|
||||||
|
return;
|
||||||
|
if (((str = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS)) == NULL) ||
|
||||||
((!spa_streq(str, "Video/Sink")) &&
|
((!spa_streq(str, "Video/Sink")) &&
|
||||||
(!spa_streq(str, "Video/Source"))))
|
(!spa_streq(str, "Video/Source"))))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pw_log_debug("found node %d type:%s", id, str);
|
if (((str = spa_dict_lookup(props, PW_KEY_OBJECT_SERIAL)) == NULL) ||
|
||||||
|
!spa_atou32(str, &serial, 10))
|
||||||
|
return;
|
||||||
|
|
||||||
|
dev = find_dev_for_serial(serial);
|
||||||
|
if (dev != SPA_ID_INVALID && dev != file->dev_id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pw_log_info("found node:%d serial:%d type:%s", id, serial, str);
|
||||||
info = &node_info;
|
info = &node_info;
|
||||||
}
|
}
|
||||||
if (info) {
|
if (info) {
|
||||||
|
@ -629,6 +667,7 @@ static void registry_event_global(void *data, uint32_t id,
|
||||||
if (info->init)
|
if (info->init)
|
||||||
info->init(g);
|
info->init(g);
|
||||||
|
|
||||||
|
file->serial = serial;
|
||||||
file->node = g;
|
file->node = g;
|
||||||
|
|
||||||
do_resync(file);
|
do_resync(file);
|
||||||
|
@ -666,13 +705,20 @@ static int v4l2_openat(int dirfd, const char *path, int oflag, mode_t mode)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
struct file *file;
|
struct file *file;
|
||||||
|
bool passthrough = true;
|
||||||
|
uint32_t dev_id = SPA_ID_INVALID;
|
||||||
|
|
||||||
if (!spa_strstartswith(path, "/dev/video0"))
|
if (spa_strstartswith(path, "/dev/video")) {
|
||||||
|
if (spa_atou32(path+10, &dev_id, 10) && dev_id < MAX_DEV)
|
||||||
|
passthrough = false;
|
||||||
|
}
|
||||||
|
if (passthrough)
|
||||||
return globals.old_fops.openat(dirfd, path, oflag, mode);
|
return globals.old_fops.openat(dirfd, path, oflag, mode);
|
||||||
|
|
||||||
if ((file = make_file()) == NULL)
|
if ((file = make_file()) == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
file->dev_id = dev_id;
|
||||||
file->props = pw_properties_new(
|
file->props = pw_properties_new(
|
||||||
PW_KEY_CLIENT_API, "v4l2",
|
PW_KEY_CLIENT_API, "v4l2",
|
||||||
NULL);
|
NULL);
|
||||||
|
@ -727,6 +773,7 @@ static int v4l2_openat(int dirfd, const char *path, int oflag, mode_t mode)
|
||||||
res, strerror(res < 0 ? errno : 0));
|
res, strerror(res < 0 ? errno : 0));
|
||||||
|
|
||||||
add_fd_map(res, file);
|
add_fd_map(res, file);
|
||||||
|
add_dev_for_serial(file->dev_id, file->serial);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue