summaryrefslogtreecommitdiffstats
path: root/app/capturing.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'app/capturing.cpp')
-rw-r--r--app/capturing.cpp404
1 files changed, 404 insertions, 0 deletions
diff --git a/app/capturing.cpp b/app/capturing.cpp
new file mode 100644
index 0000000..fd2a6c5
--- /dev/null
+++ b/app/capturing.cpp
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2019 DENSO TEN Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <string>
+#include <sstream>
+#include <getopt.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+
+#include <linux/videodev2.h>
+#include <linux/fb.h>
+
+#include <pthread.h>
+#include "capturing.h"
+
+#define CLEAR(x) memset(&(x), 0, sizeof(x))
+
+#define FIELD V4L2_FIELD_NONE
+
+using std::istringstream;
+
+CapturingItem::CapturingItem()
+ : _dev_name(""),
+ _fd(-1), _fps_count(0), _framerate(0), _timeout(60),
+ _left(0), _top(0), _width(1280), _height(1080)
+{
+}
+
+CapturingItem::~CapturingItem()
+{
+}
+
+int CapturingItem::open_device(int dev)
+{
+ struct stat st;
+ std::stringstream ss;
+ ss << "/dev/video";
+ ss << dev;
+ ss >> _dev_name;
+
+ if (-1 == stat(_dev_name.c_str(), &st)) {
+ fprintf(stderr, "Cannot identify '%s': %d, %s\n",
+ _dev_name.c_str(), errno, strerror(errno));
+ return -1;
+ }
+
+ if (!S_ISCHR(st.st_mode)) {
+ fprintf(stderr, "%s is no device\n", _dev_name.c_str());
+ return -1;
+ }
+
+ _fd = open(_dev_name.c_str(), O_RDWR /* Required */ | O_NONBLOCK, 0);
+
+ if (-1 == _fd) {
+ fprintf(stderr, "Cannot open '%s': %d, %s\n",
+ _dev_name.c_str(), errno, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int CapturingItem::init_device(void)
+{
+ struct v4l2_capability cap;
+ struct v4l2_cropcap cropcap;
+ struct v4l2_crop crop;
+ struct v4l2_format fmt;
+
+ if (-1 == xioctl(_fd, VIDIOC_QUERYCAP, &cap)) {
+ if (EINVAL == errno) {
+ fprintf(stderr, "%s is no V4L2 device\n", _dev_name.c_str());
+ exit(EXIT_FAILURE);
+ } else {
+ errno_exit("VIDIOC_QUERYCAP");
+ }
+ }
+
+ if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
+ fprintf(stderr, "%s is no video capture device\n", _dev_name.c_str());
+ exit(EXIT_FAILURE);
+ }
+
+
+ /* Select video input, video standard and adjust. */
+ CLEAR(cropcap);
+ cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (0 == xioctl(_fd, VIDIOC_CROPCAP, &cropcap)) {
+ crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ crop.c = cropcap.defrect; /* reset to default */
+
+ crop.c.left = _left;
+ crop.c.top = _top;
+ crop.c.width = _width;
+ crop.c.height = _height;
+
+ if (-1 == xioctl(_fd, VIDIOC_S_CROP, &crop)) {
+ switch (errno) {
+ case EINVAL:
+ /* Trimming is not supported. */
+ break;
+ default:
+ /* The error is ignored. */
+ break;
+ }
+ }
+ } else {
+ /* The error is ignored. */
+ }
+
+ if (_framerate) {
+ struct v4l2_streamparm parm;
+
+ parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (-1 == xioctl(_fd, VIDIOC_G_PARM, &parm))
+ errno_exit("VIDIOC_G_PARM");
+
+ parm.parm.capture.timeperframe.numerator = 1;
+ parm.parm.capture.timeperframe.denominator = _framerate;
+ if (-1 == xioctl(_fd, VIDIOC_S_PARM, &parm))
+ errno_exit("VIDIOC_S_PARM");
+ }
+
+ CLEAR(fmt);
+
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt.fmt.pix.width = _width;
+ fmt.fmt.pix.height = _height;
+ fmt.fmt.pix.field = V4L2_FIELD_NONE;
+ fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_XBGR32;
+
+ if (-1 == xioctl(_fd, VIDIOC_S_FMT, &fmt))
+ errno_exit("VIDIOC_S_FMT");
+
+ init_mmap();
+
+ return 0;
+}
+
+void CapturingItem::start_capturing(void)
+{
+ unsigned int i;
+ enum v4l2_buf_type type;
+
+ for (i = 0; i < n_buffers; ++i) {
+ struct v4l2_buffer buf;
+
+ CLEAR(buf);
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = i;
+
+ if (-1 == xioctl(_fd, VIDIOC_QBUF, &buf))
+ errno_exit("VIDIOC_QBUF");
+ }
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (-1 == xioctl(_fd, VIDIOC_STREAMON, &type))
+ errno_exit("VIDIOC_STREAMON");
+}
+
+unsigned char* CapturingItem::snap_frame(void)
+{
+ int index = 0;
+ for (;;)
+ {
+ fd_set fds;
+ struct timeval tv;
+ int r;
+
+ FD_ZERO(&fds);
+ FD_SET(_fd, &fds);
+
+ /* Timeout. */
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+
+ r = select(_fd + 1, &fds, NULL, NULL, &tv);
+
+ if (-1 == r)
+ {
+ if (EINTR == errno)
+ continue;
+ errno_exit("select");
+ }
+
+ if (0 == r)
+ {
+ fprintf(stderr, "select timeout!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ index = read_frame();
+ if (-1 != index)
+ break;
+ /* EAGAIN (continue select loop). */
+ }
+
+ return (unsigned char*)(p_buffers)[index].start;
+}
+
+int CapturingItem::read_frame(void)
+{
+ struct v4l2_buffer buf;
+
+ CLEAR(buf);
+
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+
+ if (-1 == xioctl(_fd, VIDIOC_DQBUF, &buf))
+ {
+ switch (errno)
+ {
+ case EAGAIN:
+ return -1;
+
+ case EIO:
+ /* Could ignore EIO. */
+ default:
+ errno_exit("VIDIOC_DQBUF");
+ }
+ }
+
+ assert(buf.index < n_buffers);
+
+
+
+ if (-1 == xioctl(_fd, VIDIOC_QBUF, &buf))
+ errno_exit("VIDIOC_QBUF");
+
+ return buf.index;
+}
+
+void CapturingItem::errno_exit(const char *s)
+{
+ fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno));
+ exit(EXIT_FAILURE);
+}
+
+int CapturingItem::xioctl(int fh, int request, void *arg)
+{
+ int r;
+
+ do {
+ r = ioctl(fh, request, arg);
+ } while (-1 == r && EINTR == errno);
+
+ return r;
+}
+
+void CapturingItem::uninit_device(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < n_buffers; ++i)
+ if (-1 == munmap((p_buffers)[i].start, (p_buffers)[i].length))
+ errno_exit("munmap");
+
+ free(p_buffers);
+}
+
+void CapturingItem::init_read(unsigned int buffer_size)
+{
+ p_buffers = (struct buffer*)(calloc(1, sizeof(*p_buffers)));
+
+ if (!p_buffers) {
+ fprintf(stderr, "Out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ (p_buffers)[0].length = buffer_size;
+ (p_buffers)[0].start = malloc(buffer_size);
+
+ if (!(p_buffers)[0].start) {
+ fprintf(stderr, "Out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+void CapturingItem::init_mmap(void)
+{
+ struct v4l2_requestbuffers req;
+
+ CLEAR(req);
+
+ req.count = 4;
+ req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req.memory = V4L2_MEMORY_MMAP;
+
+ if (-1 == xioctl(_fd, VIDIOC_REQBUFS, &req)) {
+ if (EINVAL == errno) {
+ fprintf(stderr, "%s does not support "
+ "memory mapping\n", _dev_name.c_str());
+ exit(EXIT_FAILURE);
+ } else {
+ errno_exit("VIDIOC_REQBUFS");
+ }
+ }
+
+ if (req.count < 2) {
+ fprintf(stderr, "Insufficient buffer memory on %s\n",
+ _dev_name.c_str());
+ exit(EXIT_FAILURE);
+ }
+
+ p_buffers = (struct buffer*)(calloc(req.count, sizeof(*p_buffers)));
+
+ if (!p_buffers) {
+ fprintf(stderr, "Out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
+ struct v4l2_buffer buf;
+
+ CLEAR(buf);
+
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = n_buffers;
+
+ if (-1 == xioctl(_fd, VIDIOC_QUERYBUF, &buf))
+ errno_exit("VIDIOC_QUERYBUF");
+
+ (p_buffers)[n_buffers].length = buf.length;
+ (p_buffers)[n_buffers].start =
+ mmap(NULL /* Start from anywhere */,
+ buf.length,
+ PROT_READ | PROT_WRITE /* Required */,
+ MAP_SHARED /* Recommended */,
+ _fd, buf.m.offset);
+
+ if (MAP_FAILED == (p_buffers)[n_buffers].start)
+ errno_exit("mmap");
+ }
+}
+
+void CapturingItem::init_userp(unsigned int buffer_size)
+{
+ struct v4l2_requestbuffers req;
+
+ CLEAR(req);
+
+ req.count = 4;
+ req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req.memory = V4L2_MEMORY_USERPTR;
+
+ if (-1 == xioctl(_fd, VIDIOC_REQBUFS, &req)) {
+ if (EINVAL == errno) {
+ fprintf(stderr, "%s does not support "
+ "user pointer i/o\n", _dev_name.c_str());
+ exit(EXIT_FAILURE);
+ } else {
+ errno_exit("VIDIOC_REQBUFS");
+ }
+ }
+
+ p_buffers = (struct buffer*)(calloc(4, sizeof(*p_buffers)));
+
+ if (!p_buffers) {
+ fprintf(stderr, "Out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ for (n_buffers = 0; n_buffers < 4; ++n_buffers) {
+ (p_buffers)[n_buffers].length = buffer_size;
+ (p_buffers)[n_buffers].start = malloc(buffer_size);
+
+ if (!(p_buffers)[n_buffers].start) {
+ fprintf(stderr, "Out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+void CapturingItem::close_device(void)
+{
+ if (-1 == close(_fd))
+ errno_exit("close");
+
+ _fd = -1;
+}