| /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| /* |
| * V4L2 Devices |
| * |
| * Copyright (C) 2018 Laurent Pinchart |
| * |
| * This file originally comes from the omap3-isp-live project |
| * (git://git.ideasonboard.org/omap3-isp-live.git) |
| * |
| * Copyright (C) 2010-2011 Ideas on board SPRL |
| * |
| * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> |
| */ |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <linux/videodev2.h> |
| |
| #include <sys/ioctl.h> |
| #include <sys/mman.h> |
| #include <sys/select.h> |
| #include <sys/time.h> |
| |
| #include "list.h" |
| #include "tools.h" |
| #include "v4l2.h" |
| |
| #ifndef V4L2_BUF_FLAG_ERROR |
| #define V4L2_BUF_FLAG_ERROR 0x0040 |
| #endif |
| |
| struct v4l2_ival_desc { |
| struct v4l2_fract min; |
| struct v4l2_fract max; |
| struct v4l2_fract step; |
| |
| struct list_entry list; |
| }; |
| |
| struct v4l2_frame_desc { |
| unsigned int min_width; |
| unsigned int min_height; |
| unsigned int max_width; |
| unsigned int max_height; |
| unsigned int step_width; |
| unsigned int step_height; |
| |
| struct list_entry list; |
| struct list_entry ivals; |
| }; |
| |
| struct v4l2_format_desc { |
| unsigned int pixelformat; |
| |
| struct list_entry list; |
| struct list_entry frames; |
| }; |
| |
| /* ----------------------------------------------------------------------------- |
| * Formats enumeration |
| */ |
| |
| static int |
| v4l2_enum_frame_intervals(struct v4l2_device *dev, struct v4l2_format_desc *format, |
| struct v4l2_frame_desc *frame) |
| { |
| struct v4l2_ival_desc *ival; |
| unsigned int i; |
| int ret; |
| |
| for (i = 0; ; ++i) { |
| struct v4l2_frmivalenum ivalenum; |
| |
| memset(&ivalenum, 0, sizeof ivalenum); |
| ivalenum.index = i; |
| ivalenum.pixel_format = format->pixelformat; |
| ivalenum.width = frame->min_width; |
| ivalenum.height = frame->min_height; |
| ret = ioctl(dev->fd, VIDIOC_ENUM_FRAMEINTERVALS, &ivalenum); |
| if (ret < 0) |
| break; |
| |
| if (i != ivalenum.index) |
| printf("Warning: driver returned wrong ival index " |
| "%u.\n", ivalenum.index); |
| if (format->pixelformat != ivalenum.pixel_format) |
| printf("Warning: driver returned wrong ival pixel " |
| "format %08x.\n", ivalenum.pixel_format); |
| if (frame->min_width != ivalenum.width) |
| printf("Warning: driver returned wrong ival width " |
| "%u.\n", ivalenum.width); |
| if (frame->min_height != ivalenum.height) |
| printf("Warning: driver returned wrong ival height " |
| "%u.\n", ivalenum.height); |
| |
| ival = malloc(sizeof *ival); |
| if (ival == NULL) |
| return -ENOMEM; |
| |
| memset(ival, 0, sizeof *ival); |
| |
| switch (ivalenum.type) { |
| case V4L2_FRMIVAL_TYPE_DISCRETE: |
| ival->min = ivalenum.discrete; |
| ival->max = ivalenum.discrete; |
| ival->step.numerator = 1; |
| ival->step.denominator = 1; |
| break; |
| |
| case V4L2_FRMIVAL_TYPE_STEPWISE: |
| ival->min = ivalenum.stepwise.min; |
| ival->max = ivalenum.stepwise.max; |
| ival->step = ivalenum.stepwise.step; |
| break; |
| |
| default: |
| printf("Error: driver returned invalid frame ival " |
| "type %u\n", ivalenum.type); |
| return -EINVAL; |
| } |
| |
| list_append(&ival->list, &frame->ivals); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| v4l2_enum_frame_sizes(struct v4l2_device *dev, struct v4l2_format_desc *format) |
| { |
| struct v4l2_frame_desc *frame; |
| unsigned int i; |
| int ret; |
| |
| for (i = 0; ; ++i) { |
| struct v4l2_frmsizeenum frmenum; |
| |
| memset(&frmenum, 0, sizeof frmenum); |
| frmenum.index = i; |
| frmenum.pixel_format = format->pixelformat; |
| |
| ret = ioctl(dev->fd, VIDIOC_ENUM_FRAMESIZES, &frmenum); |
| if (ret < 0) |
| break; |
| |
| if (i != frmenum.index) |
| printf("Warning: driver returned wrong frame index " |
| "%u.\n", frmenum.index); |
| if (format->pixelformat != frmenum.pixel_format) |
| printf("Warning: driver returned wrong frame pixel " |
| "format %08x.\n", frmenum.pixel_format); |
| |
| frame = malloc(sizeof *frame); |
| if (frame == NULL) |
| return -ENOMEM; |
| |
| memset(frame, 0, sizeof *frame); |
| |
| list_init(&frame->ivals); |
| frame->step_width = 1; |
| frame->step_height = 1; |
| |
| switch (frmenum.type) { |
| case V4L2_FRMSIZE_TYPE_DISCRETE: |
| frame->min_width = frmenum.discrete.width; |
| frame->min_height = frmenum.discrete.height; |
| frame->max_width = frmenum.discrete.width; |
| frame->max_height = frmenum.discrete.height; |
| break; |
| |
| case V4L2_FRMSIZE_TYPE_STEPWISE: |
| frame->step_width = frmenum.stepwise.step_width; |
| frame->step_height = frmenum.stepwise.step_height; |
| /* fallthrough */ |
| case V4L2_FRMSIZE_TYPE_CONTINUOUS: |
| frame->min_width = frmenum.stepwise.min_width; |
| frame->min_height = frmenum.stepwise.min_height; |
| frame->max_width = frmenum.stepwise.max_width; |
| frame->max_height = frmenum.stepwise.max_height; |
| break; |
| |
| default: |
| printf("Error: driver returned invalid frame size " |
| "type %u\n", frmenum.type); |
| return -EINVAL; |
| } |
| |
| list_append(&frame->list, &format->frames); |
| |
| ret = v4l2_enum_frame_intervals(dev, format, frame); |
| if (ret < 0) |
| return ret; |
| } |
| |
| return 0; |
| } |
| static int v4l2_enum_formats(struct v4l2_device *dev) |
| { |
| struct v4l2_format_desc *format; |
| unsigned int i; |
| int ret; |
| |
| for (i = 0; ; ++i) { |
| struct v4l2_fmtdesc fmtenum; |
| |
| memset(&fmtenum, 0, sizeof fmtenum); |
| fmtenum.index = i; |
| fmtenum.type = dev->type; |
| |
| ret = ioctl(dev->fd, VIDIOC_ENUM_FMT, &fmtenum); |
| if (ret < 0) |
| break; |
| |
| if (i != fmtenum.index) |
| printf("Warning: driver returned wrong format index " |
| "%u.\n", fmtenum.index); |
| if (dev->type != fmtenum.type) |
| printf("Warning: driver returned wrong format type " |
| "%u.\n", fmtenum.type); |
| |
| format = malloc(sizeof *format); |
| if (format == NULL) |
| return -ENOMEM; |
| |
| memset(format, 0, sizeof *format); |
| |
| list_init(&format->frames); |
| format->pixelformat = fmtenum.pixelformat; |
| |
| list_append(&format->list, &dev->formats); |
| |
| ret = v4l2_enum_frame_sizes(dev, format); |
| if (ret < 0) |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * Open/close |
| */ |
| |
| struct v4l2_device *v4l2_open(const char *devname) |
| { |
| struct v4l2_device *dev; |
| struct v4l2_capability cap; |
| __u32 capabilities; |
| int ret; |
| |
| dev = malloc(sizeof *dev); |
| if (dev == NULL) |
| return NULL; |
| |
| memset(dev, 0, sizeof *dev); |
| dev->fd = -1; |
| dev->name = strdup(devname); |
| list_init(&dev->formats); |
| |
| dev->fd = open(devname, O_RDWR | O_NONBLOCK); |
| if (dev->fd < 0) { |
| printf("Error opening device %s: %d.\n", devname, errno); |
| v4l2_close(dev); |
| return NULL; |
| } |
| |
| memset(&cap, 0, sizeof cap); |
| ret = ioctl(dev->fd, VIDIOC_QUERYCAP, &cap); |
| if (ret < 0) { |
| printf("Error opening device %s: unable to query " |
| "device.\n", devname); |
| v4l2_close(dev); |
| return NULL; |
| } |
| |
| /* |
| * If the device_caps field is set use it, otherwise use the older |
| * capabilities field. |
| */ |
| capabilities = cap.device_caps ? : cap.capabilities; |
| |
| if (capabilities & V4L2_CAP_VIDEO_CAPTURE) |
| dev->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| else if (capabilities & V4L2_CAP_VIDEO_OUTPUT) |
| dev->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; |
| else { |
| printf("Error opening device %s: neither video capture " |
| "nor video output supported.\n", devname); |
| v4l2_close(dev); |
| return NULL; |
| } |
| |
| ret = v4l2_enum_formats(dev); |
| if (ret < 0) { |
| printf("Error opening device %s: unable to enumerate " |
| "formats.\n", devname); |
| v4l2_close(dev); |
| return NULL; |
| } |
| |
| printf("Device %s opened: %s (%s).\n", devname, cap.card, cap.bus_info); |
| |
| return dev; |
| } |
| |
| void v4l2_close(struct v4l2_device *dev) |
| { |
| struct v4l2_format_desc *format, *next_fmt; |
| struct v4l2_frame_desc *frame, *next_frm; |
| struct v4l2_ival_desc *ival, *next_ival; |
| |
| if (dev == NULL) |
| return; |
| |
| list_for_each_entry_safe(format, next_fmt, &dev->formats, list) { |
| list_for_each_entry_safe(frame, next_frm, &format->frames, list) { |
| list_for_each_entry_safe(ival, next_ival, &frame->ivals, list) { |
| free(ival); |
| } |
| free(frame); |
| } |
| free(format); |
| } |
| |
| free(dev->name); |
| close(dev->fd); |
| free(dev); |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * Controls |
| */ |
| |
| int v4l2_get_control(struct v4l2_device *dev, unsigned int id, int32_t *value) |
| { |
| struct v4l2_control ctrl; |
| int ret; |
| |
| ctrl.id = id; |
| |
| ret = ioctl(dev->fd, VIDIOC_G_CTRL, &ctrl); |
| if (ret < 0) { |
| printf("%s: unable to get control (%d).\n", dev->name, errno); |
| return -errno; |
| } |
| |
| *value = ctrl.value; |
| return 0; |
| } |
| |
| int v4l2_set_control(struct v4l2_device *dev, unsigned int id, int32_t *value) |
| { |
| struct v4l2_control ctrl; |
| int ret; |
| |
| ctrl.id = id; |
| ctrl.value = *value; |
| |
| ret = ioctl(dev->fd, VIDIOC_S_CTRL, &ctrl); |
| if (ret < 0) { |
| printf("%s: unable to set control (%d).\n", dev->name, errno); |
| return -errno; |
| } |
| |
| *value = ctrl.value; |
| return 0; |
| } |
| |
| int v4l2_get_controls(struct v4l2_device *dev, unsigned int count, |
| struct v4l2_ext_control *ctrls) |
| { |
| struct v4l2_ext_controls controls; |
| int ret; |
| |
| memset(&controls, 0, sizeof controls); |
| controls.count = count; |
| controls.controls = ctrls; |
| |
| ret = ioctl(dev->fd, VIDIOC_G_EXT_CTRLS, &controls); |
| if (ret < 0) |
| printf("%s: unable to get multiple controls (%d).\n", dev->name, |
| errno); |
| |
| return ret; |
| } |
| |
| int v4l2_set_controls(struct v4l2_device *dev, unsigned int count, |
| struct v4l2_ext_control *ctrls) |
| { |
| struct v4l2_ext_controls controls; |
| int ret; |
| |
| memset(&controls, 0, sizeof controls); |
| controls.count = count; |
| controls.controls = ctrls; |
| |
| ret = ioctl(dev->fd, VIDIOC_S_EXT_CTRLS, &controls); |
| if (ret < 0) |
| printf("%s: unable to set multiple controls (%d).\n", dev->name, |
| errno); |
| |
| return ret; |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * Formats and frame rates |
| */ |
| |
| int v4l2_get_crop(struct v4l2_device *dev, struct v4l2_rect *rect) |
| { |
| struct v4l2_crop crop; |
| int ret; |
| |
| memset(&crop, 0, sizeof crop); |
| crop.type = dev->type; |
| |
| ret = ioctl(dev->fd, VIDIOC_G_CROP, &crop); |
| if (ret < 0) { |
| printf("%s: unable to get crop rectangle (%d).\n", dev->name, |
| errno); |
| return -errno; |
| } |
| |
| dev->crop = crop.c; |
| *rect = crop.c; |
| |
| return 0; |
| } |
| |
| int v4l2_set_crop(struct v4l2_device *dev, struct v4l2_rect *rect) |
| { |
| struct v4l2_crop crop; |
| int ret; |
| |
| memset(&crop, 0, sizeof crop); |
| crop.type = dev->type; |
| crop.c = *rect; |
| |
| ret = ioctl(dev->fd, VIDIOC_S_CROP, &crop); |
| if (ret < 0) { |
| printf("%s: unable to set crop rectangle (%d).\n", dev->name, |
| errno); |
| return -errno; |
| } |
| |
| dev->crop = crop.c; |
| *rect = crop.c; |
| |
| return 0; |
| } |
| |
| int v4l2_get_format(struct v4l2_device *dev, struct v4l2_pix_format *format) |
| { |
| struct v4l2_format fmt; |
| int ret; |
| |
| memset(&fmt, 0, sizeof fmt); |
| fmt.type = dev->type; |
| |
| ret = ioctl(dev->fd, VIDIOC_G_FMT, &fmt); |
| if (ret < 0) { |
| printf("%s: unable to get format (%d).\n", dev->name, errno); |
| return -errno; |
| } |
| |
| dev->format = fmt.fmt.pix; |
| *format = fmt.fmt.pix; |
| |
| return 0; |
| } |
| |
| int v4l2_set_format(struct v4l2_device *dev, struct v4l2_pix_format *format) |
| { |
| struct v4l2_format fmt; |
| int ret; |
| |
| memset(&fmt, 0, sizeof fmt); |
| fmt.type = dev->type; |
| fmt.fmt.pix.width = format->width; |
| fmt.fmt.pix.height = format->height; |
| fmt.fmt.pix.pixelformat = format->pixelformat; |
| fmt.fmt.pix.field = V4L2_FIELD_ANY; |
| |
| ret = ioctl(dev->fd, VIDIOC_S_FMT, &fmt); |
| if (ret < 0) { |
| printf("%s: unable to set format (%d).\n", dev->name, errno); |
| return -errno; |
| } |
| |
| dev->format = fmt.fmt.pix; |
| *format = fmt.fmt.pix; |
| |
| return 0; |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * Buffers management |
| */ |
| |
| int v4l2_alloc_buffers(struct v4l2_device *dev, enum v4l2_memory memtype, |
| unsigned int nbufs) |
| { |
| struct v4l2_requestbuffers rb; |
| unsigned int i; |
| int ret; |
| |
| if (dev->nbufs != 0) |
| return -EBUSY; |
| |
| if (memtype != V4L2_MEMORY_MMAP && memtype != V4L2_MEMORY_DMABUF) |
| return -EINVAL; |
| |
| /* Request the buffers from the driver. */ |
| memset(&rb, 0, sizeof rb); |
| rb.count = nbufs; |
| rb.type = dev->type; |
| rb.memory = memtype; |
| |
| ret = ioctl(dev->fd, VIDIOC_REQBUFS, &rb); |
| if (ret < 0) { |
| printf("%s: unable to request buffers (%d).\n", dev->name, |
| errno); |
| ret = -errno; |
| goto done; |
| } |
| |
| if (rb.count > nbufs) { |
| printf("%s: driver needs more buffers (%u) than available (%u).\n", |
| dev->name, rb.count, nbufs); |
| ret = -E2BIG; |
| goto done; |
| } |
| |
| printf("%s: %u buffers requested.\n", dev->name, rb.count); |
| |
| /* Allocate the buffer objects. */ |
| dev->memtype = memtype; |
| dev->nbufs = rb.count; |
| |
| dev->buffers = malloc(sizeof *dev->buffers * nbufs); |
| if (dev->buffers == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| memset(dev->buffers, 0, sizeof *dev->buffers * nbufs); |
| |
| for (i = 0; i < dev->nbufs; ++i) { |
| dev->buffers[i].index = i; |
| dev->buffers[i].dmabuf = -1; |
| } |
| |
| ret = 0; |
| |
| done: |
| if (ret < 0) |
| v4l2_free_buffers(dev); |
| |
| return ret; |
| } |
| |
| int v4l2_free_buffers(struct v4l2_device *dev) |
| { |
| struct v4l2_requestbuffers rb; |
| unsigned int i; |
| int ret; |
| |
| if (dev->nbufs == 0) |
| return 0; |
| |
| for (i = 0; i < dev->nbufs; ++i) { |
| struct v4l2_video_buffer *buffer = &dev->buffers[i]; |
| |
| if (buffer->mem) { |
| ret = munmap(buffer->mem, buffer->size); |
| if (ret < 0) { |
| printf("%s: unable to unmap buffer %u (%d)\n", |
| dev->name, i, errno); |
| return -errno; |
| } |
| |
| buffer->mem = NULL; |
| } |
| |
| if (buffer->dmabuf != -1) { |
| close(buffer->dmabuf); |
| buffer->dmabuf = -1; |
| } |
| |
| buffer->size = 0; |
| } |
| |
| memset(&rb, 0, sizeof rb); |
| rb.count = 0; |
| rb.type = dev->type; |
| rb.memory = dev->memtype; |
| |
| ret = ioctl(dev->fd, VIDIOC_REQBUFS, &rb); |
| if (ret < 0) { |
| printf("%s: unable to release buffers (%d)\n", dev->name, |
| errno); |
| return -errno; |
| } |
| |
| free(dev->buffers); |
| dev->buffers = NULL; |
| dev->nbufs = 0; |
| |
| return 0; |
| } |
| |
| int v4l2_export_buffers(struct v4l2_device *dev) |
| { |
| unsigned int i; |
| int ret; |
| |
| if (dev->nbufs == 0) |
| return -EINVAL; |
| |
| if (dev->memtype != V4L2_MEMORY_MMAP) |
| return -EINVAL; |
| |
| for (i = 0; i < dev->nbufs; ++i) { |
| struct v4l2_exportbuffer expbuf = { |
| .type = dev->type, |
| .index = i, |
| }; |
| struct v4l2_buffer buf = { |
| .index = i, |
| .type = dev->type, |
| .memory = dev->memtype, |
| }; |
| |
| ret = ioctl(dev->fd, VIDIOC_QUERYBUF, &buf); |
| if (ret < 0) { |
| printf("%s: unable to query buffer %u (%d).\n", |
| dev->name, i, errno); |
| return -errno; |
| } |
| |
| ret = ioctl(dev->fd, VIDIOC_EXPBUF, &expbuf); |
| if (ret < 0) { |
| printf("Failed to export buffer %u.\n", i); |
| return -errno; |
| } |
| |
| dev->buffers[i].size = buf.length; |
| dev->buffers[i].dmabuf = expbuf.fd; |
| |
| printf("%s: buffer %u exported with fd %u.\n", |
| dev->name, i, dev->buffers[i].dmabuf); |
| } |
| |
| return 0; |
| } |
| |
| int v4l2_import_buffers(struct v4l2_device *dev, unsigned int nbufs, |
| const struct v4l2_video_buffer *buffers) |
| { |
| unsigned int i; |
| int ret; |
| |
| if (dev->nbufs == 0 || dev->nbufs > nbufs) |
| return -EINVAL; |
| |
| if (dev->memtype != V4L2_MEMORY_DMABUF) |
| return -EINVAL; |
| |
| for (i = 0; i < dev->nbufs; ++i) { |
| const struct v4l2_video_buffer *buffer = &buffers[i]; |
| struct v4l2_buffer buf = { |
| .index = i, |
| .type = dev->type, |
| .memory = dev->memtype, |
| }; |
| int fd; |
| |
| ret = ioctl(dev->fd, VIDIOC_QUERYBUF, &buf); |
| if (ret < 0) { |
| printf("%s: unable to query buffer %u (%d).\n", |
| dev->name, i, errno); |
| return -errno; |
| } |
| |
| if (buffer->size < buf.length) { |
| printf("%s: buffer %u too small (%u bytes required, %u bytes available).\n", |
| dev->name, i, buf.length, buffer->size); |
| return -EINVAL; |
| } |
| |
| fd = dup(buffer->dmabuf); |
| if (fd < 0) { |
| printf("%s: failed to duplicate dmabuf fd %d.\n", |
| dev->name, buffer->dmabuf); |
| return ret; |
| } |
| |
| printf("%s: buffer %u valid.\n", dev->name, i); |
| |
| dev->buffers[i].dmabuf = fd; |
| dev->buffers[i].size = buffer->size; |
| } |
| |
| return 0; |
| } |
| |
| int v4l2_mmap_buffers(struct v4l2_device *dev) |
| { |
| unsigned int i; |
| int ret; |
| |
| if (dev->memtype != V4L2_MEMORY_MMAP) |
| return -EINVAL; |
| |
| for (i = 0; i < dev->nbufs; ++i) { |
| struct v4l2_video_buffer *buffer = &dev->buffers[i]; |
| struct v4l2_buffer buf = { |
| .index = i, |
| .type = dev->type, |
| .memory = dev->memtype, |
| }; |
| void *mem; |
| |
| ret = ioctl(dev->fd, VIDIOC_QUERYBUF, &buf); |
| if (ret < 0) { |
| printf("%s: unable to query buffer %u (%d).\n", |
| dev->name, i, errno); |
| return -errno; |
| } |
| |
| mem = mmap(0, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, |
| dev->fd, buf.m.offset); |
| if (mem == MAP_FAILED) { |
| printf("%s: unable to map buffer %u (%d)\n", |
| dev->name, i, errno); |
| return -errno; |
| } |
| |
| buffer->mem = mem; |
| buffer->size = buf.length; |
| |
| printf("%s: buffer %u mapped at address %p.\n", dev->name, i, |
| mem); |
| } |
| |
| return 0; |
| } |
| |
| int v4l2_dequeue_buffer(struct v4l2_device *dev, struct v4l2_video_buffer *buffer) |
| { |
| struct v4l2_buffer buf; |
| int ret; |
| |
| memset(&buf, 0, sizeof buf); |
| buf.type = dev->type; |
| buf.memory = dev->memtype; |
| |
| ret = ioctl(dev->fd, VIDIOC_DQBUF, &buf); |
| if (ret < 0) { |
| printf("%s: unable to dequeue buffer index %u/%u (%d)\n", |
| dev->name, buf.index, dev->nbufs, errno); |
| return -errno; |
| } |
| |
| *buffer = dev->buffers[buf.index]; |
| buffer->bytesused = buf.bytesused; |
| buffer->timestamp = buf.timestamp; |
| buffer->error = !!(buf.flags & V4L2_BUF_FLAG_ERROR); |
| |
| return 0; |
| } |
| |
| int v4l2_queue_buffer(struct v4l2_device *dev, struct v4l2_video_buffer *buffer) |
| { |
| struct v4l2_buffer buf; |
| int ret; |
| |
| if (buffer->index >= dev->nbufs) |
| return -EINVAL; |
| |
| memset(&buf, 0, sizeof buf); |
| buf.index = buffer->index; |
| buf.type = dev->type; |
| buf.memory = dev->memtype; |
| |
| if (dev->memtype == V4L2_MEMORY_DMABUF) |
| buf.m.fd = (unsigned long)dev->buffers[buffer->index].dmabuf; |
| |
| if (dev->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) |
| buf.bytesused = buffer->bytesused; |
| |
| ret = ioctl(dev->fd, VIDIOC_QBUF, &buf); |
| if (ret < 0) { |
| printf("%s: unable to queue buffer index %u/%u (%d)\n", |
| dev->name, buf.index, dev->nbufs, errno); |
| return -errno; |
| } |
| |
| return 0; |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * Stream management |
| */ |
| |
| int v4l2_stream_on(struct v4l2_device *dev) |
| { |
| int type = dev->type; |
| int ret; |
| |
| ret = ioctl(dev->fd, VIDIOC_STREAMON, &type); |
| if (ret < 0) |
| return -errno; |
| |
| return 0; |
| } |
| |
| int v4l2_stream_off(struct v4l2_device *dev) |
| { |
| int type = dev->type; |
| int ret; |
| |
| ret = ioctl(dev->fd, VIDIOC_STREAMOFF, &type); |
| if (ret < 0) |
| return -errno; |
| |
| return 0; |
| } |