blob: 73322424a3e7750d3a144cecc4856cdfd1aaa9a2 [file] [log] [blame]
//
// 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