Home | History | Annotate | Download | only in src
      1 /*M///////////////////////////////////////////////////////////////////////////////////////
      2 //
      3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
      4 //
      5 // By downloading, copying, installing or using the software you agree to this license.
      6 // If you do not agree to this license, do not download, install,
      7 // copy or use the software.
      8 //
      9 //
     10 //                          License Agreement
     11 //                For Open Source Computer Vision Library
     12 //
     13 // Copyright (C) 2013, OpenCV Foundation, all rights reserved.
     14 // Third party copyrights are property of their respective owners.
     15 //
     16 // Redistribution and use in source and binary forms, with or without modification,
     17 // are permitted provided that the following conditions are met:
     18 //
     19 // * Redistribution's of source code must retain the above copyright notice,
     20 // this list of conditions and the following disclaimer.
     21 //
     22 // * Redistribution's in binary form must reproduce the above copyright notice,
     23 // this list of conditions and the following disclaimer in the documentation
     24 // and/or other materials provided with the distribution.
     25 //
     26 // * The name of the copyright holders may not be used to endorse or promote products
     27 // derived from this software without specific prior written permission.
     28 //
     29 // This software is provided by the copyright holders and contributors "as is" and
     30 // any express or implied warranties, including, but not limited to, the implied
     31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
     32 // In no event shall the contributor be liable for any direct,
     33 // indirect, incidental, special, exemplary, or consequential damages
     34 // (including, but not limited to, procurement of substitute goods or services;
     35 // loss of use, data, or profits; or business interruption) however caused
     36 // and on any theory of liability, whether in contract, strict liability,
     37 // or tort (including negligence or otherwise) arising in any way out of
     38 // the use of this software, even if advised of the possibility of such damage.
     39 //
     40 //M*////////////////////////////////////////////////////////////////////////////////////////
     41 
     42 
     43 #include "precomp.hpp"
     44 #include "opencv2/imgproc.hpp"
     45 #include <iostream>
     46 #import <QTKit/QTKit.h>
     47 
     48 /********************** Declaration of class headers ************************/
     49 
     50 /*****************************************************************************
     51  *
     52  * CaptureDelegate Declaration.
     53  *
     54  * CaptureDelegate is notified on a separate thread by the OS whenever there
     55  *   is a new frame. When "updateImage" is called from the main thread, it
     56  *   copies this new frame into an IplImage, but only if this frame has not
     57  *   been copied before. When "getOutput" is called from the main thread,
     58  *   it gives the last copied IplImage.
     59  *
     60  *****************************************************************************/
     61 
     62 #ifndef QTKIT_VERSION_7_6_3
     63 #define QTKIT_VERSION_7_6_3         70603
     64 #define QTKIT_VERSION_7_0           70000
     65 #endif
     66 
     67 #ifndef QTKIT_VERSION_MAX_ALLOWED
     68 #define QTKIT_VERSION_MAX_ALLOWED QTKIT_VERSION_7_0
     69 #endif
     70 
     71 #define DISABLE_AUTO_RESTART 999
     72 
     73 @interface CaptureDelegate : NSObject
     74 {
     75     int newFrame;
     76     CVImageBufferRef  mCurrentImageBuffer;
     77     char* imagedata;
     78     IplImage* image;
     79     char* bgr_imagedata;
     80     IplImage* bgr_image;
     81     size_t currSize;
     82 }
     83 
     84 - (void)captureOutput:(QTCaptureOutput *)captureOutput
     85   didOutputVideoFrame:(CVImageBufferRef)videoFrame
     86      withSampleBuffer:(QTSampleBuffer *)sampleBuffer
     87        fromConnection:(QTCaptureConnection *)connection;
     88 
     89 - (void)captureOutput:(QTCaptureOutput *)captureOutput
     90 didDropVideoFrameWithSampleBuffer:(QTSampleBuffer *)sampleBuffer
     91        fromConnection:(QTCaptureConnection *)connection;
     92 
     93 - (int)updateImage;
     94 - (IplImage*)getOutput;
     95 
     96 @end
     97 
     98 /*****************************************************************************
     99  *
    100  * CvCaptureCAM Declaration.
    101  *
    102  * CvCaptureCAM is the instantiation of a capture source for cameras.
    103  *
    104  *****************************************************************************/
    105 
    106 class CvCaptureCAM : public CvCapture {
    107 public:
    108     CvCaptureCAM(int cameraNum = -1) ;
    109     ~CvCaptureCAM();
    110     virtual bool grabFrame();
    111     virtual IplImage* retrieveFrame(int);
    112     virtual double getProperty(int property_id) const;
    113     virtual bool setProperty(int property_id, double value);
    114     virtual int didStart();
    115 
    116 
    117 private:
    118     QTCaptureSession            *mCaptureSession;
    119     QTCaptureDeviceInput        *mCaptureDeviceInput;
    120     QTCaptureDecompressedVideoOutput    *mCaptureDecompressedVideoOutput;
    121     CaptureDelegate* capture;
    122 
    123     int startCaptureDevice(int cameraNum);
    124     void stopCaptureDevice();
    125 
    126     void setWidthHeight();
    127     bool grabFrame(double timeOut);
    128 
    129     int camNum;
    130     int width;
    131     int height;
    132     int settingWidth;
    133     int settingHeight;
    134     int started;
    135     int disableAutoRestart;
    136 
    137 };
    138 
    139 
    140 /*****************************************************************************
    141  *
    142  * CvCaptureFile Declaration.
    143  *
    144  * CvCaptureFile is the instantiation of a capture source for video files.
    145  *
    146  *****************************************************************************/
    147 
    148 class CvCaptureFile : public CvCapture {
    149 public:
    150     CvCaptureFile(const char* filename) ;
    151     ~CvCaptureFile();
    152     virtual bool grabFrame();
    153     virtual IplImage* retrieveFrame(int);
    154     virtual double getProperty(int property_id) const;
    155     virtual bool setProperty(int property_id, double value);
    156     virtual int didStart();
    157 
    158 
    159 private:
    160     QTMovie *mCaptureSession;
    161 
    162     char* imagedata;
    163     IplImage* image;
    164     char* bgr_imagedata;
    165     IplImage* bgr_image;
    166     size_t currSize;
    167 
    168     //IplImage* retrieveFrameBitmap();
    169     IplImage* retrieveFramePixelBuffer();
    170     double getFPS();
    171 
    172     int movieWidth;
    173     int movieHeight;
    174     double movieFPS;
    175     double currentFPS;
    176     double movieDuration;
    177     int changedPos;
    178 
    179     int started;
    180     QTTime endOfMovie;
    181 };
    182 
    183 
    184 /*****************************************************************************
    185  *
    186  * CvCaptureFile Declaration.
    187  *
    188  * CvCaptureFile is the instantiation of a capture source for video files.
    189  *
    190  *****************************************************************************/
    191 
    192 class CvVideoWriter_QT : public CvVideoWriter{
    193 public:
    194     CvVideoWriter_QT(const char* filename, int fourcc,
    195                    double fps, CvSize frame_size,
    196                    int is_color=1);
    197     ~CvVideoWriter_QT();
    198     bool writeFrame(const IplImage* image);
    199 private:
    200     IplImage* argbimage;
    201     QTMovie* mMovie;
    202 
    203     NSString* path;
    204     NSString* codec;
    205     double movieFPS;
    206     CvSize movieSize;
    207     int movieColor;
    208 };
    209 
    210 
    211 /****************** Implementation of interface functions ********************/
    212 
    213 
    214 CvCapture* cvCreateFileCapture_QT(const char* filename) {
    215     CvCaptureFile *retval = new CvCaptureFile(filename);
    216 
    217     if(retval->didStart())
    218         return retval;
    219     delete retval;
    220     return NULL;
    221 }
    222 
    223 CvCapture* cvCreateCameraCapture_QT(int index ) {
    224     CvCapture* retval = new CvCaptureCAM(index);
    225     if (!((CvCaptureCAM *)retval)->didStart())
    226         cvReleaseCapture(&retval);
    227     return retval;
    228 }
    229 
    230 CvVideoWriter* cvCreateVideoWriter_QT(const char* filename, int fourcc,
    231                                      double fps, CvSize frame_size,
    232                                      int is_color) {
    233     return new CvVideoWriter_QT(filename, fourcc, fps, frame_size,is_color);
    234 }
    235 
    236 /********************** Implementation of Classes ****************************/
    237 
    238 /*****************************************************************************
    239  *
    240  * CvCaptureCAM Implementation.
    241  *
    242  * CvCaptureCAM is the instantiation of a capture source for cameras.
    243  *
    244  *****************************************************************************/
    245 
    246 CvCaptureCAM::CvCaptureCAM(int cameraNum) {
    247     mCaptureSession = nil;
    248     mCaptureDeviceInput = nil;
    249     mCaptureDecompressedVideoOutput = nil;
    250     capture = nil;
    251 
    252     width = 0;
    253     height = 0;
    254     settingWidth = 0;
    255     settingHeight = 0;
    256     disableAutoRestart = 0;
    257 
    258     camNum = cameraNum;
    259 
    260     if (!startCaptureDevice(camNum)) {
    261         std::cout << "Warning, camera failed to properly initialize!" << std::endl;
    262         started = 0;
    263     } else {
    264         started = 1;
    265     }
    266 
    267 }
    268 
    269 CvCaptureCAM::~CvCaptureCAM() {
    270     stopCaptureDevice();
    271 
    272     std::cout << "Cleaned up camera." << std::endl;
    273 }
    274 
    275 int CvCaptureCAM::didStart() {
    276     return started;
    277 }
    278 
    279 
    280 bool CvCaptureCAM::grabFrame() {
    281     return grabFrame(5);
    282 }
    283 
    284 bool CvCaptureCAM::grabFrame(double timeOut) {
    285 
    286     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    287     double sleepTime = 0.005;
    288     double total = 0;
    289 
    290     // If the capture is launched in a separate thread, then
    291     // [NSRunLoop currentRunLoop] is not the same as in the main thread, and has no timer.
    292     //see https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsrunloop_Class/Reference/Reference.html
    293     // "If no input sources or timers are attached to the run loop, this
    294     // method exits immediately"
    295     // using usleep() is not a good alternative, because it may block the GUI.
    296     // Create a dummy timer so that runUntilDate does not exit immediately:
    297     [NSTimer scheduledTimerWithTimeInterval:100 target:nil selector:@selector(doFireTimer:) userInfo:nil repeats:YES];
    298     while (![capture updateImage] && (total += sleepTime)<=timeOut) {
    299         [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:sleepTime]];
    300     }
    301 
    302     [localpool drain];
    303 
    304     return total <= timeOut;
    305 }
    306 
    307 IplImage* CvCaptureCAM::retrieveFrame(int) {
    308     return [capture getOutput];
    309 }
    310 
    311 void CvCaptureCAM::stopCaptureDevice() {
    312     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    313 
    314     [mCaptureSession stopRunning];
    315 
    316     QTCaptureDevice *device = [mCaptureDeviceInput device];
    317     if ([device isOpen])  [device close];
    318 
    319     [mCaptureSession release];
    320     [mCaptureDeviceInput release];
    321 
    322     [mCaptureDecompressedVideoOutput setDelegate:mCaptureDecompressedVideoOutput];
    323     [mCaptureDecompressedVideoOutput release];
    324     [capture release];
    325     [localpool drain];
    326 
    327 }
    328 
    329 int CvCaptureCAM::startCaptureDevice(int cameraNum) {
    330     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    331 
    332     capture = [[CaptureDelegate alloc] init];
    333 
    334     QTCaptureDevice *device;
    335     NSArray* devices = [[[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo]
    336             arrayByAddingObjectsFromArray:[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeMuxed]] retain];
    337 
    338     if ([devices count] == 0) {
    339         std::cout << "QTKit didn't find any attached Video Input Devices!" << std::endl;
    340         [localpool drain];
    341         return 0;
    342     }
    343 
    344     if (cameraNum >= 0) {
    345         NSUInteger nCameras = [devices count];
    346         if( (NSUInteger)cameraNum >= nCameras ) {
    347             [localpool drain];
    348             return 0;
    349         }
    350         device = [devices objectAtIndex:cameraNum] ;
    351     } else {
    352         device = [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeVideo]  ;
    353     }
    354     int success;
    355     NSError* error;
    356 
    357     if (device) {
    358 
    359         success = [device open:&error];
    360         if (!success) {
    361             std::cout << "QTKit failed to open a Video Capture Device" << std::endl;
    362             [localpool drain];
    363             return 0;
    364         }
    365 
    366         mCaptureDeviceInput = [[QTCaptureDeviceInput alloc] initWithDevice:device] ;
    367         mCaptureSession = [[QTCaptureSession alloc] init] ;
    368 
    369         success = [mCaptureSession addInput:mCaptureDeviceInput error:&error];
    370 
    371         if (!success) {
    372             std::cout << "QTKit failed to start capture session with opened Capture Device" << std::endl;
    373             [localpool drain];
    374             return 0;
    375         }
    376 
    377 
    378         mCaptureDecompressedVideoOutput = [[QTCaptureDecompressedVideoOutput alloc] init];
    379         [mCaptureDecompressedVideoOutput setDelegate:capture];
    380         NSDictionary *pixelBufferOptions ;
    381         if (width > 0 && height > 0) {
    382             pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys:
    383                                   [NSNumber numberWithDouble:1.0*width], (id)kCVPixelBufferWidthKey,
    384                                   [NSNumber numberWithDouble:1.0*height], (id)kCVPixelBufferHeightKey,
    385                                   //[NSNumber numberWithUnsignedInt:k32BGRAPixelFormat], (id)kCVPixelBufferPixelFormatTypeKey,
    386                                   [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA],
    387                                   (id)kCVPixelBufferPixelFormatTypeKey,
    388                                   nil];
    389         } else {
    390             pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys:
    391                                   [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA],
    392                                   (id)kCVPixelBufferPixelFormatTypeKey,
    393                                   nil];
    394         }
    395         [mCaptureDecompressedVideoOutput setPixelBufferAttributes:pixelBufferOptions];
    396 
    397 #if QTKIT_VERSION_MAX_ALLOWED >= QTKIT_VERSION_7_6_3
    398         [mCaptureDecompressedVideoOutput setAutomaticallyDropsLateVideoFrames:YES];
    399 #endif
    400 
    401 
    402         success = [mCaptureSession addOutput:mCaptureDecompressedVideoOutput error:&error];
    403         if (!success) {
    404             std::cout << "QTKit failed to add Output to Capture Session" << std::endl;
    405             [localpool drain];
    406             return 0;
    407         }
    408 
    409         [mCaptureSession startRunning];
    410 
    411         grabFrame(60);
    412 
    413         [localpool drain];
    414         return 1;
    415     }
    416 
    417     [localpool drain];
    418     return 0;
    419 }
    420 
    421 void CvCaptureCAM::setWidthHeight() {
    422     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    423 
    424     [mCaptureSession stopRunning];
    425 
    426     NSDictionary* pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys:
    427                           [NSNumber numberWithDouble:1.0*width], (id)kCVPixelBufferWidthKey,
    428                           [NSNumber numberWithDouble:1.0*height], (id)kCVPixelBufferHeightKey,
    429                           [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA],
    430                           (id)kCVPixelBufferPixelFormatTypeKey,
    431                           nil];
    432 
    433     [mCaptureDecompressedVideoOutput setPixelBufferAttributes:pixelBufferOptions];
    434 
    435     [mCaptureSession startRunning];
    436 
    437     grabFrame(60);
    438     [localpool drain];
    439 }
    440 
    441 
    442 double CvCaptureCAM::getProperty(int property_id) const{
    443     int retval;
    444     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    445 
    446     NSArray* connections = [mCaptureDeviceInput	connections];
    447     QTFormatDescription* format = [[connections objectAtIndex:0] formatDescription];
    448     NSSize s1 = [[format attributeForKey:QTFormatDescriptionVideoCleanApertureDisplaySizeAttribute] sizeValue];
    449 
    450     switch (property_id) {
    451         case CV_CAP_PROP_FRAME_WIDTH:
    452             retval = s1.width;
    453             break;
    454         case CV_CAP_PROP_FRAME_HEIGHT:
    455             retval = s1.height;
    456             break;
    457         default:
    458             retval = 0;
    459             break;
    460     }
    461 
    462     [localpool drain];
    463     return retval;
    464 }
    465 
    466 bool CvCaptureCAM::setProperty(int property_id, double value) {
    467     switch (property_id) {
    468         case CV_CAP_PROP_FRAME_WIDTH:
    469             width = value;
    470             settingWidth = 1;
    471             if (settingWidth && settingHeight) {
    472                 setWidthHeight();
    473                 settingWidth =0;
    474                 settingHeight = 0;
    475             }
    476             return true;
    477         case CV_CAP_PROP_FRAME_HEIGHT:
    478             height = value;
    479             settingHeight = 1;
    480             if (settingWidth && settingHeight) {
    481                 setWidthHeight();
    482                 settingWidth =0;
    483                 settingHeight = 0;
    484             }
    485             return true;
    486         case DISABLE_AUTO_RESTART:
    487             disableAutoRestart = value;
    488             return 1;
    489         default:
    490             return false;
    491     }
    492 }
    493 
    494 
    495 /*****************************************************************************
    496  *
    497  * CaptureDelegate Implementation.
    498  *
    499  * CaptureDelegate is notified on a separate thread by the OS whenever there
    500  *   is a new frame. When "updateImage" is called from the main thread, it
    501  *   copies this new frame into an IplImage, but only if this frame has not
    502  *   been copied before. When "getOutput" is called from the main thread,
    503  *   it gives the last copied IplImage.
    504  *
    505  *****************************************************************************/
    506 
    507 
    508 @implementation CaptureDelegate
    509 
    510 - (id)init {
    511     self = [super init];
    512     if (self) {
    513         newFrame = 0;
    514         imagedata = NULL;
    515         bgr_imagedata = NULL;
    516         currSize = 0;
    517         image = NULL;
    518         bgr_image = NULL;
    519     }
    520     return self;
    521 }
    522 
    523 
    524 -(void)dealloc {
    525     if (imagedata != NULL) free(imagedata);
    526     if (bgr_imagedata != NULL) free(bgr_imagedata);
    527     cvReleaseImage(&image);
    528     cvReleaseImage(&bgr_image);
    529     [super dealloc];
    530 }
    531 
    532 - (void)captureOutput:(QTCaptureOutput *)captureOutput
    533   didOutputVideoFrame:(CVImageBufferRef)videoFrame
    534      withSampleBuffer:(QTSampleBuffer *)sampleBuffer
    535        fromConnection:(QTCaptureConnection *)connection {
    536     (void)captureOutput;
    537     (void)sampleBuffer;
    538     (void)connection;
    539 
    540     CVBufferRetain(videoFrame);
    541     CVImageBufferRef imageBufferToRelease  = mCurrentImageBuffer;
    542 
    543     @synchronized (self) {
    544 
    545         mCurrentImageBuffer = videoFrame;
    546         newFrame = 1;
    547     }
    548 
    549     CVBufferRelease(imageBufferToRelease);
    550 
    551 }
    552 - (void)captureOutput:(QTCaptureOutput *)captureOutput
    553 didDropVideoFrameWithSampleBuffer:(QTSampleBuffer *)sampleBuffer
    554        fromConnection:(QTCaptureConnection *)connection {
    555     (void)captureOutput;
    556     (void)sampleBuffer;
    557     (void)connection;
    558     std::cout << "Camera dropped frame!" << std::endl;
    559 }
    560 
    561 -(IplImage*) getOutput {
    562     return bgr_image;
    563 }
    564 
    565 -(int) updateImage {
    566     if (newFrame==0) return 0;
    567     CVPixelBufferRef pixels;
    568 
    569     @synchronized (self){
    570         pixels = CVBufferRetain(mCurrentImageBuffer);
    571         newFrame = 0;
    572     }
    573 
    574     CVPixelBufferLockBaseAddress(pixels, 0);
    575     uint32_t* baseaddress = (uint32_t*)CVPixelBufferGetBaseAddress(pixels);
    576 
    577     size_t width = CVPixelBufferGetWidth(pixels);
    578     size_t height = CVPixelBufferGetHeight(pixels);
    579     size_t rowBytes = CVPixelBufferGetBytesPerRow(pixels);
    580 
    581     if (rowBytes != 0) {
    582 
    583         if (currSize != rowBytes*height*sizeof(char)) {
    584             currSize = rowBytes*height*sizeof(char);
    585             if (imagedata != NULL) free(imagedata);
    586             if (bgr_imagedata != NULL) free(bgr_imagedata);
    587             imagedata = (char*)malloc(currSize);
    588             bgr_imagedata = (char*)malloc(currSize);
    589         }
    590 
    591         memcpy(imagedata, baseaddress, currSize);
    592 
    593         if (image == NULL) {
    594             image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 4);
    595         }
    596         image->width = (int)width;
    597         image->height = (int)height;
    598         image->nChannels = 4;
    599         image->depth = IPL_DEPTH_8U;
    600         image->widthStep = (int)rowBytes;
    601         image->imageData = imagedata;
    602         image->imageSize = (int)currSize;
    603 
    604         if (bgr_image == NULL) {
    605             bgr_image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 3);
    606         }
    607         bgr_image->width = (int)width;
    608         bgr_image->height = (int)height;
    609         bgr_image->nChannels = 3;
    610         bgr_image->depth = IPL_DEPTH_8U;
    611         bgr_image->widthStep = (int)rowBytes;
    612         bgr_image->imageData = bgr_imagedata;
    613         bgr_image->imageSize = (int)currSize;
    614 
    615         cvCvtColor(image, bgr_image, CV_BGRA2BGR);
    616 
    617     }
    618 
    619     CVPixelBufferUnlockBaseAddress(pixels, 0);
    620     CVBufferRelease(pixels);
    621 
    622     return 1;
    623 }
    624 
    625 @end
    626 
    627 
    628 /*****************************************************************************
    629  *
    630  * CvCaptureFile Implementation.
    631  *
    632  * CvCaptureFile is the instantiation of a capture source for video files.
    633  *
    634  *****************************************************************************/
    635 
    636 CvCaptureFile::CvCaptureFile(const char* filename) {
    637 
    638     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    639 
    640 
    641     mCaptureSession = nil;
    642     image = NULL;
    643     bgr_image = NULL;
    644     imagedata = NULL;
    645     bgr_imagedata = NULL;
    646     currSize = 0;
    647 
    648     movieWidth = 0;
    649     movieHeight = 0;
    650     movieFPS = 0;
    651     currentFPS = 0;
    652     movieDuration = 0;
    653     changedPos = 0;
    654 
    655     started = 0;
    656 
    657     NSError* error;
    658 
    659 
    660     mCaptureSession = [[QTMovie movieWithFile:[NSString stringWithCString:filename
    661                                                                  encoding:NSASCIIStringEncoding]
    662                                         error:&error] retain];
    663     [mCaptureSession setAttribute:[NSNumber numberWithBool:YES]
    664                            forKey:QTMovieLoopsAttribute];
    665 
    666     if (mCaptureSession == nil) {
    667         std::cout << "WARNING: Couldn't read movie file " << filename << std::endl;
    668         [localpool drain];
    669         started = 0;
    670         return;
    671     }
    672 
    673     [mCaptureSession gotoEnd];
    674     endOfMovie = [mCaptureSession currentTime];
    675 
    676     [mCaptureSession gotoBeginning];
    677 
    678     NSSize size = [[mCaptureSession attributeForKey:QTMovieNaturalSizeAttribute] sizeValue];
    679 
    680     movieWidth = size.width;
    681     movieHeight = size.height;
    682     movieFPS = getFPS();
    683     currentFPS = movieFPS;
    684 
    685     QTTime t;
    686 
    687     [[mCaptureSession attributeForKey:QTMovieDurationAttribute] getValue:&t];
    688     movieDuration = (t.timeValue *1000.0 / t.timeScale);
    689     started = 1;
    690     [localpool drain];
    691 
    692 }
    693 
    694 CvCaptureFile::~CvCaptureFile() {
    695     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    696     if (imagedata != NULL) free(imagedata);
    697     if (bgr_imagedata != NULL) free(bgr_imagedata);
    698     cvReleaseImage(&image);
    699     cvReleaseImage(&bgr_image);
    700     [mCaptureSession release];
    701     [localpool drain];
    702 }
    703 
    704 int CvCaptureFile::didStart() {
    705     return started;
    706 }
    707 
    708 bool CvCaptureFile::grabFrame() {
    709     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    710     double t1 = getProperty(CV_CAP_PROP_POS_MSEC);
    711 
    712     QTTime curTime;
    713     curTime = [mCaptureSession currentTime];
    714     bool isEnd=(QTTimeCompare(curTime,endOfMovie) == NSOrderedSame);
    715 
    716     [mCaptureSession stepForward];
    717     double t2 = getProperty(CV_CAP_PROP_POS_MSEC);
    718     if (t2>t1 && !changedPos) {
    719         currentFPS = 1000.0/(t2-t1);
    720     } else {
    721         currentFPS = movieFPS;
    722     }
    723     changedPos = 0;
    724     [localpool drain];
    725     return !isEnd;
    726 }
    727 
    728 
    729 IplImage* CvCaptureFile::retrieveFramePixelBuffer() {
    730     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    731 
    732 
    733     NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
    734                                 QTMovieFrameImageTypeCVPixelBufferRef, QTMovieFrameImageType,
    735 #ifdef MAC_OS_X_VERSION_10_6
    736                                 [NSNumber numberWithBool:YES], QTMovieFrameImageSessionMode,
    737 #endif
    738                                 nil];
    739     CVPixelBufferRef frame = (CVPixelBufferRef)[mCaptureSession frameImageAtTime:[mCaptureSession currentTime]
    740                                                                   withAttributes:attributes
    741                                                                            error:nil];
    742 
    743     CVPixelBufferRef pixels = CVBufferRetain(frame);
    744     CVPixelBufferLockBaseAddress(pixels, 0);
    745 
    746     uint32_t* baseaddress = (uint32_t*)CVPixelBufferGetBaseAddress(pixels);
    747     size_t width = CVPixelBufferGetWidth(pixels);
    748     size_t height = CVPixelBufferGetHeight(pixels);
    749     size_t rowBytes = CVPixelBufferGetBytesPerRow(pixels);
    750 
    751     if (rowBytes != 0) {
    752 
    753         if (currSize != rowBytes*height*sizeof(char)) {
    754             currSize = rowBytes*height*sizeof(char);
    755             if (imagedata != NULL) free(imagedata);
    756             if (bgr_imagedata != NULL) free(bgr_imagedata);
    757             imagedata = (char*)malloc(currSize);
    758             bgr_imagedata = (char*)malloc(currSize);
    759         }
    760 
    761         memcpy(imagedata, baseaddress, currSize);
    762 
    763         //ARGB -> BGRA
    764         for (unsigned int i = 0; i < currSize; i+=4) {
    765             char temp = imagedata[i];
    766             imagedata[i] = imagedata[i+3];
    767             imagedata[i+3] = temp;
    768             temp = imagedata[i+1];
    769             imagedata[i+1] = imagedata[i+2];
    770             imagedata[i+2] = temp;
    771         }
    772 
    773         if (image == NULL) {
    774             image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 4);
    775         }
    776 
    777         image->width = (int)width;
    778         image->height = (int)height;
    779         image->nChannels = 4;
    780         image->depth = IPL_DEPTH_8U;
    781         image->widthStep = (int)rowBytes;
    782         image->imageData = imagedata;
    783         image->imageSize = (int)currSize;
    784 
    785 
    786         if (bgr_image == NULL) {
    787             bgr_image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 3);
    788         }
    789 
    790         bgr_image->width = (int)width;
    791         bgr_image->height = (int)height;
    792         bgr_image->nChannels = 3;
    793         bgr_image->depth = IPL_DEPTH_8U;
    794         bgr_image->widthStep = (int)rowBytes;
    795         bgr_image->imageData = bgr_imagedata;
    796         bgr_image->imageSize = (int)currSize;
    797 
    798         cvCvtColor(image, bgr_image,CV_BGRA2BGR);
    799 
    800     }
    801 
    802     CVPixelBufferUnlockBaseAddress(pixels, 0);
    803     CVBufferRelease(pixels);
    804 
    805     [localpool drain];
    806 
    807     return bgr_image;
    808 }
    809 
    810 
    811 IplImage* CvCaptureFile::retrieveFrame(int) {
    812     return retrieveFramePixelBuffer();
    813 }
    814 
    815 double CvCaptureFile::getFPS() {
    816     if (mCaptureSession == nil) return 0;
    817     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    818     double now = getProperty(CV_CAP_PROP_POS_MSEC);
    819     double retval = 0;
    820     if (now == 0) {
    821         [mCaptureSession stepForward];
    822         double t2 =  getProperty(CV_CAP_PROP_POS_MSEC);
    823         [mCaptureSession stepBackward];
    824         retval = 1000.0 / (t2-now);
    825     } else {
    826         [mCaptureSession stepBackward];
    827         double t2 = getProperty(CV_CAP_PROP_POS_MSEC);
    828         [mCaptureSession stepForward];
    829         retval = 1000.0 / (now-t2);
    830     }
    831     [localpool drain];
    832     return retval;
    833 }
    834 
    835 double CvCaptureFile::getProperty(int property_id) const{
    836     if (mCaptureSession == nil) return 0;
    837 
    838     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    839 
    840     double retval;
    841     QTTime t;
    842 
    843     //cerr << "get_prop"<<std::endl;
    844     switch (property_id) {
    845         case CV_CAP_PROP_POS_MSEC:
    846             [[mCaptureSession attributeForKey:QTMovieCurrentTimeAttribute] getValue:&t];
    847             retval = t.timeValue * 1000.0 / t.timeScale;
    848             break;
    849         case CV_CAP_PROP_POS_FRAMES:
    850             retval = movieFPS * getProperty(CV_CAP_PROP_POS_MSEC) / 1000;
    851             break;
    852         case CV_CAP_PROP_POS_AVI_RATIO:
    853             retval = (getProperty(CV_CAP_PROP_POS_MSEC)) / (movieDuration );
    854             break;
    855         case CV_CAP_PROP_FRAME_WIDTH:
    856             retval = movieWidth;
    857             break;
    858         case CV_CAP_PROP_FRAME_HEIGHT:
    859             retval = movieHeight;
    860             break;
    861         case CV_CAP_PROP_FPS:
    862             retval = currentFPS;
    863             break;
    864         case CV_CAP_PROP_FRAME_COUNT:
    865             retval = movieDuration*movieFPS/1000;
    866             break;
    867         case CV_CAP_PROP_FOURCC:
    868         default:
    869             retval = 0;
    870     }
    871 
    872     [localpool drain];
    873     return retval;
    874 }
    875 
    876 bool CvCaptureFile::setProperty(int property_id, double value) {
    877 
    878     if (mCaptureSession == nil) return false;
    879 
    880     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    881 
    882     bool retval = false;
    883     QTTime t;
    884 
    885     double ms;
    886 
    887     switch (property_id) {
    888         case CV_CAP_PROP_POS_MSEC:
    889             [[mCaptureSession attributeForKey:QTMovieCurrentTimeAttribute] getValue:&t];
    890             t.timeValue = value * t.timeScale / 1000;
    891             [mCaptureSession setCurrentTime:t];
    892             changedPos = 1;
    893             retval = true;
    894             break;
    895         case CV_CAP_PROP_POS_FRAMES:
    896             ms = (value*1000.0 -5)/ currentFPS;
    897             retval = setProperty(CV_CAP_PROP_POS_MSEC, ms);
    898             break;
    899         case CV_CAP_PROP_POS_AVI_RATIO:
    900             ms = value * movieDuration;
    901             retval = setProperty(CV_CAP_PROP_POS_MSEC, ms);
    902             break;
    903         case CV_CAP_PROP_FRAME_WIDTH:
    904             //retval = movieWidth;
    905             break;
    906         case CV_CAP_PROP_FRAME_HEIGHT:
    907             //retval = movieHeight;
    908             break;
    909         case CV_CAP_PROP_FPS:
    910             //etval = currentFPS;
    911             break;
    912         case CV_CAP_PROP_FRAME_COUNT:
    913             {
    914             NSArray *videoTracks = [mCaptureSession tracksOfMediaType:QTMediaTypeVideo];
    915             if ([videoTracks count] > 0) {
    916                 QTMedia *media = [[videoTracks objectAtIndex:0] media];
    917                 retval = [[media attributeForKey:QTMediaSampleCountAttribute] longValue];
    918             } else {
    919                 retval = 0;
    920             }
    921             }
    922             break;
    923         case CV_CAP_PROP_FOURCC:
    924         default:
    925             retval = false;
    926     }
    927 
    928     [localpool drain];
    929     return retval;
    930 }
    931 
    932 
    933 /*****************************************************************************
    934  *
    935  * CvVideoWriter Implementation.
    936  *
    937  * CvVideoWriter is the instantiation of a video output class
    938  *
    939  *****************************************************************************/
    940 
    941 
    942 CvVideoWriter_QT::CvVideoWriter_QT(const char* filename, int fourcc,
    943                                double fps, CvSize frame_size,
    944                                int is_color) {
    945 
    946 
    947     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    948 
    949     movieFPS = fps;
    950     movieSize = frame_size;
    951     movieColor = is_color;
    952     mMovie = nil;
    953     path = [[[NSString stringWithCString:filename encoding:NSASCIIStringEncoding] stringByExpandingTildeInPath] retain];
    954 
    955     argbimage = cvCreateImage(movieSize, IPL_DEPTH_8U, 4);
    956 
    957 
    958     char cc[5];
    959     cc[0] = fourcc & 255;
    960     cc[1] = (fourcc >> 8) & 255;
    961     cc[2] = (fourcc >> 16) & 255;
    962     cc[3] = (fourcc >> 24) & 255;
    963     cc[4] = 0;
    964     int cc2 = CV_FOURCC(cc[0], cc[1], cc[2], cc[3]);
    965     if (cc2!=fourcc) {
    966         std::cout << "WARNING: Didn't properly encode FourCC. Expected " << fourcc
    967         << " but got " << cc2 << "." << std::endl;
    968     }
    969 
    970     codec = [[NSString stringWithCString:cc encoding:NSASCIIStringEncoding] retain];
    971 
    972     NSError *error = nil;
    973     if (!mMovie) {
    974 
    975         NSFileManager* files = [NSFileManager defaultManager];
    976         if ([files fileExistsAtPath:path]) {
    977             if (![files removeItemAtPath:path error:nil]) {
    978                 std::cout << "WARNING: Failed to remove existing file " << [path cStringUsingEncoding:NSASCIIStringEncoding] << std::endl;
    979             }
    980         }
    981 
    982         mMovie = [[QTMovie alloc] initToWritableFile:path error:&error];
    983         if (!mMovie) {
    984             std::cout << "WARNING: Could not create empty movie file container." << std::endl;
    985             [localpool drain];
    986             return;
    987         }
    988     }
    989 
    990     [mMovie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute];
    991 
    992     [localpool drain];
    993 }
    994 
    995 
    996 CvVideoWriter_QT::~CvVideoWriter_QT() {
    997     cvReleaseImage(&argbimage);
    998 
    999     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
   1000     [mMovie release];
   1001     [path release];
   1002     [codec release];
   1003     [localpool drain];
   1004 }
   1005 
   1006 bool CvVideoWriter_QT::writeFrame(const IplImage* image) {
   1007     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
   1008 
   1009     cvCvtColor(image, argbimage, CV_BGR2BGRA);
   1010 
   1011 
   1012     unsigned char* imagedata_ = (unsigned char*)argbimage->imageData;
   1013     //BGRA --> ARGB
   1014 
   1015     for (int j = 0; j < argbimage->height; j++) {
   1016         int rowstart = argbimage->widthStep * j;
   1017         for (int i = rowstart; i < rowstart+argbimage->widthStep; i+=4) {
   1018             unsigned char temp = imagedata_[i];
   1019             imagedata_[i] = 255;
   1020             imagedata_[i+3] = temp;
   1021             temp = imagedata_[i+2];
   1022             imagedata_[i+2] = imagedata_[i+1];
   1023             imagedata_[i+1] = temp;
   1024         }
   1025     }
   1026 
   1027     NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&imagedata_
   1028                                                                          pixelsWide:movieSize.width
   1029                                                                          pixelsHigh:movieSize.height
   1030                                                                       bitsPerSample:8
   1031                                                                     samplesPerPixel:4
   1032                                                                            hasAlpha:YES
   1033                                                                            isPlanar:NO
   1034                                                                      colorSpaceName:NSDeviceRGBColorSpace
   1035                                                                        bitmapFormat:NSAlphaFirstBitmapFormat
   1036                                                                         bytesPerRow:argbimage->widthStep
   1037                                                                        bitsPerPixel:32]  ;
   1038 
   1039 
   1040     NSImage* nsimage = [[NSImage alloc] init];
   1041 
   1042     [nsimage addRepresentation:imageRep];
   1043 
   1044     /*
   1045      codecLosslessQuality          = 0x00000400,
   1046      codecMaxQuality               = 0x000003FF,
   1047      codecMinQuality               = 0x00000000,
   1048      codecLowQuality               = 0x00000100,
   1049      codecNormalQuality            = 0x00000200,
   1050      codecHighQuality              = 0x00000300
   1051      */
   1052 
   1053     [mMovie addImage:nsimage forDuration:QTMakeTime(100,100*movieFPS) withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
   1054                                                                                       codec, QTAddImageCodecType,
   1055                                                                                       //[NSNumber numberWithInt:codecLowQuality], QTAddImageCodecQuality,
   1056                                                                                       [NSNumber numberWithInt:100*movieFPS], QTTrackTimeScaleAttribute,nil]];
   1057 
   1058     if (![mMovie updateMovieFile]) {
   1059         std::cout << "Didn't successfully update movie file." << std::endl;
   1060     }
   1061 
   1062     [imageRep release];
   1063     [nsimage release];
   1064     [localpool drain];
   1065 
   1066     return 1;
   1067 }
   1068