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 // License Agreement 11 // For Open Source Computer Vision Library 12 // 13 // Copyright (C) 2013, OpenCV Foundation, 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 the copyright holders 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 contributor be liable for any direct, 33 // indirect, incidental, special, exemplary, or consequential damages 34 // (including, but not limited to, procurement of substitute goods or services; 35 // loss of use, data, or profits; or business interruption) however caused 36 // and on any theory of liability, whether in contract, strict liability, 37 // or tort (including negligence or otherwise) arising in any way out of 38 // the use of this software, even if advised of the possibility of such damage. 39 // 40 //M*//////////////////////////////////////////////////////////////////////////////////////// 41 42 43 #include "precomp.hpp" 44 #include "opencv2/imgproc.hpp" 45 #include <iostream> 46 #import <QTKit/QTKit.h> 47 48 /********************** Declaration of class headers ************************/ 49 50 /***************************************************************************** 51 * 52 * CaptureDelegate Declaration. 53 * 54 * CaptureDelegate is notified on a separate thread by the OS whenever there 55 * is a new frame. When "updateImage" is called from the main thread, it 56 * copies this new frame into an IplImage, but only if this frame has not 57 * been copied before. When "getOutput" is called from the main thread, 58 * it gives the last copied IplImage. 59 * 60 *****************************************************************************/ 61 62 #ifndef QTKIT_VERSION_7_6_3 63 #define QTKIT_VERSION_7_6_3 70603 64 #define QTKIT_VERSION_7_0 70000 65 #endif 66 67 #ifndef QTKIT_VERSION_MAX_ALLOWED 68 #define QTKIT_VERSION_MAX_ALLOWED QTKIT_VERSION_7_0 69 #endif 70 71 #define DISABLE_AUTO_RESTART 999 72 73 @interface CaptureDelegate : NSObject 74 { 75 int newFrame; 76 CVImageBufferRef mCurrentImageBuffer; 77 char* imagedata; 78 IplImage* image; 79 char* bgr_imagedata; 80 IplImage* bgr_image; 81 size_t currSize; 82 } 83 84 - (void)captureOutput:(QTCaptureOutput *)captureOutput 85 didOutputVideoFrame:(CVImageBufferRef)videoFrame 86 withSampleBuffer:(QTSampleBuffer *)sampleBuffer 87 fromConnection:(QTCaptureConnection *)connection; 88 89 - (void)captureOutput:(QTCaptureOutput *)captureOutput 90 didDropVideoFrameWithSampleBuffer:(QTSampleBuffer *)sampleBuffer 91 fromConnection:(QTCaptureConnection *)connection; 92 93 - (int)updateImage; 94 - (IplImage*)getOutput; 95 96 @end 97 98 /***************************************************************************** 99 * 100 * CvCaptureCAM Declaration. 101 * 102 * CvCaptureCAM is the instantiation of a capture source for cameras. 103 * 104 *****************************************************************************/ 105 106 class CvCaptureCAM : public CvCapture { 107 public: 108 CvCaptureCAM(int cameraNum = -1) ; 109 ~CvCaptureCAM(); 110 virtual bool grabFrame(); 111 virtual IplImage* retrieveFrame(int); 112 virtual double getProperty(int property_id) const; 113 virtual bool setProperty(int property_id, double value); 114 virtual int didStart(); 115 116 117 private: 118 QTCaptureSession *mCaptureSession; 119 QTCaptureDeviceInput *mCaptureDeviceInput; 120 QTCaptureDecompressedVideoOutput *mCaptureDecompressedVideoOutput; 121 CaptureDelegate* capture; 122 123 int startCaptureDevice(int cameraNum); 124 void stopCaptureDevice(); 125 126 void setWidthHeight(); 127 bool grabFrame(double timeOut); 128 129 int camNum; 130 int width; 131 int height; 132 int settingWidth; 133 int settingHeight; 134 int started; 135 int disableAutoRestart; 136 137 }; 138 139 140 /***************************************************************************** 141 * 142 * CvCaptureFile Declaration. 143 * 144 * CvCaptureFile is the instantiation of a capture source for video files. 145 * 146 *****************************************************************************/ 147 148 class CvCaptureFile : public CvCapture { 149 public: 150 CvCaptureFile(const char* filename) ; 151 ~CvCaptureFile(); 152 virtual bool grabFrame(); 153 virtual IplImage* retrieveFrame(int); 154 virtual double getProperty(int property_id) const; 155 virtual bool setProperty(int property_id, double value); 156 virtual int didStart(); 157 158 159 private: 160 QTMovie *mCaptureSession; 161 162 char* imagedata; 163 IplImage* image; 164 char* bgr_imagedata; 165 IplImage* bgr_image; 166 size_t currSize; 167 168 //IplImage* retrieveFrameBitmap(); 169 IplImage* retrieveFramePixelBuffer(); 170 double getFPS(); 171 172 int movieWidth; 173 int movieHeight; 174 double movieFPS; 175 double currentFPS; 176 double movieDuration; 177 int changedPos; 178 179 int started; 180 QTTime endOfMovie; 181 }; 182 183 184 /***************************************************************************** 185 * 186 * CvCaptureFile Declaration. 187 * 188 * CvCaptureFile is the instantiation of a capture source for video files. 189 * 190 *****************************************************************************/ 191 192 class CvVideoWriter_QT : public CvVideoWriter{ 193 public: 194 CvVideoWriter_QT(const char* filename, int fourcc, 195 double fps, CvSize frame_size, 196 int is_color=1); 197 ~CvVideoWriter_QT(); 198 bool writeFrame(const IplImage* image); 199 private: 200 IplImage* argbimage; 201 QTMovie* mMovie; 202 203 NSString* path; 204 NSString* codec; 205 double movieFPS; 206 CvSize movieSize; 207 int movieColor; 208 }; 209 210 211 /****************** Implementation of interface functions ********************/ 212 213 214 CvCapture* cvCreateFileCapture_QT(const char* filename) { 215 CvCaptureFile *retval = new CvCaptureFile(filename); 216 217 if(retval->didStart()) 218 return retval; 219 delete retval; 220 return NULL; 221 } 222 223 CvCapture* cvCreateCameraCapture_QT(int index ) { 224 CvCapture* retval = new CvCaptureCAM(index); 225 if (!((CvCaptureCAM *)retval)->didStart()) 226 cvReleaseCapture(&retval); 227 return retval; 228 } 229 230 CvVideoWriter* cvCreateVideoWriter_QT(const char* filename, int fourcc, 231 double fps, CvSize frame_size, 232 int is_color) { 233 return new CvVideoWriter_QT(filename, fourcc, fps, frame_size,is_color); 234 } 235 236 /********************** Implementation of Classes ****************************/ 237 238 /***************************************************************************** 239 * 240 * CvCaptureCAM Implementation. 241 * 242 * CvCaptureCAM is the instantiation of a capture source for cameras. 243 * 244 *****************************************************************************/ 245 246 CvCaptureCAM::CvCaptureCAM(int cameraNum) { 247 mCaptureSession = nil; 248 mCaptureDeviceInput = nil; 249 mCaptureDecompressedVideoOutput = nil; 250 capture = nil; 251 252 width = 0; 253 height = 0; 254 settingWidth = 0; 255 settingHeight = 0; 256 disableAutoRestart = 0; 257 258 camNum = cameraNum; 259 260 if (!startCaptureDevice(camNum)) { 261 std::cout << "Warning, camera failed to properly initialize!" << std::endl; 262 started = 0; 263 } else { 264 started = 1; 265 } 266 267 } 268 269 CvCaptureCAM::~CvCaptureCAM() { 270 stopCaptureDevice(); 271 272 std::cout << "Cleaned up camera." << std::endl; 273 } 274 275 int CvCaptureCAM::didStart() { 276 return started; 277 } 278 279 280 bool CvCaptureCAM::grabFrame() { 281 return grabFrame(5); 282 } 283 284 bool CvCaptureCAM::grabFrame(double timeOut) { 285 286 NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; 287 double sleepTime = 0.005; 288 double total = 0; 289 290 // If the capture is launched in a separate thread, then 291 // [NSRunLoop currentRunLoop] is not the same as in the main thread, and has no timer. 292 //see https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsrunloop_Class/Reference/Reference.html 293 // "If no input sources or timers are attached to the run loop, this 294 // method exits immediately" 295 // using usleep() is not a good alternative, because it may block the GUI. 296 // Create a dummy timer so that runUntilDate does not exit immediately: 297 [NSTimer scheduledTimerWithTimeInterval:100 target:nil selector:@selector(doFireTimer:) userInfo:nil repeats:YES]; 298 while (![capture updateImage] && (total += sleepTime)<=timeOut) { 299 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:sleepTime]]; 300 } 301 302 [localpool drain]; 303 304 return total <= timeOut; 305 } 306 307 IplImage* CvCaptureCAM::retrieveFrame(int) { 308 return [capture getOutput]; 309 } 310 311 void CvCaptureCAM::stopCaptureDevice() { 312 NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; 313 314 [mCaptureSession stopRunning]; 315 316 QTCaptureDevice *device = [mCaptureDeviceInput device]; 317 if ([device isOpen]) [device close]; 318 319 [mCaptureSession release]; 320 [mCaptureDeviceInput release]; 321 322 [mCaptureDecompressedVideoOutput setDelegate:mCaptureDecompressedVideoOutput]; 323 [mCaptureDecompressedVideoOutput release]; 324 [capture release]; 325 [localpool drain]; 326 327 } 328 329 int CvCaptureCAM::startCaptureDevice(int cameraNum) { 330 NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; 331 332 capture = [[CaptureDelegate alloc] init]; 333 334 QTCaptureDevice *device; 335 NSArray* devices = [[[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo] 336 arrayByAddingObjectsFromArray:[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeMuxed]] retain]; 337 338 if ([devices count] == 0) { 339 std::cout << "QTKit didn't find any attached Video Input Devices!" << std::endl; 340 [localpool drain]; 341 return 0; 342 } 343 344 if (cameraNum >= 0) { 345 NSUInteger nCameras = [devices count]; 346 if( (NSUInteger)cameraNum >= nCameras ) { 347 [localpool drain]; 348 return 0; 349 } 350 device = [devices objectAtIndex:cameraNum] ; 351 } else { 352 device = [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeVideo] ; 353 } 354 int success; 355 NSError* error; 356 357 if (device) { 358 359 success = [device open:&error]; 360 if (!success) { 361 std::cout << "QTKit failed to open a Video Capture Device" << std::endl; 362 [localpool drain]; 363 return 0; 364 } 365 366 mCaptureDeviceInput = [[QTCaptureDeviceInput alloc] initWithDevice:device] ; 367 mCaptureSession = [[QTCaptureSession alloc] init] ; 368 369 success = [mCaptureSession addInput:mCaptureDeviceInput error:&error]; 370 371 if (!success) { 372 std::cout << "QTKit failed to start capture session with opened Capture Device" << std::endl; 373 [localpool drain]; 374 return 0; 375 } 376 377 378 mCaptureDecompressedVideoOutput = [[QTCaptureDecompressedVideoOutput alloc] init]; 379 [mCaptureDecompressedVideoOutput setDelegate:capture]; 380 NSDictionary *pixelBufferOptions ; 381 if (width > 0 && height > 0) { 382 pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys: 383 [NSNumber numberWithDouble:1.0*width], (id)kCVPixelBufferWidthKey, 384 [NSNumber numberWithDouble:1.0*height], (id)kCVPixelBufferHeightKey, 385 //[NSNumber numberWithUnsignedInt:k32BGRAPixelFormat], (id)kCVPixelBufferPixelFormatTypeKey, 386 [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], 387 (id)kCVPixelBufferPixelFormatTypeKey, 388 nil]; 389 } else { 390 pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys: 391 [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], 392 (id)kCVPixelBufferPixelFormatTypeKey, 393 nil]; 394 } 395 [mCaptureDecompressedVideoOutput setPixelBufferAttributes:pixelBufferOptions]; 396 397 #if QTKIT_VERSION_MAX_ALLOWED >= QTKIT_VERSION_7_6_3 398 [mCaptureDecompressedVideoOutput setAutomaticallyDropsLateVideoFrames:YES]; 399 #endif 400 401 402 success = [mCaptureSession addOutput:mCaptureDecompressedVideoOutput error:&error]; 403 if (!success) { 404 std::cout << "QTKit failed to add Output to Capture Session" << std::endl; 405 [localpool drain]; 406 return 0; 407 } 408 409 [mCaptureSession startRunning]; 410 411 grabFrame(60); 412 413 [localpool drain]; 414 return 1; 415 } 416 417 [localpool drain]; 418 return 0; 419 } 420 421 void CvCaptureCAM::setWidthHeight() { 422 NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; 423 424 [mCaptureSession stopRunning]; 425 426 NSDictionary* pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys: 427 [NSNumber numberWithDouble:1.0*width], (id)kCVPixelBufferWidthKey, 428 [NSNumber numberWithDouble:1.0*height], (id)kCVPixelBufferHeightKey, 429 [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], 430 (id)kCVPixelBufferPixelFormatTypeKey, 431 nil]; 432 433 [mCaptureDecompressedVideoOutput setPixelBufferAttributes:pixelBufferOptions]; 434 435 [mCaptureSession startRunning]; 436 437 grabFrame(60); 438 [localpool drain]; 439 } 440 441 442 double CvCaptureCAM::getProperty(int property_id) const{ 443 int retval; 444 NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; 445 446 NSArray* connections = [mCaptureDeviceInput connections]; 447 QTFormatDescription* format = [[connections objectAtIndex:0] formatDescription]; 448 NSSize s1 = [[format attributeForKey:QTFormatDescriptionVideoCleanApertureDisplaySizeAttribute] sizeValue]; 449 450 switch (property_id) { 451 case CV_CAP_PROP_FRAME_WIDTH: 452 retval = s1.width; 453 break; 454 case CV_CAP_PROP_FRAME_HEIGHT: 455 retval = s1.height; 456 break; 457 default: 458 retval = 0; 459 break; 460 } 461 462 [localpool drain]; 463 return retval; 464 } 465 466 bool CvCaptureCAM::setProperty(int property_id, double value) { 467 switch (property_id) { 468 case CV_CAP_PROP_FRAME_WIDTH: 469 width = value; 470 settingWidth = 1; 471 if (settingWidth && settingHeight) { 472 setWidthHeight(); 473 settingWidth =0; 474 settingHeight = 0; 475 } 476 return true; 477 case CV_CAP_PROP_FRAME_HEIGHT: 478 height = value; 479 settingHeight = 1; 480 if (settingWidth && settingHeight) { 481 setWidthHeight(); 482 settingWidth =0; 483 settingHeight = 0; 484 } 485 return true; 486 case DISABLE_AUTO_RESTART: 487 disableAutoRestart = value; 488 return 1; 489 default: 490 return false; 491 } 492 } 493 494 495 /***************************************************************************** 496 * 497 * CaptureDelegate Implementation. 498 * 499 * CaptureDelegate is notified on a separate thread by the OS whenever there 500 * is a new frame. When "updateImage" is called from the main thread, it 501 * copies this new frame into an IplImage, but only if this frame has not 502 * been copied before. When "getOutput" is called from the main thread, 503 * it gives the last copied IplImage. 504 * 505 *****************************************************************************/ 506 507 508 @implementation CaptureDelegate 509 510 - (id)init { 511 self = [super init]; 512 if (self) { 513 newFrame = 0; 514 imagedata = NULL; 515 bgr_imagedata = NULL; 516 currSize = 0; 517 image = NULL; 518 bgr_image = NULL; 519 } 520 return self; 521 } 522 523 524 -(void)dealloc { 525 if (imagedata != NULL) free(imagedata); 526 if (bgr_imagedata != NULL) free(bgr_imagedata); 527 cvReleaseImage(&image); 528 cvReleaseImage(&bgr_image); 529 [super dealloc]; 530 } 531 532 - (void)captureOutput:(QTCaptureOutput *)captureOutput 533 didOutputVideoFrame:(CVImageBufferRef)videoFrame 534 withSampleBuffer:(QTSampleBuffer *)sampleBuffer 535 fromConnection:(QTCaptureConnection *)connection { 536 (void)captureOutput; 537 (void)sampleBuffer; 538 (void)connection; 539 540 CVBufferRetain(videoFrame); 541 CVImageBufferRef imageBufferToRelease = mCurrentImageBuffer; 542 543 @synchronized (self) { 544 545 mCurrentImageBuffer = videoFrame; 546 newFrame = 1; 547 } 548 549 CVBufferRelease(imageBufferToRelease); 550 551 } 552 - (void)captureOutput:(QTCaptureOutput *)captureOutput 553 didDropVideoFrameWithSampleBuffer:(QTSampleBuffer *)sampleBuffer 554 fromConnection:(QTCaptureConnection *)connection { 555 (void)captureOutput; 556 (void)sampleBuffer; 557 (void)connection; 558 std::cout << "Camera dropped frame!" << std::endl; 559 } 560 561 -(IplImage*) getOutput { 562 return bgr_image; 563 } 564 565 -(int) updateImage { 566 if (newFrame==0) return 0; 567 CVPixelBufferRef pixels; 568 569 @synchronized (self){ 570 pixels = CVBufferRetain(mCurrentImageBuffer); 571 newFrame = 0; 572 } 573 574 CVPixelBufferLockBaseAddress(pixels, 0); 575 uint32_t* baseaddress = (uint32_t*)CVPixelBufferGetBaseAddress(pixels); 576 577 size_t width = CVPixelBufferGetWidth(pixels); 578 size_t height = CVPixelBufferGetHeight(pixels); 579 size_t rowBytes = CVPixelBufferGetBytesPerRow(pixels); 580 581 if (rowBytes != 0) { 582 583 if (currSize != rowBytes*height*sizeof(char)) { 584 currSize = rowBytes*height*sizeof(char); 585 if (imagedata != NULL) free(imagedata); 586 if (bgr_imagedata != NULL) free(bgr_imagedata); 587 imagedata = (char*)malloc(currSize); 588 bgr_imagedata = (char*)malloc(currSize); 589 } 590 591 memcpy(imagedata, baseaddress, currSize); 592 593 if (image == NULL) { 594 image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 4); 595 } 596 image->width = (int)width; 597 image->height = (int)height; 598 image->nChannels = 4; 599 image->depth = IPL_DEPTH_8U; 600 image->widthStep = (int)rowBytes; 601 image->imageData = imagedata; 602 image->imageSize = (int)currSize; 603 604 if (bgr_image == NULL) { 605 bgr_image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 3); 606 } 607 bgr_image->width = (int)width; 608 bgr_image->height = (int)height; 609 bgr_image->nChannels = 3; 610 bgr_image->depth = IPL_DEPTH_8U; 611 bgr_image->widthStep = (int)rowBytes; 612 bgr_image->imageData = bgr_imagedata; 613 bgr_image->imageSize = (int)currSize; 614 615 cvCvtColor(image, bgr_image, CV_BGRA2BGR); 616 617 } 618 619 CVPixelBufferUnlockBaseAddress(pixels, 0); 620 CVBufferRelease(pixels); 621 622 return 1; 623 } 624 625 @end 626 627 628 /***************************************************************************** 629 * 630 * CvCaptureFile Implementation. 631 * 632 * CvCaptureFile is the instantiation of a capture source for video files. 633 * 634 *****************************************************************************/ 635 636 CvCaptureFile::CvCaptureFile(const char* filename) { 637 638 NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; 639 640 641 mCaptureSession = nil; 642 image = NULL; 643 bgr_image = NULL; 644 imagedata = NULL; 645 bgr_imagedata = NULL; 646 currSize = 0; 647 648 movieWidth = 0; 649 movieHeight = 0; 650 movieFPS = 0; 651 currentFPS = 0; 652 movieDuration = 0; 653 changedPos = 0; 654 655 started = 0; 656 657 NSError* error; 658 659 660 mCaptureSession = [[QTMovie movieWithFile:[NSString stringWithCString:filename 661 encoding:NSASCIIStringEncoding] 662 error:&error] retain]; 663 [mCaptureSession setAttribute:[NSNumber numberWithBool:YES] 664 forKey:QTMovieLoopsAttribute]; 665 666 if (mCaptureSession == nil) { 667 std::cout << "WARNING: Couldn't read movie file " << filename << std::endl; 668 [localpool drain]; 669 started = 0; 670 return; 671 } 672 673 [mCaptureSession gotoEnd]; 674 endOfMovie = [mCaptureSession currentTime]; 675 676 [mCaptureSession gotoBeginning]; 677 678 NSSize size = [[mCaptureSession attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]; 679 680 movieWidth = size.width; 681 movieHeight = size.height; 682 movieFPS = getFPS(); 683 currentFPS = movieFPS; 684 685 QTTime t; 686 687 [[mCaptureSession attributeForKey:QTMovieDurationAttribute] getValue:&t]; 688 movieDuration = (t.timeValue *1000.0 / t.timeScale); 689 started = 1; 690 [localpool drain]; 691 692 } 693 694 CvCaptureFile::~CvCaptureFile() { 695 NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; 696 if (imagedata != NULL) free(imagedata); 697 if (bgr_imagedata != NULL) free(bgr_imagedata); 698 cvReleaseImage(&image); 699 cvReleaseImage(&bgr_image); 700 [mCaptureSession release]; 701 [localpool drain]; 702 } 703 704 int CvCaptureFile::didStart() { 705 return started; 706 } 707 708 bool CvCaptureFile::grabFrame() { 709 NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; 710 double t1 = getProperty(CV_CAP_PROP_POS_MSEC); 711 712 QTTime curTime; 713 curTime = [mCaptureSession currentTime]; 714 bool isEnd=(QTTimeCompare(curTime,endOfMovie) == NSOrderedSame); 715 716 [mCaptureSession stepForward]; 717 double t2 = getProperty(CV_CAP_PROP_POS_MSEC); 718 if (t2>t1 && !changedPos) { 719 currentFPS = 1000.0/(t2-t1); 720 } else { 721 currentFPS = movieFPS; 722 } 723 changedPos = 0; 724 [localpool drain]; 725 return !isEnd; 726 } 727 728 729 IplImage* CvCaptureFile::retrieveFramePixelBuffer() { 730 NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; 731 732 733 NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys: 734 QTMovieFrameImageTypeCVPixelBufferRef, QTMovieFrameImageType, 735 #ifdef MAC_OS_X_VERSION_10_6 736 [NSNumber numberWithBool:YES], QTMovieFrameImageSessionMode, 737 #endif 738 nil]; 739 CVPixelBufferRef frame = (CVPixelBufferRef)[mCaptureSession frameImageAtTime:[mCaptureSession currentTime] 740 withAttributes:attributes 741 error:nil]; 742 743 CVPixelBufferRef pixels = CVBufferRetain(frame); 744 CVPixelBufferLockBaseAddress(pixels, 0); 745 746 uint32_t* baseaddress = (uint32_t*)CVPixelBufferGetBaseAddress(pixels); 747 size_t width = CVPixelBufferGetWidth(pixels); 748 size_t height = CVPixelBufferGetHeight(pixels); 749 size_t rowBytes = CVPixelBufferGetBytesPerRow(pixels); 750 751 if (rowBytes != 0) { 752 753 if (currSize != rowBytes*height*sizeof(char)) { 754 currSize = rowBytes*height*sizeof(char); 755 if (imagedata != NULL) free(imagedata); 756 if (bgr_imagedata != NULL) free(bgr_imagedata); 757 imagedata = (char*)malloc(currSize); 758 bgr_imagedata = (char*)malloc(currSize); 759 } 760 761 memcpy(imagedata, baseaddress, currSize); 762 763 //ARGB -> BGRA 764 for (unsigned int i = 0; i < currSize; i+=4) { 765 char temp = imagedata[i]; 766 imagedata[i] = imagedata[i+3]; 767 imagedata[i+3] = temp; 768 temp = imagedata[i+1]; 769 imagedata[i+1] = imagedata[i+2]; 770 imagedata[i+2] = temp; 771 } 772 773 if (image == NULL) { 774 image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 4); 775 } 776 777 image->width = (int)width; 778 image->height = (int)height; 779 image->nChannels = 4; 780 image->depth = IPL_DEPTH_8U; 781 image->widthStep = (int)rowBytes; 782 image->imageData = imagedata; 783 image->imageSize = (int)currSize; 784 785 786 if (bgr_image == NULL) { 787 bgr_image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 3); 788 } 789 790 bgr_image->width = (int)width; 791 bgr_image->height = (int)height; 792 bgr_image->nChannels = 3; 793 bgr_image->depth = IPL_DEPTH_8U; 794 bgr_image->widthStep = (int)rowBytes; 795 bgr_image->imageData = bgr_imagedata; 796 bgr_image->imageSize = (int)currSize; 797 798 cvCvtColor(image, bgr_image,CV_BGRA2BGR); 799 800 } 801 802 CVPixelBufferUnlockBaseAddress(pixels, 0); 803 CVBufferRelease(pixels); 804 805 [localpool drain]; 806 807 return bgr_image; 808 } 809 810 811 IplImage* CvCaptureFile::retrieveFrame(int) { 812 return retrieveFramePixelBuffer(); 813 } 814 815 double CvCaptureFile::getFPS() { 816 if (mCaptureSession == nil) return 0; 817 NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; 818 double now = getProperty(CV_CAP_PROP_POS_MSEC); 819 double retval = 0; 820 if (now == 0) { 821 [mCaptureSession stepForward]; 822 double t2 = getProperty(CV_CAP_PROP_POS_MSEC); 823 [mCaptureSession stepBackward]; 824 retval = 1000.0 / (t2-now); 825 } else { 826 [mCaptureSession stepBackward]; 827 double t2 = getProperty(CV_CAP_PROP_POS_MSEC); 828 [mCaptureSession stepForward]; 829 retval = 1000.0 / (now-t2); 830 } 831 [localpool drain]; 832 return retval; 833 } 834 835 double CvCaptureFile::getProperty(int property_id) const{ 836 if (mCaptureSession == nil) return 0; 837 838 NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; 839 840 double retval; 841 QTTime t; 842 843 //cerr << "get_prop"<<std::endl; 844 switch (property_id) { 845 case CV_CAP_PROP_POS_MSEC: 846 [[mCaptureSession attributeForKey:QTMovieCurrentTimeAttribute] getValue:&t]; 847 retval = t.timeValue * 1000.0 / t.timeScale; 848 break; 849 case CV_CAP_PROP_POS_FRAMES: 850 retval = movieFPS * getProperty(CV_CAP_PROP_POS_MSEC) / 1000; 851 break; 852 case CV_CAP_PROP_POS_AVI_RATIO: 853 retval = (getProperty(CV_CAP_PROP_POS_MSEC)) / (movieDuration ); 854 break; 855 case CV_CAP_PROP_FRAME_WIDTH: 856 retval = movieWidth; 857 break; 858 case CV_CAP_PROP_FRAME_HEIGHT: 859 retval = movieHeight; 860 break; 861 case CV_CAP_PROP_FPS: 862 retval = currentFPS; 863 break; 864 case CV_CAP_PROP_FRAME_COUNT: 865 retval = movieDuration*movieFPS/1000; 866 break; 867 case CV_CAP_PROP_FOURCC: 868 default: 869 retval = 0; 870 } 871 872 [localpool drain]; 873 return retval; 874 } 875 876 bool CvCaptureFile::setProperty(int property_id, double value) { 877 878 if (mCaptureSession == nil) return false; 879 880 NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; 881 882 bool retval = false; 883 QTTime t; 884 885 double ms; 886 887 switch (property_id) { 888 case CV_CAP_PROP_POS_MSEC: 889 [[mCaptureSession attributeForKey:QTMovieCurrentTimeAttribute] getValue:&t]; 890 t.timeValue = value * t.timeScale / 1000; 891 [mCaptureSession setCurrentTime:t]; 892 changedPos = 1; 893 retval = true; 894 break; 895 case CV_CAP_PROP_POS_FRAMES: 896 ms = (value*1000.0 -5)/ currentFPS; 897 retval = setProperty(CV_CAP_PROP_POS_MSEC, ms); 898 break; 899 case CV_CAP_PROP_POS_AVI_RATIO: 900 ms = value * movieDuration; 901 retval = setProperty(CV_CAP_PROP_POS_MSEC, ms); 902 break; 903 case CV_CAP_PROP_FRAME_WIDTH: 904 //retval = movieWidth; 905 break; 906 case CV_CAP_PROP_FRAME_HEIGHT: 907 //retval = movieHeight; 908 break; 909 case CV_CAP_PROP_FPS: 910 //etval = currentFPS; 911 break; 912 case CV_CAP_PROP_FRAME_COUNT: 913 { 914 NSArray *videoTracks = [mCaptureSession tracksOfMediaType:QTMediaTypeVideo]; 915 if ([videoTracks count] > 0) { 916 QTMedia *media = [[videoTracks objectAtIndex:0] media]; 917 retval = [[media attributeForKey:QTMediaSampleCountAttribute] longValue]; 918 } else { 919 retval = 0; 920 } 921 } 922 break; 923 case CV_CAP_PROP_FOURCC: 924 default: 925 retval = false; 926 } 927 928 [localpool drain]; 929 return retval; 930 } 931 932 933 /***************************************************************************** 934 * 935 * CvVideoWriter Implementation. 936 * 937 * CvVideoWriter is the instantiation of a video output class 938 * 939 *****************************************************************************/ 940 941 942 CvVideoWriter_QT::CvVideoWriter_QT(const char* filename, int fourcc, 943 double fps, CvSize frame_size, 944 int is_color) { 945 946 947 NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; 948 949 movieFPS = fps; 950 movieSize = frame_size; 951 movieColor = is_color; 952 mMovie = nil; 953 path = [[[NSString stringWithCString:filename encoding:NSASCIIStringEncoding] stringByExpandingTildeInPath] retain]; 954 955 argbimage = cvCreateImage(movieSize, IPL_DEPTH_8U, 4); 956 957 958 char cc[5]; 959 cc[0] = fourcc & 255; 960 cc[1] = (fourcc >> 8) & 255; 961 cc[2] = (fourcc >> 16) & 255; 962 cc[3] = (fourcc >> 24) & 255; 963 cc[4] = 0; 964 int cc2 = CV_FOURCC(cc[0], cc[1], cc[2], cc[3]); 965 if (cc2!=fourcc) { 966 std::cout << "WARNING: Didn't properly encode FourCC. Expected " << fourcc 967 << " but got " << cc2 << "." << std::endl; 968 } 969 970 codec = [[NSString stringWithCString:cc encoding:NSASCIIStringEncoding] retain]; 971 972 NSError *error = nil; 973 if (!mMovie) { 974 975 NSFileManager* files = [NSFileManager defaultManager]; 976 if ([files fileExistsAtPath:path]) { 977 if (![files removeItemAtPath:path error:nil]) { 978 std::cout << "WARNING: Failed to remove existing file " << [path cStringUsingEncoding:NSASCIIStringEncoding] << std::endl; 979 } 980 } 981 982 mMovie = [[QTMovie alloc] initToWritableFile:path error:&error]; 983 if (!mMovie) { 984 std::cout << "WARNING: Could not create empty movie file container." << std::endl; 985 [localpool drain]; 986 return; 987 } 988 } 989 990 [mMovie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute]; 991 992 [localpool drain]; 993 } 994 995 996 CvVideoWriter_QT::~CvVideoWriter_QT() { 997 cvReleaseImage(&argbimage); 998 999 NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; 1000 [mMovie release]; 1001 [path release]; 1002 [codec release]; 1003 [localpool drain]; 1004 } 1005 1006 bool CvVideoWriter_QT::writeFrame(const IplImage* image) { 1007 NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; 1008 1009 cvCvtColor(image, argbimage, CV_BGR2BGRA); 1010 1011 1012 unsigned char* imagedata_ = (unsigned char*)argbimage->imageData; 1013 //BGRA --> ARGB 1014 1015 for (int j = 0; j < argbimage->height; j++) { 1016 int rowstart = argbimage->widthStep * j; 1017 for (int i = rowstart; i < rowstart+argbimage->widthStep; i+=4) { 1018 unsigned char temp = imagedata_[i]; 1019 imagedata_[i] = 255; 1020 imagedata_[i+3] = temp; 1021 temp = imagedata_[i+2]; 1022 imagedata_[i+2] = imagedata_[i+1]; 1023 imagedata_[i+1] = temp; 1024 } 1025 } 1026 1027 NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&imagedata_ 1028 pixelsWide:movieSize.width 1029 pixelsHigh:movieSize.height 1030 bitsPerSample:8 1031 samplesPerPixel:4 1032 hasAlpha:YES 1033 isPlanar:NO 1034 colorSpaceName:NSDeviceRGBColorSpace 1035 bitmapFormat:NSAlphaFirstBitmapFormat 1036 bytesPerRow:argbimage->widthStep 1037 bitsPerPixel:32] ; 1038 1039 1040 NSImage* nsimage = [[NSImage alloc] init]; 1041 1042 [nsimage addRepresentation:imageRep]; 1043 1044 /* 1045 codecLosslessQuality = 0x00000400, 1046 codecMaxQuality = 0x000003FF, 1047 codecMinQuality = 0x00000000, 1048 codecLowQuality = 0x00000100, 1049 codecNormalQuality = 0x00000200, 1050 codecHighQuality = 0x00000300 1051 */ 1052 1053 [mMovie addImage:nsimage forDuration:QTMakeTime(100,100*movieFPS) withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: 1054 codec, QTAddImageCodecType, 1055 //[NSNumber numberWithInt:codecLowQuality], QTAddImageCodecQuality, 1056 [NSNumber numberWithInt:100*movieFPS], QTTrackTimeScaleAttribute,nil]]; 1057 1058 if (![mMovie updateMovieFile]) { 1059 std::cout << "Didn't successfully update movie file." << std::endl; 1060 } 1061 1062 [imageRep release]; 1063 [nsimage release]; 1064 [localpool drain]; 1065 1066 return 1; 1067 } 1068