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 //                        Intel License Agreement
     11 //                For Open Source Computer Vision Library
     12 //
     13 // Copyright (C) 2000, Intel Corporation, 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 Intel Corporation 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 Intel Corporation or contributors 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 
     45 // Original implementation by   Mark Asbach
     46 //                              Institute of Communications Engineering
     47 //                              RWTH Aachen University
     48 //
     49 // For implementation details and background see:
     50 // http://developer.apple.com/samplecode/qtframestepper.win/listing1.html
     51 //
     52 // Please note that timing will only be correct for videos that contain a visual track
     53 // that has full length (compared to other tracks)
     54 
     55 
     56 // standard includes
     57 #include <cstdio>
     58 #include <cassert>
     59 
     60 // Mac OS includes
     61 #include <Carbon/Carbon.h>
     62 #include <CoreFoundation/CoreFoundation.h>
     63 #include <QuickTime/QuickTime.h>
     64 
     65 
     66 // Global state (did we call EnterMovies?)
     67 static int did_enter_movies = 0;
     68 
     69 // ----------------------------------------------------------------------------------------
     70 #pragma mark Reading Video Files
     71 
     72 /// Movie state structure for QuickTime movies
     73 typedef struct CvCapture_QT_Movie
     74 {
     75     Movie      myMovie;            // movie handle
     76     GWorldPtr  myGWorld;           // we render into an offscreen GWorld
     77 
     78     CvSize     size;               // dimensions of the movie
     79     TimeValue  movie_start_time;   // movies can start at arbitrary times
     80     long       number_of_frames;   // duration in frames
     81     long       next_frame_time;
     82     long       next_frame_number;
     83 
     84     IplImage * image_rgb;          // will point to the PixMap of myGWorld
     85     IplImage * image_bgr;          // will be returned by icvRetrieveFrame_QT()
     86 
     87 } CvCapture_QT_Movie;
     88 
     89 
     90 static       int         icvOpenFile_QT_Movie      (CvCapture_QT_Movie * capture, const char  * filename);
     91 static       int         icvClose_QT_Movie         (CvCapture_QT_Movie * capture);
     92 static       double      icvGetProperty_QT_Movie   (CvCapture_QT_Movie * capture, int property_id);
     93 static       int         icvSetProperty_QT_Movie   (CvCapture_QT_Movie * capture, int property_id, double value);
     94 static       int         icvGrabFrame_QT_Movie     (CvCapture_QT_Movie * capture);
     95 static const void      * icvRetrieveFrame_QT_Movie (CvCapture_QT_Movie * capture, int);
     96 
     97 
     98 static CvCapture_QT_Movie * icvCaptureFromFile_QT (const char * filename)
     99 {
    100     static int did_enter_movies = 0;
    101     if (! did_enter_movies)
    102     {
    103         EnterMovies();
    104         did_enter_movies = 1;
    105     }
    106 
    107     CvCapture_QT_Movie * capture = 0;
    108 
    109     if (filename)
    110     {
    111         capture = (CvCapture_QT_Movie *) cvAlloc (sizeof (*capture));
    112         memset (capture, 0, sizeof(*capture));
    113 
    114         if (!icvOpenFile_QT_Movie (capture, filename))
    115             cvFree( &capture );
    116     }
    117 
    118     return capture;
    119 }
    120 
    121 
    122 
    123 /**
    124  * convert full path to CFStringRef and open corresponding Movie. Then
    125  * step over 'interesting frame times' to count total number of frames
    126  * for video material with varying frame durations and create offscreen
    127  * GWorld for rendering the movie frames.
    128  *
    129  * @author Mark Asbach <asbach (at) ient.rwth-aachen.de>
    130  * @date   2005-11-04
    131  */
    132 static int icvOpenFile_QT_Movie (CvCapture_QT_Movie * capture, const char * filename)
    133 {
    134     Rect          myRect;
    135     short         myResID        = 0;
    136     Handle        myDataRef      = nil;
    137     OSType        myDataRefType  = 0;
    138     OSErr         myErr          = noErr;
    139 
    140 
    141     // no old errors please
    142     ClearMoviesStickyError ();
    143 
    144     // initialize pointers to zero
    145     capture->myMovie  = 0;
    146     capture->myGWorld = nil;
    147 
    148     // initialize numbers with invalid values
    149     capture->next_frame_time   = -1;
    150     capture->next_frame_number = -1;
    151     capture->number_of_frames  = -1;
    152     capture->movie_start_time  = -1;
    153     capture->size              = cvSize (-1,-1);
    154 
    155 
    156     // we would use CFStringCreateWithFileSystemRepresentation (kCFAllocatorDefault, filename) on Mac OS X 10.4
    157     CFStringRef   inPath = CFStringCreateWithCString (kCFAllocatorDefault, filename, kCFStringEncodingISOLatin1);
    158     OPENCV_ASSERT ((inPath != nil), "icvOpenFile_QT_Movie", "couldnt create CFString from a string");
    159 
    160     // create the data reference
    161     myErr = QTNewDataReferenceFromFullPathCFString (inPath, kQTPOSIXPathStyle, 0, & myDataRef, & myDataRefType);
    162     if (myErr != noErr)
    163     {
    164         fprintf (stderr, "Couldn't create QTNewDataReferenceFromFullPathCFString().\n");
    165         return 0;
    166     }
    167 
    168     // get the Movie
    169     myErr = NewMovieFromDataRef(& capture->myMovie, newMovieActive | newMovieAsyncOK /* | newMovieIdleImportOK */,
    170                                 & myResID, myDataRef, myDataRefType);
    171 
    172     // dispose of the data reference handle - we no longer need it
    173     DisposeHandle (myDataRef);
    174 
    175     // if NewMovieFromDataRef failed, we already disposed the DataRef, so just return with an error
    176     if (myErr != noErr)
    177     {
    178         fprintf (stderr, "Couldn't create a NewMovieFromDataRef() - error is %d.\n",  myErr);
    179         return 0;
    180     }
    181 
    182     // count the number of video 'frames' in the movie by stepping through all of the
    183     // video 'interesting times', or in other words, the places where the movie displays
    184     // a new video sample. The time between these interesting times is not necessarily constant.
    185     {
    186         OSType      whichMediaType = VisualMediaCharacteristic;
    187         TimeValue   theTime        = -1;
    188 
    189         // find out movie start time
    190         GetMovieNextInterestingTime (capture->myMovie, short (nextTimeMediaSample + nextTimeEdgeOK),
    191                                      1, & whichMediaType, TimeValue (0), 0, & theTime, NULL);
    192         if (theTime == -1)
    193         {
    194             fprintf (stderr, "Couldn't inquire first frame time\n");
    195             return 0;
    196         }
    197         capture->movie_start_time  = theTime;
    198         capture->next_frame_time   = theTime;
    199         capture->next_frame_number = 0;
    200 
    201         // count all 'interesting times' of the movie
    202         capture->number_of_frames  = 0;
    203         while (theTime >= 0)
    204         {
    205             GetMovieNextInterestingTime (capture->myMovie, short (nextTimeMediaSample),
    206                                          1, & whichMediaType, theTime, 0, & theTime, NULL);
    207             capture->number_of_frames++;
    208         }
    209     }
    210 
    211     // get the bounding rectangle of the movie
    212     GetMoviesError ();
    213     GetMovieBox (capture->myMovie, & myRect);
    214     capture->size = cvSize (myRect.right - myRect.left, myRect.bottom - myRect.top);
    215 
    216     // create gworld for decompressed image
    217     myErr = QTNewGWorld (& capture->myGWorld, k32ARGBPixelFormat /* k24BGRPixelFormat geht leider nicht */,
    218                          & myRect, nil, nil, 0);
    219     OPENCV_ASSERT (myErr == noErr, "icvOpenFile_QT_Movie", "couldnt create QTNewGWorld() for output image");
    220     SetMovieGWorld (capture->myMovie, capture->myGWorld, nil);
    221 
    222     // build IplImage header that will point to the PixMap of the Movie's GWorld later on
    223     capture->image_rgb = cvCreateImageHeader (capture->size, IPL_DEPTH_8U, 4);
    224 
    225     // create IplImage that hold correctly formatted result
    226     capture->image_bgr = cvCreateImage (capture->size, IPL_DEPTH_8U, 3);
    227 
    228     // okay, that's it - should we wait until the Movie is playable?
    229     return 1;
    230 }
    231 
    232 /**
    233  * dispose of QuickTime Movie and free memory buffers
    234  *
    235  * @author Mark Asbach <asbach (at) ient.rwth-aachen.de>
    236  * @date   2005-11-04
    237  */
    238 static int icvClose_QT_Movie (CvCapture_QT_Movie * capture)
    239 {
    240     OPENCV_ASSERT (capture,          "icvClose_QT_Movie", "'capture' is a NULL-pointer");
    241 
    242     // deallocate and free resources
    243     if (capture->myMovie)
    244     {
    245         cvReleaseImage       (& capture->image_bgr);
    246         cvReleaseImageHeader (& capture->image_rgb);
    247         DisposeGWorld        (capture->myGWorld);
    248         DisposeMovie         (capture->myMovie);
    249     }
    250 
    251     // okay, that's it
    252     return 1;
    253 }
    254 
    255 /**
    256  * get a capture property
    257  *
    258  * @author Mark Asbach <asbach (at) ient.rwth-aachen.de>
    259  * @date   2005-11-05
    260  */
    261 static double icvGetProperty_QT_Movie (CvCapture_QT_Movie * capture, int property_id)
    262 {
    263     OPENCV_ASSERT (capture,                        "icvGetProperty_QT_Movie", "'capture' is a NULL-pointer");
    264     OPENCV_ASSERT (capture->myMovie,               "icvGetProperty_QT_Movie", "invalid Movie handle");
    265     OPENCV_ASSERT (capture->number_of_frames >  0, "icvGetProperty_QT_Movie", "movie has invalid number of frames");
    266     OPENCV_ASSERT (capture->movie_start_time >= 0, "icvGetProperty_QT_Movie", "movie has invalid start time");
    267 
    268     // inquire desired property
    269     switch (property_id)
    270     {
    271         case CV_CAP_PROP_POS_FRAMES:
    272             return (capture->next_frame_number);
    273 
    274         case CV_CAP_PROP_POS_MSEC:
    275         case CV_CAP_PROP_POS_AVI_RATIO:
    276             {
    277                 TimeValue   position  = capture->next_frame_time - capture->movie_start_time;
    278 
    279                 if (property_id == CV_CAP_PROP_POS_MSEC)
    280                 {
    281                     TimeScale   timescale = GetMovieTimeScale (capture->myMovie);
    282                     return (static_cast<double> (position) * 1000.0 / timescale);
    283                 }
    284                 else
    285                 {
    286                     TimeValue   duration  = GetMovieDuration  (capture->myMovie);
    287                     return (static_cast<double> (position) / duration);
    288                 }
    289             }
    290             break; // never reached
    291 
    292         case CV_CAP_PROP_FRAME_WIDTH:
    293             return static_cast<double> (capture->size.width);
    294 
    295         case CV_CAP_PROP_FRAME_HEIGHT:
    296             return static_cast<double> (capture->size.height);
    297 
    298         case CV_CAP_PROP_FPS:
    299             {
    300                 TimeValue   duration  = GetMovieDuration  (capture->myMovie);
    301                 TimeScale   timescale = GetMovieTimeScale (capture->myMovie);
    302 
    303                 return (capture->number_of_frames / (static_cast<double> (duration) / timescale));
    304             }
    305 
    306         case CV_CAP_PROP_FRAME_COUNT:
    307             return static_cast<double> (capture->number_of_frames);
    308 
    309         case CV_CAP_PROP_FOURCC:  // not implemented
    310         case CV_CAP_PROP_FORMAT:  // not implemented
    311         case CV_CAP_PROP_MODE:    // not implemented
    312         default:
    313             // unhandled or unknown capture property
    314             OPENCV_ERROR (CV_StsBadArg, "icvSetProperty_QT_Movie", "unknown or unhandled property_id");
    315             return CV_StsBadArg;
    316     }
    317 
    318     return 0;
    319 }
    320 
    321 /**
    322  * set a capture property. With movie files, it is only possible to set the
    323  * position (i.e. jump to a given time or frame number)
    324  *
    325  * @author Mark Asbach <asbach (at) ient.rwth-aachen.de>
    326  * @date   2005-11-05
    327  */
    328 static int icvSetProperty_QT_Movie (CvCapture_QT_Movie * capture, int property_id, double value)
    329 {
    330     OPENCV_ASSERT (capture,                        "icvSetProperty_QT_Movie", "'capture' is a NULL-pointer");
    331     OPENCV_ASSERT (capture->myMovie,               "icvSetProperty_QT_Movie", "invalid Movie handle");
    332     OPENCV_ASSERT (capture->number_of_frames >  0, "icvSetProperty_QT_Movie", "movie has invalid number of frames");
    333     OPENCV_ASSERT (capture->movie_start_time >= 0, "icvSetProperty_QT_Movie", "movie has invalid start time");
    334 
    335     // inquire desired property
    336     //
    337     // rework these three points to really work through 'interesting times'.
    338     // with the current implementation, they result in wrong times or wrong frame numbers with content that
    339     // features varying frame durations
    340     switch (property_id)
    341     {
    342         case CV_CAP_PROP_POS_MSEC:
    343         case CV_CAP_PROP_POS_AVI_RATIO:
    344             {
    345                 TimeValue    destination;
    346                 OSType       myType     = VisualMediaCharacteristic;
    347                 OSErr        myErr      = noErr;
    348 
    349                 if (property_id == CV_CAP_PROP_POS_MSEC)
    350                 {
    351                     TimeScale  timescale   = GetMovieTimeScale      (capture->myMovie);
    352                                destination = static_cast<TimeValue> (value / 1000.0 * timescale + capture->movie_start_time);
    353                 }
    354                 else
    355                 {
    356                     TimeValue  duration    = GetMovieDuration       (capture->myMovie);
    357                                destination = static_cast<TimeValue> (value * duration + capture->movie_start_time);
    358                 }
    359 
    360                 // really seek?
    361                 if (capture->next_frame_time == destination)
    362                     break;
    363 
    364                 // seek into which direction?
    365                 if (capture->next_frame_time < destination)
    366                 {
    367                     while (capture->next_frame_time < destination)
    368                     {
    369                         capture->next_frame_number++;
    370                         GetMovieNextInterestingTime (capture->myMovie, nextTimeStep, 1, & myType, capture->next_frame_time,
    371                                                      1, & capture->next_frame_time, NULL);
    372                         myErr = GetMoviesError();
    373                         if (myErr != noErr)
    374                         {
    375                             fprintf (stderr, "Couldn't go on to GetMovieNextInterestingTime() in icvGrabFrame_QT.\n");
    376                             return 0;
    377                         }
    378                     }
    379                 }
    380                 else
    381                 {
    382                     while (capture->next_frame_time > destination)
    383                     {
    384                         capture->next_frame_number--;
    385                         GetMovieNextInterestingTime (capture->myMovie, nextTimeStep, 1, & myType, capture->next_frame_time,
    386                                                      -1, & capture->next_frame_time, NULL);
    387                         myErr = GetMoviesError();
    388                         if (myErr != noErr)
    389                         {
    390                             fprintf (stderr, "Couldn't go back to GetMovieNextInterestingTime() in icvGrabFrame_QT.\n");
    391                             return 0;
    392                         }
    393                     }
    394                 }
    395             }
    396             break;
    397 
    398         case CV_CAP_PROP_POS_FRAMES:
    399             {
    400                 TimeValue    destination = static_cast<TimeValue> (value);
    401                 short        direction   = (destination > capture->next_frame_number) ? 1 : -1;
    402                 OSType       myType      = VisualMediaCharacteristic;
    403                 OSErr        myErr       = noErr;
    404 
    405                 while (destination != capture->next_frame_number)
    406                 {
    407                     capture->next_frame_number += direction;
    408                     GetMovieNextInterestingTime (capture->myMovie, nextTimeStep, 1, & myType, capture->next_frame_time,
    409                                                  direction, & capture->next_frame_time, NULL);
    410                     myErr = GetMoviesError();
    411                     if (myErr != noErr)
    412                     {
    413                         fprintf (stderr, "Couldn't step to desired frame number in icvGrabFrame_QT.\n");
    414                         return 0;
    415                     }
    416                 }
    417             }
    418             break;
    419 
    420         default:
    421             // unhandled or unknown capture property
    422             OPENCV_ERROR (CV_StsBadArg, "icvSetProperty_QT_Movie", "unknown or unhandled property_id");
    423             return 0;
    424     }
    425 
    426     // positive result means success
    427     return 1;
    428 }
    429 
    430 /**
    431  * the original meaning of this method is to acquire raw frame data for the next video
    432  * frame but not decompress it. With the QuickTime video reader, this is reduced to
    433  * advance to the current frame time.
    434  *
    435  * @author Mark Asbach <asbach (at) ient.rwth-aachen.de>
    436  * @date   2005-11-06
    437  */
    438 static int icvGrabFrame_QT_Movie (CvCapture_QT_Movie * capture)
    439 {
    440     OPENCV_ASSERT (capture,          "icvGrabFrame_QT_Movie", "'capture' is a NULL-pointer");
    441     OPENCV_ASSERT (capture->myMovie, "icvGrabFrame_QT_Movie", "invalid Movie handle");
    442 
    443     TimeValue    myCurrTime;
    444     OSType       myType     = VisualMediaCharacteristic;
    445     OSErr        myErr      = noErr;
    446 
    447 
    448     // jump to current video sample
    449     SetMovieTimeValue (capture->myMovie, capture->next_frame_time);
    450     myErr = GetMoviesError();
    451     if (myErr != noErr)
    452     {
    453         fprintf (stderr, "Couldn't SetMovieTimeValue() in icvGrabFrame_QT_Movie.\n");
    454         return  0;
    455     }
    456 
    457     // where are we now?
    458     myCurrTime = GetMovieTime (capture->myMovie, NULL);
    459 
    460     // increment counters
    461     capture->next_frame_number++;
    462     GetMovieNextInterestingTime (capture->myMovie, nextTimeStep, 1, & myType, myCurrTime, 1, & capture->next_frame_time, NULL);
    463     myErr = GetMoviesError();
    464     if (myErr != noErr)
    465     {
    466         fprintf (stderr, "Couldn't GetMovieNextInterestingTime() in icvGrabFrame_QT_Movie.\n");
    467         return 0;
    468     }
    469 
    470     // that's it
    471     return 1;
    472 }
    473 
    474 /**
    475  * render the current frame into an image buffer and convert to OpenCV IplImage
    476  * buffer layout (BGR sampling)
    477  *
    478  * @author Mark Asbach <asbach (at) ient.rwth-aachen.de>
    479  * @date   2005-11-06
    480  */
    481 static const void * icvRetrieveFrame_QT_Movie (CvCapture_QT_Movie * capture, int)
    482 {
    483     OPENCV_ASSERT (capture,            "icvRetrieveFrame_QT_Movie", "'capture' is a NULL-pointer");
    484     OPENCV_ASSERT (capture->myMovie,   "icvRetrieveFrame_QT_Movie", "invalid Movie handle");
    485     OPENCV_ASSERT (capture->image_rgb, "icvRetrieveFrame_QT_Movie", "invalid source image");
    486     OPENCV_ASSERT (capture->image_bgr, "icvRetrieveFrame_QT_Movie", "invalid destination image");
    487 
    488     PixMapHandle  myPixMapHandle = nil;
    489     OSErr         myErr          = noErr;
    490 
    491 
    492     // invalidates the movie's display state so that the Movie Toolbox
    493     // redraws the movie the next time we call MoviesTask
    494     UpdateMovie (capture->myMovie);
    495     myErr = GetMoviesError ();
    496     if (myErr != noErr)
    497     {
    498         fprintf (stderr, "Couldn't UpdateMovie() in icvRetrieveFrame_QT_Movie().\n");
    499         return 0;
    500     }
    501 
    502     // service active movie (= redraw immediately)
    503     MoviesTask (capture->myMovie, 0L);
    504     myErr = GetMoviesError ();
    505     if (myErr != noErr)
    506     {
    507         fprintf (stderr, "MoviesTask() didn't succeed in icvRetrieveFrame_QT_Movie().\n");
    508         return 0;
    509     }
    510 
    511     // update IplImage header that points to PixMap of the Movie's GWorld.
    512     // unfortunately, cvCvtColor doesn't know ARGB, the QuickTime pixel format,
    513     // so we pass a modfied address.
    514     // ATTENTION: don't access the last pixel's alpha entry, it's inexistant
    515     myPixMapHandle = GetGWorldPixMap (capture->myGWorld);
    516     LockPixels (myPixMapHandle);
    517     cvSetData (capture->image_rgb, GetPixBaseAddr (myPixMapHandle) + 1, GetPixRowBytes (myPixMapHandle));
    518 
    519     // covert RGB of GWorld to BGR
    520     cvCvtColor (capture->image_rgb, capture->image_bgr, CV_RGBA2BGR);
    521 
    522     // allow QuickTime to access the buffer again
    523     UnlockPixels (myPixMapHandle);
    524 
    525     // always return the same image pointer
    526     return capture->image_bgr;
    527 }
    528 
    529 
    530 // ----------------------------------------------------------------------------------------
    531 #pragma mark -
    532 #pragma mark Capturing from Video Cameras
    533 
    534 #ifdef USE_VDIG_VERSION
    535 
    536     /// SequenceGrabber state structure for QuickTime
    537     typedef struct CvCapture_QT_Cam_vdig
    538     {
    539         ComponentInstance  grabber;
    540         short              channel;
    541         GWorldPtr          myGWorld;
    542         PixMapHandle       pixmap;
    543 
    544         CvSize             size;
    545         long               number_of_frames;
    546 
    547         IplImage         * image_rgb; // will point to the PixMap of myGWorld
    548         IplImage         * image_bgr; // will be returned by icvRetrieveFrame_QT()
    549 
    550     } CvCapture_QT_Cam;
    551 
    552 #else
    553 
    554     typedef struct CvCapture_QT_Cam_barg
    555     {
    556         SeqGrabComponent   grabber;
    557         SGChannel          channel;
    558         GWorldPtr          gworld;
    559         Rect               bounds;
    560         ImageSequence      sequence;
    561 
    562         volatile bool      got_frame;
    563 
    564         CvSize             size;
    565         IplImage         * image_rgb; // will point to the PixMap of myGWorld
    566         IplImage         * image_bgr; // will be returned by icvRetrieveFrame_QT()
    567 
    568     } CvCapture_QT_Cam;
    569 
    570 #endif
    571 
    572 static       int         icvOpenCamera_QT        (CvCapture_QT_Cam * capture, const int index);
    573 static       int         icvClose_QT_Cam         (CvCapture_QT_Cam * capture);
    574 static       double      icvGetProperty_QT_Cam   (CvCapture_QT_Cam * capture, int property_id);
    575 static       int         icvSetProperty_QT_Cam   (CvCapture_QT_Cam * capture, int property_id, double value);
    576 static       int         icvGrabFrame_QT_Cam     (CvCapture_QT_Cam * capture);
    577 static const void      * icvRetrieveFrame_QT_Cam (CvCapture_QT_Cam * capture, int);
    578 
    579 
    580 /**
    581  * Initialize memory structure and call method to open camera
    582  *
    583  * @author Mark Asbach <asbach (at) ient.rwth-aachen.de>
    584  * @date 2006-01-29
    585  */
    586 static CvCapture_QT_Cam * icvCaptureFromCam_QT (const int index)
    587 {
    588     if (! did_enter_movies)
    589     {
    590         EnterMovies();
    591         did_enter_movies = 1;
    592     }
    593 
    594     CvCapture_QT_Cam * capture = 0;
    595 
    596     if (index >= 0)
    597     {
    598         capture = (CvCapture_QT_Cam *) cvAlloc (sizeof (*capture));
    599         memset (capture, 0, sizeof(*capture));
    600 
    601         if (!icvOpenCamera_QT (capture, index))
    602             cvFree (&capture);
    603     }
    604 
    605     return capture;
    606 }
    607 
    608 /// capture properties currently unimplemented for QuickTime camera interface
    609 static double icvGetProperty_QT_Cam (CvCapture_QT_Cam * capture, int property_id)
    610 {
    611     assert (0);
    612     return 0;
    613 }
    614 
    615 /// capture properties currently unimplemented for QuickTime camera interface
    616 static int icvSetProperty_QT_Cam (CvCapture_QT_Cam * capture, int property_id, double value)
    617 {
    618     assert (0);
    619     return 0;
    620 }
    621 
    622 #ifdef USE_VDIG_VERSION
    623 #pragma mark Capturing using VDIG
    624 
    625 /**
    626  * Open a quicktime video grabber component. This could be an attached
    627  * IEEE1394 camera, a web cam, an iSight or digitizer card / video converter.
    628  *
    629  * @author Mark Asbach <asbach (at) ient.rwth-aachen.de>
    630  * @date 2006-01-29
    631  */
    632 static int icvOpenCamera_QT (CvCapture_QT_Cam * capture, const int index)
    633 {
    634     OPENCV_ASSERT (capture,            "icvOpenCamera_QT", "'capture' is a NULL-pointer");
    635     OPENCV_ASSERT (index >=0, "icvOpenCamera_QT", "camera index is negative");
    636 
    637     ComponentDescription	component_description;
    638     Component				component = 0;
    639     int                     number_of_inputs = 0;
    640     Rect                    myRect;
    641     ComponentResult			result = noErr;
    642 
    643 
    644     // travers all components and count video digitizer channels
    645     component_description.componentType         = videoDigitizerComponentType;
    646     component_description.componentSubType      = 0L;
    647     component_description.componentManufacturer = 0L;
    648     component_description.componentFlags        = 0L;
    649     component_description.componentFlagsMask    = 0L;
    650     do
    651     {
    652         // traverse component list
    653         component = FindNextComponent (component, & component_description);
    654 
    655         // found a component?
    656         if (component)
    657         {
    658             // dump component name
    659             #ifndef NDEBUG
    660                 ComponentDescription  desc;
    661                 Handle                nameHandle = NewHandleClear (200);
    662                 char                  nameBuffer [255];
    663 
    664                 result = GetComponentInfo (component, & desc, nameHandle, nil, nil);
    665                 OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt GetComponentInfo()");
    666                 OPENCV_ASSERT (*nameHandle, "icvOpenCamera_QT", "No name returned by GetComponentInfo()");
    667                 snprintf (nameBuffer, (**nameHandle) + 1, "%s", (char *) (* nameHandle + 1));
    668                 printf ("- Videodevice: %s\n", nameBuffer);
    669                 DisposeHandle (nameHandle);
    670             #endif
    671 
    672             // open component to count number of inputs
    673             capture->grabber = OpenComponent (component);
    674             if (capture->grabber)
    675             {
    676                 result = VDGetNumberOfInputs (capture->grabber, & capture->channel);
    677                 if (result != noErr)
    678                     fprintf (stderr, "Couldnt GetNumberOfInputs: %d\n", (int) result);
    679                 else
    680                 {
    681                     #ifndef NDEBUG
    682                         printf ("  Number of inputs: %d\n", (int) capture->channel + 1);
    683                     #endif
    684 
    685                     // add to overall number of inputs
    686                     number_of_inputs += capture->channel + 1;
    687 
    688                     // did the user select an input that falls into this device's
    689                     // range of inputs? Then leave the loop
    690                     if (number_of_inputs > index)
    691                     {
    692                         // calculate relative channel index
    693                         capture->channel = index - number_of_inputs + capture->channel + 1;
    694                         OPENCV_ASSERT (capture->channel >= 0, "icvOpenCamera_QT", "negative channel number");
    695 
    696                         // dump channel name
    697                         #ifndef NDEBUG
    698                             char  name[256];
    699                             Str255  nameBuffer;
    700 
    701                             result = VDGetInputName (capture->grabber, capture->channel, nameBuffer);
    702                             OPENCV_ASSERT (result == noErr, "ictOpenCamera_QT", "couldnt GetInputName()");
    703                             snprintf (name, *nameBuffer, "%s", (char *) (nameBuffer + 1));
    704                             printf ("  Choosing input %d - %s\n", (int) capture->channel, name);
    705                         #endif
    706 
    707                         // leave the loop
    708                         break;
    709                     }
    710                 }
    711 
    712                 // obviously no inputs of this device/component were needed
    713                 CloseComponent (capture->grabber);
    714             }
    715         }
    716     }
    717     while (component);
    718 
    719     // did we find the desired input?
    720     if (! component)
    721     {
    722         fprintf(stderr, "Not enough inputs available - can't choose input %d\n", index);
    723         return 0;
    724     }
    725 
    726     // -- Okay now, we selected the digitizer input, lets set up digitizer destination --
    727 
    728     ClearMoviesStickyError();
    729 
    730     // Select the desired input
    731     result = VDSetInput (capture->grabber, capture->channel);
    732     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt select video digitizer input");
    733 
    734     // get the bounding rectangle of the video digitizer
    735     result = VDGetActiveSrcRect (capture->grabber, capture->channel, & myRect);
    736     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt create VDGetActiveSrcRect from digitizer");
    737     myRect.right = 640; myRect.bottom = 480;
    738     capture->size = cvSize (myRect.right - myRect.left, myRect.bottom - myRect.top);
    739     printf ("Source rect is %d, %d -- %d, %d\n", (int) myRect.left, (int) myRect.top, (int) myRect.right, (int) myRect.bottom);
    740 
    741     // create offscreen GWorld
    742     result = QTNewGWorld (& capture->myGWorld, k32ARGBPixelFormat, & myRect, nil, nil, 0);
    743     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt create QTNewGWorld() for output image");
    744 
    745     // get pixmap
    746     capture->pixmap = GetGWorldPixMap (capture->myGWorld);
    747     result = GetMoviesError ();
    748     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt get pixmap");
    749 
    750     // set digitizer rect
    751     result = VDSetDigitizerRect (capture->grabber, & myRect);
    752     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt create VDGetActiveSrcRect from digitizer");
    753 
    754     // set destination of digitized input
    755     result = VDSetPlayThruDestination (capture->grabber, capture->pixmap, & myRect, nil, nil);
    756     printf ("QuickTime error: %d\n", (int) result);
    757     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set video destination");
    758 
    759     // get destination of digitized images
    760     result = VDGetPlayThruDestination (capture->grabber, & capture->pixmap, nil, nil, nil);
    761     printf ("QuickTime error: %d\n", (int) result);
    762     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt get video destination");
    763     OPENCV_ASSERT (capture->pixmap != nil, "icvOpenCamera_QT", "empty set video destination");
    764 
    765     // get the bounding rectangle of the video digitizer
    766     GetPixBounds (capture->pixmap, & myRect);
    767     capture->size = cvSize (myRect.right - myRect.left, myRect.bottom - myRect.top);
    768 
    769     // build IplImage header that will point to the PixMap of the Movie's GWorld later on
    770     capture->image_rgb = cvCreateImageHeader (capture->size, IPL_DEPTH_8U, 4);
    771     OPENCV_ASSERT (capture->image_rgb, "icvOpenCamera_QT", "couldnt create image header");
    772 
    773     // create IplImage that hold correctly formatted result
    774     capture->image_bgr = cvCreateImage (capture->size, IPL_DEPTH_8U, 3);
    775     OPENCV_ASSERT (capture->image_bgr, "icvOpenCamera_QT", "couldnt create image");
    776 
    777     // notify digitizer component, that we well be starting grabbing soon
    778     result = VDCaptureStateChanging (capture->grabber, vdFlagCaptureIsForRecord | vdFlagCaptureStarting | vdFlagCaptureLowLatency);
    779     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set capture state");
    780 
    781 
    782     // yeah, we did it
    783     return 1;
    784 }
    785 
    786 static int icvClose_QT_Cam (CvCapture_QT_Cam * capture)
    787 {
    788     OPENCV_ASSERT (capture, "icvClose_QT_Cam", "'capture' is a NULL-pointer");
    789 
    790     ComponentResult	result = noErr;
    791 
    792     // notify digitizer component, that we well be stopping grabbing soon
    793     result = VDCaptureStateChanging (capture->grabber, vdFlagCaptureStopping);
    794     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set capture state");
    795 
    796     // release memory
    797     cvReleaseImage       (& capture->image_bgr);
    798     cvReleaseImageHeader (& capture->image_rgb);
    799     DisposeGWorld        (capture->myGWorld);
    800     CloseComponent       (capture->grabber);
    801 
    802     // sucessful
    803     return 1;
    804 }
    805 
    806 static int icvGrabFrame_QT_Cam (CvCapture_QT_Cam * capture)
    807 {
    808     OPENCV_ASSERT (capture,          "icvGrabFrame_QT_Cam", "'capture' is a NULL-pointer");
    809     OPENCV_ASSERT (capture->grabber, "icvGrabFrame_QT_Cam", "'grabber' is a NULL-pointer");
    810 
    811     ComponentResult	result = noErr;
    812 
    813     // grab one frame
    814     result = VDGrabOneFrame (capture->grabber);
    815     if (result != noErr)
    816     {
    817         fprintf (stderr, "VDGrabOneFrame failed\n");
    818         return 0;
    819     }
    820 
    821     // successful
    822     return 1;
    823 }
    824 
    825 static const void * icvRetrieveFrame_QT_Cam (CvCapture_QT_Cam * capture, int)
    826 {
    827     OPENCV_ASSERT (capture, "icvRetrieveFrame_QT_Cam", "'capture' is a NULL-pointer");
    828 
    829     PixMapHandle  myPixMapHandle = nil;
    830 
    831     // update IplImage header that points to PixMap of the Movie's GWorld.
    832     // unfortunately, cvCvtColor doesn't know ARGB, the QuickTime pixel format,
    833     // so we pass a modfied address.
    834     // ATTENTION: don't access the last pixel's alpha entry, it's inexistant
    835     //myPixMapHandle = GetGWorldPixMap (capture->myGWorld);
    836     myPixMapHandle = capture->pixmap;
    837     LockPixels (myPixMapHandle);
    838     cvSetData (capture->image_rgb, GetPixBaseAddr (myPixMapHandle) + 1, GetPixRowBytes (myPixMapHandle));
    839 
    840     // covert RGB of GWorld to BGR
    841     cvCvtColor (capture->image_rgb, capture->image_bgr, CV_RGBA2BGR);
    842 
    843     // allow QuickTime to access the buffer again
    844     UnlockPixels (myPixMapHandle);
    845 
    846     // always return the same image pointer
    847     return capture->image_bgr;
    848 }
    849 
    850 #else
    851 #pragma mark Capturing using Sequence Grabber
    852 
    853 static OSErr icvDataProc_QT_Cam (SGChannel channel, Ptr raw_data, long len, long *, long, TimeValue, short, long refCon)
    854 {
    855     CvCapture_QT_Cam  * capture = (CvCapture_QT_Cam *) refCon;
    856     CodecFlags          ignore;
    857     ComponentResult     err     = noErr;
    858 
    859 
    860     // we need valid pointers
    861     OPENCV_ASSERT (capture,          "icvDataProc_QT_Cam", "'capture' is a NULL-pointer");
    862     OPENCV_ASSERT (capture->gworld,  "icvDataProc_QT_Cam", "'gworld' is a NULL-pointer");
    863     OPENCV_ASSERT (raw_data,         "icvDataProc_QT_Cam", "'raw_data' is a NULL-pointer");
    864 
    865     // create a decompression sequence the first time
    866     if (capture->sequence == 0)
    867     {
    868         ImageDescriptionHandle   description = (ImageDescriptionHandle) NewHandle(0);
    869 
    870         // we need a decompression sequence that fits the raw data coming from the camera
    871         err = SGGetChannelSampleDescription (channel, (Handle) description);
    872         OPENCV_ASSERT (err == noErr, "icvDataProc_QT_Cam", "couldnt get channel sample description");
    873 
    874         //*************************************************************************************//
    875         //This fixed a bug when Quicktime is called twice to grab a frame (black band bug) - Yannick Verdie 2010
    876         Rect sourceRect;
    877         sourceRect.top = 0;
    878         sourceRect.left = 0;
    879         sourceRect.right = (**description).width;
    880         sourceRect.bottom = (**description).height;
    881 
    882         MatrixRecord scaleMatrix;
    883         RectMatrix(&scaleMatrix,&sourceRect,&capture->bounds);
    884 
    885         err = DecompressSequenceBegin (&capture->sequence, description, capture->gworld, 0,&capture->bounds,&scaleMatrix, srcCopy, NULL, 0, codecNormalQuality, bestSpeedCodec);
    886         //**************************************************************************************//
    887 
    888         OPENCV_ASSERT (err == noErr, "icvDataProc_QT_Cam", "couldnt begin decompression sequence");
    889         DisposeHandle ((Handle) description);
    890     }
    891 
    892     // okay, we have a decompression sequence -> decompress!
    893     err = DecompressSequenceFrameS (capture->sequence, raw_data, len, 0, &ignore, nil);
    894     if (err != noErr)
    895     {
    896         fprintf (stderr, "icvDataProc_QT_Cam: couldn't decompress frame - %d\n", (int) err);
    897         return err;
    898     }
    899 
    900     // check if we dropped a frame
    901     /*#ifndef NDEBUG
    902         if (capture->got_frame)
    903             fprintf (stderr, "icvDataProc_QT_Cam: frame was dropped\n");
    904     #endif*/
    905 
    906     // everything worked as expected
    907     capture->got_frame = true;
    908     return noErr;
    909 }
    910 
    911 
    912 static int icvOpenCamera_QT (CvCapture_QT_Cam * capture, const int index)
    913 {
    914     OPENCV_ASSERT (capture,    "icvOpenCamera_QT", "'capture' is a NULL-pointer");
    915     OPENCV_ASSERT (index >= 0, "icvOpenCamera_QT", "camera index is negative");
    916 
    917     PixMapHandle  pixmap       = nil;
    918     OSErr         result       = noErr;
    919 
    920     // open sequence grabber component
    921     capture->grabber = OpenDefaultComponent (SeqGrabComponentType, 0);
    922     OPENCV_ASSERT (capture->grabber, "icvOpenCamera_QT", "couldnt create image");
    923 
    924     // initialize sequence grabber component
    925     result = SGInitialize (capture->grabber);
    926     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt initialize sequence grabber");
    927     result = SGSetDataRef (capture->grabber, 0, 0, seqGrabDontMakeMovie);
    928     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set data reference of sequence grabber");
    929 
    930     // set up video channel
    931     result = SGNewChannel (capture->grabber, VideoMediaType, & (capture->channel));
    932     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt create new video channel");
    933 
    934     // select the camera indicated by index
    935     SGDeviceList device_list = 0;
    936     result = SGGetChannelDeviceList (capture->channel, 0, & device_list);
    937     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt get channel device list");
    938     for (int i = 0, current_index = 1; i < (*device_list)->count; i++)
    939     {
    940         SGDeviceName device = (*device_list)->entry[i];
    941         if (device.flags == 0)
    942         {
    943             if (current_index == index)
    944             {
    945                 result = SGSetChannelDevice (capture->channel, device.name);
    946                 OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set the channel video device");
    947                 break;
    948             }
    949             current_index++;
    950         }
    951     }
    952     result = SGDisposeDeviceList (capture->grabber, device_list);
    953     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt dispose the channel device list");
    954 
    955     // query natural camera resolution -- this will be wrong, but will be an upper
    956     // bound on the actual resolution -- the actual resolution is set below
    957     // after starting the frame grabber
    958     result = SGGetSrcVideoBounds (capture->channel, & (capture->bounds));
    959     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set video channel bounds");
    960 
    961     // create offscreen GWorld
    962     result = QTNewGWorld (& (capture->gworld), k32ARGBPixelFormat, & (capture->bounds), 0, 0, 0);
    963     result = SGSetGWorld (capture->grabber, capture->gworld, 0);
    964     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set GWorld for sequence grabber");
    965     result = SGSetChannelBounds (capture->channel, & (capture->bounds));
    966     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set video channel bounds");
    967     result = SGSetChannelUsage (capture->channel, seqGrabRecord);
    968     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set channel usage");
    969 
    970     // start recording so we can size
    971     result = SGStartRecord (capture->grabber);
    972     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt start recording");
    973 
    974     // don't know *actual* resolution until now
    975     ImageDescriptionHandle imageDesc = (ImageDescriptionHandle)NewHandle(0);
    976     result = SGGetChannelSampleDescription(capture->channel, (Handle)imageDesc);
    977     OPENCV_ASSERT( result == noErr, "icvOpenCamera_QT", "couldn't get image size");
    978     capture->bounds.right = (**imageDesc).width;
    979     capture->bounds.bottom = (**imageDesc).height;
    980     DisposeHandle ((Handle) imageDesc);
    981 
    982     // stop grabber so that we can reset the parameters to the right size
    983     result = SGStop (capture->grabber);
    984     OPENCV_ASSERT (result == noErr, "icveClose_QT_Cam", "couldnt stop recording");
    985 
    986     // reset GWorld to correct image size
    987     GWorldPtr tmpgworld;
    988     result = QTNewGWorld( &tmpgworld, k32ARGBPixelFormat, &(capture->bounds), 0, 0, 0);
    989     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt create offscreen GWorld");
    990     result = SGSetGWorld( capture->grabber, tmpgworld, 0);
    991     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set GWorld for sequence grabber");
    992     DisposeGWorld( capture->gworld );
    993     capture->gworld = tmpgworld;
    994 
    995     result = SGSetChannelBounds (capture->channel, & (capture->bounds));
    996     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set video channel bounds");
    997 
    998     // allocate images
    999     capture->size = cvSize (capture->bounds.right - capture->bounds.left, capture->bounds.bottom - capture->bounds.top);
   1000 
   1001     // build IplImage header that points to the PixMap of the Movie's GWorld.
   1002     // unfortunately, cvCvtColor doesn't know ARGB, the QuickTime pixel format,
   1003     // so we shift the base address by one byte.
   1004     // ATTENTION: don't access the last pixel's alpha entry, it's inexistant
   1005     capture->image_rgb = cvCreateImageHeader (capture->size, IPL_DEPTH_8U, 4);
   1006     OPENCV_ASSERT (capture->image_rgb, "icvOpenCamera_QT", "couldnt create image header");
   1007     pixmap = GetGWorldPixMap (capture->gworld);
   1008     OPENCV_ASSERT (pixmap, "icvOpenCamera_QT", "didn't get GWorld PixMap handle");
   1009     LockPixels (pixmap);
   1010     cvSetData (capture->image_rgb, GetPixBaseAddr (pixmap) + 1, GetPixRowBytes (pixmap));
   1011 
   1012     // create IplImage that hold correctly formatted result
   1013     capture->image_bgr = cvCreateImage (capture->size, IPL_DEPTH_8U, 3);
   1014     OPENCV_ASSERT (capture->image_bgr, "icvOpenCamera_QT", "couldnt create image");
   1015 
   1016 
   1017     // tell the sequence grabber to invoke our data proc
   1018     result = SGSetDataProc (capture->grabber, NewSGDataUPP (icvDataProc_QT_Cam), (long) capture);
   1019     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set data proc");
   1020 
   1021     // start recording
   1022     result = SGStartRecord (capture->grabber);
   1023     OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt start recording");
   1024 
   1025     return 1;
   1026 }
   1027 
   1028 
   1029 static int icvClose_QT_Cam (CvCapture_QT_Cam * capture)
   1030 {
   1031     OPENCV_ASSERT (capture, "icvClose_QT_Cam", "'capture' is a NULL-pointer");
   1032 
   1033     OSErr  result = noErr;
   1034 
   1035 
   1036     // stop recording
   1037     result = SGStop (capture->grabber);
   1038     OPENCV_ASSERT (result == noErr, "icveClose_QT_Cam", "couldnt stop recording");
   1039 
   1040     // close sequence grabber component
   1041     result = CloseComponent (capture->grabber);
   1042     OPENCV_ASSERT (result == noErr, "icveClose_QT_Cam", "couldnt close sequence grabber component");
   1043 
   1044     // end decompression sequence
   1045     CDSequenceEnd (capture->sequence);
   1046 
   1047     // free memory
   1048     cvReleaseImage (& capture->image_bgr);
   1049     cvReleaseImageHeader (& capture->image_rgb);
   1050     DisposeGWorld (capture->gworld);
   1051 
   1052     // sucessful
   1053     return 1;
   1054 }
   1055 
   1056 static int icvGrabFrame_QT_Cam (CvCapture_QT_Cam * capture)
   1057 {
   1058     OPENCV_ASSERT (capture,          "icvGrabFrame_QT_Cam", "'capture' is a NULL-pointer");
   1059     OPENCV_ASSERT (capture->grabber, "icvGrabFrame_QT_Cam", "'grabber' is a NULL-pointer");
   1060 
   1061     ComponentResult	result = noErr;
   1062 
   1063 
   1064     // grab one frame
   1065     result = SGIdle (capture->grabber);
   1066     if (result != noErr)
   1067     {
   1068         fprintf (stderr, "SGIdle failed in icvGrabFrame_QT_Cam with error %d\n", (int) result);
   1069         return 0;
   1070     }
   1071 
   1072     // successful
   1073     return 1;
   1074 }
   1075 
   1076 static const void * icvRetrieveFrame_QT_Cam (CvCapture_QT_Cam * capture, int)
   1077 {
   1078     OPENCV_ASSERT (capture,            "icvRetrieveFrame_QT_Cam", "'capture' is a NULL-pointer");
   1079     OPENCV_ASSERT (capture->image_rgb, "icvRetrieveFrame_QT_Cam", "invalid source image");
   1080     OPENCV_ASSERT (capture->image_bgr, "icvRetrieveFrame_QT_Cam", "invalid destination image");
   1081 
   1082     OSErr         myErr          = noErr;
   1083 
   1084 
   1085     // service active sequence grabbers (= redraw immediately)
   1086     while (! capture->got_frame)
   1087     {
   1088         myErr = SGIdle (capture->grabber);
   1089         if (myErr != noErr)
   1090         {
   1091             fprintf (stderr, "SGIdle() didn't succeed in icvRetrieveFrame_QT_Cam().\n");
   1092             return 0;
   1093         }
   1094     }
   1095 
   1096     // covert RGB of GWorld to BGR
   1097     cvCvtColor (capture->image_rgb, capture->image_bgr, CV_RGBA2BGR);
   1098 
   1099     // reset grabbing status
   1100     capture->got_frame = false;
   1101 
   1102     // always return the same image pointer
   1103     return capture->image_bgr;
   1104 }
   1105 
   1106 #endif
   1107 
   1108 
   1109 typedef struct CvVideoWriter_QT {
   1110 
   1111     DataHandler data_handler;
   1112     Movie movie;
   1113     Track track;
   1114     Media video;
   1115 
   1116     ICMCompressionSessionRef compression_session_ref;
   1117 
   1118     TimeValue duration_per_sample;
   1119 } CvVideoWriter_QT;
   1120 
   1121 
   1122 static TimeScale const TIME_SCALE = 600;
   1123 
   1124 static OSStatus icvEncodedFrameOutputCallback(
   1125     void* writer,
   1126     ICMCompressionSessionRef compression_session_ref,
   1127     OSStatus error,
   1128     ICMEncodedFrameRef encoded_frame_ref,
   1129     void* reserved
   1130 );
   1131 
   1132 static void icvSourceTrackingCallback(
   1133     void *source_tracking_ref_con,
   1134     ICMSourceTrackingFlags source_tracking_flags,
   1135     void *source_frame_ref_con,
   1136     void *reserved
   1137 );
   1138 
   1139 static int icvWriteFrame_QT(
   1140     CvVideoWriter_QT * video_writer,
   1141     const IplImage * image
   1142 ) {
   1143     CVPixelBufferRef pixel_buffer_ref = NULL;
   1144     CVReturn retval =
   1145         CVPixelBufferCreate(
   1146             kCFAllocatorDefault,
   1147             image->width, image->height, k24RGBPixelFormat,
   1148             NULL /* pixel_buffer_attributes */,
   1149             &pixel_buffer_ref
   1150         );
   1151 
   1152     // convert BGR IPL image to RGB pixel buffer
   1153     IplImage* image_rgb =
   1154         cvCreateImageHeader(
   1155             cvSize( image->width, image->height ),
   1156             IPL_DEPTH_8U,
   1157             3
   1158         );
   1159 
   1160     retval = CVPixelBufferLockBaseAddress( pixel_buffer_ref, 0 );
   1161 
   1162     void* base_address = CVPixelBufferGetBaseAddress( pixel_buffer_ref );
   1163     size_t bytes_per_row = CVPixelBufferGetBytesPerRow( pixel_buffer_ref );
   1164     cvSetData( image_rgb, base_address, bytes_per_row );
   1165 
   1166     cvConvertImage( image, image_rgb, CV_CVTIMG_SWAP_RB );
   1167 
   1168     retval = CVPixelBufferUnlockBaseAddress( pixel_buffer_ref, 0 );
   1169 
   1170     cvReleaseImageHeader( &image_rgb );
   1171 
   1172     ICMSourceTrackingCallbackRecord source_tracking_callback_record;
   1173     source_tracking_callback_record.sourceTrackingCallback =
   1174         icvSourceTrackingCallback;
   1175     source_tracking_callback_record.sourceTrackingRefCon = NULL;
   1176 
   1177     OSStatus status =
   1178         ICMCompressionSessionEncodeFrame(
   1179             video_writer->compression_session_ref,
   1180             pixel_buffer_ref,
   1181             0,
   1182             video_writer->duration_per_sample,
   1183             kICMValidTime_DisplayDurationIsValid,
   1184             NULL,
   1185             &source_tracking_callback_record,
   1186             static_cast<void*>( &pixel_buffer_ref )
   1187         );
   1188 
   1189     return 0;
   1190 }
   1191 
   1192 static void icvReleaseVideoWriter_QT( CvVideoWriter_QT ** writer ) {
   1193     if ( ( writer != NULL ) && ( *writer != NULL ) ) {
   1194         CvVideoWriter_QT* video_writer = *writer;
   1195 
   1196         // force compression session to complete encoding of outstanding source
   1197         // frames
   1198         ICMCompressionSessionCompleteFrames(
   1199             video_writer->compression_session_ref, 1, 0, 0
   1200         );
   1201 
   1202         EndMediaEdits( video_writer->video );
   1203 
   1204         ICMCompressionSessionRelease( video_writer->compression_session_ref );
   1205 
   1206         InsertMediaIntoTrack(
   1207             video_writer->track,
   1208             0,
   1209             0,
   1210             GetMediaDuration( video_writer->video ),
   1211             FixRatio( 1, 1 )
   1212         );
   1213 
   1214         UpdateMovieInStorage( video_writer->movie, video_writer->data_handler );
   1215 
   1216         CloseMovieStorage( video_writer->data_handler );
   1217 
   1218 /*
   1219         // export to AVI
   1220         Handle data_ref;
   1221         OSType data_ref_type;
   1222         QTNewDataReferenceFromFullPathCFString(
   1223             CFSTR( "/Users/seibert/Desktop/test.avi" ), kQTPOSIXPathStyle, 0,
   1224             &data_ref, &data_ref_type
   1225         );
   1226 
   1227         ConvertMovieToDataRef( video_writer->movie, NULL, data_ref,
   1228             data_ref_type, kQTFileTypeAVI, 'TVOD', 0, NULL );
   1229 
   1230         DisposeHandle( data_ref );
   1231 */
   1232 
   1233         DisposeMovie( video_writer->movie );
   1234 
   1235         cvFree( writer );
   1236     }
   1237 }
   1238 
   1239 static OSStatus icvEncodedFrameOutputCallback(
   1240     void* writer,
   1241     ICMCompressionSessionRef compression_session_ref,
   1242     OSStatus error,
   1243     ICMEncodedFrameRef encoded_frame_ref,
   1244     void* reserved
   1245 ) {
   1246     CvVideoWriter_QT* video_writer = static_cast<CvVideoWriter_QT*>( writer );
   1247 
   1248     OSStatus err = AddMediaSampleFromEncodedFrame( video_writer->video,
   1249         encoded_frame_ref, NULL );
   1250 
   1251     return err;
   1252 }
   1253 
   1254 static void icvSourceTrackingCallback(
   1255     void *source_tracking_ref_con,
   1256     ICMSourceTrackingFlags source_tracking_flags,
   1257     void *source_frame_ref_con,
   1258     void *reserved
   1259 ) {
   1260     if ( source_tracking_flags & kICMSourceTracking_ReleasedPixelBuffer ) {
   1261         CVPixelBufferRelease(
   1262             *static_cast<CVPixelBufferRef*>( source_frame_ref_con )
   1263         );
   1264     }
   1265 }
   1266 
   1267 
   1268 static CvVideoWriter_QT* icvCreateVideoWriter_QT(
   1269     const char * filename,
   1270     int fourcc,
   1271     double fps,
   1272     CvSize frame_size,
   1273     int is_color
   1274 ) {
   1275     CV_FUNCNAME( "icvCreateVideoWriter" );
   1276 
   1277     CvVideoWriter_QT* video_writer =
   1278         static_cast<CvVideoWriter_QT*>( cvAlloc( sizeof( CvVideoWriter_QT ) ) );
   1279     memset( video_writer, 0, sizeof( CvVideoWriter_QT ) );
   1280 
   1281     Handle                            data_ref     = NULL;
   1282     OSType                            data_ref_type;
   1283     DataHandler                       data_handler = NULL;
   1284     Movie                             movie        = NULL;
   1285     ICMCompressionSessionOptionsRef   options_ref  = NULL;
   1286     ICMCompressionSessionRef          compression_session_ref = NULL;
   1287     CFStringRef                       out_path     = nil;
   1288     Track                             video_track  = nil;
   1289     Media                             video        = nil;
   1290     OSErr                             err          = noErr;
   1291     CodecType codecType = kRawCodecType;
   1292 
   1293     __BEGIN__
   1294 
   1295     // validate input arguments
   1296     if ( filename == NULL ) {
   1297         CV_ERROR( CV_StsBadArg, "Video file name must not be NULL" );
   1298     }
   1299     if ( fps <= 0.0 ) {
   1300         CV_ERROR( CV_StsBadArg, "FPS must be larger than 0.0" );
   1301     }
   1302     if ( ( frame_size.width <= 0 ) || ( frame_size.height <= 0 ) ) {
   1303         CV_ERROR( CV_StsBadArg,
   1304             "Frame width and height must be larger than 0" );
   1305     }
   1306 
   1307     // initialize QuickTime
   1308     if ( !did_enter_movies ) {
   1309         err = EnterMovies();
   1310         if ( err != noErr ) {
   1311             CV_ERROR( CV_StsInternal, "Unable to initialize QuickTime" );
   1312         }
   1313         did_enter_movies = 1;
   1314     }
   1315 
   1316     // convert the file name into a data reference
   1317     out_path = CFStringCreateWithCString( kCFAllocatorDefault, filename, kCFStringEncodingISOLatin1 );
   1318     CV_ASSERT( out_path != nil );
   1319     err = QTNewDataReferenceFromFullPathCFString( out_path, kQTPOSIXPathStyle,
   1320         0, &data_ref, &data_ref_type );
   1321     CFRelease( out_path );
   1322     if ( err != noErr ) {
   1323         CV_ERROR( CV_StsInternal,
   1324             "Cannot create data reference from file name" );
   1325     }
   1326 
   1327     // create a new movie on disk
   1328     err = CreateMovieStorage( data_ref, data_ref_type, 'TVOD',
   1329         smCurrentScript, newMovieActive, &data_handler, &movie );
   1330 
   1331     if ( err != noErr ) {
   1332         CV_ERROR( CV_StsInternal, "Cannot create movie storage" );
   1333     }
   1334 
   1335     // create a track with video
   1336     video_track = NewMovieTrack (movie,
   1337             FixRatio( frame_size.width, 1 ),
   1338             FixRatio( frame_size.height, 1 ),
   1339             kNoVolume);
   1340     err = GetMoviesError();
   1341     if ( err != noErr ) {
   1342         CV_ERROR( CV_StsInternal, "Cannot create video track" );
   1343     }
   1344     video = NewTrackMedia( video_track, VideoMediaType, TIME_SCALE, nil, 0 );
   1345     err = GetMoviesError();
   1346     if ( err != noErr ) {
   1347         CV_ERROR( CV_StsInternal, "Cannot create video media" );
   1348     }
   1349 
   1350     /*if( fourcc == CV_FOURCC( 'D', 'I', 'B', ' ' ))
   1351         codecType = kRawCodecType;*/
   1352 
   1353     // start a compression session
   1354     err = ICMCompressionSessionOptionsCreate( kCFAllocatorDefault,
   1355         &options_ref );
   1356     if ( err != noErr ) {
   1357         CV_ERROR( CV_StsInternal, "Cannot create compression session options" );
   1358     }
   1359     err = ICMCompressionSessionOptionsSetAllowTemporalCompression( options_ref,
   1360         true );
   1361     if ( err != noErr) {
   1362         CV_ERROR( CV_StsInternal, "Cannot enable temporal compression" );
   1363     }
   1364     err = ICMCompressionSessionOptionsSetAllowFrameReordering( options_ref,
   1365         true );
   1366     if ( err != noErr) {
   1367         CV_ERROR( CV_StsInternal, "Cannot enable frame reordering" );
   1368     }
   1369 
   1370     ICMEncodedFrameOutputRecord encoded_frame_output_record;
   1371     encoded_frame_output_record.encodedFrameOutputCallback =
   1372         icvEncodedFrameOutputCallback;
   1373     encoded_frame_output_record.encodedFrameOutputRefCon =
   1374         static_cast<void*>( video_writer );
   1375     encoded_frame_output_record.frameDataAllocator = NULL;
   1376 
   1377     err = ICMCompressionSessionCreate( kCFAllocatorDefault, frame_size.width,
   1378         frame_size.height, codecType, TIME_SCALE, options_ref,
   1379         NULL /*source_pixel_buffer_attributes*/, &encoded_frame_output_record,
   1380         &compression_session_ref );
   1381     ICMCompressionSessionOptionsRelease( options_ref );
   1382     if ( err != noErr ) {
   1383         CV_ERROR( CV_StsInternal, "Cannot create compression session" );
   1384     }
   1385 
   1386     err = BeginMediaEdits( video );
   1387     if ( err != noErr ) {
   1388         CV_ERROR( CV_StsInternal, "Cannot begin media edits" );
   1389     }
   1390 
   1391     // fill in the video writer structure
   1392     video_writer->data_handler = data_handler;
   1393     video_writer->movie = movie;
   1394     video_writer->track = video_track;
   1395     video_writer->video = video;
   1396     video_writer->compression_session_ref = compression_session_ref;
   1397     video_writer->duration_per_sample =
   1398         static_cast<TimeValue>( static_cast<double>( TIME_SCALE ) / fps );
   1399 
   1400     __END__
   1401 
   1402     // clean up in case of error (unless error processing mode is
   1403     // CV_ErrModeLeaf)
   1404     if ( err != noErr ) {
   1405         if ( options_ref != NULL ) {
   1406             ICMCompressionSessionOptionsRelease( options_ref );
   1407         }
   1408         if ( compression_session_ref != NULL ) {
   1409             ICMCompressionSessionRelease( compression_session_ref );
   1410         }
   1411         if ( data_handler != NULL ) {
   1412             CloseMovieStorage( data_handler );
   1413         }
   1414         if ( movie != NULL ) {
   1415             DisposeMovie( movie );
   1416         }
   1417         if ( data_ref != NULL ) {
   1418             DeleteMovieStorage( data_ref, data_ref_type );
   1419             DisposeHandle( data_ref );
   1420         }
   1421         cvFree( reinterpret_cast<void**>( &video_writer ) );
   1422         video_writer = NULL;
   1423     }
   1424 
   1425     return video_writer;
   1426 }
   1427 
   1428 
   1429 /**
   1430 *
   1431 *   Wrappers for the new C++ CvCapture & CvVideoWriter structures
   1432 *
   1433 */
   1434 
   1435 class CvCapture_QT_Movie_CPP : public CvCapture
   1436 {
   1437 public:
   1438     CvCapture_QT_Movie_CPP() { captureQT = 0; }
   1439     virtual ~CvCapture_QT_Movie_CPP() { close(); }
   1440 
   1441     virtual bool open( const char* filename );
   1442     virtual void close();
   1443 
   1444     virtual double getProperty(int) const;
   1445     virtual bool setProperty(int, double);
   1446     virtual bool grabFrame();
   1447     virtual IplImage* retrieveFrame(int);
   1448     virtual int getCaptureDomain() { return CV_CAP_QT; } // Return the type of the capture object: CV_CAP_VFW, etc...
   1449 protected:
   1450 
   1451     CvCapture_QT_Movie* captureQT;
   1452 };
   1453 
   1454 bool CvCapture_QT_Movie_CPP::open( const char* filename )
   1455 {
   1456     close();
   1457     captureQT = icvCaptureFromFile_QT( filename );
   1458     return captureQT != 0;
   1459 }
   1460 
   1461 void CvCapture_QT_Movie_CPP::close()
   1462 {
   1463     if( captureQT )
   1464     {
   1465         icvClose_QT_Movie( captureQT );
   1466         cvFree( &captureQT );
   1467     }
   1468 }
   1469 
   1470 bool CvCapture_QT_Movie_CPP::grabFrame()
   1471 {
   1472     return captureQT ? icvGrabFrame_QT_Movie( captureQT ) != 0 : false;
   1473 }
   1474 
   1475 IplImage* CvCapture_QT_Movie_CPP::retrieveFrame(int)
   1476 {
   1477     return captureQT ? (IplImage*)icvRetrieveFrame_QT_Movie( captureQT, 0 ) : 0;
   1478 }
   1479 
   1480 double CvCapture_QT_Movie_CPP::getProperty( int propId ) const
   1481 {
   1482     return captureQT ? icvGetProperty_QT_Movie( captureQT, propId ) : 0;
   1483 }
   1484 
   1485 bool CvCapture_QT_Movie_CPP::setProperty( int propId, double value )
   1486 {
   1487     return captureQT ? icvSetProperty_QT_Movie( captureQT, propId, value ) != 0 : false;
   1488 }
   1489 
   1490 CvCapture* cvCreateFileCapture_QT( const char* filename )
   1491 {
   1492     CvCapture_QT_Movie_CPP* capture = new CvCapture_QT_Movie_CPP;
   1493 
   1494     if( capture->open( filename ))
   1495         return capture;
   1496 
   1497     delete capture;
   1498     return 0;
   1499 }
   1500 
   1501 
   1502 /////////////////////////////////////
   1503 
   1504 class CvCapture_QT_Cam_CPP : public CvCapture
   1505 {
   1506 public:
   1507     CvCapture_QT_Cam_CPP() { captureQT = 0; }
   1508     virtual ~CvCapture_QT_Cam_CPP() { close(); }
   1509 
   1510     virtual bool open( int index );
   1511     virtual void close();
   1512 
   1513     virtual double getProperty(int) const;
   1514     virtual bool setProperty(int, double);
   1515     virtual bool grabFrame();
   1516     virtual IplImage* retrieveFrame(int);
   1517     virtual int getCaptureDomain() { return CV_CAP_QT; } // Return the type of the capture object: CV_CAP_VFW, etc...
   1518 protected:
   1519 
   1520     CvCapture_QT_Cam* captureQT;
   1521 };
   1522 
   1523 bool CvCapture_QT_Cam_CPP::open( int index )
   1524 {
   1525     close();
   1526     captureQT = icvCaptureFromCam_QT( index );
   1527     return captureQT != 0;
   1528 }
   1529 
   1530 void CvCapture_QT_Cam_CPP::close()
   1531 {
   1532     if( captureQT )
   1533     {
   1534         icvClose_QT_Cam( captureQT );
   1535         cvFree( &captureQT );
   1536     }
   1537 }
   1538 
   1539 bool CvCapture_QT_Cam_CPP::grabFrame()
   1540 {
   1541     return captureQT ? icvGrabFrame_QT_Cam( captureQT ) != 0 : false;
   1542 }
   1543 
   1544 IplImage* CvCapture_QT_Cam_CPP::retrieveFrame(int)
   1545 {
   1546     return captureQT ? (IplImage*)icvRetrieveFrame_QT_Cam( captureQT, 0 ) : 0;
   1547 }
   1548 
   1549 double CvCapture_QT_Cam_CPP::getProperty( int propId ) const
   1550 {
   1551     return captureQT ? icvGetProperty_QT_Cam( captureQT, propId ) : 0;
   1552 }
   1553 
   1554 bool CvCapture_QT_Cam_CPP::setProperty( int propId, double value )
   1555 {
   1556     return captureQT ? icvSetProperty_QT_Cam( captureQT, propId, value ) != 0 : false;
   1557 }
   1558 
   1559 CvCapture* cvCreateCameraCapture_QT( int index )
   1560 {
   1561     CvCapture_QT_Cam_CPP* capture = new CvCapture_QT_Cam_CPP;
   1562 
   1563     if( capture->open( index ))
   1564         return capture;
   1565 
   1566     delete capture;
   1567     return 0;
   1568 }
   1569 
   1570 /////////////////////////////////
   1571 
   1572 class CvVideoWriter_QT_CPP : public CvVideoWriter
   1573 {
   1574 public:
   1575     CvVideoWriter_QT_CPP() { writerQT = 0; }
   1576     virtual ~CvVideoWriter_QT_CPP() { close(); }
   1577 
   1578     virtual bool open( const char* filename, int fourcc,
   1579                        double fps, CvSize frameSize, bool isColor );
   1580     virtual void close();
   1581     virtual bool writeFrame( const IplImage* );
   1582 
   1583 protected:
   1584     CvVideoWriter_QT* writerQT;
   1585 };
   1586 
   1587 bool CvVideoWriter_QT_CPP::open( const char* filename, int fourcc,
   1588                        double fps, CvSize frameSize, bool isColor )
   1589 {
   1590     close();
   1591     writerQT = icvCreateVideoWriter_QT( filename, fourcc, fps, frameSize, isColor );
   1592     return writerQT != 0;
   1593 }
   1594 
   1595 void CvVideoWriter_QT_CPP::close()
   1596 {
   1597     if( writerQT )
   1598     {
   1599         icvReleaseVideoWriter_QT( &writerQT );
   1600         writerQT = 0;
   1601     }
   1602 }
   1603 
   1604 bool CvVideoWriter_QT_CPP::writeFrame( const IplImage* image )
   1605 {
   1606     if( !writerQT || !image )
   1607         return false;
   1608     return icvWriteFrame_QT( writerQT, image ) >= 0;
   1609 }
   1610 
   1611 CvVideoWriter* cvCreateVideoWriter_QT( const char* filename, int fourcc,
   1612                                        double fps, CvSize frameSize, int isColor )
   1613 {
   1614     CvVideoWriter_QT_CPP* writer = new CvVideoWriter_QT_CPP;
   1615     if( writer->open( filename, fourcc, fps, frameSize, isColor != 0 ))
   1616         return writer;
   1617     delete writer;
   1618     return 0;
   1619 }
   1620