diff options
author | pommicket <pommicket@gmail.com> | 2025-02-20 21:07:14 -0500 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2025-02-20 21:07:14 -0500 |
commit | 0586767eef4d1ebfb2c065106b67b2ef3639b77c (patch) | |
tree | 29e7e2984dc86c4f0e45cbe67dc91bda9007fddd | |
parent | 068e6de0885e274cb9528d69968f9583d7de0793 (diff) |
serial numbers
-rw-r--r-- | camera.c | 1 | ||||
-rw-r--r-- | camera.h | 5 | ||||
-rw-r--r-- | main.c | 96 |
3 files changed, 70 insertions, 32 deletions
@@ -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> @@ -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); @@ -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", |