diff options
Diffstat (limited to 'kstars/kstars/indi/webcam/v4l2_base.cpp')
-rw-r--r-- | kstars/kstars/indi/webcam/v4l2_base.cpp | 1189 |
1 files changed, 1189 insertions, 0 deletions
diff --git a/kstars/kstars/indi/webcam/v4l2_base.cpp b/kstars/kstars/indi/webcam/v4l2_base.cpp new file mode 100644 index 00000000..26ec3d51 --- /dev/null +++ b/kstars/kstars/indi/webcam/v4l2_base.cpp @@ -0,0 +1,1189 @@ +/* + Copyright (C) 2005 by Jasem Mutlaq + + Based on V4L 2 Example + http://v4l2spec.bytesex.org/spec-single/v4l2.html#CAPTURE-EXAMPLE + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include <iostream> + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <assert.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <sys/mman.h> +#include <string.h> +#include <asm/types.h> /* for videodev2.h */ + +#include "ccvt.h" +#include "v4l2_base.h" +#include "../eventloop.h" +#include "../indidevapi.h" + +#define ERRMSGSIZ 1024 + +#define CLEAR(x) memset (&(x), 0, sizeof (x)) + +using namespace std; + +V4L2_Base::V4L2_Base() +{ + frameRate=10; + selectCallBackID = -1; + dropFrame = false; + + xmax = xmin = 160; + ymax = ymin = 120; + + io = IO_METHOD_MMAP; + fd = -1; + buffers = NULL; + n_buffers = 0; + + YBuf = NULL; + UBuf = NULL; + VBuf = NULL; + colorBuffer = NULL; + rgb24_buffer = NULL; + callback = NULL; + +} + +V4L2_Base::~V4L2_Base() +{ + + delete (YBuf); + delete (UBuf); + delete (VBuf); + delete (colorBuffer); + delete (rgb24_buffer); + +} + +int V4L2_Base::xioctl(int fd, int request, void *arg) +{ + int r; + + do r = ioctl (fd, request, arg); + while (-1 == r && EINTR == errno); + + return r; +} + +int V4L2_Base::errno_exit(const char *s, char *errmsg) +{ + fprintf (stderr, "%s error %d, %s\n", + s, errno, strerror (errno)); + + snprintf(errmsg, ERRMSGSIZ, "%s error %d, %s\n", s, errno, strerror (errno)); + + return -1; +} + +int V4L2_Base::connectCam(const char * devpath, char *errmsg , int pixelFormat , int width , int height ) +{ + frameRate=10; + selectCallBackID = -1; + dropFrame = false; + + if (open_device (devpath, errmsg) < 0) + return -1; + + if (init_device(errmsg, pixelFormat, width, height) < 0) + return -1; + + cerr << "V4L 2 - All successful, returning\n"; + return fd; +} + +void V4L2_Base::disconnectCam() +{ + char errmsg[ERRMSGSIZ]; + delete YBuf; + delete UBuf; + delete VBuf; + YBuf = UBuf = VBuf = NULL; + + if (selectCallBackID != -1) + rmCallback(selectCallBackID); + + stop_capturing (errmsg); + + uninit_device (errmsg); + + close_device (); + + fprintf(stderr, "Disconnect cam\n"); +} + +int V4L2_Base::read_frame(char *errmsg) +{ + struct v4l2_buffer buf; + unsigned int i; + //cerr << "in read Frame" << endl; + + switch (io) { + case IO_METHOD_READ: + if (-1 == read (fd, buffers[0].start, buffers[0].length)) { + switch (errno) { + case EAGAIN: + return 0; + + case EIO: + /* Could ignore EIO, see spec. */ + + /* fall through */ + + default: + return errno_exit ("read", errmsg); + } + } + + //process_image (buffers[0].start); + + break; + + case IO_METHOD_MMAP: + 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 0; + + case EIO: + /* Could ignore EIO, see spec. */ + + /* fall through */ + + default: + return errno_exit ("VIDIOC_DQBUF", errmsg); + } + } + + assert (buf.index < n_buffers); + + switch (fmt.fmt.pix.pixelformat) + { + case V4L2_PIX_FMT_YUV420: + memcpy(YBuf,((unsigned char *) buffers[buf.index].start), fmt.fmt.pix.width * fmt.fmt.pix.height); + memcpy(UBuf,((unsigned char *) buffers[buf.index].start) + fmt.fmt.pix.width * fmt.fmt.pix.height, (fmt.fmt.pix.width/2) * (fmt.fmt.pix.height/2)); + memcpy(VBuf,((unsigned char *) buffers[buf.index].start) + fmt.fmt.pix.width * fmt.fmt.pix.height + (fmt.fmt.pix.width/2) * (fmt.fmt.pix.height/2), (fmt.fmt.pix.width/2) * (fmt.fmt.pix.width/2)); + break; + + case V4L2_PIX_FMT_YUYV: + ccvt_yuyv_420p( fmt.fmt.pix.width , fmt.fmt.pix.height, buffers[buf.index].start, YBuf, UBuf, VBuf); + break; + + case V4L2_PIX_FMT_RGB24: + RGB2YUV(fmt.fmt.pix.width, fmt.fmt.pix.height, buffers[buf.index].start, YBuf, UBuf, VBuf, 0); + break; + + case V4L2_PIX_FMT_SBGGR8: + bayer2rgb24(rgb24_buffer, ((unsigned char *) buffers[buf.index].start), fmt.fmt.pix.width, fmt.fmt.pix.height); + break; + } + + if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)) + return errno_exit ("VIDIOC_QBUF", errmsg); + + if (dropFrame) + { + dropFrame = false; + return 0; + } + + /* Call provided callback function if any */ + if (callback) + (*callback)(uptr); + + 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: + /* Could ignore EIO, see spec. */ + + /* fall through */ + + default: + errno_exit ("VIDIOC_DQBUF", errmsg); + } + } + + 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", errmsg); + + break; + } + + return 0; +} + +int V4L2_Base::stop_capturing(char *errmsg) +{ + 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; + + IERmCallback(selectCallBackID); + selectCallBackID = -1; + + dropFrame = true; + + if (-1 == xioctl (fd, VIDIOC_STREAMOFF, &type)) + return errno_exit ("VIDIOC_STREAMOFF", errmsg); + + + + break; + } + + return 0; +} + +int V4L2_Base::start_capturing(char * errmsg) +{ + 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 < 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)) + return errno_exit ("VIDIOC_QBUF", errmsg); + + } + + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (-1 == xioctl (fd, VIDIOC_STREAMON, &type)) + return errno_exit ("VIDIOC_STREAMON", errmsg); + + + + selectCallBackID = IEAddCallback(fd, newFrame, this); + + break; + + case IO_METHOD_USERPTR: + for (i = 0; i < n_buffers; ++i) { + struct v4l2_buffer buf; + + CLEAR (buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_USERPTR; + buf.m.userptr = (unsigned long) buffers[i].start; + buf.length = buffers[i].length; + + if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)) + return errno_exit ("VIDIOC_QBUF", errmsg); + } + + + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (-1 == xioctl (fd, VIDIOC_STREAMON, &type)) + return errno_exit ("VIDIOC_STREAMON", errmsg); + + break; + } + + return 0; + +} + +void V4L2_Base::newFrame(int /*fd*/, void *p) +{ + char errmsg[ERRMSGSIZ]; + + ( (V4L2_Base *) (p))->read_frame(errmsg); + +} + +int V4L2_Base::uninit_device(char *errmsg) +{ + + switch (io) { + case IO_METHOD_READ: + free (buffers[0].start); + break; + + case IO_METHOD_MMAP: + for (unsigned int i = 0; i < n_buffers; ++i) + if (-1 == munmap (buffers[i].start, buffers[i].length)) + return errno_exit ("munmap", errmsg); + break; + + case IO_METHOD_USERPTR: + for (unsigned int i = 0; i < n_buffers; ++i) + free (buffers[i].start); + break; + } + + free (buffers); + + return 0; +} + +void V4L2_Base::init_read(unsigned int buffer_size) +{ + buffers = (buffer *) 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); + } +} + +int V4L2_Base::init_mmap(char *errmsg) +{ + 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); + snprintf(errmsg, ERRMSGSIZ, "%s does not support " + "memory mapping\n", dev_name); + return -1; + } else { + return errno_exit ("VIDIOC_REQBUFS", errmsg); + } + } + + if (req.count < 2) { + fprintf (stderr, "Insufficient buffer memory on %s\n", + dev_name); + snprintf(errmsg, ERRMSGSIZ, "Insufficient buffer memory on %s\n", + dev_name); + return -1; + } + + buffers = (buffer *) calloc (req.count, sizeof (*buffers)); + + if (!buffers) + { + fprintf (stderr, "buffers. Out of memory\n"); + strncpy(errmsg, "buffers. Out of memory\n", ERRMSGSIZ); + return -1; + } + + 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)) + return errno_exit ("VIDIOC_QUERYBUF", errmsg); + + 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) + return errno_exit ("mmap", errmsg); + } + + return 0; +} + +void V4L2_Base::init_userp(unsigned int buffer_size) +{ + struct v4l2_requestbuffers req; + char errmsg[ERRMSGSIZ]; + + 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); + exit (EXIT_FAILURE); + } else { + errno_exit ("VIDIOC_REQBUFS", errmsg); + } + } + + buffers = (buffer *) 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 = malloc (buffer_size); + + if (!buffers[n_buffers].start) { + fprintf (stderr, "Out of memory\n"); + exit (EXIT_FAILURE); + } + } +} + +int V4L2_Base::init_device(char *errmsg, int pixelFormat , int width, int height) +{ + unsigned int min; + + if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) + { + if (EINVAL == errno) { + fprintf (stderr, "%s is no V4L2 device\n", + dev_name); + snprintf(errmsg, ERRMSGSIZ, "%s is no V4L2 device\n", dev_name); + return -1; + } else { + return errno_exit ("VIDIOC_QUERYCAP", errmsg); + } + } + + if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) + { + fprintf (stderr, "%s is no video capture device\n", + dev_name); + snprintf(errmsg, ERRMSGSIZ, "%s is no video capture device\n", dev_name); + return -1; + } + + switch (io) + { + case IO_METHOD_READ: + if (!(cap.capabilities & V4L2_CAP_READWRITE)) { + fprintf (stderr, "%s does not support read i/o\n", + dev_name); + snprintf(errmsg, ERRMSGSIZ, "%s does not support read i/o\n", + dev_name); + return -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", + dev_name); + snprintf(errmsg, ERRMSGSIZ, "%s does not support streaming i/o\n", + dev_name); + return -1; + } + + break; + } + + /* Select video input, video standard and tune here. */ + + cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (-1 == xioctl (fd, VIDIOC_CROPCAP, &cropcap)) { + /* Errors ignored. */ + } + + 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; + } + } + + CLEAR (fmt); + + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = width; + fmt.fmt.pix.height = height; + fmt.fmt.pix.pixelformat = pixelFormat; + //fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + + if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) + return errno_exit ("VIDIOC_S_FMT", errmsg); + + /* 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; + + /* Let's get the actual size */ + CLEAR(fmt); + + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (-1 == xioctl (fd, VIDIOC_G_FMT, &fmt)) + return errno_exit ("VIDIOC_G_FMT", errmsg); + + cerr << "width: " << fmt.fmt.pix.width << " - height: " << fmt.fmt.pix.height << endl; + + switch (pixelFormat) + { + case V4L2_PIX_FMT_YUV420: + cerr << "pixel format: V4L2_PIX_FMT_YUV420" << endl; + break; + + case V4L2_PIX_FMT_YUYV: + cerr << "pixel format: V4L2_PIX_FMT_YUYV" << endl; + break; + + case V4L2_PIX_FMT_RGB24: + cerr << "pixel format: V4L2_PIX_FMT_RGB24" << endl; + break; + + case V4L2_PIX_FMT_SBGGR8: + cerr << "pixel format: V4L2_PIX_FMT_SBGGR8" << endl; + break; + + } + + findMinMax(); + + allocBuffers(); + + switch (io) + { + case IO_METHOD_READ: + init_read (fmt.fmt.pix.sizeimage); + break; + + case IO_METHOD_MMAP: + return init_mmap(errmsg); + break; + + case IO_METHOD_USERPTR: + init_userp (fmt.fmt.pix.sizeimage); + break; + } + + return 0; +} + +void V4L2_Base::close_device(void) +{ + char errmsg[ERRMSGSIZ]; + + if (-1 == close (fd)) + errno_exit ("close", errmsg); + + fd = -1; +} + +int V4L2_Base::open_device(const char *devpath, char *errmsg) +{ + struct stat st; + + strncpy(dev_name, devpath, 64); + + if (-1 == stat (dev_name, &st)) { + fprintf (stderr, "Cannot identify '%s': %d, %s\n", + dev_name, errno, strerror (errno)); + snprintf(errmsg, ERRMSGSIZ, "Cannot identify '%s': %d, %s\n", + dev_name, errno, strerror (errno)); + return -1; + } + + if (!S_ISCHR (st.st_mode)) + { + fprintf (stderr, "%s is no device\n", dev_name); + snprintf(errmsg, ERRMSGSIZ, "%s is no device\n", dev_name); + return -1; + } + + fd = open (dev_name, O_RDWR /* required */ | O_NONBLOCK, 0); + + if (-1 == fd) + { + fprintf (stderr, "Cannot open '%s': %d, %s\n", + dev_name, errno, strerror (errno)); + snprintf(errmsg, ERRMSGSIZ, "Cannot open '%s': %d, %s\n", + dev_name, errno, strerror (errno)); + return -1; + } + + return 0; +} + + + +int V4L2_Base::getWidth() +{ + return fmt.fmt.pix.width; +} + +int V4L2_Base::getHeight() +{ + return fmt.fmt.pix.height; +} + +void V4L2_Base::setFPS(int fps) +{ + frameRate = 15;//fps; +} + +int V4L2_Base::getFPS() +{ + return 15; +} + +char * V4L2_Base::getDeviceName() +{ + return ((char *) cap.card); +} + +void V4L2_Base::allocBuffers() +{ + delete (YBuf); YBuf = NULL; + delete (UBuf); UBuf = NULL; + delete (VBuf); VBuf = NULL; + delete (colorBuffer); colorBuffer = NULL; + delete (rgb24_buffer); rgb24_buffer = NULL; + + YBuf= new unsigned char[ fmt.fmt.pix.width * fmt.fmt.pix.height]; + UBuf= new unsigned char[ fmt.fmt.pix.width * fmt.fmt.pix.height]; + VBuf= new unsigned char[ fmt.fmt.pix.width * fmt.fmt.pix.height]; + colorBuffer = new unsigned char[fmt.fmt.pix.width * fmt.fmt.pix.height * 4]; + + if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_SBGGR8) + rgb24_buffer = new unsigned char[fmt.fmt.pix.width * fmt.fmt.pix.height * 3]; +} + +void V4L2_Base::getMaxMinSize(int & x_max, int & y_max, int & x_min, int & y_min) +{ + x_max = xmax; y_max = ymax; x_min = xmin; y_min = ymin; +} + +int V4L2_Base::setSize(int x, int y) +{ + char errmsg[ERRMSGSIZ]; + int oldW, oldH; + + oldW = fmt.fmt.pix.width; + oldH = fmt.fmt.pix.height; + + fmt.fmt.pix.width = x; + fmt.fmt.pix.height = y; + + if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) + { + fmt.fmt.pix.width = oldW; + fmt.fmt.pix.height = oldH; + return errno_exit ("VIDIOC_S_FMT", errmsg); + } + + /* PWC bug? It seems that setting the "wrong" width and height will mess something in the driver. + Only 160x120, 320x280, and 640x480 are accepted. If I try to set it for example to 300x200, it wii + get set to 320x280, which is fine, but then the video information is messed up for some reason. */ + xioctl (fd, VIDIOC_S_FMT, &fmt); + + + allocBuffers(); + + return 0; +} + +void V4L2_Base::setContrast(int val) +{ + /*picture_.contrast=val; + updatePictureSettings();*/ +} + +int V4L2_Base::getContrast() +{ + return 255;//picture_.contrast; +} + +void V4L2_Base::setBrightness(int val) +{ + /*picture_.brightness=val; + updatePictureSettings();*/ +} + +int V4L2_Base::getBrightness() +{ + return 255;//picture_.brightness; +} + +void V4L2_Base::setColor(int val) +{ + /*picture_.colour=val; + updatePictureSettings();*/ +} + +int V4L2_Base::getColor() +{ + return 255; //picture_.colour; +} + +void V4L2_Base::setHue(int val) +{ + /*picture_.hue=val; + updatePictureSettings();*/ +} + +int V4L2_Base::getHue() +{ + return 255;//picture_.hue; +} + +void V4L2_Base::setWhiteness(int val) +{ + /*picture_.whiteness=val; + updatePictureSettings();*/ +} + +int V4L2_Base::getWhiteness() +{ + return 255;//picture_.whiteness; +} + +void V4L2_Base::setPictureSettings() +{ + /*if (ioctl(device_, VIDIOCSPICT, &picture_) ) { + cerr << "updatePictureSettings" << endl; + } + ioctl(device_, VIDIOCGPICT, &picture_);*/ +} + +void V4L2_Base::getPictureSettings() +{ + /*if (ioctl(device_, VIDIOCGPICT, &picture_) ) + { + cerr << "refreshPictureSettings" << endl; + }*/ +} + +unsigned char * V4L2_Base::getY() +{ + if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_SBGGR8) + RGB2YUV(fmt.fmt.pix.width, fmt.fmt.pix.height, rgb24_buffer, YBuf, UBuf, VBuf, 0); + + return YBuf; +} + +unsigned char * V4L2_Base::getU() +{ + return UBuf; +} + +unsigned char * V4L2_Base::getV() +{ + return VBuf; +} + +unsigned char * V4L2_Base::getColorBuffer() +{ + //cerr << "in get color buffer " << endl; + + switch (fmt.fmt.pix.pixelformat) + { + case V4L2_PIX_FMT_YUV420: + ccvt_420p_bgr32(fmt.fmt.pix.width, fmt.fmt.pix.height, + buffers[0].start, (void*)colorBuffer); + break; + + case V4L2_PIX_FMT_YUYV: + ccvt_yuyv_bgr32(fmt.fmt.pix.width, fmt.fmt.pix.height, + buffers[0].start, (void*)colorBuffer); + break; + + case V4L2_PIX_FMT_RGB24: + ccvt_rgb24_bgr32(fmt.fmt.pix.width, fmt.fmt.pix.height, + buffers[0].start, (void*)colorBuffer); + break; + + case V4L2_PIX_FMT_SBGGR8: + ccvt_rgb24_bgr32(fmt.fmt.pix.width, fmt.fmt.pix.height, + rgb24_buffer, (void*)colorBuffer); + break; + + default: + break; + } + + return colorBuffer; +} + +void V4L2_Base::registerCallback(WPF *fp, void *ud) +{ + callback = fp; + uptr = ud; +} + +void V4L2_Base::findMinMax() +{ + char errmsg[ERRMSGSIZ]; + struct v4l2_format tryfmt; + CLEAR(tryfmt); + + xmin = xmax = fmt.fmt.pix.width; + ymin = ymax = fmt.fmt.pix.height; + + tryfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + tryfmt.fmt.pix.width = 10; + tryfmt.fmt.pix.height = 10; + tryfmt.fmt.pix.pixelformat = fmt.fmt.pix.pixelformat; + tryfmt.fmt.pix.field = fmt.fmt.pix.field; + + if (-1 == xioctl (fd, VIDIOC_TRY_FMT, &tryfmt)) + { + errno_exit ("VIDIOC_TRY_FMT 1", errmsg); + return; + } + + xmin = tryfmt.fmt.pix.width; + ymin = tryfmt.fmt.pix.height; + + tryfmt.fmt.pix.width = 1600; + tryfmt.fmt.pix.height = 1200; + + if (-1 == xioctl (fd, VIDIOC_TRY_FMT, &tryfmt)) + { + errno_exit ("VIDIOC_TRY_FMT 2", errmsg); + return; + } + + xmax = tryfmt.fmt.pix.width; + ymax = tryfmt.fmt.pix.height; + + cerr << "Min X: " << xmin << " - Max X: " << xmax << " - Min Y: " << ymin << " - Max Y: " << ymax << endl; +} + +void V4L2_Base::enumerate_ctrl (void) +{ + char errmsg[ERRMSGSIZ]; + CLEAR(queryctrl); + + for (queryctrl.id = V4L2_CID_BASE; queryctrl.id < V4L2_CID_LASTP1; queryctrl.id++) + { + if (0 == xioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) + { + cerr << "Control " << queryctrl.name << endl; + + if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) + continue; + + cerr << "Control " << queryctrl.name << endl; + + if (queryctrl.type == V4L2_CTRL_TYPE_MENU) + enumerate_menu (); + } else + { + if (errno == EINVAL) + continue; + + errno_exit("VIDIOC_QUERYCTRL", errmsg); + return; + } + } + + for (queryctrl.id = V4L2_CID_PRIVATE_BASE; ; queryctrl.id++) + { + if (0 == xioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) + { + cerr << "Private Control " << queryctrl.name << endl; + + if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) + continue; + + if (queryctrl.type == V4L2_CTRL_TYPE_MENU) + enumerate_menu (); + } else { + if (errno == EINVAL) + break; + + errno_exit ("VIDIOC_QUERYCTRL", errmsg); + return; + } + + } + +} + +void V4L2_Base::enumerate_menu (void) +{ + char errmsg[ERRMSGSIZ]; + cerr << " Menu items:" << endl; + + CLEAR(querymenu); + querymenu.id = queryctrl.id; + + for (querymenu.index = queryctrl.minimum; querymenu.index <= queryctrl.maximum; querymenu.index++) + { + if (0 == xioctl (fd, VIDIOC_QUERYMENU, &querymenu)) + { + cerr << " " << querymenu.name << endl; + } else + { + errno_exit("VIDIOC_QUERYMENU", errmsg); + return; + } + } +} + +int V4L2_Base::query_ctrl(unsigned int ctrl_id, double & ctrl_min, double & ctrl_max, double & ctrl_step, double & ctrl_value, char *errmsg) +{ + + struct v4l2_control control; + + CLEAR(queryctrl); + + queryctrl.id = ctrl_id; + + if (-1 == ioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) + { + if (errno != EINVAL) + return errno_exit ("VIDIOC_QUERYCTRL", errmsg); + + else + { + cerr << "#" << ctrl_id << " is not supported" << endl; + snprintf(errmsg, ERRMSGSIZ, "# %d is not supported", ctrl_id); + return -1; + } + } else if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) + { + cerr << "#" << ctrl_id << " is disabled" << endl; + snprintf(errmsg, ERRMSGSIZ, "# %d is disabled", ctrl_id); + return -1; + } + + ctrl_min = queryctrl.minimum; + ctrl_max = queryctrl.maximum; + ctrl_step = queryctrl.step; + ctrl_value = queryctrl.default_value; + + /* Get current value */ + CLEAR(control); + control.id = ctrl_id; + + if (0 == xioctl(fd, VIDIOC_G_CTRL, &control)) + ctrl_value = control.value; + + cerr << queryctrl.name << " -- min: " << ctrl_min << " max: " << ctrl_max << " step: " << ctrl_step << " value: " << ctrl_value << endl; + + return 0; + +} + +int V4L2_Base::queryINTControls(INumberVectorProperty *nvp) +{ + struct v4l2_control control; + char errmsg[ERRMSGSIZ]; + CLEAR(queryctrl); + INumber *numbers = NULL; + unsigned int *num_ctrls = NULL; + int nnum=0; + + for (queryctrl.id = V4L2_CID_BASE; queryctrl.id < V4L2_CID_LASTP1; queryctrl.id++) + { + if (0 == ioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) + { + if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) + { + cerr << queryctrl.name << " is disabled." << endl; + continue; + } + + if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER) + { + numbers = (numbers == NULL) ? (INumber *) malloc (sizeof(INumber)) : + (INumber *) realloc (numbers, (nnum+1) * sizeof (INumber)); + + num_ctrls = (num_ctrls == NULL) ? (unsigned int *) malloc (sizeof (unsigned int)) : + (unsigned int *) realloc (num_ctrls, (nnum+1) * sizeof (unsigned int)); + + strncpy(numbers[nnum].name, ((char *) queryctrl.name) , MAXINDINAME); + strncpy(numbers[nnum].label, ((char *) queryctrl.name), MAXINDILABEL); + strncpy(numbers[nnum].format, "%0.f", MAXINDIFORMAT); + numbers[nnum].min = queryctrl.minimum; + numbers[nnum].max = queryctrl.maximum; + numbers[nnum].step = queryctrl.step; + numbers[nnum].value = queryctrl.default_value; + + /* Get current value if possible */ + CLEAR(control); + control.id = queryctrl.id; + if (0 == xioctl(fd, VIDIOC_G_CTRL, &control)) + numbers[nnum].value = control.value; + + /* Store ID info in INumber. This is the first time ever I make use of aux0!! */ + num_ctrls[nnum] = queryctrl.id; + + cerr << queryctrl.name << " -- min: " << queryctrl.minimum << " max: " << queryctrl.maximum << " step: " << queryctrl.step << " value: " << numbers[nnum].value << endl; + + nnum++; + + } + } else if (errno != EINVAL) + return errno_exit ("VIDIOC_QUERYCTRL", errmsg); + + } + + for (queryctrl.id = V4L2_CID_PRIVATE_BASE; ; queryctrl.id++) + { + if (0 == ioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) + { + if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) + { + cerr << queryctrl.name << " is disabled." << endl; + continue; + } + + if (queryctrl.type == V4L2_CTRL_TYPE_INTEGER) + { + numbers = (numbers == NULL) ? (INumber *) malloc (sizeof(INumber)) : + (INumber *) realloc (numbers, (nnum+1) * sizeof (INumber)); + + num_ctrls = (num_ctrls == NULL) ? (unsigned int *) malloc (sizeof (unsigned int)) : + (unsigned int *) realloc (num_ctrls, (nnum+1) * sizeof (unsigned int)); + + strncpy(numbers[nnum].name, ((char *) queryctrl.name) , MAXINDINAME); + strncpy(numbers[nnum].label, ((char *) queryctrl.name), MAXINDILABEL); + strncpy(numbers[nnum].format, "%0.f", MAXINDIFORMAT); + numbers[nnum].min = queryctrl.minimum; + numbers[nnum].max = queryctrl.maximum; + numbers[nnum].step = queryctrl.step; + numbers[nnum].value = queryctrl.default_value; + + /* Get current value if possible */ + CLEAR(control); + control.id = queryctrl.id; + if (0 == xioctl(fd, VIDIOC_G_CTRL, &control)) + numbers[nnum].value = control.value; + + /* Store ID info in INumber. This is the first time ever I make use of aux0!! */ + num_ctrls[nnum] = queryctrl.id; + + nnum++; + + } + } + else break; + } + + /* Store numbers in aux0 */ + for (int i=0; i < nnum; i++) + numbers[i].aux0 = &num_ctrls[i]; + + nvp->np = numbers; + nvp->nnp = nnum; + + return nnum; + +} + +int V4L2_Base::setINTControl(unsigned int ctrl_id, double new_value, char *errmsg) +{ + struct v4l2_control control; + + CLEAR(control); + + cerr << "The id is " << ctrl_id << " new value is " << new_value << endl; + + control.id = ctrl_id; + control.value = (int) new_value; + + if (-1 == xioctl(fd, VIDIOC_S_CTRL, &control)) + return errno_exit ("VIDIOC_S_CTRL", errmsg); + + return 0; +} + |