summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2025-02-20 21:07:14 -0500
committerpommicket <pommicket@gmail.com>2025-02-20 21:07:14 -0500
commit0586767eef4d1ebfb2c065106b67b2ef3639b77c (patch)
tree29e7e2984dc86c4f0e45cbe67dc91bda9007fddd
parent068e6de0885e274cb9528d69968f9583d7de0793 (diff)
serial numbers
-rw-r--r--camera.c1
-rw-r--r--camera.h5
-rw-r--r--main.c96
3 files changed, 70 insertions, 32 deletions
diff --git a/camera.c b/camera.c
index c96dc73..a03f21c 100644
--- a/camera.c
+++ b/camera.c
@@ -1,7 +1,6 @@
#include "camera.h"
#include <linux/videodev2.h>
#include <sodium.h>
-#include <string.h>
#include <libv4l2.h>
#include <sys/mman.h>
#include <poll.h>
diff --git a/camera.h b/camera.h
index 5fe1c4d..d4f1a0e 100644
--- a/camera.h
+++ b/camera.h
@@ -4,6 +4,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
+#include <string.h>
#include <GL/glcorearb.h>
typedef uint32_t PixelFormat;
@@ -92,6 +93,10 @@ typedef struct {
#undef declare_proc
} GlProcs;
+static bool hash_eq(Hash h1, Hash h2) {
+ return memcmp(h1.hash, h2.hash, sizeof h1.hash) == 0;
+}
+
void camera_init(const GlProcs *procs);
bool pix_fmt_supported(uint32_t pixfmt);
int picture_format_cmp_resolution(const PictureFormat *a, const PictureFormat *b);
diff --git a/main.c b/main.c
index ea75ee0..def990a 100644
--- a/main.c
+++ b/main.c
@@ -11,7 +11,6 @@ TODO
#include <linux/videodev2.h>
#include <inttypes.h>
#include <errno.h>
-#include <string.h>
#include <SDL.h>
#include <SDL_ttf.h>
#include <time.h>
@@ -20,6 +19,7 @@ TODO
#include <fontconfig/fontconfig.h>
#include <fcntl.h>
#include <unistd.h>
+#include <dirent.h>
#include "ds.h"
#include "camera.h"
@@ -39,14 +39,14 @@ typedef enum {
enum {
MENU_OPT_QUIT = 1,
MENU_OPT_RESOLUTION,
- MENU_OPT_INPUT,
+ MENU_OPT_VIDEO_INPUT,
MENU_OPT_PIXFMT,
MENU_OPT_IMGFMT,
};
// use char for MenuOption type so that we can use strlen
typedef char MenuOption;
static const MenuOption main_menu[] = {
- MENU_OPT_INPUT,
+ MENU_OPT_VIDEO_INPUT,
MENU_OPT_RESOLUTION,
MENU_OPT_IMGFMT,
MENU_OPT_PIXFMT,
@@ -73,6 +73,7 @@ typedef struct {
Camera **cameras;
ImageFormat image_format;
SDL_Rect *menu_option_rects;
+ Hash *camera_precedence;
} State;
#if crypto_generichash_BYTES_MIN > HASH_SIZE
@@ -260,7 +261,7 @@ static void menu_select(State *state) {
}
arr_free(resolutions);
} break;
- case MENU_OPT_INPUT:
+ case MENU_OPT_VIDEO_INPUT:
if (state->cameras) {
state->curr_menu = MENU_INPUT;
state->menu_needs_rerendering = true;
@@ -337,6 +338,26 @@ static void menu_select(State *state) {
}
}
+static void select_camera(State *state) {
+ arr_foreach_ptr(state->camera_precedence, const Hash, h) {
+ arr_foreach_ptr(state->cameras, Camera *const, pcamera) {
+ Camera *c = *pcamera;
+ if (hash_eq(camera_hash(c), *h)) {
+ if (state->camera == c)
+ return;
+ state->camera = c;
+ break;
+ }
+ }
+ if (state->camera) break;
+ }
+ if (!state->camera) {
+ state->camera = state->cameras[0];
+ arr_add(state->camera_precedence, camera_hash(state->camera));
+ }
+ camera_open(state->camera);
+}
+
int menu_get_option_at_pos(State *state, int x, int y) {
// technically this may be wrong for a single frame when the menu options change, but who cares.
arr_foreach_ptr(state->menu_option_rects, SDL_Rect, r) {
@@ -630,16 +651,6 @@ void main() {\n\
{
struct udev_enumerate *enumerate = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(enumerate, "video4linux");
- /*
- udev_enumerate_add_match_subsystem(enumerate, "usb");
- udev_list_entry_foreach(device, devices) {
- const char *serial = udev_device_get_sysattr_value(dev, "serial");
- if (!serial || !*serial) continue;
- TODO: walk through device directory here to see if it has any video4linux children.
- NOTE: bus_info seems to be not a good way of identifying devices (it's a bit mysterious)
- and we'd have to support nested USB hubs which is a pain anyways.
- }
- */
udev_enumerate_scan_devices(enumerate);
struct udev_list_entry *device = NULL, *devices = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(device, devices) {
@@ -648,17 +659,43 @@ void main() {\n\
const char *devnode = udev_device_get_devnode(dev);
if (!devnode) continue;
const char *subsystem = udev_device_get_sysattr_value(dev, "subsystem");
- const char *serial = udev_device_get_sysattr_value(dev, "serial");
- if (strcmp(subsystem, "video4linux") == 0) {
- int status = access(devnode, R_OK);
- if (status != 0 && errno == EACCES) {
- // can't read from this device
- goto cont;
+ if (!subsystem || strcmp(subsystem, "video4linux") != 0) goto cont1;
+ int status = access(devnode, R_OK);
+ if (status != 0 && errno == EACCES) {
+ // can't read from this device
+ goto cont1;
+ }
+ if (status != 0) goto cont1;
+ /*
+ build up a serial number for the camera by taking its "serial" value,
+ together with the serial of its ancestors
+ (my personal camera doesn't have a serial on the video4linux device,
+ but does have one on its grandparent- this makes sense since a single
+ physical device can have multiple cameras, as well as microphones, etc.)
+ */
+ // NOTE: we don't need to unref the return value of udev_device_get_parent
+ struct udev_device *parent = udev_device_get_parent(dev);
+ const char *serial_str = udev_device_get_sysattr_value(dev, "serial");
+ StrBuilder serial = str_builder_new();
+ if (serial_str && *serial_str)
+ str_builder_appendf(&serial, "%s;", serial_str);
+ for (int k = 0; k < 100 /* prevent infinite loop due to some fucked up device state */; k++) {
+ const char *parent_serial = udev_device_get_sysattr_value(parent, "serial");
+ if (parent_serial && strlen(parent_serial) >= 12 &&
+ parent_serial[4] == ':' && parent_serial[7] == ':' && parent_serial[10] == '.') {
+ // this is actually a USB interface! e.g. 0000:06:00.3
+ // so it is not tied to the camera
+ break;
}
- if (status) break;
- cameras_from_device(devnode, serial, &state->cameras);
+ if (parent_serial && *parent_serial)
+ str_builder_appendf(&serial, "%s;", parent_serial);
+ struct udev_device *grandparent = udev_device_get_parent(parent);
+ if (!grandparent) break;
+ parent = grandparent;
}
- cont:
+ cameras_from_device(devnode, serial.str, &state->cameras);
+ str_builder_free(&serial);
+ cont1:
udev_device_unref(dev);
}
udev_enumerate_unref(enumerate);
@@ -672,12 +709,9 @@ void main() {\n\
printf("\n");
}
}
- if (arr_len(state->cameras) == 0) {
- state->camera = NULL;
- } else {
- state->camera = state->cameras[0];
- if (!camera_open(state->camera))
- return EXIT_FAILURE;
+ state->camera = NULL;
+ if (arr_len(state->cameras) != 0) {
+ select_camera(state);
}
double flash_time = -INFINITY;
uint32_t last_frame_pixfmt = 0;
@@ -864,8 +898,8 @@ void main() {\n\
option = a_sprintf("Resolution: None");
}
break;
- case MENU_OPT_INPUT:
- option = a_sprintf("Input: %s", state->camera ? camera_name(state->camera) : "None");
+ case MENU_OPT_VIDEO_INPUT:
+ option = a_sprintf("Video Input: %s", state->camera ? camera_name(state->camera) : "None");
break;
case MENU_OPT_PIXFMT:
option = a_sprintf("Picture format: %s",