cap_qt.cpp

Chris Nichols, 2010-06-20 08:30 pm

Download (53.3 kB)

 
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
#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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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
                
875
                //*************************************************************************************//
876
                //This fixed a bug when Quicktime is called twice to grab a frame (black band bug) - Yannick Verdie 2010
877
                Rect sourceRect;
878
                sourceRect.top = 0;
879
                sourceRect.left = 0;        
880
                sourceRect.right = (**description).width;
881
                sourceRect.bottom = (**description).height;
882
                
883
                MatrixRecord scaleMatrix;
884
                RectMatrix(&scaleMatrix,&sourceRect,&capture->bounds);
885
                
886
                err = DecompressSequenceBegin (&capture->sequence, description, capture->gworld, 0,&capture->bounds,&scaleMatrix, srcCopy, NULL, 0, codecNormalQuality, bestSpeedCodec);
887
                //**************************************************************************************//
888
                
889
                OPENCV_ASSERT (err == noErr, "icvDataProc_QT_Cam", "couldnt begin decompression sequence");
890
                DisposeHandle ((Handle) description);
891
        }
892
893
        // okay, we have a decompression sequence -> decompress!
894
        err = DecompressSequenceFrameS (capture->sequence, raw_data, len, 0, &ignore, nil);
895
        if (err != noErr)
896
        {
897
                fprintf (stderr, "icvDataProc_QT_Cam: couldn't decompress frame - %d\n", (int) err);
898
                return err;
899
        }
900
901
        // check if we dropped a frame
902
        /*#ifndef NDEBUG
903
                if (capture->got_frame)
904
                        fprintf (stderr, "icvDataProc_QT_Cam: frame was dropped\n");
905
        #endif*/
906
907
        // everything worked as expected
908
        capture->got_frame = true;
909
        return noErr;
910
}
911
912
913
static int icvOpenCamera_QT (CvCapture_QT_Cam * capture, const int index)
914
{
915
        OPENCV_ASSERT (capture,    "icvOpenCamera_QT", "'capture' is a NULL-pointer");
916
        OPENCV_ASSERT (index >= 0, "icvOpenCamera_QT", "camera index is negative");
917
918
        PixMapHandle  pixmap       = nil;
919
        OSErr         result       = noErr;
920
921
        // open sequence grabber component
922
        capture->grabber = OpenDefaultComponent (SeqGrabComponentType, 0);
923
        OPENCV_ASSERT (capture->grabber, "icvOpenCamera_QT", "couldnt create image");
924
925
        // initialize sequence grabber component
926
        result = SGInitialize (capture->grabber);
927
        OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt initialize sequence grabber");
928
        result = SGSetDataRef (capture->grabber, 0, 0, seqGrabDontMakeMovie);
929
        OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set data reference of sequence grabber");
930
931
        // set up video channel
932
        result = SGNewChannel (capture->grabber, VideoMediaType, & (capture->channel));
933
        OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt create new video channel");
934
935
    // select the camera indicated by index
936
    SGDeviceList device_list = 0;
937
    result = SGGetChannelDeviceList (capture->channel, 0, & device_list);
938
    OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt get channel device list");
939
    for (int i = 0, current_index = 1; i < (*device_list)->count; i++)
940
    {
941
        SGDeviceName device = (*device_list)->entry[i];
942
        if (device.flags == 0)
943
        {
944
            if (current_index == index)
945
            {
946
                result = SGSetChannelDevice (capture->channel, device.name);
947
                OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set the channel video device");
948
                break;
949
            }
950
            current_index++;
951
        }
952
    }   
953
    result = SGDisposeDeviceList (capture->grabber, device_list);
954
    OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt dispose the channel device list");
955
    
956
        // query natural camera resolution -- this will be wrong, but will be an upper
957
        // bound on the actual resolution -- the actual resolution is set below
958
        // after starting the frame grabber
959
        result = SGGetSrcVideoBounds (capture->channel, & (capture->bounds));
960
        OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set video channel bounds");
961
962
        // create offscreen GWorld
963
        result = QTNewGWorld (& (capture->gworld), k32ARGBPixelFormat, & (capture->bounds), 0, 0, 0);
964
        result = SGSetGWorld (capture->grabber, capture->gworld, 0);
965
        OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set GWorld for sequence grabber");
966
        result = SGSetChannelBounds (capture->channel, & (capture->bounds));
967
        OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set video channel bounds");
968
        result = SGSetChannelUsage (capture->channel, seqGrabRecord);
969
        OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set channel usage");
970
971
    // start recording so we can size
972
        result = SGStartRecord (capture->grabber);
973
        OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt start recording");
974
975
        // don't know *actual* resolution until now
976
        ImageDescriptionHandle imageDesc = (ImageDescriptionHandle)NewHandle(0);
977
        result = SGGetChannelSampleDescription(capture->channel, (Handle)imageDesc);
978
        OPENCV_ASSERT( result == noErr, "icvOpenCamera_QT", "couldn't get image size");
979
        capture->bounds.right = (**imageDesc).width;
980
        capture->bounds.bottom = (**imageDesc).height;
981
        DisposeHandle ((Handle) imageDesc);
982
983
        // stop grabber so that we can reset the parameters to the right size
984
        result = SGStop (capture->grabber);
985
        OPENCV_ASSERT (result == noErr, "icveClose_QT_Cam", "couldnt stop recording");
986
987
        // reset GWorld to correct image size
988
        GWorldPtr tmpgworld;
989
        result = QTNewGWorld( &tmpgworld, k32ARGBPixelFormat, &(capture->bounds), 0, 0, 0);
990
        OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt create offscreen GWorld");
991
        result = SGSetGWorld( capture->grabber, tmpgworld, 0);
992
        OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set GWorld for sequence grabber");
993
        DisposeGWorld( capture->gworld );
994
        capture->gworld = tmpgworld;
995
996
        result = SGSetChannelBounds (capture->channel, & (capture->bounds));
997
        OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set video channel bounds");
998
999
        // allocate images
1000
        capture->size = cvSize (capture->bounds.right - capture->bounds.left, capture->bounds.bottom - capture->bounds.top);
1001
1002
        // build IplImage header that points to the PixMap of the Movie's GWorld.
1003
        // unfortunately, cvCvtColor doesn't know ARGB, the QuickTime pixel format,
1004
        // so we shift the base address by one byte.
1005
        // ATTENTION: don't access the last pixel's alpha entry, it's inexistant
1006
        capture->image_rgb = cvCreateImageHeader (capture->size, IPL_DEPTH_8U, 4);
1007
        OPENCV_ASSERT (capture->image_rgb, "icvOpenCamera_QT", "couldnt create image header");
1008
        pixmap = GetGWorldPixMap (capture->gworld);
1009
        OPENCV_ASSERT (pixmap, "icvOpenCamera_QT", "didn't get GWorld PixMap handle");
1010
        LockPixels (pixmap);
1011
        cvSetData (capture->image_rgb, GetPixBaseAddr (pixmap) + 1, GetPixRowBytes (pixmap));
1012
1013
        // create IplImage that hold correctly formatted result
1014
        capture->image_bgr = cvCreateImage (capture->size, IPL_DEPTH_8U, 3);
1015
        OPENCV_ASSERT (capture->image_bgr, "icvOpenCamera_QT", "couldnt create image");
1016
1017
1018
        // tell the sequence grabber to invoke our data proc
1019
        result = SGSetDataProc (capture->grabber, NewSGDataUPP (icvDataProc_QT_Cam), (long) capture);
1020
        OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt set data proc");
1021
1022
        // start recording
1023
        result = SGStartRecord (capture->grabber);
1024
        OPENCV_ASSERT (result == noErr, "icvOpenCamera_QT", "couldnt start recording");
1025
1026
        return 1;
1027
}
1028
1029
1030
static int icvClose_QT_Cam (CvCapture_QT_Cam * capture)
1031
{
1032
        OPENCV_ASSERT (capture, "icvClose_QT_Cam", "'capture' is a NULL-pointer");
1033
1034
        OSErr  result = noErr;
1035
1036
1037
        // stop recording
1038
        result = SGStop (capture->grabber);
1039
        OPENCV_ASSERT (result == noErr, "icveClose_QT_Cam", "couldnt stop recording");
1040
1041
        // close sequence grabber component
1042
        result = CloseComponent (capture->grabber);
1043
        OPENCV_ASSERT (result == noErr, "icveClose_QT_Cam", "couldnt close sequence grabber component");
1044
1045
        // end decompression sequence
1046
        CDSequenceEnd (capture->sequence);
1047
1048
        // free memory
1049
        cvReleaseImage (& capture->image_bgr);
1050
        cvReleaseImageHeader (& capture->image_rgb);
1051
        DisposeGWorld (capture->gworld);
1052
1053
        // sucessful
1054
        return 1;
1055
}
1056
1057
static int icvGrabFrame_QT_Cam (CvCapture_QT_Cam * capture)
1058
{
1059
        OPENCV_ASSERT (capture,          "icvGrabFrame_QT_Cam", "'capture' is a NULL-pointer");
1060
        OPENCV_ASSERT (capture->grabber, "icvGrabFrame_QT_Cam", "'grabber' is a NULL-pointer");
1061
1062
        ComponentResult        result = noErr;
1063
1064
1065
        // grab one frame
1066
        result = SGIdle (capture->grabber);
1067
        if (result != noErr)
1068
        {
1069
                fprintf (stderr, "SGIdle failed in icvGrabFrame_QT_Cam with error %d\n", (int) result);
1070
                return 0;
1071
        }
1072
1073
        // successful
1074
        return 1;
1075
}
1076
1077
static const void * icvRetrieveFrame_QT_Cam (CvCapture_QT_Cam * capture, int)
1078
{
1079
        OPENCV_ASSERT (capture,            "icvRetrieveFrame_QT_Cam", "'capture' is a NULL-pointer");
1080
        OPENCV_ASSERT (capture->image_rgb, "icvRetrieveFrame_QT_Cam", "invalid source image");
1081
        OPENCV_ASSERT (capture->image_bgr, "icvRetrieveFrame_QT_Cam", "invalid destination image");
1082
1083
        OSErr         myErr          = noErr;
1084
1085
1086
        // service active sequence grabbers (= redraw immediately)
1087
        while (! capture->got_frame)
1088
        {
1089
                myErr = SGIdle (capture->grabber);
1090
                if (myErr != noErr)
1091
                {
1092
                        fprintf (stderr, "SGIdle() didn't succeed in icvRetrieveFrame_QT_Cam().\n");
1093
                        return 0;
1094
                }
1095
        }
1096
1097
        // covert RGB of GWorld to BGR
1098
        cvCvtColor (capture->image_rgb, capture->image_bgr, CV_RGBA2BGR);
1099
1100
        // reset grabbing status
1101
        capture->got_frame = false;
1102
1103
    // always return the same image pointer
1104
        return capture->image_bgr;
1105
}
1106
1107
#endif
1108
1109
1110
typedef struct CvVideoWriter_QT {
1111
1112
    DataHandler data_handler;
1113
    Movie movie;
1114
    Track track;
1115
    Media video;
1116
1117
    ICMCompressionSessionRef compression_session_ref;
1118
1119
    TimeValue duration_per_sample;
1120
} CvVideoWriter_QT;
1121
1122
1123
static TimeScale const TIME_SCALE = 600;
1124
1125
static OSStatus icvEncodedFrameOutputCallback(
1126
    void* writer,
1127
    ICMCompressionSessionRef compression_session_ref,
1128
    OSStatus error,
1129
    ICMEncodedFrameRef encoded_frame_ref,
1130
    void* reserved
1131
);
1132
1133
static void icvSourceTrackingCallback(
1134
    void *source_tracking_ref_con,
1135
    ICMSourceTrackingFlags source_tracking_flags,
1136
    void *source_frame_ref_con,
1137
    void *reserved
1138
);
1139
1140
static int icvWriteFrame_QT(
1141
    CvVideoWriter_QT * video_writer,
1142
    const IplImage * image
1143
) {
1144
    CVPixelBufferRef pixel_buffer_ref = NULL;
1145
    CVReturn retval =
1146
        CVPixelBufferCreate(
1147
            kCFAllocatorDefault,
1148
            image->width, image->height, k24RGBPixelFormat,
1149
            NULL /* pixel_buffer_attributes */,
1150
            &pixel_buffer_ref
1151
        );
1152
1153
    // convert BGR IPL image to RGB pixel buffer
1154
    IplImage* image_rgb =
1155
        cvCreateImageHeader(
1156
            cvSize( image->width, image->height ),
1157
            IPL_DEPTH_8U,
1158
            3
1159
        );
1160
1161
    retval = CVPixelBufferLockBaseAddress( pixel_buffer_ref, 0 );
1162
1163
    void* base_address = CVPixelBufferGetBaseAddress( pixel_buffer_ref );
1164
    size_t bytes_per_row = CVPixelBufferGetBytesPerRow( pixel_buffer_ref );
1165
    cvSetData( image_rgb, base_address, bytes_per_row );
1166
1167
    cvConvertImage( image, image_rgb, CV_CVTIMG_SWAP_RB );
1168
1169
    retval = CVPixelBufferUnlockBaseAddress( pixel_buffer_ref, 0 );
1170
1171
    cvReleaseImageHeader( &image_rgb );
1172
1173
    ICMSourceTrackingCallbackRecord source_tracking_callback_record;
1174
    source_tracking_callback_record.sourceTrackingCallback =
1175
        icvSourceTrackingCallback;
1176
    source_tracking_callback_record.sourceTrackingRefCon = NULL;
1177
1178
    OSStatus status =
1179
        ICMCompressionSessionEncodeFrame(
1180
            video_writer->compression_session_ref,
1181
            pixel_buffer_ref,
1182
            0,
1183
            video_writer->duration_per_sample,
1184
            kICMValidTime_DisplayDurationIsValid,
1185
            NULL,
1186
            &source_tracking_callback_record,
1187
            static_cast<void*>( &pixel_buffer_ref )
1188
        );
1189
1190
    return 0;
1191
}
1192
1193
static void icvReleaseVideoWriter_QT( CvVideoWriter_QT ** writer ) {
1194
    if ( ( writer != NULL ) && ( *writer != NULL ) ) {
1195
        CvVideoWriter_QT* video_writer = *writer;
1196
1197
        // force compression session to complete encoding of outstanding source
1198
        // frames
1199
        ICMCompressionSessionCompleteFrames(
1200
            video_writer->compression_session_ref, TRUE, 0, 0
1201
        );
1202
1203
        EndMediaEdits( video_writer->video );
1204
1205
        ICMCompressionSessionRelease( video_writer->compression_session_ref );
1206
1207
        InsertMediaIntoTrack(
1208
            video_writer->track,
1209
            0,
1210
            0,
1211
            GetMediaDuration( video_writer->video ),
1212
            FixRatio( 1, 1 )
1213
        );
1214
1215
        UpdateMovieInStorage( video_writer->movie, video_writer->data_handler );
1216
1217
        CloseMovieStorage( video_writer->data_handler );
1218
1219
/*
1220
        // export to AVI
1221
        Handle data_ref;
1222
        OSType data_ref_type;
1223
        QTNewDataReferenceFromFullPathCFString(
1224
            CFSTR( "/Users/seibert/Desktop/test.avi" ), kQTPOSIXPathStyle, 0,
1225
            &data_ref, &data_ref_type
1226
        );
1227
1228
        ConvertMovieToDataRef( video_writer->movie, NULL, data_ref,
1229
            data_ref_type, kQTFileTypeAVI, 'TVOD', 0, NULL );
1230
1231
        DisposeHandle( data_ref );
1232
*/
1233
1234
        DisposeMovie( video_writer->movie );
1235
1236
        cvFree( writer );
1237
    }
1238
}
1239
1240
static OSStatus icvEncodedFrameOutputCallback(
1241
    void* writer,
1242
    ICMCompressionSessionRef compression_session_ref,
1243
    OSStatus error,
1244
    ICMEncodedFrameRef encoded_frame_ref,
1245
    void* reserved
1246
) {
1247
    CvVideoWriter_QT* video_writer = static_cast<CvVideoWriter_QT*>( writer );
1248
1249
    OSStatus err = AddMediaSampleFromEncodedFrame( video_writer->video,
1250
        encoded_frame_ref, NULL );
1251
1252
    return err;
1253
}
1254
1255
static void icvSourceTrackingCallback(
1256
    void *source_tracking_ref_con,
1257
    ICMSourceTrackingFlags source_tracking_flags,
1258
    void *source_frame_ref_con,
1259
    void *reserved
1260
) {
1261
    if ( source_tracking_flags & kICMSourceTracking_ReleasedPixelBuffer ) {
1262
        CVPixelBufferRelease(
1263
            *static_cast<CVPixelBufferRef*>( source_frame_ref_con )
1264
        );
1265
    }
1266
}
1267
1268
1269
static CvVideoWriter_QT* icvCreateVideoWriter_QT(
1270
    const char * filename,
1271
    int fourcc,
1272
    double fps,
1273
    CvSize frame_size,
1274
    int is_color
1275
) {
1276
    CV_FUNCNAME( "icvCreateVideoWriter" );
1277
1278
    CvVideoWriter_QT* video_writer =
1279
        static_cast<CvVideoWriter_QT*>( cvAlloc( sizeof( CvVideoWriter_QT ) ) );
1280
    memset( video_writer, 0, sizeof( CvVideoWriter_QT ) );
1281
1282
    Handle                            data_ref     = NULL;
1283
    OSType                            data_ref_type;
1284
    DataHandler                       data_handler = NULL;
1285
    Movie                             movie        = NULL;
1286
    ICMCompressionSessionOptionsRef   options_ref  = NULL;
1287
    ICMCompressionSessionRef          compression_session_ref = NULL;
1288
    CFStringRef                       out_path     = nil;
1289
    Track                             video_track  = nil;
1290
    Media                             video        = nil;
1291
    OSErr                             err          = noErr;
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
    CodecType codecType;
1351
    switch ( fourcc ) {
1352
//        case CV_FOURCC( 'D', 'I', 'B', ' ' ):
1353
//            codecType = kRawCodecType;
1354
//            break;
1355
        default:
1356
            codecType = kRawCodecType;
1357
            break;
1358
    }
1359
1360
    // start a compression session
1361
    err = ICMCompressionSessionOptionsCreate( kCFAllocatorDefault,
1362
        &options_ref );
1363
    if ( err != noErr ) {
1364
        CV_ERROR( CV_StsInternal, "Cannot create compression session options" );
1365
    }
1366
    err = ICMCompressionSessionOptionsSetAllowTemporalCompression( options_ref,
1367
        true );
1368
    if ( err != noErr) {
1369
        CV_ERROR( CV_StsInternal, "Cannot enable temporal compression" );
1370
    }
1371
    err = ICMCompressionSessionOptionsSetAllowFrameReordering( options_ref,
1372
        true );
1373
    if ( err != noErr) {
1374
        CV_ERROR( CV_StsInternal, "Cannot enable frame reordering" );
1375
    }
1376
1377
    ICMEncodedFrameOutputRecord encoded_frame_output_record;
1378
    encoded_frame_output_record.encodedFrameOutputCallback =
1379
        icvEncodedFrameOutputCallback;
1380
    encoded_frame_output_record.encodedFrameOutputRefCon =
1381
        static_cast<void*>( video_writer );
1382
    encoded_frame_output_record.frameDataAllocator = NULL;
1383
1384
    err = ICMCompressionSessionCreate( kCFAllocatorDefault, frame_size.width,
1385
        frame_size.height, codecType, TIME_SCALE, options_ref,
1386
        NULL /*source_pixel_buffer_attributes*/, &encoded_frame_output_record,
1387
        &compression_session_ref );
1388
    ICMCompressionSessionOptionsRelease( options_ref );
1389
    if ( err != noErr ) {
1390
        CV_ERROR( CV_StsInternal, "Cannot create compression session" );
1391
    }
1392
1393
    err = BeginMediaEdits( video );
1394
    if ( err != noErr ) {
1395
        CV_ERROR( CV_StsInternal, "Cannot begin media edits" );
1396
    }
1397
1398
    // fill in the video writer structure
1399
    video_writer->data_handler = data_handler;
1400
    video_writer->movie = movie;
1401
    video_writer->track = video_track;
1402
    video_writer->video = video;
1403
    video_writer->compression_session_ref = compression_session_ref;
1404
    video_writer->duration_per_sample =
1405
        static_cast<TimeValue>( static_cast<double>( TIME_SCALE ) / fps );
1406
1407
    __END__
1408
1409
    // clean up in case of error (unless error processing mode is
1410
    // CV_ErrModeLeaf)
1411
    if ( err != noErr ) {
1412
        if ( options_ref != NULL ) {
1413
            ICMCompressionSessionOptionsRelease( options_ref );
1414
        }
1415
        if ( compression_session_ref != NULL ) {
1416
            ICMCompressionSessionRelease( compression_session_ref );
1417
        }
1418
        if ( data_handler != NULL ) {
1419
            CloseMovieStorage( data_handler );
1420
        }
1421
        if ( movie != NULL ) {
1422
            DisposeMovie( movie );
1423
        }
1424
        if ( data_ref != NULL ) {
1425
            DeleteMovieStorage( data_ref, data_ref_type );
1426
            DisposeHandle( data_ref );
1427
        }
1428
        cvFree( reinterpret_cast<void**>( &video_writer ) );
1429
        video_writer = NULL;
1430
    }
1431
1432
    return video_writer;
1433
}
1434
1435
1436
/**
1437
*
1438
*   Wrappers for the new C++ CvCapture & CvVideoWriter structures
1439
*
1440
*/
1441
1442
class CvCapture_QT_Movie_CPP : public CvCapture
1443
{
1444
public:
1445
    CvCapture_QT_Movie_CPP() { captureQT = 0; }
1446
    virtual ~CvCapture_QT_Movie_CPP() { close(); }
1447
1448
    virtual bool open( const char* filename );
1449
    virtual void close();
1450
1451
    virtual double getProperty(int);
1452
    virtual bool setProperty(int, double);
1453
    virtual bool grabFrame();
1454
    virtual IplImage* retrieveFrame(int);
1455
        virtual int getCaptureDomain() { return CV_CAP_QT; } // Return the type of the capture object: CV_CAP_VFW, etc...
1456
protected:
1457
1458
    CvCapture_QT_Movie* captureQT;
1459
};
1460
1461
bool CvCapture_QT_Movie_CPP::open( const char* filename )
1462
{
1463
    close();
1464
    captureQT = icvCaptureFromFile_QT( filename );
1465
    return captureQT != 0;
1466
}
1467
1468
void CvCapture_QT_Movie_CPP::close()
1469
{
1470
    if( captureQT )
1471
    {
1472
        icvClose_QT_Movie( captureQT );
1473
        cvFree( &captureQT );
1474
    }
1475
}
1476
1477
bool CvCapture_QT_Movie_CPP::grabFrame()
1478
{
1479
    return captureQT ? icvGrabFrame_QT_Movie( captureQT ) != 0 : false;
1480
}
1481
1482
IplImage* CvCapture_QT_Movie_CPP::retrieveFrame(int)
1483
{
1484
    return captureQT ? (IplImage*)icvRetrieveFrame_QT_Movie( captureQT, 0 ) : 0;
1485
}
1486
1487
double CvCapture_QT_Movie_CPP::getProperty( int propId )
1488
{
1489
    return captureQT ? icvGetProperty_QT_Movie( captureQT, propId ) : 0;
1490
}
1491
1492
bool CvCapture_QT_Movie_CPP::setProperty( int propId, double value )
1493
{
1494
    return captureQT ? icvSetProperty_QT_Movie( captureQT, propId, value ) != 0 : false;
1495
}
1496
1497
CvCapture* cvCreateFileCapture_QT( const char* filename )
1498
{
1499
    CvCapture_QT_Movie_CPP* capture = new CvCapture_QT_Movie_CPP;
1500
1501
    if( capture->open( filename ))
1502
        return capture;
1503
1504
    delete capture;
1505
    return 0;
1506
}
1507
1508
1509
/////////////////////////////////////
1510
1511
class CvCapture_QT_Cam_CPP : public CvCapture
1512
{
1513
public:
1514
    CvCapture_QT_Cam_CPP() { captureQT = 0; }
1515
    virtual ~CvCapture_QT_Cam_CPP() { close(); }
1516
1517
    virtual bool open( int index );
1518
    virtual void close();
1519
1520
    virtual double getProperty(int);
1521
    virtual bool setProperty(int, double);
1522
    virtual bool grabFrame();
1523
    virtual IplImage* retrieveFrame(int);
1524
        virtual int getCaptureDomain() { return CV_CAP_QT; } // Return the type of the capture object: CV_CAP_VFW, etc...
1525
protected:
1526
1527
    CvCapture_QT_Cam* captureQT;
1528
};
1529
1530
bool CvCapture_QT_Cam_CPP::open( int index )
1531
{
1532
    close();
1533
    captureQT = icvCaptureFromCam_QT( index );
1534
    return captureQT != 0;
1535
}
1536
1537
void CvCapture_QT_Cam_CPP::close()
1538
{
1539
    if( captureQT )
1540
    {
1541
        icvClose_QT_Cam( captureQT );
1542
        cvFree( &captureQT );
1543
    }
1544
}
1545
1546
bool CvCapture_QT_Cam_CPP::grabFrame()
1547
{
1548
    return captureQT ? icvGrabFrame_QT_Cam( captureQT ) != 0 : false;
1549
}
1550
1551
IplImage* CvCapture_QT_Cam_CPP::retrieveFrame(int)
1552
{
1553
    return captureQT ? (IplImage*)icvRetrieveFrame_QT_Cam( captureQT, 0 ) : 0;
1554
}
1555
1556
double CvCapture_QT_Cam_CPP::getProperty( int propId )
1557
{
1558
    return captureQT ? icvGetProperty_QT_Cam( captureQT, propId ) : 0;
1559
}
1560
1561
bool CvCapture_QT_Cam_CPP::setProperty( int propId, double value )
1562
{
1563
    return captureQT ? icvSetProperty_QT_Cam( captureQT, propId, value ) != 0 : false;
1564
}
1565
1566
CvCapture* cvCreateCameraCapture_QT( int index )
1567
{
1568
    CvCapture_QT_Cam_CPP* capture = new CvCapture_QT_Cam_CPP;
1569
1570
    if( capture->open( index ))
1571
        return capture;
1572
1573
    delete capture;
1574
    return 0;
1575
}
1576
1577
/////////////////////////////////
1578
1579
class CvVideoWriter_QT_CPP : public CvVideoWriter
1580
{
1581
public:
1582
    CvVideoWriter_QT_CPP() { writerQT = 0; }
1583
    virtual ~CvVideoWriter_QT_CPP() { close(); }
1584
1585
    virtual bool open( const char* filename, int fourcc,
1586
                       double fps, CvSize frameSize, bool isColor );
1587
    virtual void close();
1588
    virtual bool writeFrame( const IplImage* );
1589
1590
protected:
1591
    CvVideoWriter_QT* writerQT;
1592
};
1593
1594
bool CvVideoWriter_QT_CPP::open( const char* filename, int fourcc,
1595
                       double fps, CvSize frameSize, bool isColor )
1596
{
1597
    close();
1598
    writerQT = icvCreateVideoWriter_QT( filename, fourcc, fps, frameSize, isColor );
1599
    return writerQT != 0;
1600
}
1601
1602
void CvVideoWriter_QT_CPP::close()
1603
{
1604
    if( writerQT )
1605
    {
1606
        icvReleaseVideoWriter_QT( &writerQT );
1607
        writerQT = 0;
1608
    }
1609
}
1610
1611
bool CvVideoWriter_QT_CPP::writeFrame( const IplImage* image )
1612
{
1613
    if( !writerQT || !image )
1614
        return false;
1615
    return icvWriteFrame_QT( writerQT, image ) >= 0;
1616
}
1617
1618
CvVideoWriter* cvCreateVideoWriter_QT( const char* filename, int fourcc,
1619
                                       double fps, CvSize frameSize, int isColor )
1620
{
1621
    CvVideoWriter_QT_CPP* writer = new CvVideoWriter_QT_CPP;
1622
    if( writer->open( filename, fourcc, fps, frameSize, isColor != 0 ))
1623
        return writer;
1624
    delete writer;
1625
    return 0;
1626
}