/* Copyright (C) 2009 Giacomo Spigler This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* * Copyright (C) 2013 Enrique Medina Gremaldos (Lliurex team) */ #include #include #include #include #include /* getopt_long() */ #include /* low-level i/o */ #include #include #include #include #include #include #include #include #include /* for videodev2.h */ #include #define CLEAR(x) memset (&(x), 0, sizeof (x)) #include "libcam.h" static void errno_exit (const char * s) { fprintf (stderr, "%s error %d, %s\n", s, errno, strerror (errno)); exit (EXIT_FAILURE); } static int xioctl(int fd, int request, void *arg) { int r,itt=0; do { r = ioctl (fd, request, arg); itt++; } while ((-1 == r) && (EINTR == errno) && (itt<100)); return r; } Camera::Camera(const char *n, int w, int h, int f) { name=n; width=w; height=h; fps=f; w2=w/2; io=IO_METHOD_MMAP; data=(unsigned char *)malloc(w*h*4); this->Open(); this->Init(); this->Start(); initialised = true; } Camera::~Camera() { this->StopCam(); } void Camera::StopCam() { if (initialised) { this->Stop(); this->UnInit(); this->Close(); free(data); initialised = false; } } void Camera::Open() { struct stat st; if(-1==stat(name, &st)) { fprintf(stderr, "Cannot identify '%s' : %d, %s\n", name, errno, strerror(errno)); exit(1); } if(!S_ISCHR(st.st_mode)) { fprintf(stderr, "%s is no device\n", name); exit(1); } fd=open(name, O_RDWR | O_NONBLOCK, 0); if(-1 == fd) { fprintf(stderr, "Cannot open '%s': %d, %s\n", name, errno, strerror(errno)); exit(1); } } void Camera::Close() { if(-1==close(fd)) { errno_exit("close"); } fd=-1; } void Camera::Init() { struct v4l2_capability cap; struct v4l2_cropcap cropcap; struct v4l2_crop crop; struct v4l2_format fmt; unsigned int min; if(-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) { if (EINVAL == errno) { fprintf(stderr, "%s is no V4L2 device\n",name); exit(1); } else { errno_exit("VIDIOC_QUERYCAP"); } } if(!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { fprintf(stderr, "%s is no video capture device\n", name); exit(1); } struct v4l2_streamparm str; str.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; if(-1 == xioctl (fd, VIDIOC_G_PARM, &str)) { if (EINVAL == errno) { fprintf(stderr, "%s fail to read capabilities\n",name); exit(1); } else { errno_exit("VIDIOC_G_PARM"); } } if(str.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) { printf("TIMERPERFRAME is supported\n"); } switch(io) { case IO_METHOD_READ: if(!(cap.capabilities & V4L2_CAP_READWRITE)) { fprintf(stderr, "%s does not support read i/o\n", name); exit (1); } break; case IO_METHOD_MMAP: case IO_METHOD_USERPTR: if(!(cap.capabilities & V4L2_CAP_STREAMING)) { fprintf (stderr, "%s does not support streaming i/o\n", name); exit(1); } break; } 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 */ if(-1 == xioctl (fd, VIDIOC_S_CROP, &crop)) { switch (errno) { case EINVAL: /* Cropping not supported. */ break; default: /* Errors ignored. */ break; } } } else { /* Errors ignored. */ } CLEAR (fmt); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = width; fmt.fmt.pix.height = height; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; if(-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) errno_exit ("VIDIOC_S_FMT"); /* struct v4l2_standard s; s.name[0]='A'; s.frameperiod.numerator=1; s.frameperiod.denominator=fps; if(-1==xioctl(fd, VIDIOC_G_STD, &s)) errno_exit("VIDIOC_G_STD"); printf("frameperiod:%d/%d\n",s.frameperiod.numerator,s.frameperiod.denominator); */ struct v4l2_streamparm p; p.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; //p.parm.capture.capability=V4L2_CAP_TIMEPERFRAME; //p.parm.capture.capturemode=V4L2_MODE_HIGHQUALITY; p.parm.capture.timeperframe.numerator=1; p.parm.capture.timeperframe.denominator=fps; p.parm.output.timeperframe.numerator=1; p.parm.output.timeperframe.denominator=fps; //p.parm.output.outputmode=V4L2_MODE_HIGHQUALITY; //p.parm.capture.extendedmode=0; //p.parm.capture.readbuffers=n_buffers; if(-1==xioctl(fd, VIDIOC_S_PARM, &p)) errno_exit("VIDIOC_S_PARM"); //default values, mins and maxes struct v4l2_queryctrl queryctrl; memset(&queryctrl, 0, sizeof(queryctrl)); queryctrl.id = V4L2_CID_BRIGHTNESS; if(-1 == xioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) { if(errno != EINVAL) { //perror ("VIDIOC_QUERYCTRL"); //exit(EXIT_FAILURE); printf("brightness error\n"); } else { printf("brightness is not supported\n"); } } else if(queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) { printf ("brightness is not supported\n"); } mb=queryctrl.minimum; Mb=queryctrl.maximum; db=queryctrl.default_value; memset(&queryctrl, 0, sizeof(queryctrl)); queryctrl.id = V4L2_CID_CONTRAST; if(-1 == xioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) { if(errno != EINVAL) { //perror ("VIDIOC_QUERYCTRL"); //exit(EXIT_FAILURE); printf("contrast error\n"); } else { printf("contrast is not supported\n"); } } else if(queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) { printf ("contrast is not supported\n"); } mc=queryctrl.minimum; Mc=queryctrl.maximum; dc=queryctrl.default_value; memset(&queryctrl, 0, sizeof(queryctrl)); queryctrl.id = V4L2_CID_SATURATION; if(-1 == xioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) { if(errno != EINVAL) { //perror ("VIDIOC_QUERYCTRL"); //exit(EXIT_FAILURE); printf("saturation error\n"); } else { printf("saturation is not supported\n"); } } else if(queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) { printf ("saturation is not supported\n"); } ms=queryctrl.minimum; Ms=queryctrl.maximum; ds=queryctrl.default_value; memset(&queryctrl, 0, sizeof(queryctrl)); queryctrl.id = V4L2_CID_HUE; if(-1 == xioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) { if(errno != EINVAL) { //perror ("VIDIOC_QUERYCTRL"); //exit(EXIT_FAILURE); printf("hue error\n"); } else { printf("hue is not supported\n"); } } else if(queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) { printf ("hue is not supported\n"); } mh=queryctrl.minimum; Mh=queryctrl.maximum; dh=queryctrl.default_value; memset(&queryctrl, 0, sizeof(queryctrl)); queryctrl.id = V4L2_CID_HUE_AUTO; if(-1 == xioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) { if(errno != EINVAL) { //perror ("VIDIOC_QUERYCTRL"); //exit(EXIT_FAILURE); printf("hueauto error\n"); } else { printf("hueauto is not supported\n"); } } else if(queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) { printf ("hueauto is not supported\n"); } ha=queryctrl.default_value; memset(&queryctrl, 0, sizeof(queryctrl)); queryctrl.id = V4L2_CID_SHARPNESS; if(-1 == xioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) { if(errno != EINVAL) { //perror ("VIDIOC_QUERYCTRL"); //exit(EXIT_FAILURE); printf("sharpness error\n"); } else { printf("sharpness is not supported\n"); } } else if(queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) { printf ("sharpness is not supported\n"); } msh=queryctrl.minimum; Msh=queryctrl.maximum; dsh=queryctrl.default_value; //TODO: TO ADD SETTINGS //here should go custom calls to xioctl //END TO ADD SETTINGS /* Note VIDIOC_S_FMT may change width and height. */ /* Buggy driver paranoia. */ min = fmt.fmt.pix.width * 2; if(fmt.fmt.pix.bytesperline < min) fmt.fmt.pix.bytesperline = min; min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height; if(fmt.fmt.pix.sizeimage < min) fmt.fmt.pix.sizeimage = min; switch(io) { case IO_METHOD_READ: init_read(fmt.fmt.pix.sizeimage); break; case IO_METHOD_MMAP: init_mmap(); break; case IO_METHOD_USERPTR: init_userp(fmt.fmt.pix.sizeimage); break; } } void Camera::init_userp(unsigned int buffer_size) { /* struct v4l2_requestbuffers req; unsigned int page_size; page_size = getpagesize(); buffer_size = (buffer_size + page_size - 1) & ~(page_size - 1); 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", name); exit (EXIT_FAILURE); } else { errno_exit ("VIDIOC_REQBUFS"); } } buffers = calloc (4, sizeof (*buffers)); if (!buffers) { fprintf (stderr, "Out of memory\n"); exit (EXIT_FAILURE); } for (n_buffers = 0; n_buffers < 4; ++n_buffers) { buffers[n_buffers].length = buffer_size; buffers[n_buffers].start = memalign (page_size, buffer_size); if (!buffers[n_buffers].start) { fprintf (stderr, "Out of memory\n"); exit (EXIT_FAILURE); } } */ } void Camera::init_mmap() { 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", name); exit (1); } else { errno_exit ("VIDIOC_REQBUFS"); } } if(req.count < 2) { fprintf (stderr, "Insufficient buffer memory on %s\n", name); exit(1); } buffers = (buffer *)calloc(req.count, sizeof (*buffers)); if(!buffers) { fprintf(stderr, "Out of memory\n"); exit(1); } for(n_buffers = 0; n_buffers < (int)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"); buffers[n_buffers].length = buf.length; buffers[n_buffers].start = mmap (NULL /* start anywhere */, buf.length, PROT_READ | PROT_WRITE /* required */, MAP_SHARED /* recommended */, fd, buf.m.offset); if(MAP_FAILED == buffers[n_buffers].start) errno_exit ("mmap"); } } void Camera::init_read (unsigned int buffer_size) { /* buffers = calloc (1, sizeof (*buffers)); if (!buffers) { fprintf (stderr, "Out of memory\n"); exit (EXIT_FAILURE); } buffers[0].length = buffer_size; buffers[0].start = malloc (buffer_size); if (!buffers[0].start) { fprintf (stderr, "Out of memory\n"); exit (EXIT_FAILURE); } */ } void Camera::UnInit() { unsigned int i; switch(io) { case IO_METHOD_READ: free (buffers[0].start); break; case IO_METHOD_MMAP: for(i = 0; i < (unsigned int)n_buffers; ++i) if(-1 == munmap (buffers[i].start, buffers[i].length)) errno_exit ("munmap"); break; case IO_METHOD_USERPTR: for (i = 0; i < (unsigned int)n_buffers; ++i) free (buffers[i].start); break; } free (buffers); } void Camera::Start() { unsigned int i; enum v4l2_buf_type type; switch(io) { case IO_METHOD_READ: /* Nothing to do. */ break; case IO_METHOD_MMAP: for(i = 0; i < (unsigned int)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"); break; case IO_METHOD_USERPTR: for(i = 0; i < (unsigned int)n_buffers; ++i) { struct v4l2_buffer buf; CLEAR (buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_USERPTR; buf.index = i; buf.m.userptr = (unsigned long) buffers[i].start; buf.length = buffers[i].length; 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"); break; } } void Camera::Stop() { enum v4l2_buf_type type; switch(io) { case IO_METHOD_READ: /* Nothing to do. */ break; case IO_METHOD_MMAP: case IO_METHOD_USERPTR: type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if(-1 == xioctl (fd, VIDIOC_STREAMOFF, &type)) errno_exit ("VIDIOC_STREAMOFF"); break; } } unsigned char *Camera::Get() { struct v4l2_buffer buf; switch(io) { case IO_METHOD_READ: /* if (-1 == read (fd, buffers[0].start, buffers[0].length)) { switch (errno) { case EAGAIN: return 0; case EIO: default: errno_exit ("read"); } } process_image (buffers[0].start); */ break; case IO_METHOD_MMAP: CLEAR(buf); gettimeofday(&p1,NULL); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if(-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) { switch (errno) { case EAGAIN: return 0; case EIO: default: return 0; //errno_exit ("VIDIOC_DQBUF"); } } gettimeofday(&p2,NULL); assert(buf.index < (unsigned int)n_buffers); memcpy(data, (unsigned char *)buffers[buf.index].start, buffers[buf.index].length); gettimeofday(&p3,NULL); if(-1 == xioctl (fd, VIDIOC_QBUF, &buf)) return 0; //errno_exit ("VIDIOC_QBUF"); gettimeofday(&p4,NULL); this->timestamp = buf.timestamp; return data; break; case IO_METHOD_USERPTR: /* CLEAR (buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_USERPTR; if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) { switch (errno) { case EAGAIN: return 0; case EIO: default: errno_exit ("VIDIOC_DQBUF"); } } for (i = 0; i < n_buffers; ++i) if (buf.m.userptr == (unsigned long) buffers[i].start && buf.length == buffers[i].length) break; assert (i < n_buffers); process_image ((void *) buf.m.userptr); if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)) errno_exit ("VIDIOC_QBUF"); */ break; } return 0; } bool Camera::Update(unsigned int t, int timeout_ms) { bool grabbed = false; int grab_time_uS = 0; while (!grabbed) { if ((!grabbed) && (this->Get()!=0)) grabbed = true; if (!grabbed) { usleep(t); grab_time_uS+=(int)t; if (grab_time_uS > timeout_ms * 1000) { break; } } } return grabbed; } bool Camera::Update(Camera *c2, unsigned int t, int timeout_ms) { bool left_grabbed = false; bool right_grabbed = false; int grab_time_uS = 0; while (!(left_grabbed && right_grabbed)) { if ((!left_grabbed) && (this->Get()!=0)) left_grabbed = true; if ((!right_grabbed) && (c2->Get()!=0)) right_grabbed = true; if (!(left_grabbed && right_grabbed)) { usleep(t); grab_time_uS+=(int)t; if (grab_time_uS > timeout_ms * 1000) { break; } } } return left_grabbed & right_grabbed; } int Camera::minBrightness() { return mb; } int Camera::maxBrightness() { return Mb; } int Camera::defaultBrightness() { return db; } int Camera::minContrast() { return mc; } int Camera::maxContrast() { return Mc; } int Camera::defaultContrast() { return dc; } int Camera::minSaturation() { return ms; } int Camera::maxSaturation() { return Ms; } int Camera::defaultSaturation() { return ds; } int Camera::minHue() { return mh; } int Camera::maxHue() { return Mh; } int Camera::defaultHue() { return dh; } bool Camera::isHueAuto() { return ha; } int Camera::minSharpness() { return msh; } int Camera::maxSharpness() { return Msh; } int Camera::defaultSharpness() { return dsh; } int Camera::setBrightness(int v) { if(vMb) return -1; struct v4l2_control control; control.id = V4L2_CID_BRIGHTNESS; control.value = v; if(-1 == ioctl (fd, VIDIOC_S_CTRL, &control)) { perror("error setting brightness"); return -1; } return 1; } int Camera::setContrast(int v) { if(vMc) return -1; struct v4l2_control control; control.id = V4L2_CID_CONTRAST; control.value = v; if(-1 == ioctl (fd, VIDIOC_S_CTRL, &control)) { perror("error setting contrast"); return -1; } return 1; } int Camera::setSaturation(int v) { if(vMs) return -1; struct v4l2_control control; control.id = V4L2_CID_SATURATION; control.value = v; if(-1 == ioctl (fd, VIDIOC_S_CTRL, &control)) { perror("error setting saturation"); return -1; } return 1; } int Camera::setHue(int v) { if(vMh) return -1; struct v4l2_control control; control.id = V4L2_CID_HUE; control.value = v; if(-1 == ioctl (fd, VIDIOC_S_CTRL, &control)) { perror("error setting hue"); return -1; } return 1; } int Camera::setHueAuto(bool v) { if(vMh) return -1; struct v4l2_control control; control.id = V4L2_CID_HUE_AUTO; control.value = v; if(-1 == ioctl (fd, VIDIOC_S_CTRL, &control)) { perror("error setting hue auto"); return -1; } return 1; } int Camera::setSharpness(int v) { if(vMh) return -1; struct v4l2_control control; control.id = V4L2_CID_SHARPNESS; control.value = v; if(-1 == ioctl (fd, VIDIOC_S_CTRL, &control)) { perror("error setting sharpness"); return -1; } return 1; }