Enumerate v4l2 devices
This commit is contained in:
parent
328198745b
commit
0a37c63d00
43
cmd/list-devices/main.c
Normal file
43
cmd/list-devices/main.c
Normal file
@ -0,0 +1,43 @@
|
||||
#include "util/opts/log.h"
|
||||
#include "util/opts/fourcc.h"
|
||||
#include "device/device.h"
|
||||
#include "device/device_list.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
log_options_t log_options = {
|
||||
.debug = true,
|
||||
.verbose = true
|
||||
};
|
||||
|
||||
void print_formats(const char *type, device_info_formats_t *formats)
|
||||
{
|
||||
if (!formats->n) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf("- %s: ", type);
|
||||
for (int j = 0; j < formats->n; j++) {
|
||||
printf("%s ", fourcc_to_string(formats->formats[j]).buf);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
device_list_t *list = device_list_v4l2();
|
||||
|
||||
printf("Found %d devices\n", list->ndevices);
|
||||
|
||||
for (int i = 0; i < list->ndevices; i++) {
|
||||
device_info_t * info = &list->devices[i];
|
||||
|
||||
printf("Device %d: %s / %s / camera: %d, m2m: %d\n", i, info->name, info->path, info->camera, info->m2m);
|
||||
print_formats("Output / IN", &info->output_formats);
|
||||
print_formats("Capture / OUT", &info->capture_formats);
|
||||
}
|
||||
|
||||
device_list_free(list);
|
||||
return 0;
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
#include "camera.h"
|
||||
|
||||
#include "device/device.h"
|
||||
#include "device/device_list.h"
|
||||
#include "device/buffer_list.h"
|
||||
#include "device/links.h"
|
||||
#include "util/opts/log.h"
|
||||
@ -11,6 +12,7 @@ camera_t *camera_open(camera_options_t *options)
|
||||
camera_t *camera = calloc(1, sizeof(camera_t));
|
||||
camera->name = "CAMERA";
|
||||
camera->options = *options;
|
||||
camera->device_list = device_list_v4l2();
|
||||
|
||||
if (camera_configure_input(camera) < 0) {
|
||||
goto error;
|
||||
@ -50,7 +52,7 @@ void camera_close(camera_t **camerap)
|
||||
}
|
||||
}
|
||||
|
||||
memset(camera->links, 0, sizeof(camera->links));
|
||||
device_list_free(camera->device_list);
|
||||
free(camera);
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,8 @@ typedef struct camera_s {
|
||||
};
|
||||
};
|
||||
|
||||
struct device_list_s *device_list;
|
||||
|
||||
link_t links[MAX_DEVICES];
|
||||
int nlinks;
|
||||
} camera_t;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "device/buffer.h"
|
||||
#include "device/buffer_list.h"
|
||||
#include "device/device.h"
|
||||
#include "device/device_list.h"
|
||||
#include "device/links.h"
|
||||
#include "util/opts/log.h"
|
||||
#include "util/opts/fourcc.h"
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "device/buffer.h"
|
||||
#include "device/buffer_list.h"
|
||||
#include "device/device.h"
|
||||
#include "device/device_list.h"
|
||||
#include "device/links.h"
|
||||
#include "util/opts/log.h"
|
||||
#include "util/opts/fourcc.h"
|
||||
@ -43,7 +44,14 @@ static const char *isp_names[2] = {
|
||||
|
||||
int camera_configure_legacy_isp(camera_t *camera, buffer_list_t *src_capture, float div, int res)
|
||||
{
|
||||
camera->legacy_isp[res] = device_v4l2_open(isp_names[res], "/dev/video12");
|
||||
device_info_t *device = device_list_find_m2m_format(camera->device_list, src_capture->fmt.format, V4L2_PIX_FMT_YUYV);
|
||||
|
||||
if (!device) {
|
||||
LOG_INFO(camera, "Cannot find ISP to scale from '%s' to 'YUYV'", fourcc_to_string(src_capture->fmt.format).buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
camera->legacy_isp[res] = device_v4l2_open(isp_names[res], device->path);
|
||||
|
||||
buffer_list_t *isp_output = device_open_buffer_list_output(
|
||||
camera->legacy_isp[res], src_capture);
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "device/buffer.h"
|
||||
#include "device/buffer_list.h"
|
||||
#include "device/device.h"
|
||||
#include "device/device_list.h"
|
||||
#include "device/links.h"
|
||||
#include "util/opts/log.h"
|
||||
#include "util/opts/fourcc.h"
|
||||
@ -55,7 +56,14 @@ static int camera_configure_h264_output(camera_t *camera, buffer_list_t *src_cap
|
||||
return 0;
|
||||
}
|
||||
|
||||
camera->codec_h264[res] = device_v4l2_open(h264_names[res], "/dev/video11");
|
||||
device_info_t *device = device_list_find_m2m_format(camera->device_list, src_capture->fmt.format, V4L2_PIX_FMT_H264);
|
||||
|
||||
if (!device) {
|
||||
LOG_INFO(camera, "Cannot find H264 encoder to convert from '%s'", fourcc_to_string(src_capture->fmt.format).buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
camera->codec_h264[res] = device_v4l2_open(h264_names[res], device->path);
|
||||
|
||||
buffer_list_t *output = device_open_buffer_list_output(camera->codec_h264[res], src_capture);
|
||||
buffer_list_t *capture = device_open_buffer_list_capture(camera->codec_h264[res], output, 1.0, V4L2_PIX_FMT_H264, true);
|
||||
@ -76,7 +84,18 @@ static int camera_configure_jpeg_output(camera_t *camera, buffer_list_t *src_cap
|
||||
return 0;
|
||||
}
|
||||
|
||||
camera->codec_jpeg[res] = device_v4l2_open(jpeg_names[res], "/dev/video31");
|
||||
device_info_t *device = device_list_find_m2m_format(camera->device_list, src_capture->fmt.format, V4L2_PIX_FMT_JPEG);
|
||||
|
||||
if (!device) {
|
||||
device = device_list_find_m2m_format(camera->device_list, src_capture->fmt.format, V4L2_PIX_FMT_MJPEG);
|
||||
}
|
||||
|
||||
if (!device) {
|
||||
LOG_INFO(camera, "Cannot find JPEG encoder to convert from '%s'", fourcc_to_string(src_capture->fmt.format).buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
camera->codec_jpeg[res] = device_v4l2_open(jpeg_names[res], device->path);
|
||||
|
||||
buffer_list_t *output = device_open_buffer_list_output(camera->codec_jpeg[res], src_capture);
|
||||
buffer_list_t *capture = device_open_buffer_list_capture(camera->codec_jpeg[res], output, 1.0, V4L2_PIX_FMT_JPEG, true);
|
||||
@ -123,9 +142,25 @@ int camera_configure_output_rescaler(camera_t *camera, buffer_list_t *src_captur
|
||||
|
||||
int camera_configure_decoder(camera_t *camera, buffer_list_t *src_capture)
|
||||
{
|
||||
unsigned decode_formats[] = {
|
||||
V4L2_PIX_FMT_YUYV,
|
||||
V4L2_PIX_FMT_YUV420,
|
||||
V4L2_PIX_FMT_YVU420,
|
||||
V4L2_PIX_FMT_NV12,
|
||||
V4L2_PIX_FMT_NV21,
|
||||
0
|
||||
};
|
||||
unsigned chosen_format = 0;
|
||||
device_info_t *device = device_list_find_m2m_formats(camera->device_list, src_capture->fmt.format, decode_formats, &chosen_format);
|
||||
|
||||
if (!device) {
|
||||
LOG_INFO(camera, "Cannot find '%s' decoder", fourcc_to_string(src_capture->fmt.format).buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
device_video_force_key(camera->camera);
|
||||
|
||||
camera->decoder = device_v4l2_open("DECODER", "/dev/video10");
|
||||
camera->decoder = device_v4l2_open("DECODER", device->path);
|
||||
|
||||
buffer_list_t *decoder_output = device_open_buffer_list_output(
|
||||
camera->decoder, src_capture);
|
||||
|
68
device/device_list.c
Normal file
68
device/device_list.c
Normal file
@ -0,0 +1,68 @@
|
||||
#include "device/device_list.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
bool device_info_has_format(device_info_t *info, bool capture, unsigned format)
|
||||
{
|
||||
if (!info) {
|
||||
return false;
|
||||
}
|
||||
|
||||
device_info_formats_t *formats = capture ? &info->capture_formats : &info->output_formats;
|
||||
|
||||
for (int i = 0; i < formats->n; i++) {
|
||||
if (formats->formats[i] == format) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
device_info_t *device_list_find_m2m_format(device_list_t *list, unsigned output, unsigned capture)
|
||||
{
|
||||
if (!list)
|
||||
return NULL;
|
||||
|
||||
for (int i = 0; i < list->ndevices; i++) {
|
||||
device_info_t *info = &list->devices[i];
|
||||
|
||||
if (info->m2m && device_info_has_format(info, false, output) && device_info_has_format(info, true, capture)) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device_info_t *device_list_find_m2m_formats(device_list_t *list, unsigned output, unsigned capture_formats[], unsigned *found_format)
|
||||
{
|
||||
for (int i = 0; capture_formats[i]; i++) {
|
||||
device_info_t *info = device_list_find_m2m_format(list, output, capture_formats[i]);
|
||||
if (info) {
|
||||
if (found_format) {
|
||||
*found_format = capture_formats[i];
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void device_list_free(device_list_t *list)
|
||||
{
|
||||
if (!list)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < list->ndevices; i++) {
|
||||
device_info_t *info = &list->devices[i];
|
||||
free(info->name);
|
||||
free(info->path);
|
||||
free(info->output_formats.formats);
|
||||
free(info->capture_formats.formats);
|
||||
}
|
||||
|
||||
free(list);
|
||||
}
|
30
device/device_list.h
Normal file
30
device/device_list.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct device_info_formats_s {
|
||||
unsigned *formats;
|
||||
unsigned n;
|
||||
} device_info_formats_t;
|
||||
|
||||
typedef struct device_info_s {
|
||||
char *name;
|
||||
char *path;
|
||||
|
||||
bool camera;
|
||||
bool m2m;
|
||||
|
||||
device_info_formats_t output_formats;
|
||||
device_info_formats_t capture_formats;
|
||||
} device_info_t;
|
||||
|
||||
typedef struct device_list_s {
|
||||
device_info_t *devices;
|
||||
int ndevices;
|
||||
} device_list_t;
|
||||
|
||||
device_list_t *device_list_v4l2();
|
||||
bool device_info_has_format(device_info_t *info, bool capture, unsigned format);
|
||||
device_info_t *device_list_find_m2m_format(device_list_t *list, unsigned output, unsigned capture);
|
||||
device_info_t *device_list_find_m2m_formats(device_list_t *list, unsigned output, unsigned capture_formats[], unsigned *found_format);
|
||||
void device_list_free(device_list_t *list);
|
97
device/v4l2/device_list.c
Normal file
97
device/v4l2/device_list.c
Normal file
@ -0,0 +1,97 @@
|
||||
#include "v4l2.h"
|
||||
#include "device/device_list.h"
|
||||
#include "util/opts/log.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
|
||||
static void device_list_read_formats(int fd, device_info_formats_t *formats, enum v4l2_buf_type buf_type)
|
||||
{
|
||||
for (int i = 0; ; ++i) {
|
||||
struct v4l2_fmtdesc format_desc;
|
||||
memset(&format_desc, 0, sizeof(format_desc));
|
||||
format_desc.type = (enum v4l2_buf_type) buf_type;
|
||||
format_desc.index = i;
|
||||
|
||||
if (-1 == ioctl(fd, VIDIOC_ENUM_FMT, &format_desc)) {
|
||||
break;
|
||||
}
|
||||
|
||||
formats->n++;
|
||||
formats->formats = realloc(formats->formats, sizeof(formats->formats[0]) * formats->n);
|
||||
formats->formats[formats->n - 1] = format_desc.pixelformat;
|
||||
}
|
||||
}
|
||||
|
||||
static bool device_list_read_dev(device_info_t *info, const char *name)
|
||||
{
|
||||
asprintf(&info->path, "/dev/%s", name);
|
||||
|
||||
int fd = open(info->path, O_RDWR|O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
LOG_ERROR(NULL, "Can't open device: %s", info->path);
|
||||
}
|
||||
|
||||
struct v4l2_capability v4l2_cap;
|
||||
ERR_IOCTL(info, fd, VIDIOC_QUERYCAP, &v4l2_cap, "Can't query device capabilities");
|
||||
info->name = strdup((const char *)v4l2_cap.card);
|
||||
|
||||
if (!(v4l2_cap.capabilities & V4L2_CAP_STREAMING)) {
|
||||
LOG_VERBOSE(info, "Device (%s) does not support streaming (skipping)", info->path);
|
||||
goto error;
|
||||
} else if ((v4l2_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) && !(v4l2_cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)) {
|
||||
info->camera = true;
|
||||
} else if (!(v4l2_cap.capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_M2M_MPLANE))) {
|
||||
LOG_VERBOSE(info, "Device (%s) does not support capture (skipping)", info->path);
|
||||
goto error;
|
||||
} else if (!(v4l2_cap.capabilities & (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_VIDEO_M2M_MPLANE))) {
|
||||
LOG_VERBOSE(info, "Device (%s) does not support output (skipping)", info->path);
|
||||
goto error;
|
||||
} else if ((v4l2_cap.capabilities & V4L2_CAP_VIDEO_M2M) || (v4l2_cap.capabilities & V4L2_CAP_VIDEO_M2M_MPLANE)) {
|
||||
info->m2m = true;
|
||||
}
|
||||
|
||||
device_list_read_formats(fd, &info->capture_formats, V4L2_BUF_TYPE_VIDEO_CAPTURE);
|
||||
device_list_read_formats(fd, &info->capture_formats, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
|
||||
device_list_read_formats(fd, &info->output_formats, V4L2_BUF_TYPE_VIDEO_OUTPUT);
|
||||
device_list_read_formats(fd, &info->output_formats, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
|
||||
close(fd);
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
free(info->name);
|
||||
free(info->path);
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
device_list_t *device_list_v4l2()
|
||||
{
|
||||
DIR *dev = opendir("/dev");
|
||||
if (!dev) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device_list_t *list = calloc(1, sizeof(device_list_t));
|
||||
struct dirent *ent;
|
||||
|
||||
while ((ent = readdir(dev)) != NULL) {
|
||||
if (strstr(ent->d_name, "video") != ent->d_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
device_info_t info = {NULL};
|
||||
if (device_list_read_dev(&info, ent->d_name)) {
|
||||
list->ndevices++;
|
||||
list->devices = realloc(list->devices, sizeof(info) * list->ndevices);
|
||||
list->devices[list->ndevices-1] = info;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dev);
|
||||
|
||||
return list;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user