Enumerate v4l2 devices

This commit is contained in:
Kamil Trzcinski 2022-09-02 20:21:11 +02:00
parent 328198745b
commit 0a37c63d00
9 changed files with 291 additions and 5 deletions

43
cmd/list-devices/main.c Normal file
View 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;
}

View File

@ -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);
}

View File

@ -66,6 +66,8 @@ typedef struct camera_s {
};
};
struct device_list_s *device_list;
link_t links[MAX_DEVICES];
int nlinks;
} camera_t;

View File

@ -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"

View File

@ -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);

View File

@ -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
View 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
View 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
View 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;
}