@@ -8,7 +8,7 @@
Copyright 2017 Pavel Machek, LGPL
Needs sdl2, sdl2_image libraries. sudo aptitude install libsdl2-dev
- libsdl2-image-dev on Debian systems.
+ libsdl2-image-dev libjpeg-dev on Debian systems.
*/
#include <time.h>
@@ -26,6 +26,7 @@
#include <sys/time.h>
#include <sys/ioctl.h>
#include <fcntl.h>
+#include <dirent.h>
#include <jpeglib.h>
@@ -1172,11 +1173,18 @@ static void sdl_iteration(struct sdl *m)
}
}
+static int open_complex(char *name, int v4l2_flags);
+
static void cam_open(struct dev_info *dev, char *name)
{
struct v4l2_format *fmt = &dev->fmt;
- dev->fd = v4l2_open(name, O_RDWR);
+ // dev->fd = v4l2_open(name, O_RDWR);
+ //dev->fd = open_complex("/my/tui/camera/n900.cv", 0);
+ dev->fd = open_complex("/data/pavel/g/v4l-utils/test.txt", 0);
+ //dev->fd = open(name, O_RDWR);
+ //printf("fd = %d\n", dev->fd);
+ //dev->fd = v4l2_fd_open(dev->fd, 0);
if (dev->fd < 0) {
printf("video %s open failed: %m\n", name);
exit(1);
@@ -1198,6 +1206,179 @@ static void cam_open(struct dev_info *dev, char *name)
/* ------------------------------------------------------------------ */
+static void scan_devices(char **device_names, int *device_fds, int num)
+{
+ struct dirent **namelist;
+ int n;
+ char *class_v4l = "/sys/class/video4linux";
+
+ n = scandir(class_v4l, &namelist, NULL, alphasort);
+ if (n < 0) {
+ perror("scandir");
+ return;
+ }
+
+ while (n--) {
+ if (namelist[n]->d_name[0] != '.') {
+ char filename[1024], content[1024];
+ sprintf(filename, "%s/%s/name", class_v4l, namelist[n]->d_name);
+ FILE *f = fopen(filename, "r");
+ if (!f) {
+ printf("Strange, can't open %s", filename);
+ } else {
+ fgets(content, 1024, f);
+ fclose(f);
+ printf("device %s : %s\n", namelist[n]->d_name, content);
+ int i;
+ for (i = num-1; i >=0; i--) {
+ if (!strcmp(content, device_names[i])) {
+ sprintf(filename, "/dev/%s", namelist[n]->d_name);
+ device_fds[i] = open(filename, O_RDWR);
+ if (device_fds[i] < 0) {
+ printf("Error opening %s: %m\n", filename);
+ }
+ printf("*** found match - %s %d\n", filename, device_fds[i]);
+ }
+ }
+ }
+ }
+ free(namelist[n]);
+ }
+ free(namelist);
+
+}
+
+static int open_complex(char *name, int v4l2_flags)
+{
+#define perr(s) printf("v4l2: open_complex: " s "\n");
+#define BUF 256
+ FILE *f = fopen(name, "r");
+
+ int res = -1;
+ char buf[BUF];
+ int version, num_modes, num_devices, num_controls;
+ int dev, control;
+
+ if (!f) {
+ perr("open of .cv file failed: %m");
+ goto err;
+ }
+
+ if (fscanf(f, "Complex Video: %d\n", &version) != 1) {
+ perr(".cv file does not have required header");
+ goto close;
+ }
+
+ if (version != 0) {
+ perr(".cv file has unknown version");
+ goto close;
+ }
+
+ if (fscanf(f, "#modes: %d\n", &num_modes) != 1) {
+ perr("could not parse modes");
+ goto close;
+ }
+
+ if (num_modes != 1) {
+ perr("only single mode is supported for now");
+ goto close;
+ }
+
+ if (fscanf(f, "Mode: %s\n", buf) != 1) {
+ perr("could not parse mode name");
+ goto close;
+ }
+
+ if (fscanf(f, " #devices: %d\n", &num_devices) != 1) {
+ perr("could not parse number of devices");
+ goto close;
+ }
+#define MAX_DEVICES 16
+ char *device_names[MAX_DEVICES] = { NULL, };
+ int device_fds[MAX_DEVICES];
+ if (num_devices > MAX_DEVICES) {
+ perr("too many devices");
+ goto close;
+ }
+
+ for (dev = 0; dev < num_devices; dev++) {
+ int tmp;
+ if (fscanf(f, "%d: ", &tmp) != 1) {
+ perr("could not parse device");
+ goto free_devices;
+ }
+ if (tmp != dev) {
+ perr("bad device number");
+ goto free_devices;
+ }
+ fgets(buf, BUF, f);
+ printf("Device %d %d %s\n", dev, tmp, buf);
+ device_names[dev] = strdup(buf);
+ device_fds[dev] = -1;
+ }
+
+ scan_devices(device_names, device_fds, num_devices);
+
+ for (dev = 0; dev < num_devices; dev++) {
+ printf("Device %d fd %d\n", dev, device_fds[dev]);
+ if (device_fds[dev] == -1) {
+ perr("Could not open all required devices");
+ goto close_devices;
+ }
+ }
+
+ if (fscanf(f, " #controls: %d\n", &num_controls) != 1) {
+ perr("can not parse number of controls");
+ goto close_devices;
+ }
+
+ struct v4l2_controls_map *map = malloc(sizeof(struct v4l2_controls_map) +
+ num_controls*sizeof(struct v4l2_control_map));
+
+ map->num_controls = num_controls;
+ map->num_fds = num_devices;
+ map->main_fd = device_fds[0];
+
+ for (control = 0; control < num_controls; control++) {
+ unsigned long num;
+ int dev;
+ if (fscanf(f, "0x%lx: %d\n", &num, &dev) != 2) {
+ perr("could not parse control");
+ goto free_map;
+ }
+ if ((dev < 0) || (dev >= num_devices)) {
+ perr("device out of range");
+ goto free_map;
+ }
+ map->map[control].control = num;
+ map->map[control].fd = device_fds[dev];
+ printf("---> map: %lx %d\n", num, device_fds[dev]);
+ }
+ if (fscanf(f, "%s", buf) > 0) {
+ perr("junk at end of file");
+ goto free_map;
+ }
+
+ printf("Success, main fd is %d\n", map->main_fd);
+ res = v4l2_open_pipeline(map, v4l2_flags);
+
+ if (res < 0) {
+free_map:
+ free(map);
+close_devices:
+ for (dev = 0; dev < num_devices; dev++)
+ close(device_fds[dev]);
+ }
+free_devices:
+ for (dev = 0; dev < num_devices; dev++) {
+ free(device_names[dev]);
+ }
+close:
+ fclose(f);
+err:
+ return res;
+}
+
static struct dev_info dev;
@@ -1206,6 +1387,8 @@ int main(void)
int i;
struct v4l2_format *fmt = &dev.fmt;
+ //open_complex("/my/tui/camera/n900.cv");
+
dtime();
cam_open(&dev, "/dev/video0");
@@ -109,6 +109,23 @@ LIBV4L_PUBLIC int v4l2_get_control(int fd, int cid);
(note the fd is left open in this case). */
LIBV4L_PUBLIC int v4l2_fd_open(int fd, int v4l2_flags);
+struct v4l2_control_map {
+ unsigned long control;
+ int fd;
+};
+
+struct v4l2_controls_map {
+ int main_fd;
+ int num_fds;
+ int num_controls;
+ struct v4l2_control_map map[];
+};
+
+LIBV4L_PUBLIC int v4l2_open_pipeline(struct v4l2_controls_map *map, int v4l2_flags);
+
+LIBV4L_PUBLIC int v4l2_get_fd_for_control(int fd, unsigned long control);
+
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
@@ -104,6 +104,7 @@ struct v4l2_dev_info {
void *plugin_library;
void *dev_ops_priv;
const struct libv4l_dev_ops *dev_ops;
+ struct v4l2_controls_map *map;
};
/* From v4l2-plugin.c */
@@ -787,6 +787,8 @@ no_capture:
if (index >= devices_used)
devices_used = index + 1;
+ devices[index].map = NULL;
+
/* Note we always tell v4lconvert to optimize src fmt selection for
our default fps, the only exception is the app explicitly selecting
a frame rate using the S_PARM ioctl after a S_FMT */
@@ -1056,12 +1058,47 @@ static int v4l2_s_fmt(int index, struct v4l2_format *dest_fmt)
return 0;
}
+int v4l2_get_fd_for_control(int fd, unsigned long control)
+{
+ int index = v4l2_get_index(fd);
+ struct v4l2_controls_map *map;
+ int lo = 0;
+ int hi;
+
+ if (index < 0)
+ return fd;
+
+ map = devices[index].map;
+ if (!map)
+ return fd;
+ hi = map->num_controls;
+
+ while (lo < hi) {
+ int i = (lo + hi) / 2;
+ if (map->map[i].control == control) {
+ return map->map[i].fd;
+ }
+ if (map->map[i].control > control) {
+ hi = i;
+ continue;
+ }
+ if (map->map[i].control < control) {
+ lo = i+1;
+ continue;
+ }
+ printf("Bad: impossible condition in binary search\n");
+ exit(1);
+ }
+ return fd;
+}
+
int v4l2_ioctl(int fd, unsigned long int request, ...)
{
void *arg;
va_list ap;
int result, index, saved_err;
- int is_capture_request = 0, stream_needs_locking = 0;
+ int is_capture_request = 0, stream_needs_locking = 0,
+ is_subdev_request = 0;
va_start(ap, request);
arg = va_arg(ap, void *);
@@ -1076,18 +1113,20 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
ioctl, causing it to get sign extended, depending upon this behavior */
request = (unsigned int)request;
+ /* FIXME */
if (devices[index].convert == NULL)
goto no_capture_request;
/* Is this a capture request and do we need to take the stream lock? */
switch (request) {
- case VIDIOC_QUERYCAP:
case VIDIOC_QUERYCTRL:
case VIDIOC_G_CTRL:
case VIDIOC_S_CTRL:
case VIDIOC_G_EXT_CTRLS:
- case VIDIOC_TRY_EXT_CTRLS:
case VIDIOC_S_EXT_CTRLS:
+ is_subdev_request = 1;
+ case VIDIOC_QUERYCAP:
+ case VIDIOC_TRY_EXT_CTRLS:
case VIDIOC_ENUM_FRAMESIZES:
case VIDIOC_ENUM_FRAMEINTERVALS:
is_capture_request = 1;
@@ -1151,10 +1190,15 @@ int v4l2_ioctl(int fd, unsigned long int request, ...)
}
if (!is_capture_request) {
+ int sub_fd;
no_capture_request:
+ sub_fd = fd;
+ if (is_subdev_request) {
+ sub_fd = v4l2_get_fd_for_control(index, ((struct v4l2_queryctrl *) arg)->id);
+ }
result = devices[index].dev_ops->ioctl(
devices[index].dev_ops_priv,
- fd, request, arg);
+ sub_fd, request, arg);
saved_err = errno;
v4l2_log_ioctl(request, arg, result);
errno = saved_err;
@@ -1782,3 +1826,28 @@ int v4l2_get_control(int fd, int cid)
(qctrl.maximum - qctrl.minimum) / 2) /
(qctrl.maximum - qctrl.minimum);
}
+
+
+int v4l2_open_pipeline(struct v4l2_controls_map *map, int v4l2_flags)
+{
+ int index;
+ int i;
+
+ for (i=0; i<map->num_controls; i++) {
+ printf("%lx %d\n", map->map[i].control, map->map[i].fd);
+ if (map->map[i].fd <= 0) {
+ printf("Bad fd in map\n");
+ return -1;
+ }
+ if (i>=1 && map->map[i].control <= map->map[i-1].control) {
+ printf("Not sorted\n");
+ return -1;
+ }
+ }
+
+ i = v4l2_fd_open(map->main_fd, v4l2_flags);
+ index = v4l2_get_index(map->main_fd);
+ devices[index].map = map;
+ return i;
+}
+
@@ -863,6 +863,7 @@ int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg)
struct v4l2_queryctrl *ctrl = arg;
int retval;
uint32_t orig_id = ctrl->id;
+ int fd;
/* if we have an exact match return it */
for (i = 0; i < V4LCONTROL_COUNT; i++)
@@ -872,8 +873,9 @@ int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg)
return 0;
}
+ fd = v4l2_get_fd_for_control(data->fd, ctrl->id);
/* find out what the kernel driver would respond. */
- retval = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+ retval = data->dev_ops->ioctl(data->dev_ops_priv, fd,
VIDIOC_QUERYCTRL, arg);
if ((data->priv_flags & V4LCONTROL_SUPPORTS_NEXT_CTRL) &&
@@ -903,6 +905,7 @@ int v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data *data, void *arg)
{
int i;
struct v4l2_control *ctrl = arg;
+ int fd;
for (i = 0; i < V4LCONTROL_COUNT; i++)
if ((data->controls & (1 << i)) &&
@@ -911,7 +914,8 @@ int v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data *data, void *arg)
return 0;
}
- return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+ fd = v4l2_get_fd_for_control(data->fd, ctrl->id);
+ return data->dev_ops->ioctl(data->dev_ops_priv, fd,
VIDIOC_G_CTRL, arg);
}
@@ -994,6 +998,7 @@ int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg)
{
int i;
struct v4l2_control *ctrl = arg;
+ int fd;
for (i = 0; i < V4LCONTROL_COUNT; i++)
if ((data->controls & (1 << i)) &&
@@ -1008,7 +1013,8 @@ int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg)
return 0;
}
- return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
+ fd = v4l2_get_fd_for_control(data->fd, ctrl->id);
+ return data->dev_ops->ioctl(data->dev_ops_priv, fd,
VIDIOC_S_CTRL, arg);
}