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