Move remaining device
methods
This commit is contained in:
@ -2,18 +2,88 @@
|
||||
#include "device/hw/v4l2.h"
|
||||
#include "device/hw/device.h"
|
||||
|
||||
device_hw_t v4l2_device_hw = {
|
||||
.buffer_open = v4l2_buffer_open,
|
||||
.buffer_close = v4l2_buffer_close,
|
||||
.buffer_enqueue = v4l2_buffer_enqueue,
|
||||
.buffer_list_dequeue = v4l2_buffer_list_dequeue,
|
||||
.buffer_list_pollfd = v4l2_buffer_list_pollfd,
|
||||
.buffer_list_set_format = v4l2_buffer_list_set_format,
|
||||
.buffer_list_set_buffers = v4l2_buffer_list_set_buffers,
|
||||
.buffer_list_set_stream = v4l2_buffer_list_set_stream
|
||||
};
|
||||
|
||||
device_t *device_v4l2_open(const char *name, const char *path)
|
||||
int v4l2_device_open(device_t *dev)
|
||||
{
|
||||
return device_open(name, path, &v4l2_device_hw);
|
||||
dev->fd = -1;
|
||||
dev->subdev_fd = -1;
|
||||
|
||||
dev->fd = open(dev->path, O_RDWR|O_NONBLOCK);
|
||||
if (dev->fd < 0) {
|
||||
E_LOG_ERROR(dev, "Can't open device: %s", dev->path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
E_LOG_DEBUG(dev, "Querying device capabilities ...");
|
||||
struct v4l2_capability v4l2_cap;
|
||||
E_XIOCTL(dev, dev->fd, VIDIOC_QUERYCAP, &v4l2_cap, "Can't query device capabilities");
|
||||
|
||||
if (!(v4l2_cap.capabilities & V4L2_CAP_STREAMING)) {
|
||||
E_LOG_ERROR(dev, "Device doesn't support streaming IO");
|
||||
}
|
||||
|
||||
strcpy(dev->bus_info, v4l2_cap.bus_info);
|
||||
dev->subdev_fd = v4l2_device_open_v4l2_subdev(dev, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
void v4l2_device_close(device_t *dev)
|
||||
{
|
||||
if (dev->subdev_fd >= 0) {
|
||||
close(dev->subdev_fd);
|
||||
}
|
||||
|
||||
if(dev->fd >= 0) {
|
||||
close(dev->fd);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int v4l2_device_set_decoder_start(device_t *dev, bool do_on)
|
||||
{
|
||||
struct v4l2_decoder_cmd cmd = {0};
|
||||
|
||||
cmd.cmd = do_on ? V4L2_DEC_CMD_START : V4L2_DEC_CMD_STOP;
|
||||
|
||||
E_LOG_DEBUG(dev, "Setting decoder state %s...", do_on ? "Start" : "Stop");
|
||||
E_XIOCTL(dev, dev->fd, VIDIOC_DECODER_CMD, &cmd, "Cannot set decoder state");
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int v4l2_device_video_force_key(device_t *dev)
|
||||
{
|
||||
struct v4l2_control ctl = {0};
|
||||
ctl.id = V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME;
|
||||
ctl.value = 1;
|
||||
E_LOG_DEBUG(dev, "Forcing keyframe ...");
|
||||
E_XIOCTL(dev, dev->fd, VIDIOC_S_CTRL, &ctl, "Can't force keyframe");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int v4l2_device_set_fps(device_t *dev, int desired_fps)
|
||||
{
|
||||
struct v4l2_streamparm setfps = {0};
|
||||
|
||||
if (!dev) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
setfps.parm.output.timeperframe.numerator = 1;
|
||||
setfps.parm.output.timeperframe.denominator = desired_fps;
|
||||
E_LOG_DEBUG(dev, "Configuring FPS ...");
|
||||
E_XIOCTL(dev, dev->fd, VIDIOC_S_PARM, &setfps, "Can't set FPS");
|
||||
return 0;
|
||||
error:
|
||||
return -1;
|
||||
}
|
||||
|
27
device/v4l2/device_hw.c
Normal file
27
device/v4l2/device_hw.c
Normal file
@ -0,0 +1,27 @@
|
||||
#include "device/v4l2/v4l2.h"
|
||||
#include "device/hw/v4l2.h"
|
||||
#include "device/hw/device.h"
|
||||
|
||||
device_hw_t v4l2_device_hw = {
|
||||
.device_open = v4l2_device_open,
|
||||
.device_close = v4l2_device_close,
|
||||
.device_set_decoder_start = v4l2_device_set_decoder_start,
|
||||
.device_video_force_key = v4l2_device_video_force_key,
|
||||
.device_set_fps = v4l2_device_set_fps,
|
||||
.device_set_option = v4l2_device_set_option,
|
||||
|
||||
.buffer_open = v4l2_buffer_open,
|
||||
.buffer_close = v4l2_buffer_close,
|
||||
.buffer_enqueue = v4l2_buffer_enqueue,
|
||||
|
||||
.buffer_list_dequeue = v4l2_buffer_list_dequeue,
|
||||
.buffer_list_pollfd = v4l2_buffer_list_pollfd,
|
||||
.buffer_list_set_format = v4l2_buffer_list_set_format,
|
||||
.buffer_list_set_buffers = v4l2_buffer_list_set_buffers,
|
||||
.buffer_list_set_stream = v4l2_buffer_list_set_stream
|
||||
};
|
||||
|
||||
device_t *device_v4l2_open(const char *name, const char *path)
|
||||
{
|
||||
return device_open(name, path, &v4l2_device_hw);
|
||||
}
|
132
device/v4l2/device_media.c
Normal file
132
device/v4l2/device_media.c
Normal file
@ -0,0 +1,132 @@
|
||||
#include "device/v4l2/v4l2.h"
|
||||
#include "device/hw/device.h"
|
||||
#include "device/hw/v4l2.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <linux/media.h>
|
||||
|
||||
int v4l2_device_open_media_device(device_t *dev)
|
||||
{
|
||||
struct stat st;
|
||||
if (fstat(dev->fd, &st) < 0) {
|
||||
E_LOG_ERROR(dev, "Cannot get fstat");
|
||||
return -1;
|
||||
}
|
||||
if (~st.st_mode & S_IFCHR) {
|
||||
E_LOG_ERROR(dev, "FD is not char");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char path[256];
|
||||
sprintf(path, "/sys/dev/char/%d:%d/device", major(st.st_rdev), minor(st.st_rdev));
|
||||
|
||||
struct dirent **namelist;
|
||||
int n = scandir(path, &namelist, NULL, NULL);
|
||||
if (n < 0) {
|
||||
E_LOG_ERROR(dev, "Cannot scan: %s", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = -1;
|
||||
while (n--) {
|
||||
if (ret == -1 && strstr(namelist[n]->d_name, "media") == namelist[n]->d_name) {
|
||||
path[0] = 0;
|
||||
sprintf(path, "/dev/%s", namelist[n]->d_name);
|
||||
ret = open(path, O_RDWR);
|
||||
if (ret >= 0) {
|
||||
E_LOG_VERBOSE(dev, "Opened '%s' (fd=%d)", path, ret);
|
||||
}
|
||||
}
|
||||
|
||||
free(namelist[n]);
|
||||
}
|
||||
free(namelist);
|
||||
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int v4l2_device_open_v4l2_subdev(device_t *dev, int subdev)
|
||||
{
|
||||
int media_fd = -1;
|
||||
unsigned int last_id = 0;
|
||||
int ret = -1;
|
||||
|
||||
media_fd = v4l2_device_open_media_device(dev);
|
||||
if (media_fd < 0) {
|
||||
E_LOG_ERROR(dev, "Cannot find media controller");
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
struct media_entity_desc entity = {
|
||||
.id = MEDIA_ENT_ID_FLAG_NEXT | last_id,
|
||||
};
|
||||
|
||||
int rc = ioctl(media_fd, MEDIA_IOC_ENUM_ENTITIES, &entity);
|
||||
if (rc < 0 && errno == EINVAL)
|
||||
break;
|
||||
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
last_id = entity.id;
|
||||
char path[256];
|
||||
sprintf(path, "/sys/dev/char/%d:%d", entity.dev.major, entity.dev.minor);
|
||||
|
||||
char link[256];
|
||||
if (readlink(path, link, sizeof(link)) < 0) {
|
||||
E_LOG_ERROR(dev, "Cannot readlink '%s'", path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
char * last = strrchr(link, '/');
|
||||
if (!last) {
|
||||
E_LOG_ERROR(dev, "Link '%s' for '%s' does not end with '/'", link, path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (strstr(last, "/v4l-subdev") != last) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
sprintf(path, "/dev%s", last);
|
||||
ret = open(path, O_RDWR);
|
||||
if (ret >= 0) {
|
||||
E_LOG_INFO(dev, "Opened '%s' (fd=%d)", path, ret);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
error:
|
||||
close(media_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int v4l2_device_set_pad_format(device_t *dev, unsigned width, unsigned height, unsigned format)
|
||||
{
|
||||
struct v4l2_subdev_format fmt = {0};
|
||||
|
||||
if (dev->subdev_fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fmt.pad = 0;
|
||||
fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
|
||||
fmt.format.code = format;
|
||||
fmt.format.width = width;
|
||||
fmt.format.height = height;
|
||||
fmt.format.colorspace = V4L2_COLORSPACE_RAW;
|
||||
fmt.format.field = V4L2_FIELD_ANY;
|
||||
|
||||
E_LOG_DEBUG(dev, "Configuring mpad %d (subdev_fd=%d)...", fmt.pad, dev->subdev_fd);
|
||||
E_XIOCTL(dev, dev->subdev_fd, VIDIOC_SUBDEV_S_FMT, &fmt, "Can't configure mpad %d (subdev_fd=%d)", fmt.pad, dev->subdev_fd);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return -1;
|
||||
}
|
172
device/v4l2/device_options.c
Normal file
172
device/v4l2/device_options.c
Normal file
@ -0,0 +1,172 @@
|
||||
#include "device/v4l2/v4l2.h"
|
||||
#include "device/hw/v4l2.h"
|
||||
#include "device/hw/device.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
int v4l2_device_set_option_by_id(device_t *dev, const char *name, uint32_t id, int32_t value)
|
||||
{
|
||||
struct v4l2_control ctl = {0};
|
||||
|
||||
if (!dev) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctl.id = id;
|
||||
ctl.value = value;
|
||||
E_LOG_DEBUG(dev, "Configuring option %s (%08x) = %d", name, id, value);
|
||||
E_XIOCTL(dev, dev->subdev_fd >= 0 ? dev->subdev_fd : dev->fd, VIDIOC_S_CTRL, &ctl, "Can't set option %s", name);
|
||||
return 0;
|
||||
error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
void v4l2_device_option_normalize_name(char *in)
|
||||
{
|
||||
char *out = in;
|
||||
|
||||
while (*in) {
|
||||
if (isalnum(*in)) {
|
||||
*out++ = tolower(*in++);
|
||||
} else if (isprint(*in)) {
|
||||
*out++ = '_';
|
||||
while (*++in && isprint(*in) && !isalnum(*in));
|
||||
} else {
|
||||
in++;
|
||||
}
|
||||
}
|
||||
|
||||
*out++ = 0;
|
||||
}
|
||||
|
||||
static int v4l2_device_set_option_string_fd_iter_id(device_t *dev, int fd, uint32_t *id, char *name, char *value)
|
||||
{
|
||||
struct v4l2_query_ext_ctrl qctrl = { .id = *id };
|
||||
void *data = NULL;
|
||||
|
||||
if (0 != ioctl (fd, VIDIOC_QUERY_EXT_CTRL, &qctrl)) {
|
||||
*id = 0;
|
||||
return 0;
|
||||
}
|
||||
*id = qctrl.id;
|
||||
|
||||
v4l2_device_option_normalize_name(qctrl.name);
|
||||
|
||||
if (strcmp(qctrl.name, name) != 0)
|
||||
return 0;
|
||||
|
||||
if (qctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
|
||||
E_LOG_INFO(dev, "The '%s' is disabled", name);
|
||||
return 0;
|
||||
} else if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) {
|
||||
E_LOG_INFO(dev, "The '%s' is read-only", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch(qctrl.type) {
|
||||
case V4L2_CTRL_TYPE_INTEGER:
|
||||
case V4L2_CTRL_TYPE_BOOLEAN:
|
||||
case V4L2_CTRL_TYPE_MENU:
|
||||
{
|
||||
struct v4l2_control ctl = {
|
||||
.id = *id,
|
||||
.value = atoi(value)
|
||||
};
|
||||
E_LOG_INFO(dev, "Configuring option %s (%08x) = %d", name, ctl.id, ctl.value);
|
||||
E_XIOCTL(dev, fd, VIDIOC_S_CTRL, &ctl, "Can't set option %s", name);
|
||||
}
|
||||
return 1;
|
||||
|
||||
case V4L2_CTRL_TYPE_U8:
|
||||
case V4L2_CTRL_TYPE_U16:
|
||||
case V4L2_CTRL_TYPE_U32:
|
||||
{
|
||||
struct v4l2_ext_control ctl = {
|
||||
.id = *id,
|
||||
.size = qctrl.elem_size * qctrl.elems,
|
||||
.ptr = data = calloc(qctrl.elems, qctrl.elem_size)
|
||||
};
|
||||
struct v4l2_ext_controls ctrls = {
|
||||
.count = 1,
|
||||
.ctrl_class = V4L2_CTRL_ID2CLASS(*id),
|
||||
.controls = &ctl,
|
||||
};
|
||||
|
||||
char *string = value;
|
||||
char *token;
|
||||
int tokens = 0;
|
||||
|
||||
for ( ; token = strsep(&string, ","); tokens++) {
|
||||
if (tokens >= qctrl.elems)
|
||||
continue;
|
||||
|
||||
switch(qctrl.type) {
|
||||
case V4L2_CTRL_TYPE_U8:
|
||||
ctl.p_u8[tokens] = atoi(token);
|
||||
break;
|
||||
|
||||
case V4L2_CTRL_TYPE_U16:
|
||||
ctl.p_u16[tokens] = atoi(token);
|
||||
break;
|
||||
|
||||
case V4L2_CTRL_TYPE_U32:
|
||||
ctl.p_u32[tokens] = atoi(token);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
E_LOG_INFO(dev, "Configuring option %s (%08x) = [%d tokens, expected %d]", name, ctl.id, tokens, qctrl.elems);
|
||||
E_XIOCTL(dev, fd, VIDIOC_S_EXT_CTRLS, &ctrls, "Can't set option %s", name);
|
||||
free(data);
|
||||
}
|
||||
return 1;
|
||||
|
||||
default:
|
||||
E_LOG_INFO(dev, "The '%s' control type '%d' is not supported", name, qctrl.type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
error:
|
||||
free(data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int v4l2_device_set_option_string_fd(device_t *dev, int fd, char *name, char *value)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
uint32_t id = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
|
||||
while ((ret = v4l2_device_set_option_string_fd_iter_id(dev, fd, &id, name, value)) == 0 && id) {
|
||||
id |= V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int v4l2_device_set_option(device_t *dev, const char *key, const char *value)
|
||||
{
|
||||
char *keyp = strdup(key);
|
||||
char *valuep = strdup(value);
|
||||
|
||||
int ret = 0;
|
||||
|
||||
v4l2_device_option_normalize_name(keyp);
|
||||
|
||||
if (dev->subdev_fd >= 0)
|
||||
ret = v4l2_device_set_option_string_fd(dev, dev->subdev_fd, keyp, valuep);
|
||||
if (ret <= 0)
|
||||
ret = v4l2_device_set_option_string_fd(dev, dev->fd, keyp, valuep);
|
||||
|
||||
free(keyp);
|
||||
free(valuep);
|
||||
|
||||
if (ret == 0)
|
||||
E_LOG_ERROR(dev, "The '%s=%s' was failed to find.", key, value);
|
||||
else if (ret < 0)
|
||||
E_LOG_ERROR(dev, "The '%s=%s' did fail to be set.", key, value);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return -1;
|
||||
}
|
59
device/v4l2/v4l2.c
Normal file
59
device/v4l2/v4l2.c
Normal file
@ -0,0 +1,59 @@
|
||||
#include "device/v4l2/v4l2.h"
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/v4l2-subdev.h>
|
||||
|
||||
int xioctl(const char *name, int fd, int request, void *arg)
|
||||
{
|
||||
int retries = XIOCTL_RETRIES;
|
||||
int retval = -1;
|
||||
|
||||
do {
|
||||
retval = ioctl(fd, request, arg);
|
||||
} while (
|
||||
retval
|
||||
&& retries--
|
||||
&& (
|
||||
errno == EINTR
|
||||
|| errno == EAGAIN
|
||||
|| errno == ETIMEDOUT
|
||||
)
|
||||
);
|
||||
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (retval && retries <= 0) {
|
||||
E_LOG_PERROR(NULL, "%s: ioctl(%08x) retried %u times; giving up", name, request, XIOCTL_RETRIES);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static size_t align_size(size_t size, size_t to)
|
||||
{
|
||||
return ((size + (to - 1)) & ~(to - 1));
|
||||
}
|
||||
|
||||
unsigned fourcc_to_stride(unsigned width, unsigned format)
|
||||
{
|
||||
switch (format) {
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
case V4L2_PIX_FMT_UYVY:
|
||||
case V4L2_PIX_FMT_RGB565:
|
||||
return align_size(width * 2, 32);
|
||||
|
||||
case V4L2_PIX_FMT_YUV420:
|
||||
return align_size(width * 3 / 2, 32);
|
||||
|
||||
case V4L2_PIX_FMT_RGB24:
|
||||
return align_size(width * 3, 32);
|
||||
|
||||
case V4L2_PIX_FMT_SRGGB10P:
|
||||
return align_size(width * 5 / 4, 32);
|
||||
|
||||
case V4L2_PIX_FMT_JPEG:
|
||||
case V4L2_PIX_FMT_H264:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
E_LOG_PERROR(NULL, "Unknown format: %s", fourcc_to_string(format).buf);
|
||||
}
|
||||
}
|
@ -5,8 +5,16 @@
|
||||
|
||||
typedef struct buffer_s buffer_t;
|
||||
typedef struct buffer_list_s buffer_list_t;
|
||||
typedef struct device_s device_t;
|
||||
struct pollfd;
|
||||
|
||||
int v4l2_device_open(device_t *dev);
|
||||
void v4l2_device_close(device_t *dev);
|
||||
int v4l2_device_set_decoder_start(device_t *dev, bool do_on);
|
||||
int v4l2_device_video_force_key(device_t *dev);
|
||||
int v4l2_device_set_fps(device_t *dev, int desired_fps);
|
||||
int v4l2_device_set_option(device_t *dev, const char *key, const char *value);
|
||||
|
||||
int v4l2_buffer_open(buffer_t *buf);
|
||||
void v4l2_buffer_close(buffer_t *buf);
|
||||
int v4l2_buffer_enqueue(buffer_t *buf, const char *who);
|
||||
@ -17,3 +25,22 @@ int v4l2_buffer_list_pollfd(buffer_list_t *buf_list, struct pollfd *pollfd, bool
|
||||
int v4l2_buffer_list_set_format(buffer_list_t *buf_list, unsigned width, unsigned height, unsigned format, unsigned bytesperline);
|
||||
int v4l2_buffer_list_set_buffers(buffer_list_t *buf_list, int nbufs);
|
||||
int v4l2_buffer_list_set_stream(buffer_list_t *buf_list, bool do_on);
|
||||
|
||||
int v4l2_device_open_media_device(device_t *dev);
|
||||
int v4l2_device_open_v4l2_subdev(device_t *dev, int subdev);
|
||||
int v4l2_device_set_pad_format(device_t *dev, unsigned width, unsigned height, unsigned format);
|
||||
|
||||
#ifndef CFG_XIOCTL_RETRIES
|
||||
# define CFG_XIOCTL_RETRIES 4
|
||||
#endif
|
||||
#define XIOCTL_RETRIES ((unsigned)(CFG_XIOCTL_RETRIES))
|
||||
|
||||
unsigned fourcc_to_stride(unsigned width, unsigned format);
|
||||
int xioctl(const char *name, int fd, int request, void *arg);
|
||||
|
||||
#define E_XIOCTL(dev, _fd, _request, _value, _msg, ...) do { \
|
||||
int ret; \
|
||||
if ((ret = xioctl(dev_name(dev), _fd, _request, _value)) < 0) { \
|
||||
E_LOG_ERROR(dev, "xioctl(ret=%d): " _msg, ret, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
||||
|
Reference in New Issue
Block a user