/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2013, OpenCV Foundation, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of the copyright holders may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the contributor be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. // //M*//////////////////////////////////////////////////////////////////////////////////////// #include "precomp.hpp" #include "opencv2/imgproc.hpp" #include #import /********************** Declaration of class headers ************************/ /***************************************************************************** * * CaptureDelegate Declaration. * * CaptureDelegate is notified on a separate thread by the OS whenever there * is a new frame. When "updateImage" is called from the main thread, it * copies this new frame into an IplImage, but only if this frame has not * been copied before. When "getOutput" is called from the main thread, * it gives the last copied IplImage. * *****************************************************************************/ #ifndef QTKIT_VERSION_7_6_3 #define QTKIT_VERSION_7_6_3 70603 #define QTKIT_VERSION_7_0 70000 #endif #ifndef QTKIT_VERSION_MAX_ALLOWED #define QTKIT_VERSION_MAX_ALLOWED QTKIT_VERSION_7_0 #endif #define DISABLE_AUTO_RESTART 999 @interface CaptureDelegate : NSObject { int newFrame; CVImageBufferRef mCurrentImageBuffer; char* imagedata; IplImage* image; char* bgr_imagedata; IplImage* bgr_image; size_t currSize; } - (void)captureOutput:(QTCaptureOutput *)captureOutput didOutputVideoFrame:(CVImageBufferRef)videoFrame withSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection; - (void)captureOutput:(QTCaptureOutput *)captureOutput didDropVideoFrameWithSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection; - (int)updateImage; - (IplImage*)getOutput; @end /***************************************************************************** * * CvCaptureCAM Declaration. * * CvCaptureCAM is the instantiation of a capture source for cameras. * *****************************************************************************/ class CvCaptureCAM : public CvCapture { public: CvCaptureCAM(int cameraNum = -1) ; ~CvCaptureCAM(); virtual bool grabFrame(); virtual IplImage* retrieveFrame(int); virtual double getProperty(int property_id) const; virtual bool setProperty(int property_id, double value); virtual int didStart(); private: QTCaptureSession *mCaptureSession; QTCaptureDeviceInput *mCaptureDeviceInput; QTCaptureDecompressedVideoOutput *mCaptureDecompressedVideoOutput; CaptureDelegate* capture; int startCaptureDevice(int cameraNum); void stopCaptureDevice(); void setWidthHeight(); bool grabFrame(double timeOut); int camNum; int width; int height; int settingWidth; int settingHeight; int started; int disableAutoRestart; }; /***************************************************************************** * * CvCaptureFile Declaration. * * CvCaptureFile is the instantiation of a capture source for video files. * *****************************************************************************/ class CvCaptureFile : public CvCapture { public: CvCaptureFile(const char* filename) ; ~CvCaptureFile(); virtual bool grabFrame(); virtual IplImage* retrieveFrame(int); virtual double getProperty(int property_id) const; virtual bool setProperty(int property_id, double value); virtual int didStart(); private: QTMovie *mCaptureSession; char* imagedata; IplImage* image; char* bgr_imagedata; IplImage* bgr_image; size_t currSize; //IplImage* retrieveFrameBitmap(); IplImage* retrieveFramePixelBuffer(); double getFPS(); int movieWidth; int movieHeight; double movieFPS; double currentFPS; double movieDuration; int changedPos; int started; QTTime endOfMovie; }; /***************************************************************************** * * CvCaptureFile Declaration. * * CvCaptureFile is the instantiation of a capture source for video files. * *****************************************************************************/ class CvVideoWriter_QT : public CvVideoWriter{ public: CvVideoWriter_QT(const char* filename, int fourcc, double fps, CvSize frame_size, int is_color=1); ~CvVideoWriter_QT(); bool writeFrame(const IplImage* image); private: IplImage* argbimage; QTMovie* mMovie; NSString* path; NSString* codec; double movieFPS; CvSize movieSize; int movieColor; }; /****************** Implementation of interface functions ********************/ CvCapture* cvCreateFileCapture_QT(const char* filename) { CvCaptureFile *retval = new CvCaptureFile(filename); if(retval->didStart()) return retval; delete retval; return NULL; } CvCapture* cvCreateCameraCapture_QT(int index ) { CvCapture* retval = new CvCaptureCAM(index); if (!((CvCaptureCAM *)retval)->didStart()) cvReleaseCapture(&retval); return retval; } CvVideoWriter* cvCreateVideoWriter_QT(const char* filename, int fourcc, double fps, CvSize frame_size, int is_color) { return new CvVideoWriter_QT(filename, fourcc, fps, frame_size,is_color); } /********************** Implementation of Classes ****************************/ /***************************************************************************** * * CvCaptureCAM Implementation. * * CvCaptureCAM is the instantiation of a capture source for cameras. * *****************************************************************************/ CvCaptureCAM::CvCaptureCAM(int cameraNum) { mCaptureSession = nil; mCaptureDeviceInput = nil; mCaptureDecompressedVideoOutput = nil; capture = nil; width = 0; height = 0; settingWidth = 0; settingHeight = 0; disableAutoRestart = 0; camNum = cameraNum; if (!startCaptureDevice(camNum)) { std::cout << "Warning, camera failed to properly initialize!" << std::endl; started = 0; } else { started = 1; } } CvCaptureCAM::~CvCaptureCAM() { stopCaptureDevice(); std::cout << "Cleaned up camera." << std::endl; } int CvCaptureCAM::didStart() { return started; } bool CvCaptureCAM::grabFrame() { return grabFrame(5); } bool CvCaptureCAM::grabFrame(double timeOut) { NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; double sleepTime = 0.005; double total = 0; // 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]; return total <= timeOut; } IplImage* CvCaptureCAM::retrieveFrame(int) { return [capture getOutput]; } void CvCaptureCAM::stopCaptureDevice() { NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; [mCaptureSession stopRunning]; QTCaptureDevice *device = [mCaptureDeviceInput device]; if ([device isOpen]) [device close]; [mCaptureSession release]; [mCaptureDeviceInput release]; [mCaptureDecompressedVideoOutput setDelegate:mCaptureDecompressedVideoOutput]; [mCaptureDecompressedVideoOutput release]; [capture release]; [localpool drain]; } int CvCaptureCAM::startCaptureDevice(int cameraNum) { NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; capture = [[CaptureDelegate alloc] init]; QTCaptureDevice *device; NSArray* devices = [[[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo] arrayByAddingObjectsFromArray:[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeMuxed]] retain]; if ([devices count] == 0) { std::cout << "QTKit didn't find any attached Video Input Devices!" << std::endl; [localpool drain]; return 0; } if (cameraNum >= 0) { NSUInteger nCameras = [devices count]; if( (NSUInteger)cameraNum >= nCameras ) { [localpool drain]; return 0; } device = [devices objectAtIndex:cameraNum] ; } else { device = [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeVideo] ; } int success; NSError* error; if (device) { success = [device open:&error]; if (!success) { std::cout << "QTKit failed to open a Video Capture Device" << std::endl; [localpool drain]; return 0; } mCaptureDeviceInput = [[QTCaptureDeviceInput alloc] initWithDevice:device] ; mCaptureSession = [[QTCaptureSession alloc] init] ; success = [mCaptureSession addInput:mCaptureDeviceInput error:&error]; if (!success) { std::cout << "QTKit failed to start capture session with opened Capture Device" << std::endl; [localpool drain]; return 0; } mCaptureDecompressedVideoOutput = [[QTCaptureDecompressedVideoOutput alloc] init]; [mCaptureDecompressedVideoOutput setDelegate:capture]; NSDictionary *pixelBufferOptions ; if (width > 0 && height > 0) { pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithDouble:1.0*width], (id)kCVPixelBufferWidthKey, [NSNumber numberWithDouble:1.0*height], (id)kCVPixelBufferHeightKey, //[NSNumber numberWithUnsignedInt:k32BGRAPixelFormat], (id)kCVPixelBufferPixelFormatTypeKey, [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], (id)kCVPixelBufferPixelFormatTypeKey, nil]; } else { pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], (id)kCVPixelBufferPixelFormatTypeKey, nil]; } [mCaptureDecompressedVideoOutput setPixelBufferAttributes:pixelBufferOptions]; #if QTKIT_VERSION_MAX_ALLOWED >= QTKIT_VERSION_7_6_3 [mCaptureDecompressedVideoOutput setAutomaticallyDropsLateVideoFrames:YES]; #endif success = [mCaptureSession addOutput:mCaptureDecompressedVideoOutput error:&error]; if (!success) { std::cout << "QTKit failed to add Output to Capture Session" << std::endl; [localpool drain]; return 0; } [mCaptureSession startRunning]; grabFrame(60); [localpool drain]; return 1; } [localpool drain]; return 0; } void CvCaptureCAM::setWidthHeight() { NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; [mCaptureSession stopRunning]; NSDictionary* pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithDouble:1.0*width], (id)kCVPixelBufferWidthKey, [NSNumber numberWithDouble:1.0*height], (id)kCVPixelBufferHeightKey, [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], (id)kCVPixelBufferPixelFormatTypeKey, nil]; [mCaptureDecompressedVideoOutput setPixelBufferAttributes:pixelBufferOptions]; [mCaptureSession startRunning]; grabFrame(60); [localpool drain]; } double CvCaptureCAM::getProperty(int property_id) const{ int retval; NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; NSArray* connections = [mCaptureDeviceInput connections]; QTFormatDescription* format = [[connections objectAtIndex:0] formatDescription]; NSSize s1 = [[format attributeForKey:QTFormatDescriptionVideoCleanApertureDisplaySizeAttribute] sizeValue]; switch (property_id) { case CV_CAP_PROP_FRAME_WIDTH: retval = s1.width; break; case CV_CAP_PROP_FRAME_HEIGHT: retval = s1.height; break; default: retval = 0; break; } [localpool drain]; return retval; } bool CvCaptureCAM::setProperty(int property_id, double value) { switch (property_id) { case CV_CAP_PROP_FRAME_WIDTH: width = value; settingWidth = 1; if (settingWidth && settingHeight) { setWidthHeight(); settingWidth =0; settingHeight = 0; } return true; case CV_CAP_PROP_FRAME_HEIGHT: height = value; settingHeight = 1; if (settingWidth && settingHeight) { setWidthHeight(); settingWidth =0; settingHeight = 0; } return true; case DISABLE_AUTO_RESTART: disableAutoRestart = value; return 1; default: return false; } } /***************************************************************************** * * CaptureDelegate Implementation. * * CaptureDelegate is notified on a separate thread by the OS whenever there * is a new frame. When "updateImage" is called from the main thread, it * copies this new frame into an IplImage, but only if this frame has not * been copied before. When "getOutput" is called from the main thread, * it gives the last copied IplImage. * *****************************************************************************/ @implementation CaptureDelegate - (id)init { self = [super init]; if (self) { newFrame = 0; imagedata = NULL; bgr_imagedata = NULL; currSize = 0; image = NULL; bgr_image = NULL; } return self; } -(void)dealloc { if (imagedata != NULL) free(imagedata); if (bgr_imagedata != NULL) free(bgr_imagedata); cvReleaseImage(&image); cvReleaseImage(&bgr_image); [super dealloc]; } - (void)captureOutput:(QTCaptureOutput *)captureOutput didOutputVideoFrame:(CVImageBufferRef)videoFrame withSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection { (void)captureOutput; (void)sampleBuffer; (void)connection; CVBufferRetain(videoFrame); CVImageBufferRef imageBufferToRelease = mCurrentImageBuffer; @synchronized (self) { mCurrentImageBuffer = videoFrame; newFrame = 1; } CVBufferRelease(imageBufferToRelease); } - (void)captureOutput:(QTCaptureOutput *)captureOutput didDropVideoFrameWithSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection { (void)captureOutput; (void)sampleBuffer; (void)connection; std::cout << "Camera dropped frame!" << std::endl; } -(IplImage*) getOutput { return bgr_image; } -(int) updateImage { if (newFrame==0) return 0; CVPixelBufferRef pixels; @synchronized (self){ pixels = CVBufferRetain(mCurrentImageBuffer); newFrame = 0; } CVPixelBufferLockBaseAddress(pixels, 0); uint32_t* baseaddress = (uint32_t*)CVPixelBufferGetBaseAddress(pixels); size_t width = CVPixelBufferGetWidth(pixels); size_t height = CVPixelBufferGetHeight(pixels); size_t rowBytes = CVPixelBufferGetBytesPerRow(pixels); if (rowBytes != 0) { if (currSize != rowBytes*height*sizeof(char)) { currSize = rowBytes*height*sizeof(char); if (imagedata != NULL) free(imagedata); if (bgr_imagedata != NULL) free(bgr_imagedata); imagedata = (char*)malloc(currSize); bgr_imagedata = (char*)malloc(currSize); } memcpy(imagedata, baseaddress, currSize); if (image == NULL) { image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 4); } image->width = (int)width; image->height = (int)height; image->nChannels = 4; image->depth = IPL_DEPTH_8U; image->widthStep = (int)rowBytes; image->imageData = imagedata; image->imageSize = (int)currSize; if (bgr_image == NULL) { bgr_image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 3); } bgr_image->width = (int)width; bgr_image->height = (int)height; bgr_image->nChannels = 3; bgr_image->depth = IPL_DEPTH_8U; bgr_image->widthStep = (int)rowBytes; bgr_image->imageData = bgr_imagedata; bgr_image->imageSize = (int)currSize; cvCvtColor(image, bgr_image, CV_BGRA2BGR); } CVPixelBufferUnlockBaseAddress(pixels, 0); CVBufferRelease(pixels); return 1; } @end /***************************************************************************** * * CvCaptureFile Implementation. * * CvCaptureFile is the instantiation of a capture source for video files. * *****************************************************************************/ CvCaptureFile::CvCaptureFile(const char* filename) { NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; mCaptureSession = nil; image = NULL; bgr_image = NULL; imagedata = NULL; bgr_imagedata = NULL; currSize = 0; movieWidth = 0; movieHeight = 0; movieFPS = 0; currentFPS = 0; movieDuration = 0; changedPos = 0; started = 0; NSError* error; mCaptureSession = [[QTMovie movieWithFile:[NSString stringWithCString:filename encoding:NSASCIIStringEncoding] error:&error] retain]; [mCaptureSession setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieLoopsAttribute]; if (mCaptureSession == nil) { std::cout << "WARNING: Couldn't read movie file " << filename << std::endl; [localpool drain]; started = 0; return; } [mCaptureSession gotoEnd]; endOfMovie = [mCaptureSession currentTime]; [mCaptureSession gotoBeginning]; NSSize size = [[mCaptureSession attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]; movieWidth = size.width; movieHeight = size.height; movieFPS = getFPS(); currentFPS = movieFPS; QTTime t; [[mCaptureSession attributeForKey:QTMovieDurationAttribute] getValue:&t]; movieDuration = (t.timeValue *1000.0 / t.timeScale); started = 1; [localpool drain]; } CvCaptureFile::~CvCaptureFile() { NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; if (imagedata != NULL) free(imagedata); if (bgr_imagedata != NULL) free(bgr_imagedata); cvReleaseImage(&image); cvReleaseImage(&bgr_image); [mCaptureSession release]; [localpool drain]; } int CvCaptureFile::didStart() { return started; } bool CvCaptureFile::grabFrame() { NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; double t1 = getProperty(CV_CAP_PROP_POS_MSEC); QTTime curTime; curTime = [mCaptureSession currentTime]; bool isEnd=(QTTimeCompare(curTime,endOfMovie) == NSOrderedSame); [mCaptureSession stepForward]; double t2 = getProperty(CV_CAP_PROP_POS_MSEC); if (t2>t1 && !changedPos) { currentFPS = 1000.0/(t2-t1); } else { currentFPS = movieFPS; } changedPos = 0; [localpool drain]; return !isEnd; } IplImage* CvCaptureFile::retrieveFramePixelBuffer() { NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys: QTMovieFrameImageTypeCVPixelBufferRef, QTMovieFrameImageType, #ifdef MAC_OS_X_VERSION_10_6 [NSNumber numberWithBool:YES], QTMovieFrameImageSessionMode, #endif nil]; CVPixelBufferRef frame = (CVPixelBufferRef)[mCaptureSession frameImageAtTime:[mCaptureSession currentTime] withAttributes:attributes error:nil]; CVPixelBufferRef pixels = CVBufferRetain(frame); CVPixelBufferLockBaseAddress(pixels, 0); uint32_t* baseaddress = (uint32_t*)CVPixelBufferGetBaseAddress(pixels); size_t width = CVPixelBufferGetWidth(pixels); size_t height = CVPixelBufferGetHeight(pixels); size_t rowBytes = CVPixelBufferGetBytesPerRow(pixels); if (rowBytes != 0) { if (currSize != rowBytes*height*sizeof(char)) { currSize = rowBytes*height*sizeof(char); if (imagedata != NULL) free(imagedata); if (bgr_imagedata != NULL) free(bgr_imagedata); imagedata = (char*)malloc(currSize); bgr_imagedata = (char*)malloc(currSize); } memcpy(imagedata, baseaddress, currSize); //ARGB -> BGRA for (unsigned int i = 0; i < currSize; i+=4) { char temp = imagedata[i]; imagedata[i] = imagedata[i+3]; imagedata[i+3] = temp; temp = imagedata[i+1]; imagedata[i+1] = imagedata[i+2]; imagedata[i+2] = temp; } if (image == NULL) { image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 4); } image->width = (int)width; image->height = (int)height; image->nChannels = 4; image->depth = IPL_DEPTH_8U; image->widthStep = (int)rowBytes; image->imageData = imagedata; image->imageSize = (int)currSize; if (bgr_image == NULL) { bgr_image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 3); } bgr_image->width = (int)width; bgr_image->height = (int)height; bgr_image->nChannels = 3; bgr_image->depth = IPL_DEPTH_8U; bgr_image->widthStep = (int)rowBytes; bgr_image->imageData = bgr_imagedata; bgr_image->imageSize = (int)currSize; cvCvtColor(image, bgr_image,CV_BGRA2BGR); } CVPixelBufferUnlockBaseAddress(pixels, 0); CVBufferRelease(pixels); [localpool drain]; return bgr_image; } IplImage* CvCaptureFile::retrieveFrame(int) { return retrieveFramePixelBuffer(); } double CvCaptureFile::getFPS() { if (mCaptureSession == nil) return 0; NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; double now = getProperty(CV_CAP_PROP_POS_MSEC); double retval = 0; if (now == 0) { [mCaptureSession stepForward]; double t2 = getProperty(CV_CAP_PROP_POS_MSEC); [mCaptureSession stepBackward]; retval = 1000.0 / (t2-now); } else { [mCaptureSession stepBackward]; double t2 = getProperty(CV_CAP_PROP_POS_MSEC); [mCaptureSession stepForward]; retval = 1000.0 / (now-t2); } [localpool drain]; return retval; } double CvCaptureFile::getProperty(int property_id) const{ if (mCaptureSession == nil) return 0; NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; double retval; QTTime t; //cerr << "get_prop"< 0) { QTMedia *media = [[videoTracks objectAtIndex:0] media]; retval = [[media attributeForKey:QTMediaSampleCountAttribute] longValue]; } else { retval = 0; } } break; case CV_CAP_PROP_FOURCC: default: retval = false; } [localpool drain]; return retval; } /***************************************************************************** * * CvVideoWriter Implementation. * * CvVideoWriter is the instantiation of a video output class * *****************************************************************************/ CvVideoWriter_QT::CvVideoWriter_QT(const char* filename, int fourcc, double fps, CvSize frame_size, int is_color) { NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; movieFPS = fps; movieSize = frame_size; movieColor = is_color; mMovie = nil; path = [[[NSString stringWithCString:filename encoding:NSASCIIStringEncoding] stringByExpandingTildeInPath] retain]; argbimage = cvCreateImage(movieSize, IPL_DEPTH_8U, 4); char cc[5]; cc[0] = fourcc & 255; cc[1] = (fourcc >> 8) & 255; cc[2] = (fourcc >> 16) & 255; cc[3] = (fourcc >> 24) & 255; cc[4] = 0; int cc2 = CV_FOURCC(cc[0], cc[1], cc[2], cc[3]); if (cc2!=fourcc) { std::cout << "WARNING: Didn't properly encode FourCC. Expected " << fourcc << " but got " << cc2 << "." << std::endl; } codec = [[NSString stringWithCString:cc encoding:NSASCIIStringEncoding] retain]; NSError *error = nil; if (!mMovie) { NSFileManager* files = [NSFileManager defaultManager]; if ([files fileExistsAtPath:path]) { if (![files removeItemAtPath:path error:nil]) { std::cout << "WARNING: Failed to remove existing file " << [path cStringUsingEncoding:NSASCIIStringEncoding] << std::endl; } } mMovie = [[QTMovie alloc] initToWritableFile:path error:&error]; if (!mMovie) { std::cout << "WARNING: Could not create empty movie file container." << std::endl; [localpool drain]; return; } } [mMovie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute]; [localpool drain]; } CvVideoWriter_QT::~CvVideoWriter_QT() { cvReleaseImage(&argbimage); NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; [mMovie release]; [path release]; [codec release]; [localpool drain]; } bool CvVideoWriter_QT::writeFrame(const IplImage* image) { NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; cvCvtColor(image, argbimage, CV_BGR2BGRA); unsigned char* imagedata_ = (unsigned char*)argbimage->imageData; //BGRA --> ARGB for (int j = 0; j < argbimage->height; j++) { int rowstart = argbimage->widthStep * j; for (int i = rowstart; i < rowstart+argbimage->widthStep; i+=4) { unsigned char temp = imagedata_[i]; imagedata_[i] = 255; imagedata_[i+3] = temp; temp = imagedata_[i+2]; imagedata_[i+2] = imagedata_[i+1]; imagedata_[i+1] = temp; } } NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&imagedata_ pixelsWide:movieSize.width pixelsHigh:movieSize.height bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bitmapFormat:NSAlphaFirstBitmapFormat bytesPerRow:argbimage->widthStep bitsPerPixel:32] ; NSImage* nsimage = [[NSImage alloc] init]; [nsimage addRepresentation:imageRep]; /* codecLosslessQuality = 0x00000400, codecMaxQuality = 0x000003FF, codecMinQuality = 0x00000000, codecLowQuality = 0x00000100, codecNormalQuality = 0x00000200, codecHighQuality = 0x00000300 */ [mMovie addImage:nsimage forDuration:QTMakeTime(100,100*movieFPS) withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: codec, QTAddImageCodecType, //[NSNumber numberWithInt:codecLowQuality], QTAddImageCodecQuality, [NSNumber numberWithInt:100*movieFPS], QTTrackTimeScaleAttribute,nil]]; if (![mMovie updateMovieFile]) { std::cout << "Didn't successfully update movie file." << std::endl; } [imageRep release]; [nsimage release]; [localpool drain]; return 1; }