diff --git a/device/buffer_queue.c b/device/buffer_queue.c index c475507..1d26e29 100644 --- a/device/buffer_queue.c +++ b/device/buffer_queue.c @@ -2,6 +2,7 @@ #include "device/buffer_list.h" #include "device/hw/device.h" #include "device/hw/v4l2.h" + #include pthread_mutex_t buffer_lock = PTHREAD_MUTEX_INITIALIZER; diff --git a/device/camera.c b/device/camera.c index 1625b39..101041e 100644 --- a/device/camera.c +++ b/device/camera.c @@ -51,7 +51,8 @@ camera_t *camera_open(camera_options_t *options) camera->camera->allow_dma = false; } - device_set_pad_format(camera->camera, camera->options.width, camera->options.height, 0); + // TODO: mpad format + // device_set_pad_format(camera->camera, camera->options.width, camera->options.height, 0); if (device_open_buffer_list(camera->camera, true, camera->options.width, camera->options.height, camera->options.format, 0, camera->options.nbufs, true) < 0) { goto error; @@ -120,14 +121,15 @@ int camera_set_params(camera_t *camera) // DEVICE_SET_OPTION(camera->isp_srgb, BLUE_BALANCE, 1472); // DEVICE_SET_OPTION(camera->isp_srgb, DIGITAL_GAIN, 1007); - DEVICE_SET_OPTION2(camera->codec_jpeg, JPEG, COMPRESSION_QUALITY, 80); + device_set_option_string(camera->codec_jpeg, "compression_quality", "80"); - DEVICE_SET_OPTION2(camera->codec_h264, MPEG_VIDEO, BITRATE, 5000 * 1000); - DEVICE_SET_OPTION2(camera->codec_h264, MPEG_VIDEO, H264_I_PERIOD, 30); - DEVICE_SET_OPTION2(camera->codec_h264, MPEG_VIDEO, H264_LEVEL, V4L2_MPEG_VIDEO_H264_LEVEL_4_0); - DEVICE_SET_OPTION2(camera->codec_h264, MPEG_VIDEO, REPEAT_SEQ_HEADER, 1); - DEVICE_SET_OPTION2(camera->codec_h264, MPEG_VIDEO, H264_MIN_QP, 16); - DEVICE_SET_OPTION2(camera->codec_h264, MPEG_VIDEO, H264_MIN_QP, 32); + device_set_option_string(camera->codec_h264, "video_bitrate_mode", "0"); + device_set_option_string(camera->codec_h264, "video_bitrate", "5000000"); + device_set_option_string(camera->codec_h264, "h264_i_frame_period", "30"); + device_set_option_string(camera->codec_h264, "h264_level", "11"); + device_set_option_string(camera->codec_h264, "h264_profile", "4"); + device_set_option_string(camera->codec_h264, "h264_minimum_qp_value", "16"); + device_set_option_string(camera->codec_h264, "h264_maximum_qp_value", "32"); return 0; } diff --git a/device/hw/device.c b/device/hw/device.c index 8f16385..18b5ac3 100644 --- a/device/hw/device.c +++ b/device/hw/device.c @@ -2,32 +2,22 @@ #include "device/buffer.h" #include "device/buffer_list.h" #include "device/hw/v4l2.h" +#include "opts/opts.h" device_t *device_open(const char *name, const char *path, device_hw_t *hw) { device_t *dev = calloc(1, sizeof(device_t)); dev->name = strdup(name); dev->path = strdup(path); dev->hw = hw; - dev->fd = open(path, O_RDWR|O_NONBLOCK); + dev->fd = -1; dev->subdev_fd = -1; dev->allow_dma = true; - if(dev->fd < 0) { + + if (dev->hw->device_open(dev) < 0) { E_LOG_ERROR(dev, "Can't open device: %s", path); - } - - 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); + } E_LOG_INFO(dev, "Device path=%s fd=%d opened", dev->path, dev->fd); - - dev->subdev_fd = device_open_v4l2_subdev(dev, 0); return dev; error: @@ -50,14 +40,7 @@ void device_close(device_t *dev) { dev->output_list = NULL; } - if (dev->subdev_fd >= 0) { - close(dev->subdev_fd); - } - - if(dev->fd >= 0) { - close(dev->fd); - } - + dev->hw->device_close(dev); free(dev->name); free(dev->path); free(dev); @@ -141,11 +124,14 @@ int device_open_buffer_list_capture(device_t *dev, buffer_list_t *output_list, f int device_set_stream(device_t *dev, bool do_on) { + // TODO: support events +#if 0 struct v4l2_event_subscription sub = {0}; sub.type = V4L2_EVENT_SOURCE_CHANGE; E_LOG_DEBUG(dev, "Subscribing to DV-timings events ..."); xioctl(dev_name(dev), dev->fd, do_on ? VIDIOC_SUBSCRIBE_EVENT : VIDIOC_UNSUBSCRIBE_EVENT, &sub); +#endif if (dev->capture_list) { if (buffer_list_set_stream(dev->capture_list, do_on) < 0) { @@ -162,23 +148,11 @@ int device_set_stream(device_t *dev, bool do_on) return 0; } -int 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"); - dev->decoder_started = do_on; - return 0; - -error: - return -1; -} - int device_consume_event(device_t *dev) { + // TODO: support events + +#if 0 struct v4l2_event event; if (!dev) { @@ -200,39 +174,69 @@ int device_consume_event(device_t *dev) return 0; error: +#endif + return -1; } +int device_set_decoder_start(device_t *dev, bool do_on) +{ + if (!dev || dev->hw->device_set_decoder_start(dev, do_on) < 0) + return -1; + + dev->decoder_started = do_on; + return 0; +} + int device_video_force_key(device_t *dev) { - if (!dev) { + if (!dev || dev->hw->device_video_force_key(dev) < 0) return -1; - } - 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 device_set_fps(device_t *dev, int desired_fps) { - struct v4l2_streamparm setfps = {0}; + if (!dev || dev->hw->device_set_fps(dev, desired_fps) < 0) + return -1; + return 0; +} + +int device_set_option_string(device_t *dev, const char *key, const char *value) +{ 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; + return dev->hw->device_set_option(dev, key, value); +} + +void device_set_option_list(device_t *dev, const char *option_list) +{ + if (!dev || !option_list || !option_list[0]) { + return; + } + + char *start = strdup(option_list); + char *string = start; + char *option; + + while (option = strsep(&string, OPTION_VALUE_LIST_SEP)) { + char *value = option; + char *key = strsep(&value, "="); + + if (value) { + device_set_option_string(dev, key, value); + } else { + E_LOG_INFO(dev, "Missing 'key=value' for '%s'", option); + continue; + } + + // consume all separators + while (strsep(&value, "=")); + } + + free(start); } diff --git a/device/hw/device.h b/device/hw/device.h index 8b467b4..ea35c19 100644 --- a/device/hw/device.h +++ b/device/hw/device.h @@ -10,6 +10,13 @@ typedef struct device_s device_t; struct pollfd; typedef struct device_hw_s { + int (*device_open)(device_t *dev); + void (*device_close)(device_t *dev); + int (*device_set_decoder_start)(device_t *dev, bool do_on); + int (*device_video_force_key)(device_t *dev); + int (*device_set_fps)(device_t *dev, int desired_fps); + int (*device_set_option)(device_t *dev, const char *key, const char *value); + int (*buffer_open)(buffer_t *buf); void (*buffer_close)(buffer_t *buf); int (*buffer_enqueue)(buffer_t *buf, const char *who); @@ -39,9 +46,6 @@ typedef struct device_s { } device_t; device_t *device_open(const char *name, const char *path, device_hw_t *hw); -device_t *device_v4l2_open(const char *name, const char *path); -int device_open_media_device(device_t *dev); -int device_open_v4l2_subdev(device_t *dev, int subdev); void device_close(device_t *device); int device_open_buffer_list(device_t *dev, bool do_capture, unsigned width, unsigned height, unsigned format, unsigned bytesperline, int nbufs, bool do_mmap); @@ -54,16 +58,7 @@ int device_set_decoder_start(device_t *dev, bool do_on); int device_video_force_key(device_t *dev); int device_set_fps(device_t *dev, int desired_fps); -int device_set_pad_format(device_t *device, unsigned width, unsigned height, unsigned format); -int device_set_option(device_t *dev, const char *name, uint32_t id, int32_t value); -int device_set_option_string(device_t *dev, const char *option); +int device_set_option_string(device_t *dev, const char *option, const char *value); void device_set_option_list(device_t *dev, const char *option_list); -#define DEVICE_SET_OPTION(dev, name, value) \ - device_set_option(dev, #name, V4L2_CID_##name, value) - -#define DEVICE_SET_OPTION2(dev, type, name, value) \ - device_set_option(dev, #name, V4L2_CID_##type##_##name, value) - -#define DEVICE_SET_OPTION2_FATAL(dev, type, name, value) \ - do { if (DEVICE_SET_OPTION2(dev, type, name, value) < 0) return -1; } while(0) +device_t *device_v4l2_open(const char *name, const char *path); diff --git a/device/hw/v4l2.c b/device/hw/v4l2.c index 78bfcb5..bf7027a 100644 --- a/device/hw/v4l2.c +++ b/device/hw/v4l2.c @@ -1,28 +1,4 @@ -#include "device/hw/v4l2.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; -} +#include "v4l2.h" fourcc_string fourcc_to_string(unsigned format) { @@ -43,34 +19,3 @@ fourcc_string fourcc_to_string(unsigned format) *ptr++ = 0; return fourcc; } - -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); - } -} diff --git a/device/hw/v4l2.h b/device/hw/v4l2.h index db8daa7..1dd0f4d 100644 --- a/device/hw/v4l2.h +++ b/device/hw/v4l2.h @@ -21,23 +21,8 @@ #include "opts/log.h" -#ifndef CFG_XIOCTL_RETRIES -# define CFG_XIOCTL_RETRIES 4 -#endif -#define XIOCTL_RETRIES ((unsigned)(CFG_XIOCTL_RETRIES)) - typedef struct { char buf[10]; } fourcc_string; - fourcc_string fourcc_to_string(unsigned format); -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) diff --git a/device/v4l2/device.c b/device/v4l2/device.c index 3b32a9a..9633344 100644 --- a/device/v4l2/device.c +++ b/device/v4l2/device.c @@ -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; } diff --git a/device/v4l2/device_hw.c b/device/v4l2/device_hw.c new file mode 100644 index 0000000..1dbe8b5 --- /dev/null +++ b/device/v4l2/device_hw.c @@ -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); +} diff --git a/device/hw/device_media.c b/device/v4l2/device_media.c similarity index 90% rename from device/hw/device_media.c rename to device/v4l2/device_media.c index 06b938a..66f481e 100644 --- a/device/hw/device_media.c +++ b/device/v4l2/device_media.c @@ -1,3 +1,4 @@ +#include "device/v4l2/v4l2.h" #include "device/hw/device.h" #include "device/hw/v4l2.h" @@ -8,7 +9,7 @@ #include #include -int device_open_media_device(device_t *dev) +int v4l2_device_open_media_device(device_t *dev) { struct stat st; if (fstat(dev->fd, &st) < 0) { @@ -49,13 +50,13 @@ error: return ret; } -int device_open_v4l2_subdev(device_t *dev, int subdev) +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 = device_open_media_device(dev); + media_fd = v4l2_device_open_media_device(dev); if (media_fd < 0) { E_LOG_ERROR(dev, "Cannot find media controller"); } @@ -106,7 +107,7 @@ error: return ret; } -int device_set_pad_format(device_t *dev, unsigned width, unsigned height, unsigned format) +int v4l2_device_set_pad_format(device_t *dev, unsigned width, unsigned height, unsigned format) { struct v4l2_subdev_format fmt = {0}; diff --git a/device/hw/device_options.c b/device/v4l2/device_options.c similarity index 68% rename from device/hw/device_options.c rename to device/v4l2/device_options.c index e9c5211..fe32894 100644 --- a/device/hw/device_options.c +++ b/device/v4l2/device_options.c @@ -1,10 +1,10 @@ -#include "device/hw/device.h" +#include "device/v4l2/v4l2.h" #include "device/hw/v4l2.h" -#include "opts/opts.h" +#include "device/hw/device.h" #include -int device_set_option(device_t *dev, const char *name, uint32_t id, int32_t value) +int v4l2_device_set_option_by_id(device_t *dev, const char *name, uint32_t id, int32_t value) { struct v4l2_control ctl = {0}; @@ -21,7 +21,7 @@ error: return -1; } -void device_option_normalize_name(char *in) +void v4l2_device_option_normalize_name(char *in) { char *out = in; @@ -39,7 +39,7 @@ void device_option_normalize_name(char *in) *out++ = 0; } -static int device_set_option_string_fd_by_id(device_t *dev, int fd, uint32_t *id, char *name, char *value) +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; @@ -50,7 +50,7 @@ static int device_set_option_string_fd_by_id(device_t *dev, int fd, uint32_t *id } *id = qctrl.id; - device_option_normalize_name(qctrl.name); + v4l2_device_option_normalize_name(qctrl.name); if (strcmp(qctrl.name, name) != 0) return 0; @@ -131,61 +131,42 @@ error: return -1; } -static int device_set_option_string_fd(device_t *dev, int fd, char *name, char *value) +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 = device_set_option_string_fd_by_id(dev, fd, &id, name, value)) == 0 && id) { + 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 device_set_option_string(device_t *dev, const char *option) +int v4l2_device_set_option(device_t *dev, const char *key, const char *value) { - int ret = -1; - char *name = strdup(option); - strcpy(name, option); + char *keyp = strdup(key); + char *valuep = strdup(value); - char *value = strchr(name, '='); - if (!value) { - E_LOG_ERROR(dev, "Missing 'key=value': '%s'", option); - } - *value++ = 0; + int ret = 0; - device_option_normalize_name(name); + v4l2_device_option_normalize_name(keyp); if (dev->subdev_fd >= 0) - ret = device_set_option_string_fd(dev, dev->subdev_fd, name, value); + ret = v4l2_device_set_option_string_fd(dev, dev->subdev_fd, keyp, valuep); if (ret <= 0) - ret = device_set_option_string_fd(dev, dev->fd, name, value); - if (ret == 0) - E_LOG_ERROR(dev, "The '%s' was failed to find.", option); - else if (ret < 0) - E_LOG_ERROR(dev, "The '%s' did fail to be set.", option); + ret = v4l2_device_set_option_string_fd(dev, dev->fd, keyp, valuep); - ret = 0; + 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: - free(name); - return ret; + return -1; } - -void device_set_option_list(device_t *dev, const char *option_list) -{ - if (!dev || !option_list || !option_list[0]) { - return; - } - - char *start = strdup(option_list); - char *string = start; - char *option; - - while (option = strsep(&string, OPTION_VALUE_LIST_SEP)) { - device_set_option_string(dev, option); - } - - free(start); -} \ No newline at end of file diff --git a/device/v4l2/v4l2.c b/device/v4l2/v4l2.c new file mode 100644 index 0000000..e602d2d --- /dev/null +++ b/device/v4l2/v4l2.c @@ -0,0 +1,59 @@ +#include "device/v4l2/v4l2.h" + +#include +#include + +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); + } +} diff --git a/device/v4l2/v4l2.h b/device/v4l2/v4l2.h index 80ecbdd..e1fb125 100644 --- a/device/v4l2/v4l2.h +++ b/device/v4l2/v4l2.h @@ -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)