--- modules/highgui/src/cap_v4l.cpp 2010-06-01 15:17:09.992987951 +0100 +++ trisk/cap_v4l.cpp 2010-06-02 09:38:57.335969021 +0100 @@ -154,6 +154,14 @@ - USE_TEMP_BUFFER fixes the main problem (improper buffer management) and prevents bad images in the first place +11th patch: May 9th, 2010, Trisk (ticket #336 - improvements on v4l capture) +* Made V4L1 optional +* Fixed V4L2 cvInitImageHeader w/h parameters +* Fixed V4L2 mmap buffer use to remove memcpy workaround +* Fixed V4L2 buffer selection when converting +* Added support for OpenSolaris? V4L2 +* Added support for reading without polling +* Added support for V4L2_CAP_READWRITE make & enjoy! @@ -202,7 +210,7 @@ #include "precomp.hpp" -#if !defined WIN32 && defined HAVE_CAMV4L +#if !defined WIN32 && (defined HAVE_CAMV4L || defined HAVE_CAMV4L2) #define CLEAR(x) memset (&(x), 0, sizeof (x)) @@ -214,17 +222,24 @@ #include #include +#ifdef HAVE_CAMV4L #include +#endif #include #include -#include /* for videodev2.h */ #include #include #include #ifdef HAVE_CAMV4L2 +#if defined __linux || defined __linux__ +#include #include +#else +#include +#include +#endif #endif /* Defaults - If your board can do better, set it here. Set for the most common type inputs. */ @@ -255,8 +270,6 @@ size_t length; }; -static unsigned int n_buffers = 0; - /* Additional V4L2 pixelformats support for Sonix SN9C10x base webcams */ #ifndef V4L2_PIX_FMT_SBGGR8 #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B','A','8','1') /* 8 BGBG.. GRGR.. */ @@ -286,18 +299,25 @@ int deviceHandle; int bufferIndex; int FirstCapture; + bool devicePollable; + +#ifdef HAVE_CAMV4L + struct video_capability capability; struct video_window captureWindow; struct video_picture imageProperties; struct video_mbuf memoryBuffer; struct video_mmap *mmaps; char *memoryMap; +#endif /* HAVE_CAMV4L */ + IplImage frame; #ifdef HAVE_CAMV4L2 /* V4L2 variables */ buffer buffers[MAX_V4L_BUFFERS + 1]; + unsigned int n_buffers; struct v4l2_capability cap; struct v4l2_input inp; struct v4l2_format form; @@ -391,6 +411,7 @@ }; /* End icvInitCapture_V4L */ +#ifdef HAVE_CAMV4L static int try_palette(int fd, struct video_picture *cam_pic, @@ -407,6 +428,7 @@ return 1; return 0; } +#endif /* HAVE_CAMV4L */ #ifdef HAVE_CAMV4L2 @@ -431,8 +453,9 @@ } #endif /* HAVE_CAMV4L2 */ +#ifdef HAVE_CAMV4L -static int try_init_v4l(CvCaptureCAM_V4L* capture, char *deviceName) +static int try_init_v4l(CvCaptureCAM_V4L* capture, const char *deviceName) { // if detect = -1 then unable to open device @@ -474,9 +497,10 @@ } +#endif /* HAVE_CAMV4L */ #ifdef HAVE_CAMV4L2 -static int try_init_v4l2(CvCaptureCAM_V4L* capture, char *deviceName) +static int try_init_v4l2(CvCaptureCAM_V4L* capture, const char *deviceName) { // if detect = -1 then unable to open device @@ -491,7 +515,6 @@ capture->deviceHandle = open (deviceName, O_RDWR /* required */ | O_NONBLOCK, 0); - if (capture->deviceHandle == 0) { detect = -1; @@ -510,6 +533,8 @@ } else { + +#ifdef HAVE_CAMV4L CLEAR (capture->capability); capture->capability.type = capture->cap.capabilities; @@ -518,6 +543,10 @@ { detect = 1; } +#else + detect = 1; +#endif /* HAVE_CAMV4L */ + } } @@ -544,10 +573,6 @@ else #ifdef HAVE_JPEG -#ifdef __USE_GNU - /* support for MJPEG is only available with libjpeg and gcc, - because it's use libjepg and fmemopen() - */ if (try_palette_v4l2(capture, V4L2_PIX_FMT_MJPEG) == 0 || try_palette_v4l2(capture, V4L2_PIX_FMT_JPEG) == 0) { @@ -555,7 +580,6 @@ } else #endif -#endif if (try_palette_v4l2(capture, V4L2_PIX_FMT_YUYV) == 0) { @@ -590,6 +614,7 @@ } #endif /* HAVE_CAMV4L2 */ +#ifdef HAVE_CAMV4L static int autosetup_capture_mode_v4l(CvCaptureCAM_V4L* capture) { @@ -624,6 +649,7 @@ } +#endif /* HAVE_CAMV4L */ #ifdef HAVE_CAMV4L2 static void v4l2_scan_controls_enumerate_menu(CvCaptureCAM_V4L* capture) @@ -648,7 +674,7 @@ static void v4l2_scan_controls(CvCaptureCAM_V4L* capture) { - __u32 ctrl_id; + unsigned int ctrl_id; for (ctrl_id = V4L2_CID_BASE; ctrl_id < V4L2_CID_LASTP1; @@ -794,7 +820,111 @@ } -static int _capture_V4L2 (CvCaptureCAM_V4L *capture, char *deviceName) +static int init_read_v4l2 (CvCaptureCAM_V4L *capture) +{ + unsigned int size = capture->form.fmt.pix.sizeimage; + + capture->buffers[0].start = malloc( size ); + if (!capture->buffers[0].start) { + perror ("malloc"); + return -1; + } + capture->buffers[0].length = size; + + capture->buffers[MAX_V4L_BUFFERS].start = malloc( size ); + if (!capture->buffers[MAX_V4L_BUFFERS].start) { + perror ("malloc"); + return -1; + } + capture->buffers[MAX_V4L_BUFFERS].length = size; + + capture->n_buffers = 0; + + return 0; +} + +static int init_mmap_v4l2 (CvCaptureCAM_V4L *capture, const char *deviceName) +{ + unsigned int buffer_number = DEFAULT_V4L_BUFFERS; + + CLEAR (capture->req); + + try_again: + + capture->req.count = buffer_number; + capture->req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + capture->req.memory = V4L2_MEMORY_MMAP; + + if (-1 == xioctl (capture->deviceHandle, VIDIOC_REQBUFS, &capture->req)) + { + if (EINVAL == errno) + { + fprintf (stderr, "%s does not support memory mapping\n", deviceName); + } else { + perror ("VIDIOC_REQBUFS"); + } + return -1; + } + + if (capture->req.count < buffer_number) + { + if (buffer_number == 1) + { + fprintf (stderr, "Insufficient buffer memory on %s\n", deviceName); + + return -1; + } else { + buffer_number--; + fprintf (stderr, "Insufficient buffer memory on %s -- decreaseing buffers\n", deviceName); + + goto try_again; + } + } + + for (unsigned int n_buffers = 0; n_buffers < capture->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 (capture->deviceHandle, VIDIOC_QUERYBUF, &buf)) { + perror ("VIDIOC_QUERYBUF"); + return -1; + } + + capture->buffers[n_buffers].length = buf.length; + capture->buffers[n_buffers].start = + mmap (NULL /* start anywhere */, + buf.length, + PROT_READ | PROT_WRITE /* required */, + MAP_SHARED /* recommended */, + capture->deviceHandle, buf.m.offset); + + if (MAP_FAILED == capture->buffers[n_buffers].start) { + perror ("mmap"); + return -1; + } + } + + unsigned int size = capture->form.fmt.pix.sizeimage; + + capture->buffers[MAX_V4L_BUFFERS].start = malloc( size ); + if (!capture->buffers[MAX_V4L_BUFFERS].start) { + perror ("malloc"); + return -1; + } + capture->buffers[MAX_V4L_BUFFERS].length = size; + + capture->n_buffers = capture->req.count; + + return 0; +} + +static int _capture_V4L2 (CvCaptureCAM_V4L *capture, const char *deviceName) { int detect_v4l2 = 0; @@ -870,10 +1000,6 @@ return -1; } - if (V4L2_SUPPORT == 0) - { - } - if (autosetup_capture_mode_v4l2(capture) == -1) return -1; @@ -892,90 +1018,28 @@ if (capture->form.fmt.pix.sizeimage < min) capture->form.fmt.pix.sizeimage = min; - CLEAR (capture->req); - - unsigned int buffer_number = DEFAULT_V4L_BUFFERS; - - try_again: - - capture->req.count = buffer_number; - capture->req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - capture->req.memory = V4L2_MEMORY_MMAP; - - if (-1 == xioctl (capture->deviceHandle, VIDIOC_REQBUFS, &capture->req)) - { - if (EINVAL == errno) - { - fprintf (stderr, "%s does not support memory mapping\n", deviceName); - } else { - perror ("VIDIOC_REQBUFS"); - } - /* free capture, and returns an error code */ - icvCloseCAM_V4L (capture); - return -1; - } - - if (capture->req.count < buffer_number) - { - if (buffer_number == 1) - { - fprintf (stderr, "Insufficient buffer memory on %s\n", deviceName); - - /* free capture, and returns an error code */ - icvCloseCAM_V4L (capture); - return -1; - } else { - buffer_number--; - fprintf (stderr, "Insufficient buffer memory on %s -- decreaseing buffers\n", deviceName); - - goto try_again; - } - } - - for (n_buffers = 0; n_buffers < capture->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 (capture->deviceHandle, VIDIOC_QUERYBUF, &buf)) { - perror ("VIDIOC_QUERYBUF"); - - /* free capture, and returns an error code */ - icvCloseCAM_V4L (capture); + if (capture->cap.capabilities & V4L2_CAP_STREAMING) { + if (-1 == init_mmap_v4l2(capture, deviceName)) { + icvCloseCAM_V4L(capture); return -1; } - - capture->buffers[n_buffers].length = buf.length; - capture->buffers[n_buffers].start = - mmap (NULL /* start anywhere */, - buf.length, - PROT_READ | PROT_WRITE /* required */, - MAP_SHARED /* recommended */, - capture->deviceHandle, buf.m.offset); - - if (MAP_FAILED == capture->buffers[n_buffers].start) { - perror ("mmap"); - - /* free capture, and returns an error code */ - icvCloseCAM_V4L (capture); + } else { + if (capture->cap.capabilities & V4L2_CAP_READWRITE) { + if (-1 == init_read_v4l2(capture)) { + icvCloseCAM_V4L(capture); + return -1; + } + } else { + fprintf( stderr, "HIGHGUI ERROR: V4L2: device %s does not support either memory mapped or read/write I/O.\n",deviceName); + icvCloseCAM_V4L(capture); return -1; } - - if (n_buffers == 0) { - capture->buffers[MAX_V4L_BUFFERS].start = malloc( buf.length ); - capture->buffers[MAX_V4L_BUFFERS].length = buf.length; - } } /* Set up Image data */ cvInitImageHeader( &capture->frame, - cvSize( capture->captureWindow.width, - capture->captureWindow.height ), + cvSize( capture->form.fmt.pix.width, + capture->form.fmt.pix.height ), IPL_DEPTH_8U, 3, IPL_ORIGIN_TL, 4 ); /* Allocate space for RGBA data */ capture->frame.imageData = (char *)cvAlloc(capture->frame.imageSize); @@ -984,8 +1048,9 @@ }; /* End _capture_V4L2 */ #endif /* HAVE_CAMV4L2 */ +#ifdef HAVE_CAMV4L -static int _capture_V4L (CvCaptureCAM_V4L *capture, char *deviceName) +static int _capture_V4L (CvCaptureCAM_V4L *capture, const char *deviceName) { int detect_v4l = 0; @@ -1103,6 +1168,8 @@ return 1; }; /* End _capture_V4L */ +#endif /* HAVE_CAMV4L */ + static CvCaptureCAM_V4L * icvCaptureFromCAM_V4L (int index) { static int autoindex; @@ -1152,10 +1219,12 @@ icvCloseCAM_V4L(capture); V4L2_SUPPORT = 0; #endif /* HAVE_CAMV4L2 */ +#ifdef HAVE_CAMV4L if (_capture_V4L (capture, deviceName) == -1) { icvCloseCAM_V4L(capture); return NULL; } +#endif /* HAVE_CAMV4L */ #ifdef HAVE_CAMV4L2 } else { V4L2_SUPPORT = 1; @@ -1168,6 +1237,26 @@ #ifdef HAVE_CAMV4L2 static int read_frame_v4l2(CvCaptureCAM_V4L* capture) { + if (-1 == read(capture->deviceHandle, + capture->buffers[0].start, + capture->buffers[0].length)) { + switch (errno) { + case EAGAIN: + return 0; + + case EIO: + default: + /* display the error and stop processing */ + perror ("read"); + return 1; + } + } + capture->bufferIndex = 0; + + return 1; +} + +static int mmap_read_frame_v4l2(CvCaptureCAM_V4L* capture) { struct v4l2_buffer buf; CLEAR (buf); @@ -1197,18 +1286,12 @@ } } - assert(buf.index < capture->req.count); + assert(buf.index < capture->n_buffers); - memcpy(capture->buffers[MAX_V4L_BUFFERS].start, - capture->buffers[buf.index].start, - capture->buffers[MAX_V4L_BUFFERS].length ); - capture->bufferIndex = MAX_V4L_BUFFERS; + capture->bufferIndex = buf.index; //printf("got data in buff %d, len=%d, flags=0x%X, seq=%d, used=%d)\n", // buf.index, buf.length, buf.flags, buf.sequence, buf.bytesused); - if (-1 == xioctl (capture->deviceHandle, VIDIOC_QBUF, &buf)) - perror ("VIDIOC_QBUF"); - return 1; } @@ -1218,7 +1301,7 @@ count = 1; while (count-- > 0) { - for (;;) { + for (; capture->devicePollable == true;) { fd_set fds; struct timeval tv; int r; @@ -1233,10 +1316,12 @@ r = select (capture->deviceHandle+1, &fds, NULL, NULL, &tv); if (-1 == r) { - if (EINTR == errno) + if (EINTR == errno || EAGAIN == errno) continue; - - perror ("select"); + if (ENXIO == errno) + capture->devicePollable = false; + else + perror ("select"); } if (0 == r) { @@ -1245,7 +1330,12 @@ /* end the infinite loop */ break; } + } + if (capture->n_buffers != 0) { + if (mmap_read_frame_v4l2 (capture)) + break; + } else { if (read_frame_v4l2 (capture)) break; } @@ -1267,9 +1357,7 @@ if (V4L2_SUPPORT == 1) { - for (capture->bufferIndex = 0; - capture->bufferIndex < ((int)capture->req.count); - ++capture->bufferIndex) + for (unsigned int n_buffers = 0; n_buffers < capture->n_buffers; ++n_buffers) { struct v4l2_buffer buf; @@ -1278,25 +1366,28 @@ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; - buf.index = (unsigned long)capture->bufferIndex; + buf.index = (unsigned long)n_buffers; if (-1 == xioctl (capture->deviceHandle, VIDIOC_QBUF, &buf)) { - perror ("VIDIOC_QBUF"); + perror ("VIDIOC_QBUF1373"); return 0; } } - /* enable the streaming */ - capture->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (-1 == xioctl (capture->deviceHandle, VIDIOC_STREAMON, - &capture->type)) { - /* error enabling the stream */ - perror ("VIDIOC_STREAMON"); - return 0; - } + if (capture->n_buffers != 0) { + /* enable the streaming */ + capture->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl (capture->deviceHandle, VIDIOC_STREAMON, + &capture->type)) { + /* error enabling the stream */ + perror ("VIDIOC_STREAMON"); + return 0; + } + } } else #endif /* HAVE_CAMV4L2 */ { +#ifdef HAVE_CAMV4L for (capture->bufferIndex = 0; capture->bufferIndex < (capture->memoryBuffer.frames-1); @@ -1313,9 +1404,12 @@ } } +#else + return 0; +#endif /* HAVE_CAMV4L */ } -#if defined(V4L_ABORT_BADJPEG) && defined(HAVE_CAMV4L2) +#if defined V4L_ABORT_BADJPEG && defined HAVE_CAMV4L2 if (V4L2_SUPPORT == 1) { // skip first frame. it is often bad -- this is unnotied in traditional apps, @@ -1338,6 +1432,7 @@ } else #endif /* HAVE_CAMV4L2 */ { +#ifdef HAVE_CAMV4L capture->mmaps[capture->bufferIndex].frame = capture->bufferIndex; capture->mmaps[capture->bufferIndex].width = capture->captureWindow.width; @@ -1355,6 +1450,7 @@ capture->bufferIndex = 0; } +#endif /* HAVE_CAMV4L */ } return(1); @@ -2006,8 +2102,7 @@ unsigned char *addr; if (!init_done) { - /* do sonix_decompress_init first! */ - return -1; + sonix_decompress_init(); } bitpos = 0; @@ -2074,12 +2169,16 @@ if (V4L2_SUPPORT == 0) #endif /* HAVE_CAMV4L2 */ { +#ifdef HAVE_CAMV4L /* [FD] this really belongs here */ if (ioctl(capture->deviceHandle, VIDIOCSYNC, &capture->mmaps[capture->bufferIndex].frame) == -1) { fprintf( stderr, "HIGHGUI ERROR: V4L: Could not SYNC to video stream. %s\n", strerror(errno)); } +#else + return 0; +#endif /* HAVE_CAMV4L */ } /* Now get what has already been captured as a IplImage return */ @@ -2104,6 +2203,7 @@ } else #endif /* HAVE_CAMV4L2 */ { +#ifdef HAVE_CAMV4L if((capture->frame.width != capture->mmaps[capture->bufferIndex].width) || (capture->frame.height != capture->mmaps[capture->bufferIndex].height)) { @@ -2115,6 +2215,7 @@ capture->frame.imageData = (char *)cvAlloc(capture->frame.imageSize); } +#endif /* HAVE_CAMV4L */ } #ifdef HAVE_CAMV4L2 @@ -2140,10 +2241,6 @@ (unsigned char*)capture->frame.imageData); #ifdef HAVE_JPEG -#ifdef __USE_GNU - /* support for MJPEG is only available with libjpeg and gcc, - because it's use libjepg and fmemopen() - */ if (PALETTE_MJPEG == 1) if (!mjpeg_to_rgb24(capture->form.fmt.pix.width, capture->form.fmt.pix.height, @@ -2153,7 +2250,6 @@ (unsigned char*)capture->frame.imageData)) return 0; #endif -#endif if (PALETTE_YUYV == 1) yuyv_to_rgb24(capture->form.fmt.pix.width, @@ -2177,16 +2273,14 @@ if (PALETTE_SN9C10X == 1) { - sonix_decompress_init(); - sonix_decompress(capture->form.fmt.pix.width, capture->form.fmt.pix.height, (unsigned char*)capture->buffers[capture->bufferIndex].start, - (unsigned char*)capture->buffers[(capture->bufferIndex+1) % capture->req.count].start); + (unsigned char*)capture->buffers[MAX_V4L_BUFFERS].start); bayer2rgb24(capture->form.fmt.pix.width, capture->form.fmt.pix.height, - (unsigned char*)capture->buffers[(capture->bufferIndex+1) % capture->req.count].start, + (unsigned char*)capture->buffers[MAX_V4L_BUFFERS].start, (unsigned char*)capture->frame.imageData); } @@ -2194,13 +2288,14 @@ { sgbrg2rgb24(capture->form.fmt.pix.width, capture->form.fmt.pix.height, - (unsigned char*)capture->buffers[(capture->bufferIndex+1) % capture->req.count].start, + (unsigned char*)capture->buffers[capture->bufferIndex].start, (unsigned char*)capture->frame.imageData); } } else #endif /* HAVE_CAMV4L2 */ { +#ifdef HAVE_CAMV4L switch(capture->imageProperties.palette) { case VIDEO_PALETTE_RGB24: @@ -2234,7 +2329,23 @@ return 0; } +#endif /* HAVE_CAMV4L */ + } + +#ifdef HAVE_CAMV4L2 + if (capture->n_buffers != 0) { + struct v4l2_buffer buf; + + CLEAR (buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = (unsigned long)capture->bufferIndex; + + if (-1 == xioctl (capture->deviceHandle, VIDIOC_QBUF, &buf)) + perror ("VIDIOC_QBUF2347"); } +#endif /* HAVE_CAMV4L2 */ return(&capture->frame); } @@ -2358,6 +2469,7 @@ } else #endif /* HAVE_CAMV4L2 */ { +#ifdef HAVE_CAMV4L int retval = -1; @@ -2413,6 +2525,9 @@ /* all was OK, so convert to 0.0 - 1.0 range, and return the value */ return float (retval) / 0xFFFF; +#else + return -1; +#endif /* HAVE_CAMV4L */ } }; @@ -2482,6 +2597,7 @@ } else #endif /* HAVE_CAMV4L2 */ { +#ifdef HAVE_CAMV4L if (capture==0) return 0; if (w>capture->capability.maxwidth) { @@ -2506,6 +2622,7 @@ capture->FirstCapture = 1; +#endif /* HAVE_CAMV4L */ } return 0; @@ -2641,6 +2758,7 @@ } else #endif /* HAVE_CAMV4L2 */ { +#ifdef HAVE_CAMV4L int v4l_value; @@ -2683,6 +2801,9 @@ icvCloseCAM_V4L(capture); return -1; } +#else + return -1; +#endif /* HAVE_CAMV4L */ } /* all was OK */ @@ -2745,31 +2866,41 @@ if (V4L2_SUPPORT == 0) #endif /* HAVE_CAMV4L2 */ { +#ifdef HAVE_CAMV4L if (capture->mmaps) free(capture->mmaps); if (capture->memoryMap) munmap(capture->memoryMap, capture->memoryBuffer.size); +#endif /* HAVE_CAMV4L */ } #ifdef HAVE_CAMV4L2 else { capture->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl(capture->deviceHandle, VIDIOC_STREAMOFF, &capture->type) < 0) { - perror ("Unable to stop the stream."); + if (capture->n_buffers != 0) { + if (ioctl(capture->deviceHandle, VIDIOC_STREAMOFF, &capture->type) < 0) { + perror ("Unable to stop the stream."); + } } - for (unsigned int n_buffers = 0; n_buffers < capture->req.count; ++n_buffers) + if (capture->buffers[MAX_V4L_BUFFERS].start != NULL) { - if (-1 == munmap (capture->buffers[n_buffers].start, capture->buffers[n_buffers].length)) { + free(capture->buffers[MAX_V4L_BUFFERS].start); + capture->buffers[MAX_V4L_BUFFERS].start = NULL; + } + + for (unsigned int n_buffers = 0; n_buffers < capture->n_buffers; ++n_buffers) + { + if (-1 == munmap ((char *)capture->buffers[n_buffers].start, capture->buffers[n_buffers].length)) { perror ("munmap"); } } - if (capture->buffers[MAX_V4L_BUFFERS].start) + if (capture->n_buffers == 0) { - free(capture->buffers[MAX_V4L_BUFFERS].start); - capture->buffers[MAX_V4L_BUFFERS].start = 0; + free(capture->buffers[0].start); + capture->buffers[0].start = NULL; } } #endif /* HAVE_CAMV4L2 */ @@ -2848,4 +2979,4 @@ return 0; } -#endif +#endif /* !WIN32 && (HAVE_CAMV4L || HAVE_CAMV4L2) */