Home | History | Annotate | Download | only in src
      1 /*
      2  *  cap_avfoundation.mm
      3  *  For iOS video I/O
      4  *  by Xiaochao Yang on 06/15/11 modified from
      5  *  cap_qtkit.mm for Nicholas Butko for Mac OS version.
      6  *  Copyright 2011. All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions are met:
     10  *
     11  * 1. Redistributions of source code must retain the above copyright notice,
     12  *    this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright notice,
     14  *    this list of conditions and the following disclaimer in the documentation
     15  *    and/or other materials provided with the distribution.
     16  * 3. The name of the author may not be used to endorse or promote products
     17  *    derived from this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
     20  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     21  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     22  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     25  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     27  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     28  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  *
     30  */
     31 
     32 #include "precomp.hpp"
     33 #include "opencv2/imgproc.hpp"
     34 #include <iostream>
     35 #import <AVFoundation/AVFoundation.h>
     36 #import <Foundation/NSException.h>
     37 
     38 
     39 /********************** Declaration of class headers ************************/
     40 
     41 /*****************************************************************************
     42  *
     43  * CaptureDelegate Declaration.
     44  *
     45  * CaptureDelegate is notified on a separate thread by the OS whenever there
     46  *   is a new frame. When "updateImage" is called from the main thread, it
     47  *   copies this new frame into an IplImage, but only if this frame has not
     48  *   been copied before. When "getOutput" is called from the main thread,
     49  *   it gives the last copied IplImage.
     50  *
     51  *****************************************************************************/
     52 
     53 #define DISABLE_AUTO_RESTART 999
     54 
     55 @interface CaptureDelegate : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
     56 {
     57     int newFrame;
     58     CVImageBufferRef  mCurrentImageBuffer;
     59     char* imagedata;
     60     IplImage* image;
     61     char* bgr_imagedata;
     62     IplImage* bgr_image;
     63     IplImage* bgr_image_r90;
     64     size_t currSize;
     65 }
     66 
     67 - (void)captureOutput:(AVCaptureOutput *)captureOutput
     68 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
     69 fromConnection:(AVCaptureConnection *)connection;
     70 
     71 
     72 - (int)updateImage;
     73 - (IplImage*)getOutput;
     74 
     75 @end
     76 
     77 /*****************************************************************************
     78  *
     79  * CvCaptureCAM Declaration.
     80  *
     81  * CvCaptureCAM is the instantiation of a capture source for cameras.
     82  *
     83  *****************************************************************************/
     84 
     85 class CvCaptureCAM : public CvCapture {
     86     public:
     87         CvCaptureCAM(int cameraNum = -1) ;
     88         ~CvCaptureCAM();
     89         virtual bool grabFrame();
     90         virtual IplImage* retrieveFrame(int);
     91         virtual IplImage* queryFrame();
     92         virtual double getProperty(int property_id) const;
     93         virtual bool setProperty(int property_id, double value);
     94         virtual int didStart();
     95 
     96     private:
     97         AVCaptureSession            *mCaptureSession;
     98         AVCaptureDeviceInput        *mCaptureDeviceInput;
     99         AVCaptureVideoDataOutput    *mCaptureDecompressedVideoOutput;
    100         AVCaptureDevice 						*mCaptureDevice;
    101         CaptureDelegate							*capture;
    102 
    103         int startCaptureDevice(int cameraNum);
    104         void stopCaptureDevice();
    105 
    106         void setWidthHeight();
    107         bool grabFrame(double timeOut);
    108 
    109         int camNum;
    110         int width;
    111         int height;
    112         int settingWidth;
    113         int settingHeight;
    114         int started;
    115         int disableAutoRestart;
    116 };
    117 
    118 
    119 /*****************************************************************************
    120  *
    121  * CvCaptureFile Declaration.
    122  *
    123  * CvCaptureFile is the instantiation of a capture source for video files.
    124  *
    125  *****************************************************************************/
    126 
    127 class CvCaptureFile : public CvCapture {
    128     public:
    129 
    130         CvCaptureFile(const char* filename) ;
    131         ~CvCaptureFile();
    132         virtual bool grabFrame();
    133         virtual IplImage* retrieveFrame(int);
    134         virtual IplImage* queryFrame();
    135         virtual double getProperty(int property_id) const;
    136         virtual bool setProperty(int property_id, double value);
    137         virtual int didStart();
    138 
    139     private:
    140 
    141         AVAssetReader *mMovieReader;
    142         char* imagedata;
    143         IplImage* image;
    144         char* bgr_imagedata;
    145         IplImage* bgr_image;
    146         size_t currSize;
    147 
    148         IplImage* retrieveFramePixelBuffer();
    149         double getFPS();
    150 
    151         int movieWidth;
    152         int movieHeight;
    153         double movieFPS;
    154         double currentFPS;
    155         double movieDuration;
    156         int changedPos;
    157 
    158         int started;
    159 };
    160 
    161 
    162 /*****************************************************************************
    163  *
    164  * CvCaptureFile Declaration.
    165  *
    166  * CvCaptureFile is the instantiation of a capture source for video files.
    167  *
    168  *****************************************************************************/
    169 
    170 class CvVideoWriter_AVFoundation : public CvVideoWriter{
    171     public:
    172         CvVideoWriter_AVFoundation(const char* filename, int fourcc,
    173                 double fps, CvSize frame_size,
    174                 int is_color=1);
    175         ~CvVideoWriter_AVFoundation();
    176         bool writeFrame(const IplImage* image);
    177     private:
    178         IplImage* argbimage;
    179 
    180         AVAssetWriter *mMovieWriter;
    181         AVAssetWriterInput* mMovieWriterInput;
    182         AVAssetWriterInputPixelBufferAdaptor* mMovieWriterAdaptor;
    183 
    184         NSString* path;
    185         NSString* codec;
    186         NSString* fileType;
    187         double movieFPS;
    188         CvSize movieSize;
    189         int movieColor;
    190         unsigned long frameCount;
    191 };
    192 
    193 
    194 /****************** Implementation of interface functions ********************/
    195 
    196 
    197 CvCapture* cvCreateFileCapture_AVFoundation(const char* filename) {
    198     CvCaptureFile *retval = new CvCaptureFile(filename);
    199 
    200     if(retval->didStart())
    201         return retval;
    202     delete retval;
    203     return NULL;
    204 }
    205 
    206 CvCapture* cvCreateCameraCapture_AVFoundation(int index ) {
    207 
    208     CvCapture* retval = new CvCaptureCAM(index);
    209     if (!((CvCaptureCAM *)retval)->didStart())
    210         cvReleaseCapture(&retval);
    211     return retval;
    212 
    213 }
    214 
    215 CvVideoWriter* cvCreateVideoWriter_AVFoundation(const char* filename, int fourcc,
    216         double fps, CvSize frame_size,
    217         int is_color) {
    218     return new CvVideoWriter_AVFoundation(filename, fourcc, fps, frame_size,is_color);
    219 }
    220 
    221 /********************** Implementation of Classes ****************************/
    222 /*****************************************************************************
    223  *
    224  * CvCaptureCAM Implementation.
    225  *
    226  * CvCaptureCAM is the instantiation of a capture source for cameras.
    227  *
    228  *****************************************************************************/
    229 
    230 CvCaptureCAM::CvCaptureCAM(int cameraNum) {
    231     mCaptureSession = nil;
    232     mCaptureDeviceInput = nil;
    233     mCaptureDecompressedVideoOutput = nil;
    234     capture = nil;
    235 
    236     width = 0;
    237     height = 0;
    238     settingWidth = 0;
    239     settingHeight = 0;
    240     disableAutoRestart = 0;
    241 
    242     camNum = cameraNum;
    243 
    244     if (!startCaptureDevice(camNum)) {
    245         std::cout << "Warning, camera failed to properly initialize!" << std::endl;
    246         started = 0;
    247     } else {
    248         started = 1;
    249     }
    250 
    251 }
    252 
    253 CvCaptureCAM::~CvCaptureCAM() {
    254     stopCaptureDevice();
    255     //cout << "Cleaned up camera." << endl;
    256 }
    257 
    258 int CvCaptureCAM::didStart() {
    259     return started;
    260 }
    261 
    262 
    263 bool CvCaptureCAM::grabFrame() {
    264     return grabFrame(5);
    265 }
    266 
    267 bool CvCaptureCAM::grabFrame(double timeOut) {
    268 
    269     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    270     double sleepTime = 0.005;
    271     double total = 0;
    272 
    273     NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:sleepTime];
    274     while (![capture updateImage] && (total += sleepTime)<=timeOut &&
    275             [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
    276             beforeDate:loopUntil])
    277         loopUntil = [NSDate dateWithTimeIntervalSinceNow:sleepTime];
    278 
    279     [localpool drain];
    280 
    281     return total <= timeOut;
    282 }
    283 
    284 IplImage* CvCaptureCAM::retrieveFrame(int) {
    285     return [capture getOutput];
    286 }
    287 
    288 IplImage* CvCaptureCAM::queryFrame() {
    289     while (!grabFrame()) {
    290         std::cout << "WARNING: Couldn't grab new frame from camera!!!" << std::endl;
    291         /*
    292              cout << "Attempting to restart camera; set capture property DISABLE_AUTO_RESTART to disable." << endl;
    293              stopCaptureDevice();
    294              startCaptureDevice(camNum);
    295          */
    296     }
    297     return retrieveFrame(0);
    298 }
    299 
    300 void CvCaptureCAM::stopCaptureDevice() {
    301     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    302 
    303     [mCaptureSession stopRunning];
    304 
    305     [mCaptureSession release];
    306     [mCaptureDeviceInput release];
    307 
    308     [mCaptureDecompressedVideoOutput release];
    309     [capture release];
    310     [localpool drain];
    311 
    312 }
    313 
    314 int CvCaptureCAM::startCaptureDevice(int cameraNum) {
    315     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    316 
    317     capture = [[CaptureDelegate alloc] init];
    318 
    319     AVCaptureDevice *device;
    320     NSArray* devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    321     if ([devices count] == 0) {
    322         std::cout << "AV Foundation didn't find any attached Video Input Devices!" << std::endl;
    323         [localpool drain];
    324         return 0;
    325     }
    326 
    327     if (cameraNum >= 0) {
    328         camNum = cameraNum % [devices count];
    329         if (camNum != cameraNum) {
    330             std::cout << "Warning: Max Camera Num is " << [devices count]-1 << "; Using camera " << camNum << std::endl;
    331         }
    332         device = [devices objectAtIndex:camNum];
    333     } else {
    334         device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]  ;
    335     }
    336     mCaptureDevice = device;
    337     //int success;
    338     NSError* error;
    339 
    340     if (device) {
    341 
    342         mCaptureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error] ;
    343         mCaptureSession = [[AVCaptureSession alloc] init] ;
    344 
    345         /*
    346              success = [mCaptureSession addInput:mCaptureDeviceInput];
    347 
    348              if (!success) {
    349              cout << "AV Foundation failed to start capture session with opened Capture Device" << endl;
    350              [localpool drain];
    351              return 0;
    352              }
    353          */
    354 
    355         mCaptureDecompressedVideoOutput = [[AVCaptureVideoDataOutput alloc] init];
    356 
    357         dispatch_queue_t queue = dispatch_queue_create("cameraQueue", NULL);
    358         [mCaptureDecompressedVideoOutput setSampleBufferDelegate:capture queue:queue];
    359         dispatch_release(queue);
    360 
    361 
    362         NSDictionary *pixelBufferOptions ;
    363         if (width > 0 && height > 0) {
    364             pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys:
    365                 [NSNumber numberWithDouble:1.0*width], (id)kCVPixelBufferWidthKey,
    366                 [NSNumber numberWithDouble:1.0*height], (id)kCVPixelBufferHeightKey,
    367                 [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA],
    368                 (id)kCVPixelBufferPixelFormatTypeKey,
    369                 nil];
    370         } else {
    371             pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys:
    372                 [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA],
    373                 (id)kCVPixelBufferPixelFormatTypeKey,
    374                 nil];
    375         }
    376 
    377         //TODO: add new interface for setting fps and capturing resolution.
    378         [mCaptureDecompressedVideoOutput setVideoSettings:pixelBufferOptions];
    379         mCaptureDecompressedVideoOutput.alwaysDiscardsLateVideoFrames = YES;
    380 
    381 #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
    382         mCaptureDecompressedVideoOutput.minFrameDuration = CMTimeMake(1, 30);
    383 #endif
    384 
    385         //Slow. 1280*720 for iPhone4, iPod back camera. 640*480 for front camera
    386         //mCaptureSession.sessionPreset = AVCaptureSessionPresetHigh; // fps ~= 5 slow for OpenCV
    387 
    388         mCaptureSession.sessionPreset = AVCaptureSessionPresetMedium; //480*360
    389         if (width == 0 ) width = 480;
    390         if (height == 0 ) height = 360;
    391 
    392         [mCaptureSession addInput:mCaptureDeviceInput];
    393         [mCaptureSession addOutput:mCaptureDecompressedVideoOutput];
    394 
    395         /*
    396         // Does not work! This is the preferred way (hardware acceleration) to change pixel buffer orientation.
    397         // I'm now using cvtranspose and cvflip instead, which takes cpu cycles.
    398         AVCaptureConnection *connection = [[mCaptureDecompressedVideoOutput connections] objectAtIndex:0];
    399         if([connection isVideoOrientationSupported]) {
    400             //NSLog(@"Setting pixel buffer orientation");
    401             connection.videoOrientation = AVCaptureVideoOrientationPortrait;
    402         }
    403         */
    404 
    405         [mCaptureSession startRunning];
    406 
    407         grabFrame(60);
    408         [localpool drain];
    409         return 1;
    410     }
    411 
    412     [localpool drain];
    413     return 0;
    414 }
    415 
    416 void CvCaptureCAM::setWidthHeight() {
    417     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    418     NSDictionary* pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys:
    419         [NSNumber numberWithDouble:1.0*width], (id)kCVPixelBufferWidthKey,
    420         [NSNumber numberWithDouble:1.0*height], (id)kCVPixelBufferHeightKey,
    421         [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA],
    422         (id)kCVPixelBufferPixelFormatTypeKey,
    423         nil];
    424 
    425     [mCaptureDecompressedVideoOutput setVideoSettings:pixelBufferOptions];
    426     grabFrame(60);
    427     [localpool drain];
    428 }
    429 
    430 //added macros into headers in videoio_c.h
    431 /*
    432 #define CV_CAP_PROP_IOS_DEVICE_FOCUS 9001
    433 #define CV_CAP_PROP_IOS_DEVICE_EXPOSURE 9002
    434 #define CV_CAP_PROP_IOS_DEVICE_FLASH 9003
    435 #define CV_CAP_PROP_IOS_DEVICE_WHITEBALANCE 9004
    436 #define CV_CAP_PROP_IOS_DEVICE_TORCH 9005
    437 */
    438 
    439 
    440 /*
    441 // All available settings are taken from iOS API
    442 
    443 enum {
    444    AVCaptureFlashModeOff    = 0,
    445    AVCaptureFlashModeOn     = 1,
    446    AVCaptureFlashModeAuto   = 2
    447 };
    448 typedef NSInteger AVCaptureFlashMode;
    449 
    450 enum {
    451    AVCaptureTorchModeOff    = 0,
    452    AVCaptureTorchModeOn     = 1,
    453    AVCaptureTorchModeAuto   = 2
    454 };
    455 typedef NSInteger AVCaptureTorchMode;
    456 
    457 enum {
    458    AVCaptureFocusModeLocked                = 0,
    459    AVCaptureFocusModeAutoFocus             = 1,
    460    AVCaptureFocusModeContinuousAutoFocus   = 2,
    461 };
    462 typedef NSInteger AVCaptureFocusMode;
    463 
    464 enum {
    465    AVCaptureExposureModeLocked                    = 0,
    466    AVCaptureExposureModeAutoExpose                = 1,
    467    AVCaptureExposureModeContinuousAutoExposure    = 2,
    468 };
    469 typedef NSInteger AVCaptureExposureMode;
    470 
    471 enum {
    472    AVCaptureWhiteBalanceModeLocked             = 0,
    473    AVCaptureWhiteBalanceModeAutoWhiteBalance   = 1,
    474    AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance = 2,
    475 };
    476 typedef NSInteger AVCaptureWhiteBalanceMode;
    477 */
    478 
    479 double CvCaptureCAM::getProperty(int property_id) const{
    480     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    481 
    482     /*
    483          NSArray* connections = [mCaptureDeviceInput	connections];
    484          QTFormatDescription* format = [[connections objectAtIndex:0] formatDescription];
    485          NSSize s1 = [[format attributeForKey:QTFormatDescriptionVideoCleanApertureDisplaySizeAttribute] sizeValue];
    486      */
    487 
    488     NSArray* ports = mCaptureDeviceInput.ports;
    489     CMFormatDescriptionRef format = [[ports objectAtIndex:0] formatDescription];
    490     CGSize s1 = CMVideoFormatDescriptionGetPresentationDimensions(format, YES, YES);
    491 
    492     int w=(int)s1.width, h=(int)s1.height;
    493 
    494     [localpool drain];
    495 
    496     switch (property_id) {
    497         case CV_CAP_PROP_FRAME_WIDTH:
    498             return w;
    499         case CV_CAP_PROP_FRAME_HEIGHT:
    500             return h;
    501 
    502         case CV_CAP_PROP_IOS_DEVICE_FOCUS:
    503             return mCaptureDevice.focusMode;
    504         case CV_CAP_PROP_IOS_DEVICE_EXPOSURE:
    505             return mCaptureDevice.exposureMode;
    506         case CV_CAP_PROP_IOS_DEVICE_FLASH:
    507             return mCaptureDevice.flashMode;
    508         case CV_CAP_PROP_IOS_DEVICE_WHITEBALANCE:
    509             return mCaptureDevice.whiteBalanceMode;
    510         case CV_CAP_PROP_IOS_DEVICE_TORCH:
    511             return mCaptureDevice.torchMode;
    512 
    513         default:
    514             return 0;
    515     }
    516 
    517 
    518 }
    519 
    520 bool CvCaptureCAM::setProperty(int property_id, double value) {
    521     switch (property_id) {
    522         case CV_CAP_PROP_FRAME_WIDTH:
    523             width = value;
    524             settingWidth = 1;
    525             if (settingWidth && settingHeight) {
    526                 setWidthHeight();
    527                 settingWidth =0;
    528                 settingHeight = 0;
    529             }
    530             return true;
    531 
    532         case CV_CAP_PROP_FRAME_HEIGHT:
    533             height = value;
    534             settingHeight = 1;
    535             if (settingWidth && settingHeight) {
    536                 setWidthHeight();
    537                 settingWidth =0;
    538                 settingHeight = 0;
    539             }
    540             return true;
    541 
    542         case CV_CAP_PROP_IOS_DEVICE_FOCUS:
    543             if ([mCaptureDevice isFocusModeSupported:(AVCaptureFocusMode)value]){
    544                 NSError* error = nil;
    545                 [mCaptureDevice lockForConfiguration:&error];
    546                 if (error) return false;
    547                 [mCaptureDevice setFocusMode:(AVCaptureFocusMode)value];
    548                 [mCaptureDevice unlockForConfiguration];
    549                 //NSLog(@"Focus set");
    550                 return true;
    551             }else {
    552                 return false;
    553             }
    554 
    555         case CV_CAP_PROP_IOS_DEVICE_EXPOSURE:
    556             if ([mCaptureDevice isExposureModeSupported:(AVCaptureExposureMode)value]){
    557                 NSError* error = nil;
    558                 [mCaptureDevice lockForConfiguration:&error];
    559                 if (error) return false;
    560                 [mCaptureDevice setExposureMode:(AVCaptureExposureMode)value];
    561                 [mCaptureDevice unlockForConfiguration];
    562                 //NSLog(@"Exposure set");
    563                 return true;
    564             }else {
    565                 return false;
    566             }
    567 
    568         case CV_CAP_PROP_IOS_DEVICE_FLASH:
    569             if ( [mCaptureDevice hasFlash] && [mCaptureDevice isFlashModeSupported:(AVCaptureFlashMode)value]){
    570                 NSError* error = nil;
    571                 [mCaptureDevice lockForConfiguration:&error];
    572                 if (error) return false;
    573                 [mCaptureDevice setFlashMode:(AVCaptureFlashMode)value];
    574                 [mCaptureDevice unlockForConfiguration];
    575                 //NSLog(@"Flash mode set");
    576                 return true;
    577             }else {
    578                 return false;
    579             }
    580 
    581         case CV_CAP_PROP_IOS_DEVICE_WHITEBALANCE:
    582             if ([mCaptureDevice isWhiteBalanceModeSupported:(AVCaptureWhiteBalanceMode)value]){
    583                 NSError* error = nil;
    584                 [mCaptureDevice lockForConfiguration:&error];
    585                 if (error) return false;
    586                 [mCaptureDevice setWhiteBalanceMode:(AVCaptureWhiteBalanceMode)value];
    587                 [mCaptureDevice unlockForConfiguration];
    588                 //NSLog(@"White balance set");
    589                 return true;
    590             }else {
    591                 return false;
    592             }
    593 
    594         case CV_CAP_PROP_IOS_DEVICE_TORCH:
    595             if ([mCaptureDevice hasFlash] && [mCaptureDevice isTorchModeSupported:(AVCaptureTorchMode)value]){
    596                 NSError* error = nil;
    597                 [mCaptureDevice lockForConfiguration:&error];
    598                 if (error) return false;
    599                 [mCaptureDevice setTorchMode:(AVCaptureTorchMode)value];
    600                 [mCaptureDevice unlockForConfiguration];
    601                 //NSLog(@"Torch mode set");
    602                 return true;
    603             }else {
    604                 return false;
    605             }
    606 
    607         case DISABLE_AUTO_RESTART:
    608             disableAutoRestart = value;
    609             return 1;
    610         default:
    611             return false;
    612     }
    613 }
    614 
    615 
    616 /*****************************************************************************
    617  *
    618  * CaptureDelegate Implementation.
    619  *
    620  * CaptureDelegate is notified on a separate thread by the OS whenever there
    621  *   is a new frame. When "updateImage" is called from the main thread, it
    622  *   copies this new frame into an IplImage, but only if this frame has not
    623  *   been copied before. When "getOutput" is called from the main thread,
    624  *   it gives the last copied IplImage.
    625  *
    626  *****************************************************************************/
    627 
    628 
    629 @implementation CaptureDelegate
    630 
    631 - (id)init {
    632     [super init];
    633     newFrame = 0;
    634     imagedata = NULL;
    635     bgr_imagedata = NULL;
    636     currSize = 0;
    637     image = NULL;
    638     bgr_image = NULL;
    639     bgr_image_r90 = NULL;
    640     return self;
    641 }
    642 
    643 
    644 -(void)dealloc {
    645     if (imagedata != NULL) free(imagedata);
    646     if (bgr_imagedata != NULL) free(bgr_imagedata);
    647     cvReleaseImage(&image);
    648     cvReleaseImage(&bgr_image);
    649     cvReleaseImage(&bgr_image_r90);
    650     [super dealloc];
    651 }
    652 
    653 
    654 
    655 - (void)captureOutput:(AVCaptureOutput *)captureOutput
    656 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
    657 fromConnection:(AVCaptureConnection *)connection{
    658 
    659     // Failed
    660     // connection.videoOrientation = AVCaptureVideoOrientationPortrait;
    661     (void)captureOutput;
    662     (void)connection;
    663 
    664     CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    665 
    666     CVBufferRetain(imageBuffer);
    667     CVImageBufferRef imageBufferToRelease  = mCurrentImageBuffer;
    668 
    669     @synchronized (self) {
    670 
    671         mCurrentImageBuffer = imageBuffer;
    672         newFrame = 1;
    673     }
    674 
    675     CVBufferRelease(imageBufferToRelease);
    676 
    677 }
    678 
    679 
    680 -(IplImage*) getOutput {
    681     //return bgr_image;
    682     return bgr_image_r90;
    683 }
    684 
    685 -(int) updateImage {
    686     if (newFrame==0) return 0;
    687     CVPixelBufferRef pixels;
    688 
    689     @synchronized (self){
    690         pixels = CVBufferRetain(mCurrentImageBuffer);
    691         newFrame = 0;
    692     }
    693 
    694     CVPixelBufferLockBaseAddress(pixels, 0);
    695     uint32_t* baseaddress = (uint32_t*)CVPixelBufferGetBaseAddress(pixels);
    696 
    697     size_t width = CVPixelBufferGetWidth(pixels);
    698     size_t height = CVPixelBufferGetHeight(pixels);
    699     size_t rowBytes = CVPixelBufferGetBytesPerRow(pixels);
    700 
    701     if (rowBytes != 0) {
    702 
    703         if (currSize != rowBytes*height*sizeof(char)) {
    704             currSize = rowBytes*height*sizeof(char);
    705             if (imagedata != NULL) free(imagedata);
    706             if (bgr_imagedata != NULL) free(bgr_imagedata);
    707             imagedata = (char*)malloc(currSize);
    708             bgr_imagedata = (char*)malloc(currSize);
    709         }
    710 
    711         memcpy(imagedata, baseaddress, currSize);
    712 
    713         if (image == NULL) {
    714             image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 4);
    715         }
    716         image->width = (int)width;
    717         image->height = (int)height;
    718         image->nChannels = 4;
    719         image->depth = IPL_DEPTH_8U;
    720         image->widthStep = (int)rowBytes;
    721         image->imageData = imagedata;
    722         image->imageSize = (int)currSize;
    723 
    724         if (bgr_image == NULL) {
    725             bgr_image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 3);
    726         }
    727         bgr_image->width = (int)width;
    728         bgr_image->height = (int)height;
    729         bgr_image->nChannels = 3;
    730         bgr_image->depth = IPL_DEPTH_8U;
    731         bgr_image->widthStep = (int)rowBytes;
    732         bgr_image->imageData = bgr_imagedata;
    733         bgr_image->imageSize = (int)currSize;
    734 
    735         cvCvtColor(image, bgr_image, CV_BGRA2BGR);
    736 
    737         // image taken from the buffer is incorrected rotated. I'm using cvTranspose + cvFlip.
    738         // There should be an option in iOS API to rotate the buffer output orientation.
    739         // iOS provides hardware accelerated rotation through AVCaptureConnection class
    740         // I can't get it work.
    741         if (bgr_image_r90 == NULL){
    742             bgr_image_r90 = cvCreateImage(cvSize((int)height, (int)width), IPL_DEPTH_8U, 3);
    743         }
    744         cvTranspose(bgr_image, bgr_image_r90);
    745         cvFlip(bgr_image_r90, NULL, 1);
    746 
    747     }
    748 
    749     CVPixelBufferUnlockBaseAddress(pixels, 0);
    750     CVBufferRelease(pixels);
    751 
    752     return 1;
    753 }
    754 
    755 @end
    756 
    757 
    758 /*****************************************************************************
    759  *
    760  * CvCaptureFile Implementation.
    761  *
    762  * CvCaptureFile is the instantiation of a capture source for video files.
    763  *
    764  *****************************************************************************/
    765 
    766 CvCaptureFile::CvCaptureFile(const char* filename) {
    767 
    768     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    769 
    770     mMovieReader = nil;
    771     image = NULL;
    772     bgr_image = NULL;
    773     imagedata = NULL;
    774     bgr_imagedata = NULL;
    775     currSize = 0;
    776 
    777     movieWidth = 0;
    778     movieHeight = 0;
    779     movieFPS = 0;
    780     currentFPS = 0;
    781     movieDuration = 0;
    782     changedPos = 0;
    783 
    784     started = 0;
    785 
    786     AVURLAsset *asset = [AVURLAsset URLAssetWithURL:
    787         [NSURL fileURLWithPath: [NSString stringWithUTF8String:filename]]
    788         options:nil];
    789 
    790     AVAssetTrack* videoTrack = nil;
    791     NSArray* tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
    792     if ([tracks count] == 1)
    793     {
    794         videoTrack = [tracks objectAtIndex:0];
    795 
    796         movieWidth = videoTrack.naturalSize.width;
    797         movieHeight = videoTrack.naturalSize.height;
    798         movieFPS = videoTrack.nominalFrameRate;
    799 
    800         currentFPS = movieFPS; //Debugging !! should be getFPS();
    801         //Debugging. need to be checked
    802 
    803         // In ms
    804         movieDuration = videoTrack.timeRange.duration.value/videoTrack.timeRange.duration.timescale * 1000;
    805 
    806         started = 1;
    807         NSError* error = nil;
    808         mMovieReader = [[AVAssetReader alloc] initWithAsset:asset error:&error];
    809         if (error)
    810             NSLog(@"%@", [error localizedDescription]);
    811 
    812         NSDictionary* videoSettings =
    813             [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA]
    814             forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey];
    815 
    816         [mMovieReader addOutput:[AVAssetReaderTrackOutput
    817             assetReaderTrackOutputWithTrack:videoTrack
    818             outputSettings:videoSettings]];
    819         [mMovieReader startReading];
    820     }
    821 
    822     /*
    823     // Asynchronously open the video in another thread. Always fail.
    824     [asset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:@"tracks"] completionHandler:
    825     ^{
    826     // The completion block goes here.
    827     dispatch_async(dispatch_get_main_queue(),
    828     ^{
    829     AVAssetTrack* ::videoTrack = nil;
    830     NSArray* ::tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
    831     if ([tracks count] == 1)
    832     {
    833     videoTrack = [tracks objectAtIndex:0];
    834 
    835     movieWidth = videoTrack.naturalSize.width;
    836     movieHeight = videoTrack.naturalSize.height;
    837     movieFPS = videoTrack.nominalFrameRate;
    838     currentFPS = movieFPS; //Debugging !! should be getFPS();
    839     //Debugging. need to be checked
    840     movieDuration = videoTrack.timeRange.duration.value/videoTrack.timeRange.duration.timescale * 1000;
    841     started = 1;
    842 
    843     NSError* ::error = nil;
    844     // mMovieReader is a member variable
    845     mMovieReader = [[AVAssetReader alloc] initWithAsset:asset error:&error];
    846     if (error)
    847     NSLog(@"%@", [error localizedDescription]);
    848 
    849     NSDictionary* ::videoSettings =
    850     [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA]
    851 forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey];
    852 
    853 [mMovieReader addOutput:[AVAssetReaderTrackOutput
    854 assetReaderTrackOutputWithTrack:videoTrack
    855 outputSettings:videoSettings]];
    856 [mMovieReader startReading];
    857 }
    858 });
    859 
    860 }];
    861      */
    862 
    863 [localpool drain];
    864 }
    865 
    866 CvCaptureFile::~CvCaptureFile() {
    867 
    868     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    869     if (imagedata != NULL) free(imagedata);
    870     if (bgr_imagedata != NULL) free(bgr_imagedata);
    871     cvReleaseImage(&image);
    872     cvReleaseImage(&bgr_image);
    873     [mMovieReader release];
    874     [localpool drain];
    875 }
    876 
    877 int CvCaptureFile::didStart() {
    878     return started;
    879 }
    880 
    881 bool CvCaptureFile::grabFrame() {
    882 
    883     //everything is done in queryFrame;
    884     currentFPS = movieFPS;
    885     return 1;
    886 
    887 
    888     /*
    889             double t1 = getProperty(CV_CAP_PROP_POS_MSEC);
    890             [mCaptureSession stepForward];
    891             double t2 = getProperty(CV_CAP_PROP_POS_MSEC);
    892             if (t2>t1 && !changedPos) {
    893             currentFPS = 1000.0/(t2-t1);
    894             } else {
    895             currentFPS = movieFPS;
    896             }
    897             changedPos = 0;
    898 
    899      */
    900 
    901 }
    902 
    903 
    904 IplImage* CvCaptureFile::retrieveFramePixelBuffer() {
    905     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    906 
    907     if (mMovieReader.status != AVAssetReaderStatusReading){
    908 
    909         return NULL;
    910     }
    911 
    912 
    913     AVAssetReaderTrackOutput * output = [mMovieReader.outputs objectAtIndex:0];
    914     CMSampleBufferRef sampleBuffer = [output copyNextSampleBuffer];
    915     if (!sampleBuffer) {
    916         [localpool drain];
    917         return NULL;
    918     }
    919     CVPixelBufferRef frame = CMSampleBufferGetImageBuffer(sampleBuffer);
    920     CVPixelBufferRef pixels = CVBufferRetain(frame);
    921 
    922     CVPixelBufferLockBaseAddress(pixels, 0);
    923 
    924     uint32_t* baseaddress = (uint32_t*)CVPixelBufferGetBaseAddress(pixels);
    925     size_t width = CVPixelBufferGetWidth(pixels);
    926     size_t height = CVPixelBufferGetHeight(pixels);
    927     size_t rowBytes = CVPixelBufferGetBytesPerRow(pixels);
    928 
    929     if (rowBytes != 0) {
    930 
    931         if (currSize != rowBytes*height*sizeof(char)) {
    932             currSize = rowBytes*height*sizeof(char);
    933             if (imagedata != NULL) free(imagedata);
    934             if (bgr_imagedata != NULL) free(bgr_imagedata);
    935             imagedata = (char*)malloc(currSize);
    936             bgr_imagedata = (char*)malloc(currSize);
    937         }
    938 
    939         memcpy(imagedata, baseaddress, currSize);
    940 
    941         if (image == NULL) {
    942             image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 4);
    943         }
    944 
    945         image->width = (int)width;
    946         image->height = (int)height;
    947         image->nChannels = 4;
    948         image->depth = IPL_DEPTH_8U;
    949         image->widthStep = (int)rowBytes;
    950         image->imageData = imagedata;
    951         image->imageSize = (int)currSize;
    952 
    953 
    954         if (bgr_image == NULL) {
    955             bgr_image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 3);
    956         }
    957 
    958         bgr_image->width = (int)width;
    959         bgr_image->height = (int)height;
    960         bgr_image->nChannels = 3;
    961         bgr_image->depth = IPL_DEPTH_8U;
    962         bgr_image->widthStep = (int)rowBytes;
    963         bgr_image->imageData = bgr_imagedata;
    964         bgr_image->imageSize = (int)currSize;
    965 
    966         cvCvtColor(image, bgr_image,CV_BGRA2BGR);
    967 
    968     }
    969 
    970     CVPixelBufferUnlockBaseAddress(pixels, 0);
    971     CVBufferRelease(pixels);
    972     CMSampleBufferInvalidate(sampleBuffer);
    973     CFRelease(sampleBuffer);
    974 
    975     [localpool drain];
    976     return bgr_image;
    977 }
    978 
    979 
    980 IplImage* CvCaptureFile::retrieveFrame(int) {
    981     return retrieveFramePixelBuffer();
    982 }
    983 
    984 IplImage* CvCaptureFile::queryFrame() {
    985     grabFrame();
    986     return retrieveFrame(0);
    987 }
    988 
    989 double CvCaptureFile::getFPS() {
    990 
    991     /*
    992          if (mCaptureSession == nil) return 0;
    993          NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
    994          double now = getProperty(CV_CAP_PROP_POS_MSEC);
    995          double retval = 0;
    996          if (now == 0) {
    997          [mCaptureSession stepForward];
    998          double t2 =  getProperty(CV_CAP_PROP_POS_MSEC);
    999          [mCaptureSession stepBackward];
   1000          retval = 1000.0 / (t2-now);
   1001          } else {
   1002          [mCaptureSession stepBackward];
   1003          double t2 = getProperty(CV_CAP_PROP_POS_MSEC);
   1004          [mCaptureSession stepForward];
   1005          retval = 1000.0 / (now-t2);
   1006          }
   1007          [localpool drain];
   1008          return retval;
   1009      */
   1010     return 30.0; //TODO: Debugging
   1011 }
   1012 
   1013 double CvCaptureFile::getProperty(int /*property_id*/) const{
   1014 
   1015     /*
   1016          if (mCaptureSession == nil) return 0;
   1017 
   1018          NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
   1019 
   1020          double retval;
   1021          QTTime t;
   1022 
   1023          switch (property_id) {
   1024          case CV_CAP_PROP_POS_MSEC:
   1025          [[mCaptureSession attributeForKey:QTMovieCurrentTimeAttribute] getValue:&t];
   1026          retval = t.timeValue * 1000.0 / t.timeScale;
   1027          break;
   1028          case CV_CAP_PROP_POS_FRAMES:
   1029          retval = movieFPS * getProperty(CV_CAP_PROP_POS_MSEC) / 1000;
   1030          break;
   1031          case CV_CAP_PROP_POS_AVI_RATIO:
   1032          retval = (getProperty(CV_CAP_PROP_POS_MSEC)) / (movieDuration );
   1033          break;
   1034          case CV_CAP_PROP_FRAME_WIDTH:
   1035          retval = movieWidth;
   1036          break;
   1037          case CV_CAP_PROP_FRAME_HEIGHT:
   1038          retval = movieHeight;
   1039          break;
   1040          case CV_CAP_PROP_FPS:
   1041          retval = currentFPS;
   1042          break;
   1043          case CV_CAP_PROP_FOURCC:
   1044          default:
   1045          retval = 0;
   1046          }
   1047 
   1048          [localpool drain];
   1049          return retval;
   1050      */
   1051     return 1.0; //Debugging
   1052 }
   1053 
   1054 bool CvCaptureFile::setProperty(int /*property_id*/, double /*value*/) {
   1055 
   1056     /*
   1057          if (mCaptureSession == nil) return false;
   1058 
   1059          NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
   1060 
   1061          bool retval = false;
   1062          QTTime t;
   1063 
   1064          double ms;
   1065 
   1066          switch (property_id) {
   1067          case CV_CAP_PROP_POS_MSEC:
   1068          [[mCaptureSession attributeForKey:QTMovieCurrentTimeAttribute] getValue:&t];
   1069          t.timeValue = value * t.timeScale / 1000;
   1070          [mCaptureSession setCurrentTime:t];
   1071          changedPos = 1;
   1072          retval = true;
   1073          break;
   1074          case CV_CAP_PROP_POS_FRAMES:
   1075          ms = (value*1000.0 -5)/ currentFPS;
   1076          retval = setProperty(CV_CAP_PROP_POS_MSEC, ms);
   1077          break;
   1078          case CV_CAP_PROP_POS_AVI_RATIO:
   1079          ms = value * movieDuration;
   1080          retval = setProperty(CV_CAP_PROP_POS_MSEC, ms);
   1081          break;
   1082          case CV_CAP_PROP_FRAME_WIDTH:
   1083     //retval = movieWidth;
   1084     break;
   1085     case CV_CAP_PROP_FRAME_HEIGHT:
   1086     //retval = movieHeight;
   1087     break;
   1088     case CV_CAP_PROP_FPS:
   1089     //etval = currentFPS;
   1090     break;
   1091     case CV_CAP_PROP_FOURCC:
   1092     default:
   1093     retval = false;
   1094     }
   1095 
   1096     [localpool drain];
   1097 
   1098     return retval;
   1099      */
   1100     return true;
   1101 }
   1102 
   1103 
   1104 /*****************************************************************************
   1105  *
   1106  * CvVideoWriter Implementation.
   1107  *
   1108  * CvVideoWriter is the instantiation of a video output class
   1109  *
   1110  *****************************************************************************/
   1111 
   1112 
   1113 CvVideoWriter_AVFoundation::CvVideoWriter_AVFoundation(const char* filename, int fourcc,
   1114         double fps, CvSize frame_size,
   1115         int is_color) {
   1116 
   1117     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
   1118 
   1119 
   1120     frameCount = 0;
   1121     movieFPS = fps;
   1122     movieSize = frame_size;
   1123     movieColor = is_color;
   1124     argbimage = cvCreateImage(movieSize, IPL_DEPTH_8U, 4);
   1125     path = [[[NSString stringWithCString:filename encoding:NSASCIIStringEncoding] stringByExpandingTildeInPath] retain];
   1126 
   1127 
   1128     /*
   1129          AVFileTypeQuickTimeMovie
   1130          UTI for the QuickTime movie file format.
   1131          The value of this UTI is com.apple.quicktime-movie. Files are identified with the .mov and .qt extensions.
   1132 
   1133          AVFileTypeMPEG4
   1134          UTI for the MPEG-4 file format.
   1135          The value of this UTI is public.mpeg-4. Files are identified with the .mp4 extension.
   1136 
   1137          AVFileTypeAppleM4V
   1138          UTI for the iTunes video file format.
   1139          The value of this UTI is com.apple.mpeg-4-video. Files are identified with the .m4v extension.
   1140 
   1141          AVFileType3GPP
   1142          UTI for the 3GPP file format.
   1143          The value of this UTI is public.3gpp. Files are identified with the .3gp, .3gpp, and .sdv extensions.
   1144      */
   1145 
   1146     NSString *fileExt =[[[path pathExtension] lowercaseString] copy];
   1147     if ([fileExt isEqualToString:@"mov"] || [fileExt isEqualToString:@"qt"]){
   1148         fileType = [AVFileTypeQuickTimeMovie copy];
   1149     }else if ([fileExt isEqualToString:@"mp4"]){
   1150         fileType = [AVFileTypeMPEG4 copy];
   1151     }else if ([fileExt isEqualToString:@"m4v"]){
   1152         fileType = [AVFileTypeAppleM4V copy];
   1153 #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
   1154     }else if ([fileExt isEqualToString:@"3gp"] || [fileExt isEqualToString:@"3gpp"] || [fileExt isEqualToString:@"sdv"]  ){
   1155         fileType = [AVFileType3GPP copy];
   1156 #endif
   1157     } else{
   1158         fileType = [AVFileTypeMPEG4 copy];  //default mp4
   1159     }
   1160     [fileExt release];
   1161 
   1162     char cc[5];
   1163     cc[0] = fourcc & 255;
   1164     cc[1] = (fourcc >> 8) & 255;
   1165     cc[2] = (fourcc >> 16) & 255;
   1166     cc[3] = (fourcc >> 24) & 255;
   1167     cc[4] = 0;
   1168     int cc2 = CV_FOURCC(cc[0], cc[1], cc[2], cc[3]);
   1169     if (cc2!=fourcc) {
   1170         std::cout << "WARNING: Didn't properly encode FourCC. Expected " << fourcc
   1171             << " but got " << cc2 << "." << std::endl;
   1172         //exception;
   1173     }
   1174 
   1175     // Two codec supported AVVideoCodecH264 AVVideoCodecJPEG
   1176     // On iPhone 3G H264 is not supported.
   1177     if (fourcc == CV_FOURCC('J','P','E','G') || fourcc == CV_FOURCC('j','p','e','g') ||
   1178             fourcc == CV_FOURCC('M','J','P','G') || fourcc == CV_FOURCC('m','j','p','g') ){
   1179         codec = [AVVideoCodecJPEG copy]; // Use JPEG codec if specified, otherwise H264
   1180     }else if(fourcc == CV_FOURCC('H','2','6','4') || fourcc == CV_FOURCC('a','v','c','1')){
   1181             codec = [AVVideoCodecH264 copy];
   1182     }else{
   1183         codec = [AVVideoCodecH264 copy]; // default canonical H264.
   1184 
   1185     }
   1186 
   1187     //NSLog(@"Path: %@", path);
   1188 
   1189     NSError *error = nil;
   1190 
   1191 
   1192     // Make sure the file does not already exist. Necessary to overwirte??
   1193     /*
   1194     NSFileManager *fileManager = [NSFileManager defaultManager];
   1195     if ([fileManager fileExistsAtPath:path]){
   1196         [fileManager removeItemAtPath:path error:&error];
   1197     }
   1198     */
   1199 
   1200     // Wire the writer:
   1201     // Supported file types:
   1202     //      AVFileTypeQuickTimeMovie AVFileTypeMPEG4 AVFileTypeAppleM4V AVFileType3GPP
   1203 
   1204     mMovieWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path]
   1205         fileType:fileType
   1206         error:&error];
   1207     //NSParameterAssert(mMovieWriter);
   1208 
   1209     NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
   1210         codec, AVVideoCodecKey,
   1211         [NSNumber numberWithInt:movieSize.width], AVVideoWidthKey,
   1212         [NSNumber numberWithInt:movieSize.height], AVVideoHeightKey,
   1213         nil];
   1214 
   1215     mMovieWriterInput = [[AVAssetWriterInput
   1216         assetWriterInputWithMediaType:AVMediaTypeVideo
   1217         outputSettings:videoSettings] retain];
   1218 
   1219     //NSParameterAssert(mMovieWriterInput);
   1220     //NSParameterAssert([mMovieWriter canAddInput:mMovieWriterInput]);
   1221 
   1222     [mMovieWriter addInput:mMovieWriterInput];
   1223 
   1224     mMovieWriterAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc] initWithAssetWriterInput:mMovieWriterInput sourcePixelBufferAttributes:nil];
   1225 
   1226 
   1227     //Start a session:
   1228     [mMovieWriter startWriting];
   1229     [mMovieWriter startSessionAtSourceTime:kCMTimeZero];
   1230 
   1231 
   1232     if(mMovieWriter.status == AVAssetWriterStatusFailed){
   1233         NSLog(@"%@", [mMovieWriter.error localizedDescription]);
   1234         // TODO: error handling, cleanup. Throw execption?
   1235         // return;
   1236     }
   1237 
   1238     [localpool drain];
   1239 }
   1240 
   1241 
   1242 CvVideoWriter_AVFoundation::~CvVideoWriter_AVFoundation() {
   1243     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
   1244 
   1245     [mMovieWriterInput markAsFinished];
   1246     [mMovieWriter finishWriting];
   1247     [mMovieWriter release];
   1248     [mMovieWriterInput release];
   1249     [mMovieWriterAdaptor release];
   1250     [path release];
   1251     [codec release];
   1252     [fileType release];
   1253     cvReleaseImage(&argbimage);
   1254 
   1255     [localpool drain];
   1256 
   1257 }
   1258 
   1259 bool CvVideoWriter_AVFoundation::writeFrame(const IplImage* iplimage) {
   1260     NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
   1261 
   1262     // writer status check
   1263     if (![mMovieWriterInput isReadyForMoreMediaData] || mMovieWriter.status !=  AVAssetWriterStatusWriting ) {
   1264         NSLog(@"[mMovieWriterInput isReadyForMoreMediaData] Not ready for media data or ...");
   1265         NSLog(@"mMovieWriter.status: %d. Error: %@", (int)mMovieWriter.status, [mMovieWriter.error localizedDescription]);
   1266         [localpool drain];
   1267         return false;
   1268     }
   1269 
   1270     BOOL success = FALSE;
   1271 
   1272     if (iplimage->height!=movieSize.height || iplimage->width!=movieSize.width){
   1273         std::cout<<"Frame size does not match video size."<<std::endl;
   1274         [localpool drain];
   1275         return false;
   1276     }
   1277 
   1278     if (movieColor) {
   1279         //assert(iplimage->nChannels == 3);
   1280         cvCvtColor(iplimage, argbimage, CV_BGR2BGRA);
   1281     }else{
   1282         //assert(iplimage->nChannels == 1);
   1283         cvCvtColor(iplimage, argbimage, CV_GRAY2BGRA);
   1284     }
   1285     //IplImage -> CGImage conversion
   1286     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
   1287     NSData *nsData = [NSData dataWithBytes:argbimage->imageData length:argbimage->imageSize];
   1288     CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)nsData);
   1289     CGImageRef cgImage = CGImageCreate(argbimage->width, argbimage->height,
   1290             argbimage->depth, argbimage->depth * argbimage->nChannels, argbimage->widthStep,
   1291             colorSpace, kCGImageAlphaLast|kCGBitmapByteOrderDefault,
   1292             provider, NULL, false, kCGRenderingIntentDefault);
   1293 
   1294     //CGImage -> CVPixelBufferRef coversion
   1295     CVPixelBufferRef pixelBuffer = NULL;
   1296     CFDataRef cfData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
   1297     int status = CVPixelBufferCreateWithBytes(NULL,
   1298             movieSize.width,
   1299             movieSize.height,
   1300             kCVPixelFormatType_32BGRA,
   1301             (void*)CFDataGetBytePtr(cfData),
   1302             CGImageGetBytesPerRow(cgImage),
   1303             NULL,
   1304             0,
   1305             NULL,
   1306             &pixelBuffer);
   1307     if(status == kCVReturnSuccess){
   1308         success = [mMovieWriterAdaptor appendPixelBuffer:pixelBuffer
   1309             withPresentationTime:CMTimeMake(frameCount, movieFPS)];
   1310     }
   1311 
   1312     //cleanup
   1313     CFRelease(cfData);
   1314     CVPixelBufferRelease(pixelBuffer);
   1315     CGImageRelease(cgImage);
   1316     CGDataProviderRelease(provider);
   1317     CGColorSpaceRelease(colorSpace);
   1318 
   1319     [localpool drain];
   1320 
   1321     if (success) {
   1322         frameCount ++;
   1323         //NSLog(@"Frame #%d", frameCount);
   1324         return true;
   1325     }else{
   1326         NSLog(@"Frame appendPixelBuffer failed.");
   1327         return false;
   1328     }
   1329 
   1330 }
   1331