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