Support UVC cameras somewhat

This commit is contained in:
Martijn Braam
2023-07-09 21:31:51 +02:00
parent b13d15d5de
commit e4a39c4747
8 changed files with 218 additions and 71 deletions

View File

@@ -1,31 +0,0 @@
Version = 1;
Make: "UVC";
Model: "UVC";
Rear: {
SensorDriver: "ov5640";
BridgeDriver: "uvcvideo";
FlashPath: "/sys/class/leds/white:flash";
IsoMin: 100;
IsoMax: 64000;
Modes: (
{
Width: 2592;
Height: 1944;
Rate: 15;
Format: "BGGR8";
Rotate: 270;
FocalLength: 3.33;
FNumber: 3.0;
},
{
Width: 1280;
Height: 720;
Rate: 30;
Format: "BGGR8";
FocalLength: 3.33;
FNumber: 3.0;
}
);
};

View File

@@ -75,17 +75,23 @@ typedef struct _lmp_camera libmegapixels_camera;
struct _lmp_device_config {
char *path;
char *make;
char *model;
const char *make;
const char *model;
int count;
int loaded_config;
int loaded_uvc;
libmegapixels_camera **cameras;
};
typedef struct _lmp_device_config libmegapixels_devconfig;
EXPORT int
libmegapixels_init(libmegapixels_devconfig **config);
EXPORT int
libmegapixels_load_file(libmegapixels_devconfig **config, const char *file);
libmegapixels_load_file(libmegapixels_devconfig *config, const char *file);
EXPORT int
libmegapixels_load_uvc(libmegapixels_devconfig *config);
EXPORT int
libmegapixels_open(libmegapixels_camera *camera);

View File

@@ -4,12 +4,6 @@
#include "mode.h"
struct libmegapixels_modename {
char *name;
uint32_t v4l_pixel_format;
uint32_t media_bus_format;
};
// TODO: The 16 bit formats are imported from millipixels and seem broken
static struct libmegapixels_modename mode_lut[] = {
{
@@ -90,7 +84,7 @@ static struct libmegapixels_modename mode_lut[] = {
{
.name = "GRBG16",
.v4l_pixel_format = V4L2_PIX_FMT_SGRBG16,
.media_bus_format =MEDIA_BUS_FMT_SGRBG10_1X10 ,
.media_bus_format =MEDIA_BUS_FMT_SGRBG10_1X10,
},
{
.name = "RGGB16",
@@ -131,4 +125,16 @@ format_name_to_media_busfmt(const char *name)
}
}
return 0;
}
struct libmegapixels_modename *
v4l_pixfmt_to_mode(uint32_t pixfmt)
{
int count = sizeof(mode_lut) / sizeof(mode_lut[0]);
for (int i = 0; i < count; i++) {
if (mode_lut[i].v4l_pixel_format == pixfmt) {
return &mode_lut[i];
}
}
return 0;
}

View File

@@ -1,7 +1,17 @@
#pragma once
struct libmegapixels_modename {
char *name;
uint32_t v4l_pixel_format;
uint32_t media_bus_format;
};
uint32_t
format_name_to_v4l_pixfmt(const char *name);
uint32_t
format_name_to_media_busfmt(const char *name);
struct libmegapixels_modename *
v4l_pixfmt_to_mode(uint32_t pixfmt);

View File

@@ -10,6 +10,7 @@
#include <linux/media.h>
#include <stdint.h>
#include <limits.h>
#include <linux/videodev2.h>
#include "libmegapixels.h"
#include "mode.h"
#include "util.h"
@@ -317,15 +318,20 @@ load_camera(libmegapixels_devconfig *config, config_t *cfg, const char *name)
}
int
libmegapixels_load_file(libmegapixels_devconfig **config, const char *file)
libmegapixels_load_file(libmegapixels_devconfig *config, const char *file)
{
if (config->loaded_config) {
log_error("Config already loaded\n");
return 0;
}
config->loaded_config = 1;
config_t cfg;
config_setting_t *setting, *member;
*config = malloc(sizeof(libmegapixels_devconfig));
(*config)->path = strdup(file);
(*config)->count = 0;
(*config)->cameras = malloc(sizeof((*config)->cameras));
config->path = strdup(file);
if (config->count == 0) {
config->cameras = malloc(0);
}
config_init(&cfg);
if (!config_read_file(&cfg, file)) {
@@ -345,11 +351,11 @@ libmegapixels_load_file(libmegapixels_devconfig **config, const char *file)
return 0;
}
if (!config_lookup_string(&cfg, "Make", (const char **) &(*config)->make)) {
(*config)->make = strdup("Megapixels");
if (!config_lookup_string(&cfg, "Make", &config->make)) {
config->make = strdup("Megapixels");
}
if (!config_lookup_string(&cfg, "Model", (const char **) &(*config)->model)) {
(*config)->model = strdup("Camera");
if (!config_lookup_string(&cfg, "Model", &config->model)) {
config->model = strdup("Camera");
}
setting = config_root_setting(&cfg);
@@ -370,8 +376,127 @@ libmegapixels_load_file(libmegapixels_devconfig **config, const char *file)
if (strcmp(member->name, "Model") == 0) {
continue;
}
load_camera(*config, &cfg, member->name);
load_camera(config, &cfg, member->name);
}
return 1;
}
void
uvc_create_modes(libmegapixels_camera *camera, int fd)
{
struct v4l2_fmtdesc fmtdesc = {0};
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while (xioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
fmtdesc.index++;
struct libmegapixels_modename *mn = v4l_pixfmt_to_mode(fmtdesc.pixelformat);
if (mn == NULL) {
continue;
}
struct v4l2_frmsizeenum framesize = {0};
framesize.pixel_format = fmtdesc.pixelformat;
while (xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &framesize) == 0) {
if (framesize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
struct v4l2_frmivalenum frameinterval = {0};
frameinterval.pixel_format = framesize.pixel_format;
frameinterval.width = framesize.discrete.width;
frameinterval.height = framesize.discrete.height;
while (xioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frameinterval) == 0) {
frameinterval.index++;
if (frameinterval.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
libmegapixels_mode *mode = calloc(1, sizeof(libmegapixels_mode));
mode->v4l_pixfmt = fmtdesc.pixelformat;
mode->width = (int) framesize.discrete.width;
mode->height = (int) framesize.discrete.height;
mode->media_busfmt = mn->media_bus_format;
mode->rotation = 0;
mode->rate = (int) ((double) frameinterval.discrete.denominator /
(double) frameinterval.discrete.numerator);
camera->modes = realloc(camera->modes, (camera->num_modes + 1) * sizeof(camera->modes));
if (camera->modes == NULL) {
return;
}
camera->modes[camera->num_modes++] = mode;
}
}
}
framesize.index++;
}
}
}
int
libmegapixels_load_uvc(libmegapixels_devconfig *config)
{
if (config->loaded_uvc) {
log_error("libmegapixels_load_uvc was already called\n");
return 0;
}
config->loaded_uvc = 1;
struct dirent *dir;
DIR *d = opendir("/dev");
while ((dir = readdir(d)) != NULL) {
if (strncmp(dir->d_name, "video", 5) == 0) {
char path[PATH_MAX];
snprintf(path, PATH_MAX, "/dev/%s", dir->d_name);
int fd = open(path, O_RDWR);
if (fd < 0) {
continue;
}
struct v4l2_capability vid_cap = {0};
if (xioctl(fd, VIDIOC_QUERYCAP, &vid_cap) == -1) {
perror("QUERYCAP");
continue;
}
if (strcmp(vid_cap.driver, "uvcvideo") != 0) {
continue;
}
if (!(vid_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
continue;
}
if (!(vid_cap.capabilities & V4L2_CAP_STREAMING)) {
continue;
}
struct v4l2_fmtdesc fmtdesc = {0};
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != 0) {
continue;
}
libmegapixels_camera *camera;
camera = calloc(1, sizeof(libmegapixels_camera));
camera->name = strdup((const char *) vid_cap.card);
camera->video_path = strdup(path);
uvc_create_modes(camera, fd);
close(fd);
config->cameras = realloc(config->cameras, (config->count + 1) * sizeof(config->cameras));
if (config->cameras == NULL) {
return -1;
}
config->cameras[config->count++] = camera;
}
}
closedir(d);
return 1;
}
int
libmegapixels_init(libmegapixels_devconfig **config)
{
*config = calloc(1, sizeof(libmegapixels_devconfig));
return 1;
}

View File

@@ -129,16 +129,20 @@ libmegapixels_open(libmegapixels_camera *camera)
return -1;
}
camera->media_fd = open(camera->media_path, O_RDWR);
if (camera->media_fd < 0) {
log_error("Could not open %s: %s\n", camera->media_path, strerror(errno));
return -1;
if (camera->media_path) {
camera->media_fd = open(camera->media_path, O_RDWR);
if (camera->media_fd < 0) {
log_error("Could not open %s: %s\n", camera->media_path, strerror(errno));
return -1;
}
}
camera->sensor_fd = open(camera->sensor_path, O_RDWR);
if (camera->sensor_fd < 0) {
log_error("Could not open %s: %s\n", camera->sensor_path, strerror(errno));
return -1;
if (camera->sensor_path) {
camera->sensor_fd = open(camera->sensor_path, O_RDWR);
if (camera->sensor_fd < 0) {
log_error("Could not open %s: %s\n", camera->sensor_path, strerror(errno));
return -1;
}
}
camera->video_fd = open(camera->video_path, O_RDWR);
@@ -147,9 +151,16 @@ libmegapixels_open(libmegapixels_camera *camera)
return -1;
}
int ret = load_entity_ids(camera);
if (ret < 0) {
return ret;
// If this is an UVC camera the sensor _is_ the video device
if (camera->sensor_fd == 0) {
camera->sensor_fd = camera->video_fd;
}
if (camera->media_fd > 0) {
int ret = load_entity_ids(camera);
if (ret < 0) {
return ret;
}
}
return 0;
@@ -158,6 +169,11 @@ libmegapixels_open(libmegapixels_camera *camera)
void
libmegapixels_close(libmegapixels_camera *camera)
{
int uvc = 0;
if (camera->sensor_fd != 0 && camera->sensor_fd == camera->video_fd) {
uvc = 1;
}
if (camera->media_fd != 0) {
close(camera->media_fd);
camera->media_fd = 0;
@@ -165,6 +181,9 @@ libmegapixels_close(libmegapixels_camera *camera)
if (camera->sensor_fd != 0) {
close(camera->sensor_fd);
camera->sensor_fd = 0;
if (uvc) {
camera->video_fd = 0;
}
}
if (camera->video_fd != 0) {
close(camera->video_fd);

View File

@@ -33,14 +33,19 @@ main(int argc, char *argv[])
}
libmegapixels_devconfig *config = {0};
libmegapixels_init(&config);
if (!ret) {
printf("No config found\n");
return 1;
} else {
printf("Using config: %s\n", configpath);
if (!libmegapixels_load_file(config, configpath)) {
printf("Could not load config\n");
}
}
printf("Using config: %s\n", configpath);
if (!libmegapixels_load_file(&config, configpath)) {
libmegapixels_load_uvc(config);
if (config->count == 0) {
return 1;
}
@@ -49,9 +54,13 @@ main(int argc, char *argv[])
for (int i = 0; i < config->count; i++) {
printf("\n----[ Camera %s (%d) ]----\n", config->cameras[i]->name, i);
printf("Media : %s (%s)\n", config->cameras[i]->bridge_name, config->cameras[i]->media_path);
printf("Sensor: %s (%s)\n", config->cameras[i]->sensor_name, config->cameras[i]->sensor_path);
printf("Bridge: %s\n", config->cameras[i]->video_path);
if (config->cameras[i]->bridge_name) {
printf("Media : %s (%s)\n", config->cameras[i]->bridge_name, config->cameras[i]->media_path);
}
if (config->cameras[i]->sensor_name) {
printf("Sensor: %s (%s)\n", config->cameras[i]->sensor_name, config->cameras[i]->sensor_path);
}
printf("Video : %s\n", config->cameras[i]->video_path);
printf("Modes : ");
for (int j = 0; j < config->cameras[i]->num_modes; j++) {
if (j > 0) {

View File

@@ -67,21 +67,24 @@ main(int argc, char *argv[])
char configpath[PATH_MAX];
int ret = libmegapixels_find_config(configpath);
libmegapixels_devconfig *config = {0};
libmegapixels_init(&config);
if (ret) {
printf("Using config: %s\n", configpath);
libmegapixels_load_file(&config, configpath);
libmegapixels_load_file(config, configpath);
} else {
fprintf(stderr, "No config found for this device\n");
return 1;
}
libmegapixels_load_uvc(config);
if (config->count == 0) {
fprintf(stderr, "No valid camera configuration\n");
return 1;
}
if (camera_id > config->count) {
if (camera_id > config->count - 1) {
fprintf(stderr, "Camera id %d does not exist\n", camera_id);
return 1;
}
libmegapixels_camera *camera = config->cameras[camera_id];