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