From c0fe522c9df08e46dcea87fc30409df03df87256 Mon Sep 17 00:00:00 2001 From: Pavel Rojtberg Date: Fri, 9 Oct 2015 16:01:14 +0200 Subject: [PATCH] allow changing FPS and Image Size using V4L2 use logic similar to cap_libv4l: replace icvSetVideoSize by v4l2_reset as it was not used for V4L1, the actual frame format is negotiated in try_palette_v4l2 and the stream has to restarted anyway. --- modules/videoio/src/cap_v4l.cpp | 194 +++++++++++--------------------- 1 file changed, 67 insertions(+), 127 deletions(-) diff --git a/modules/videoio/src/cap_v4l.cpp b/modules/videoio/src/cap_v4l.cpp index b43bed60ea..3dfbe95ecc 100644 --- a/modules/videoio/src/cap_v4l.cpp +++ b/modules/videoio/src/cap_v4l.cpp @@ -244,6 +244,7 @@ make & enjoy! /* Defaults - If your board can do better, set it here. Set for the most common type inputs. */ #define DEFAULT_V4L_WIDTH 640 #define DEFAULT_V4L_HEIGHT 480 +#define DEFAULT_V4L_FPS 30 #define CHANNEL_NUMBER 1 #define MAX_CAMERAS 8 @@ -315,6 +316,9 @@ typedef struct CvCaptureCAM_V4L #ifdef HAVE_CAMV4L2 enum PALETTE_TYPE palette; + int index; + int width, height; + __u32 fps; /* V4L2 variables */ buffer buffers[MAX_V4L_BUFFERS + 1]; struct v4l2_capability cap; @@ -373,8 +377,6 @@ static IplImage* icvRetrieveFrameCAM_V4L( CvCaptureCAM_V4L* capture, int ); static double icvGetPropertyCAM_V4L( CvCaptureCAM_V4L* capture, int property_id ); static int icvSetPropertyCAM_V4L( CvCaptureCAM_V4L* capture, int property_id, double value ); -static int icvSetVideoSize( CvCaptureCAM_V4L* capture, int w, int h); - /*********************** Implementations ***************************************/ static int numCameras = 0; @@ -440,8 +442,8 @@ static int try_palette_v4l2(CvCaptureCAM_V4L* capture, unsigned long colorspace) capture->form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; capture->form.fmt.pix.pixelformat = colorspace; capture->form.fmt.pix.field = V4L2_FIELD_ANY; - capture->form.fmt.pix.width = DEFAULT_V4L_WIDTH; - capture->form.fmt.pix.height = DEFAULT_V4L_HEIGHT; + capture->form.fmt.pix.width = capture->width; + capture->form.fmt.pix.height = capture->height; if (-1 == ioctl (capture->deviceHandle, VIDIOC_S_FMT, &capture->form)) return -1; @@ -725,8 +727,21 @@ static void v4l2_scan_controls(CvCaptureCAM_V4L* capture) v4l2_control_range(capture, V4L2_CID_FOCUS_ABSOLUTE); } -static int _capture_V4L2 (CvCaptureCAM_V4L *capture, char *deviceName) +static int v4l2_set_fps(CvCaptureCAM_V4L* capture) { + v4l2_streamparm setfps; + CLEAR(setfps); + setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + setfps.parm.capture.timeperframe.numerator = 1; + setfps.parm.capture.timeperframe.denominator = capture->fps; + return ioctl (capture->deviceHandle, VIDIOC_S_PARM, &setfps); +} + +static int _capture_V4L2 (CvCaptureCAM_V4L *capture) { + char deviceName[MAX_DEVICE_DRIVER_NAME]; + /* Print the CameraNumber at the end of the string with a width of one character */ + sprintf(deviceName, "/dev/video%1d", capture->index); + if (try_init_v4l2(capture, deviceName) != 1) { /* init of the v4l2 device is not OK */ return -1; @@ -777,14 +792,11 @@ static int _capture_V4L2 (CvCaptureCAM_V4L *capture, char *deviceName) return -1; } - if (V4L2_SUPPORT == 0) - { - } - if (autosetup_capture_mode_v4l2(capture) == -1) return -1; - icvSetVideoSize(capture, DEFAULT_V4L_WIDTH, DEFAULT_V4L_HEIGHT); + /* try to set framerate */ + v4l2_set_fps(capture); unsigned int min; @@ -887,9 +899,22 @@ static int _capture_V4L2 (CvCaptureCAM_V4L *capture, char *deviceName) /* Allocate space for RGBA data */ capture->frame.imageData = (char *)cvAlloc(capture->frame.imageSize); + // reinitialize buffers + capture->FirstCapture = 1; + return 1; }; /* End _capture_V4L2 */ +/** + * some properties can not be changed while the device is in streaming mode. + * this method closes and re-opens the device to re-start the stream. + * this also causes buffers to be reallocated if the frame size was changed. + */ +static int v4l2_reset( CvCaptureCAM_V4L* capture) { + icvCloseCAM_V4L(capture); + return _capture_V4L2(capture); +} + #endif /* HAVE_CAMV4L2 */ #ifdef HAVE_CAMV4L @@ -1019,8 +1044,6 @@ static CvCaptureCAM_V4L * icvCaptureFromCAM_V4L (int index) static int autoindex; autoindex = 0; - char deviceName[MAX_DEVICE_DRIVER_NAME]; - if (!numCameras) icvInitCapture_V4L(); /* Havent called icvInitCapture yet - do it now! */ if (!numCameras) @@ -1049,8 +1072,7 @@ static CvCaptureCAM_V4L * icvCaptureFromCAM_V4L (int index) index=autoindex; autoindex++;// i can recall icvOpenCAM_V4l with index=-1 for next camera } - /* Print the CameraNumber at the end of the string with a width of one character */ - sprintf(deviceName, "/dev/video%1d", index); + capture->index = index; /* w/o memset some parts arent initialized - AKA: Fill it with zeros so it is clean */ memset(capture,0,sizeof(CvCaptureCAM_V4L)); @@ -1059,19 +1081,25 @@ static CvCaptureCAM_V4L * icvCaptureFromCAM_V4L (int index) capture->FirstCapture = 1; #ifdef HAVE_CAMV4L2 - if (_capture_V4L2 (capture, deviceName) == -1) { + capture->width = DEFAULT_V4L_WIDTH; + capture->height = DEFAULT_V4L_HEIGHT; + capture->fps = DEFAULT_V4L_FPS; + + if (_capture_V4L2 (capture) == -1) { icvCloseCAM_V4L(capture); V4L2_SUPPORT = 0; #endif /* HAVE_CAMV4L2 */ #ifdef HAVE_CAMV4L + char deviceName[MAX_DEVICE_DRIVER_NAME]; + /* Print the CameraNumber at the end of the string with a width of one character */ + sprintf(deviceName, "/dev/video%1d", capture->index); + if (_capture_V4L (capture, deviceName) == -1) { icvCloseCAM_V4L(capture); return NULL; } #endif /* HAVE_CAMV4L */ #ifdef HAVE_CAMV4L2 - } else { - V4L2_SUPPORT = 1; } #endif /* HAVE_CAMV4L2 */ @@ -2247,6 +2275,18 @@ static double icvGetPropertyCAM_V4L (CvCaptureCAM_V4L* capture, return capture->form.fmt.pix.height; } + if(property_id == CV_CAP_PROP_FPS) { + struct v4l2_streamparm sp; + CLEAR(sp); + sp.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl(capture->deviceHandle, VIDIOC_G_PARM, &sp) < 0){ + fprintf(stderr, "VIDEOIO ERROR: V4L: Unable to get camera FPS\n"); + return -1; + } + + return sp.parm.capture.timeperframe.denominator / (double)sp.parm.capture.timeperframe.numerator; + } + /* initialize the control structure */ if(property_id == CV_CAP_PROP_POS_MSEC) { @@ -2376,113 +2416,6 @@ static double icvGetPropertyCAM_V4L (CvCaptureCAM_V4L* capture, }; -static int icvSetVideoSize( CvCaptureCAM_V4L* capture, int w, int h) { - -#ifdef HAVE_CAMV4L2 - - if (V4L2_SUPPORT == 1) - { - - CLEAR (capture->cropcap); - capture->cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - if (ioctl (capture->deviceHandle, VIDIOC_CROPCAP, &capture->cropcap) < 0) { - fprintf(stderr, "VIDEOIO ERROR: V4L/V4L2: VIDIOC_CROPCAP\n"); - } else { - - CLEAR (capture->crop); - capture->crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - capture->crop.c= capture->cropcap.defrect; - - /* set the crop area, but don't exit if the device don't support croping */ - if (ioctl (capture->deviceHandle, VIDIOC_S_CROP, &capture->crop) < 0) { - fprintf(stderr, "VIDEOIO ERROR: V4L/V4L2: VIDIOC_S_CROP\n"); - } - } - - CLEAR (capture->form); - capture->form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - /* read the current setting, mainly to retreive the pixelformat information */ - ioctl (capture->deviceHandle, VIDIOC_G_FMT, &capture->form); - - /* set the values we want to change */ - capture->form.fmt.pix.width = w; - capture->form.fmt.pix.height = h; - capture->form.fmt.win.chromakey = 0; - capture->form.fmt.win.field = V4L2_FIELD_ANY; - capture->form.fmt.win.clips = 0; - capture->form.fmt.win.clipcount = 0; - capture->form.fmt.pix.field = V4L2_FIELD_ANY; - - /* ask the device to change the size - * don't test if the set of the size is ok, because some device - * don't allow changing the size, and we will get the real size - * later */ - ioctl (capture->deviceHandle, VIDIOC_S_FMT, &capture->form); - - /* try to set framerate to 30 fps */ - struct v4l2_streamparm setfps; - memset (&setfps, 0, sizeof(struct v4l2_streamparm)); - setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - setfps.parm.capture.timeperframe.numerator = 1; - setfps.parm.capture.timeperframe.denominator = 30; - ioctl (capture->deviceHandle, VIDIOC_S_PARM, &setfps); - - /* we need to re-initialize some things, like buffers, because the size has - * changed */ - capture->FirstCapture = 1; - - /* Get window info again, to get the real value */ - if (-1 == ioctl (capture->deviceHandle, VIDIOC_G_FMT, &capture->form)) - { - fprintf(stderr, "VIDEOIO ERROR: V4L/V4L2: Could not obtain specifics of capture window.\n\n"); - - icvCloseCAM_V4L(capture); - - return 0; - } - - return 0; - - } -#endif /* HAVE_CAMV4L2 */ -#if defined(HAVE_CAMV4L) && defined(HAVE_CAMV4L2) - else -#endif /* HAVE_CAMV4L && HAVE_CAMV4L2 */ -#ifdef HAVE_CAMV4L - { - - if (capture==0) return 0; - if (w>capture->capability.maxwidth) { - w=capture->capability.maxwidth; - } - if (h>capture->capability.maxheight) { - h=capture->capability.maxheight; - } - - capture->captureWindow.width=w; - capture->captureWindow.height=h; - - if (ioctl(capture->deviceHandle, VIDIOCSWIN, &capture->captureWindow) < 0) { - icvCloseCAM_V4L(capture); - return 0; - } - - if (ioctl(capture->deviceHandle, VIDIOCGWIN, &capture->captureWindow) < 0) { - icvCloseCAM_V4L(capture); - return 0; - } - - capture->FirstCapture = 1; - - } -#endif /* HAVE_CAMV4L */ - - return 0; - -} - static int icvSetControl (CvCaptureCAM_V4L* capture, int property_id, double value) { @@ -2589,23 +2522,30 @@ static int icvSetPropertyCAM_V4L( CvCaptureCAM_V4L* capture, /* two subsequent calls setting WIDTH and HEIGHT will change the video size */ - /* the first one will return an error, though. */ switch (property_id) { case CV_CAP_PROP_FRAME_WIDTH: width = cvRound(value); if(width !=0 && height != 0) { - retval = icvSetVideoSize( capture, width, height); + capture->width = width; + capture->height = height; + retval = v4l2_reset( capture); width = height = 0; } break; case CV_CAP_PROP_FRAME_HEIGHT: height = cvRound(value); if(width !=0 && height != 0) { - retval = icvSetVideoSize( capture, width, height); + capture->width = width; + capture->height = height; + retval = v4l2_reset( capture); width = height = 0; } break; + case CV_CAP_PROP_FPS: + capture->fps = value; + retval = v4l2_reset( capture); + break; default: retval = icvSetControl(capture, property_id, value); break;