From 4ad12a680c03deb0c4d9ff6065831dad036de312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Devernay?= Date: Fri, 26 Jul 2013 18:39:03 +0200 Subject: [PATCH 1/2] fix cap_qtkit.mm for multithreaded applications cap_qtkit does not work when the capture is run outside of the main thread. If the capture is launched in a separate thread, then [NSRunLoop currentRunLoop] is not the same as in the main thread, and has no timer. see https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/F oundation/Classes/nsrunloop_Class/Reference/Reference.html "If no input sources or timers are attached to the run loop, this method exits immediately" Using usleep() (which I previously proposed, and was reverted) is not a good alternative, because it may block the GUI. Here is the new proposed solution: - create a dummy timer so that runUntilDate does not exit immediately - simplify the loop by using runUntilDate instead of runMode:beforeDate - fix potential memory leaks (pointed out by Xcode's static analysis) - fix init to follow Objective-C guidelines - fax warnings about conversions from size_t to int --- modules/highgui/src/cap_qtkit.mm | 91 +++++++++++++++++++------------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/modules/highgui/src/cap_qtkit.mm b/modules/highgui/src/cap_qtkit.mm index c7afffa075..2335e5c215 100644 --- a/modules/highgui/src/cap_qtkit.mm +++ b/modules/highgui/src/cap_qtkit.mm @@ -277,11 +277,17 @@ bool CvCaptureCAM::grabFrame(double timeOut) { double sleepTime = 0.005; double total = 0; - NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:sleepTime]; - while (![capture updateImage] && (total += sleepTime)<=timeOut && - [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode - beforeDate:loopUntil]) - loopUntil = [NSDate dateWithTimeIntervalSinceNow:sleepTime]; + // If the capture is launched in a separate thread, then + // [NSRunLoop currentRunLoop] is not the same as in the main thread, and has no timer. + //see https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsrunloop_Class/Reference/Reference.html + // "If no input sources or timers are attached to the run loop, this + // method exits immediately" + // using usleep() is not a good alternative, because it may block the GUI. + // Create a dummy timer so that runUntilDate does not exit immediately: + [NSTimer scheduledTimerWithTimeInterval:100 target:nil selector:@selector(doFireTimer:) userInfo:nil repeats:YES]; + while (![capture updateImage] && (total += sleepTime)<=timeOut) { + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:sleepTime]]; + } [localpool drain]; @@ -326,9 +332,11 @@ int CvCaptureCAM::startCaptureDevice(int cameraNum) { } if (cameraNum >= 0) { - int nCameras = [devices count]; - if( cameraNum < 0 || cameraNum >= nCameras ) + NSUInteger nCameras = [devices count]; + if( cameraNum < 0 || cameraNum >= nCameras ) { + [localpool drain]; return 0; + } device = [devices objectAtIndex:cameraNum] ; } else { device = [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeVideo] ; @@ -392,6 +400,7 @@ int CvCaptureCAM::startCaptureDevice(int cameraNum) { grabFrame(60); + [localpool drain]; return 1; } @@ -415,6 +424,7 @@ void CvCaptureCAM::setWidthHeight() { double CvCaptureCAM::getProperty(int property_id){ + int retval; NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; NSArray* connections = [mCaptureDeviceInput connections]; @@ -424,15 +434,18 @@ double CvCaptureCAM::getProperty(int property_id){ int width=s1.width, height=s1.height; switch (property_id) { case CV_CAP_PROP_FRAME_WIDTH: - return width; + retval = width; + break; case CV_CAP_PROP_FRAME_HEIGHT: - return height; + retval = height; + break; default: - return 0; + retval = 0; + break; } [localpool drain]; - + return retval; } bool CvCaptureCAM::setProperty(int property_id, double value) { @@ -480,13 +493,15 @@ bool CvCaptureCAM::setProperty(int property_id, double value) { @implementation CaptureDelegate - (id)init { - [super init]; - newFrame = 0; - imagedata = NULL; - bgr_imagedata = NULL; - currSize = 0; - image = NULL; - bgr_image = NULL; + self = [super init]; + if (self) { + newFrame = 0; + imagedata = NULL; + bgr_imagedata = NULL; + currSize = 0; + image = NULL; + bgr_image = NULL; + } return self; } @@ -561,26 +576,26 @@ didDropVideoFrameWithSampleBuffer:(QTSampleBuffer *)sampleBuffer memcpy(imagedata, baseaddress, currSize); if (image == NULL) { - image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, 4); + image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 4); } - image->width =width; - image->height = height; + image->width = (int)width; + image->height = (int)height; image->nChannels = 4; image->depth = IPL_DEPTH_8U; - image->widthStep = rowBytes; + image->widthStep = (int)rowBytes; image->imageData = imagedata; - image->imageSize = currSize; + image->imageSize = (int)currSize; if (bgr_image == NULL) { - bgr_image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, 3); + bgr_image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 3); } - bgr_image->width =width; - bgr_image->height = height; + bgr_image->width = (int)width; + bgr_image->height = (int)height; bgr_image->nChannels = 3; bgr_image->depth = IPL_DEPTH_8U; - bgr_image->widthStep = rowBytes; + bgr_image->widthStep = (int)rowBytes; bgr_image->imageData = bgr_imagedata; - bgr_image->imageSize = currSize; + bgr_image->imageSize = (int)currSize; cvCvtColor(image, bgr_image, CV_BGRA2BGR); @@ -734,29 +749,29 @@ IplImage* CvCaptureFile::retrieveFramePixelBuffer() { } if (image == NULL) { - image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, 4); + image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 4); } - image->width =width; - image->height = height; + image->width = (int)width; + image->height = (int)height; image->nChannels = 4; image->depth = IPL_DEPTH_8U; - image->widthStep = rowBytes; + image->widthStep = (int)rowBytes; image->imageData = imagedata; - image->imageSize = currSize; + image->imageSize = (int)currSize; if (bgr_image == NULL) { - bgr_image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, 3); + bgr_image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 3); } - bgr_image->width =width; - bgr_image->height = height; + bgr_image->width = (int)width; + bgr_image->height = (int)height; bgr_image->nChannels = 3; bgr_image->depth = IPL_DEPTH_8U; - bgr_image->widthStep = rowBytes; + bgr_image->widthStep = (int)rowBytes; bgr_image->imageData = bgr_imagedata; - bgr_image->imageSize = currSize; + bgr_image->imageSize = (int)currSize; cvCvtColor(image, bgr_image,CV_BGRA2BGR); From 6be8757e8b93ca7593de76f66d2479737eb69c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Devernay?= Date: Fri, 26 Jul 2013 21:32:35 +0200 Subject: [PATCH 2/2] fix signedness error OpenCV's automatic builds don't care if you store an unsigned int into an int, but they don't want you to compare signed with unsigned. Does that make sense? --- modules/highgui/src/cap_qtkit.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/highgui/src/cap_qtkit.mm b/modules/highgui/src/cap_qtkit.mm index 2335e5c215..bde200e410 100644 --- a/modules/highgui/src/cap_qtkit.mm +++ b/modules/highgui/src/cap_qtkit.mm @@ -333,7 +333,7 @@ int CvCaptureCAM::startCaptureDevice(int cameraNum) { if (cameraNum >= 0) { NSUInteger nCameras = [devices count]; - if( cameraNum < 0 || cameraNum >= nCameras ) { + if( (NSUInteger)cameraNum >= nCameras ) { [localpool drain]; return 0; }