| // |
| // VideoCaptureMacQTKitObjC.cpp |
| // |
| // |
| |
| |
| #define DEFAULT_CAPTURE_DEVICE_INDEX 1 |
| #define DEFAULT_FRAME_RATE 30 |
| #define DEFAULT_FRAME_WIDTH 352 |
| #define DEFAULT_FRAME_HEIGHT 288 |
| #define ROTATE_CAPTURED_FRAME 1 |
| #define LOW_QUALITY 1 |
| |
| |
| |
| #import "video_capture_qtkit_objc.h" |
| #include "video_capture_qtkit_utility.h" |
| #include "trace.h" |
| |
| using namespace webrtc; |
| using namespace videocapturemodule; |
| |
| @implementation VideoCaptureMacQTKitObjC |
| |
| #pragma mark **** over-written OS methods |
| |
| /// ***** Objective-C. Similar to C++ constructor, although must be invoked |
| /// manually. |
| /// ***** Potentially returns an instance of self |
| -(id)init{ |
| WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, |
| "%s:%d", __FUNCTION__, __LINE__); |
| self = [super init]; |
| if(nil != self) |
| { |
| [self checkOSSupported]; |
| [self initializeVariables]; |
| } |
| else |
| { |
| return nil; |
| } |
| return self; |
| } |
| |
| /// ***** Objective-C. Similar to C++ destructor |
| /// ***** Returns nothing |
| - (void)dealloc { |
| WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, |
| "%s:%d", __FUNCTION__, __LINE__); |
| if(_captureSession) |
| { |
| [_captureSession stopRunning]; |
| [_captureSession release]; |
| } |
| [super dealloc]; |
| } |
| |
| #pragma mark **** public methods |
| |
| |
| |
| /// ***** Registers the class's owner, which is where the delivered frames are |
| /// sent |
| /// ***** Returns 0 on success, -1 otherwise. |
| - (NSNumber*)registerOwner:(VideoCaptureMacQTKit*)owner{ |
| WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, "%s:%d", __FUNCTION__, __LINE__); |
| if(!owner){ |
| return [NSNumber numberWithInt:-1]; |
| } |
| _owner = owner; |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| /// ***** Sets the QTCaptureSession's input device from a char* |
| /// ***** Sets several member variables. Can signal the error system if one has |
| /// occurred |
| /// ***** Returns 0 on success, -1 otherwise. |
| - (NSNumber*)setCaptureDeviceById:(char*)uniqueId{ |
| WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, |
| "%s:%d name=%s", __FUNCTION__, __LINE__, uniqueId); |
| if(NO == _OSSupported) |
| { |
| WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0, |
| "%s:%d OS version does not support necessary APIs", |
| __FUNCTION__, __LINE__); |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| if(!uniqueId || (0 == strcmp("", uniqueId))) |
| { |
| WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0, |
| "%s:%d \"\" was passed in for capture device name", |
| __FUNCTION__, __LINE__); |
| memset(_captureDeviceNameUTF8, 0, 1024); |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| if(0 == strcmp(uniqueId, _captureDeviceNameUniqueID)) |
| { |
| // camera already set |
| WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0, |
| "%s:%d Capture device is already set to %s", __FUNCTION__, |
| __LINE__, _captureDeviceNameUTF8); |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| bool success = NO; |
| QTCaptureDevice* tempCaptureDevice; |
| for(int index = 0; index < _captureDeviceCount; index++) |
| { |
| tempCaptureDevice = (QTCaptureDevice*)[_captureDevices |
| objectAtIndex:index]; |
| char tempCaptureDeviceId[1024] = ""; |
| [[tempCaptureDevice uniqueID] |
| getCString:tempCaptureDeviceId maxLength:1024 |
| encoding:NSUTF8StringEncoding]; |
| if(0 == strcmp(uniqueId, tempCaptureDeviceId)) |
| { |
| WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0, |
| "%s:%d Found capture device id %s as index %d", |
| __FUNCTION__, __LINE__, tempCaptureDeviceId, index); |
| success = YES; |
| [[tempCaptureDevice localizedDisplayName] |
| getCString:_captureDeviceNameUTF8 |
| maxLength:1024 |
| encoding:NSUTF8StringEncoding]; |
| [[tempCaptureDevice uniqueID] |
| getCString:_captureDeviceNameUniqueID |
| maxLength:1024 |
| encoding:NSUTF8StringEncoding]; |
| break; |
| } |
| |
| } |
| |
| if(NO == success) |
| { |
| // camera not found |
| // nothing has been changed yet, so capture device will stay in it's |
| // state |
| WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0, |
| "%s:%d Capture device id %s was not found in list of " |
| "available devices.", __FUNCTION__, __LINE__, uniqueId); |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| NSError* error; |
| success = [tempCaptureDevice open:&error]; |
| if(!success) |
| { |
| WEBRTC_TRACE(kTraceError, kTraceVideoCapture, 0, |
| "%s:%d Failed to open capture device: %s", |
| __FUNCTION__, __LINE__, _captureDeviceNameUTF8); |
| return [NSNumber numberWithInt:-1]; |
| } |
| |
| if(_captureVideoDeviceInput) |
| { |
| [_captureVideoDeviceInput release]; |
| } |
| _captureVideoDeviceInput = [[QTCaptureDeviceInput alloc] |
| initWithDevice:tempCaptureDevice]; |
| |
| success = [_captureSession addInput:_captureVideoDeviceInput error:&error]; |
| if(!success) |
| { |
| WEBRTC_TRACE(kTraceError, kTraceVideoCapture, 0, |
| "%s:%d Failed to add input from %s to the capture session", |
| __FUNCTION__, __LINE__, _captureDeviceNameUTF8); |
| return [NSNumber numberWithInt:-1]; |
| } |
| |
| WEBRTC_TRACE(kTraceInfo, kTraceVideoCapture, 0, |
| "%s:%d successfully added capture device: %s", __FUNCTION__, |
| __LINE__, _captureDeviceNameUTF8); |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| |
| /// ***** Updates the capture devices size and frequency |
| /// ***** Sets member variables _frame* and _captureDecompressedVideoOutput |
| /// ***** Returns 0 on success, -1 otherwise. |
| - (NSNumber*)setCaptureHeight:(int)height AndWidth:(int)width |
| AndFrameRate:(int)frameRate{ |
| WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, |
| "%s:%d height=%d width=%d frameRate=%d", __FUNCTION__, |
| __LINE__, height, width, frameRate); |
| if(NO == _OSSupported) |
| { |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| _frameWidth = width; |
| _frameHeight = height; |
| _frameRate = frameRate; |
| |
| // TODO(mflodman) Check fps settings. |
| // [_captureDecompressedVideoOutput |
| // setMinimumVideoFrameInterval:(NSTimeInterval)1/(float)_frameRate]; |
| NSDictionary* captureDictionary = [NSDictionary dictionaryWithObjectsAndKeys: |
| [NSNumber numberWithDouble:_frameWidth], (id)kCVPixelBufferWidthKey, |
| [NSNumber numberWithDouble:_frameHeight], (id)kCVPixelBufferHeightKey, |
| [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32ARGB], |
| (id)kCVPixelBufferPixelFormatTypeKey, nil]; |
| [_captureDecompressedVideoOutput performSelectorOnMainThread:@selector(setPixelBufferAttributes:) withObject:captureDictionary waitUntilDone:NO]; |
| // [_captureDecompressedVideoOutput setPixelBufferAttributes:captureDictionary]; |
| |
| |
| // these methods return type void so there isn't much we can do about |
| // checking success |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| /// ***** Starts the QTCaptureSession, assuming correct state. Also ensures that |
| /// an NSRunLoop is running |
| /// ***** Without and NSRunLoop to process events, the OS doesn't check for a |
| /// new frame. |
| /// ***** Sets member variables _capturing |
| /// ***** Returns 0 on success, -1 otherwise. |
| - (NSNumber*)startCapture{ |
| WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, |
| "%s:%d", __FUNCTION__, __LINE__); |
| if(NO == _OSSupported) |
| { |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| if(YES == _capturing) |
| { |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| // NSLog(@"--------------- before ---------------"); |
| [[NSRunLoop mainRunLoop] runUntilDate:[NSDate distantFuture]]; |
| // NSLog(@"--------------- after ---------------"); |
| |
| if(NO == _captureInitialized) |
| { |
| // this should never be called..... it is initialized on class init |
| [self initializeVideoCapture]; |
| } |
| [_captureSession startRunning]; |
| |
| |
| _capturing = YES; |
| |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| /// ***** Stops the QTCaptureSession, assuming correct state |
| /// ***** Sets member variables _capturing |
| /// ***** Returns 0 on success, -1 otherwise. |
| - (NSNumber*)stopCapture{ |
| WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, |
| "%s:%d", __FUNCTION__, __LINE__); |
| |
| if(NO == _OSSupported) |
| { |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| if(nil == _captureSession) |
| { |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| if(NO == _capturing) |
| { |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| if(YES == _capturing) |
| { |
| [_captureSession stopRunning]; |
| } |
| |
| _capturing = NO; |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| // ********** "private" functions below here ********** |
| #pragma mark **** "private" methods |
| |
| /// ***** Class member variables are initialized here |
| /// ***** Returns 0 on success, -1 otherwise. |
| - (NSNumber*)initializeVariables{ |
| WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, |
| "%s:%d", __FUNCTION__, __LINE__); |
| |
| if(NO == _OSSupported) |
| { |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| _pool = [[NSAutoreleasePool alloc]init]; |
| |
| memset(_captureDeviceNameUTF8, 0, 1024); |
| _counter = 0; |
| _framesDelivered = 0; |
| _framesRendered = 0; |
| _captureDeviceCount = 0; |
| _capturing = NO; |
| _captureInitialized = NO; |
| _frameRate = DEFAULT_FRAME_RATE; |
| _frameWidth = DEFAULT_FRAME_WIDTH; |
| _frameHeight = DEFAULT_FRAME_HEIGHT; |
| _captureDeviceName = [[NSString alloc] initWithFormat:@""]; |
| _rLock = [[VideoCaptureRecursiveLock alloc] init]; |
| _captureSession = [[QTCaptureSession alloc] init]; |
| _captureDecompressedVideoOutput = [[QTCaptureDecompressedVideoOutput alloc] |
| init]; |
| [_captureDecompressedVideoOutput setDelegate:self]; |
| |
| [self getCaptureDevices]; |
| [self initializeVideoCapture]; |
| |
| return [NSNumber numberWithInt:0]; |
| |
| } |
| |
| // Checks to see if the QTCaptureSession framework is available in the OS |
| // If it is not, isOSSupprted = NO. |
| // Throughout the rest of the class isOSSupprted is checked and functions |
| // are/aren't called depending |
| // The user can use weak linking to the QTKit framework and run on older |
| // versions of the OS. I.E. Backwards compaitibility |
| // Returns nothing. Sets member variable |
| - (void)checkOSSupported{ |
| WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, |
| "%s:%d", __FUNCTION__, __LINE__); |
| |
| Class osSupportedTest = NSClassFromString(@"QTCaptureSession"); |
| _OSSupported = NO; |
| if(nil == osSupportedTest) |
| { |
| } |
| _OSSupported = YES; |
| } |
| |
| /// ***** Retrieves the number of capture devices currently available |
| /// ***** Stores them in an NSArray instance |
| /// ***** Returns 0 on success, -1 otherwise. |
| - (NSNumber*)getCaptureDevices{ |
| WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, |
| "%s:%d", __FUNCTION__, __LINE__); |
| |
| if(NO == _OSSupported) |
| { |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| if(_captureDevices) |
| { |
| [_captureDevices release]; |
| } |
| _captureDevices = [[NSArray alloc] initWithArray: |
| [QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo]]; |
| |
| _captureDeviceCount = _captureDevices.count; |
| if(_captureDeviceCount < 1) |
| { |
| return [NSNumber numberWithInt:0]; |
| } |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| // Initializes a QTCaptureSession (member variable) to deliver frames via |
| // callback |
| // QTCapture* member variables affected |
| // The image format and frequency are setup here |
| // Returns 0 on success, -1 otherwise. |
| - (NSNumber*)initializeVideoCapture{ |
| WEBRTC_TRACE(kTraceModuleCall, kTraceVideoCapture, 0, |
| "%s:%d", __FUNCTION__, __LINE__); |
| |
| if(YES == _captureInitialized) |
| { |
| return [NSNumber numberWithInt:-1]; |
| } |
| |
| QTCaptureDevice* videoDevice = |
| (QTCaptureDevice*)[_captureDevices objectAtIndex:0]; |
| |
| bool success = NO; |
| NSError* error; |
| |
| success = [videoDevice open:&error]; |
| if(!success) |
| { |
| return [NSNumber numberWithInt:-1]; |
| } |
| |
| [_captureDecompressedVideoOutput setPixelBufferAttributes: |
| [NSDictionary dictionaryWithObjectsAndKeys: |
| [NSNumber numberWithDouble:_frameWidth], (id)kCVPixelBufferWidthKey, |
| [NSNumber numberWithDouble:_frameHeight], (id)kCVPixelBufferHeightKey, |
| [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32ARGB], |
| (id)kCVPixelBufferPixelFormatTypeKey, nil]]; |
| |
| // TODO(mflodman) Check fps settings. |
| //[_captureDecompressedVideoOutput setMinimumVideoFrameInterval: |
| // (NSTimeInterval)1/(float)_frameRate]; |
| //[_captureDecompressedVideoOutput setAutomaticallyDropsLateVideoFrames:YES]; |
| |
| success = [_captureSession addOutput:_captureDecompressedVideoOutput |
| error:&error]; |
| |
| if(!success) |
| { |
| return [NSNumber numberWithInt:-1]; |
| } |
| |
| _captureInitialized = YES; |
| |
| return [NSNumber numberWithInt:0]; |
| } |
| |
| // This is the callback that is called when the OS has a frame to deliver to us. |
| // Starts being called when [_captureSession startRunning] is called. Stopped |
| // similarly. |
| // Parameter videoFrame contains the image. The format, size, and frequency |
| // were setup earlier. |
| // Returns 0 on success, -1 otherwise. |
| - (void)captureOutput:(QTCaptureOutput *)captureOutput |
| didOutputVideoFrame:(CVImageBufferRef)videoFrame |
| withSampleBuffer:(QTSampleBuffer *)sampleBuffer |
| fromConnection:(QTCaptureConnection *)connection{ |
| |
| if(YES == [_rLock tryLock]) |
| { |
| [_rLock lock]; |
| } |
| else |
| { |
| return; |
| } |
| |
| if(NO == _OSSupported) |
| { |
| return; |
| } |
| |
| const int LOCK_FLAGS = 0; // documentation says to pass 0 |
| |
| // get size of the frame |
| CVPixelBufferLockBaseAddress(videoFrame, LOCK_FLAGS); |
| void* baseAddress = CVPixelBufferGetBaseAddress(videoFrame); |
| size_t bytesPerRow = CVPixelBufferGetBytesPerRow(videoFrame); |
| int frameHeight = CVPixelBufferGetHeight(videoFrame); |
| CVPixelBufferUnlockBaseAddress(videoFrame, LOCK_FLAGS); |
| |
| if(_owner) |
| { |
| |
| int frameSize = bytesPerRow * frameHeight; // 32 bit ARGB format |
| CVBufferRetain(videoFrame); |
| VideoCaptureCapability tempCaptureCapability; |
| tempCaptureCapability.width = _frameWidth; |
| tempCaptureCapability.height = _frameHeight; |
| tempCaptureCapability.maxFPS = _frameRate; |
| // TODO(wu) : Update actual type and not hard-coded value. |
| tempCaptureCapability.rawType = kVideoBGRA; |
| |
| _owner->IncomingFrame((unsigned char*)baseAddress, |
| frameSize, |
| tempCaptureCapability, |
| 0); |
| |
| CVBufferRelease(videoFrame); |
| } |
| |
| _framesDelivered++; |
| _framesRendered++; |
| |
| if(YES == [_rLock locked]) |
| { |
| [_rLock unlock]; |
| } |
| } |
| |
| @end |