Browse Source

linux-v4l2: Change search strategy for v4l2loopback devices

Instead of trying to open devices /dev/video<0-63> check /dev for
all /dev/videoX devices that are existent in the tree. This allows
finding devices with higher names than 63. E.g. /dev/video100 can
now be found and opened which previously failed.

_GNU_SOURCE #define is introduced for versionsort() which is a GNU
extension.

Fixes #4347.
Florian Zwoch 4 years ago
parent
commit
88aec3b43c
1 changed files with 34 additions and 16 deletions
  1. 34 16
      plugins/linux-v4l2/v4l2-output.c

+ 34 - 16
plugins/linux-v4l2/v4l2-output.c

@@ -1,3 +1,5 @@
+#define _GNU_SOURCE
+
 #include <obs-module.h>
 #include <util/dstr.h>
 #include <util/platform.h>
@@ -5,8 +7,7 @@
 #include <sys/ioctl.h>
 #include <fcntl.h>
 #include <unistd.h>
-
-#define MAX_DEVICES 64
+#include <dirent.h>
 
 struct virtualcam_data {
 	obs_output_t *output;
@@ -109,7 +110,7 @@ static void *virtualcam_create(obs_data_t *settings, obs_output_t *output)
 	return vcam;
 }
 
-static bool try_connect(void *data, int device)
+static bool try_connect(void *data, const char *device)
 {
 	struct virtualcam_data *vcam = (struct virtualcam_data *)data;
 	struct v4l2_format format;
@@ -121,12 +122,7 @@ static bool try_connect(void *data, int device)
 
 	vcam->frame_size = width * height * 2;
 
-	char new_device[16];
-	if (device < 0 || device >= MAX_DEVICES)
-		return false;
-	snprintf(new_device, 16, "/dev/video%d", device);
-
-	vcam->device = open(new_device, O_RDWR);
+	vcam->device = open(device, O_RDWR);
 
 	if (vcam->device < 0)
 		return false;
@@ -172,24 +168,46 @@ static bool try_connect(void *data, int device)
 	return true;
 }
 
+static int scanfilter(const struct dirent *entry)
+{
+	return !astrcmp_n(entry->d_name, "video", 5);
+}
+
 static bool virtualcam_start(void *data)
 {
 	struct virtualcam_data *vcam = (struct virtualcam_data *)data;
+	struct dirent **list;
+	bool success = false;
+	int n;
 
 	if (!loopback_module_loaded()) {
 		if (loopback_module_load() != 0)
 			return false;
 	}
 
-	for (int i = 0; i < MAX_DEVICES; i++) {
-		if (!try_connect(vcam, i))
-			continue;
-		else
-			return true;
+	n = scandir("/dev", &list, scanfilter, versionsort);
+	if (n == -1)
+		return false;
+
+	for (int i = 0; i < n; i++) {
+		char device[32];
+
+		snprintf(device, 32, "/dev/%s", list[i]->d_name);
+
+		if (try_connect(vcam, device)) {
+			success = true;
+			break;
+		}
 	}
 
-	blog(LOG_WARNING, "Failed to start virtual camera");
-	return false;
+	while (n--)
+		free(list[n]);
+	free(list);
+
+	if (!success)
+		blog(LOG_WARNING, "Failed to start virtual camera");
+
+	return success;
 }
 
 static void virtualcam_stop(void *data, uint64_t ts)