Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
503 lines (408 sloc) 15 KB
#import "UVCCameraControl.h"
const uvc_controls_t uvc_controls = {
.autoExposure = {
.unit = UVC_INPUT_TERMINAL_ID,
.selector = 0x02,
.size = 1,
},
// Logitech Pro 9000
// .exposure = {
// .unit = UVC_INPUT_TERMINAL_ID,
// .selector = 0x04,
// .size = 4,
// },
// Microsoft LifeCam
.exposure = {
.unit = UVC_INPUT_TERMINAL_ID,
.selector = 0x04,
.size = 4,
},
.brightness = {
.unit = UVC_PROCESSING_UNIT_ID,
.selector = 0x02,
.size = 2,
},
.contrast = {
.unit = UVC_PROCESSING_UNIT_ID,
.selector = 0x03,
.size = 2,
},
.gain = {
.unit = UVC_PROCESSING_UNIT_ID,
.selector = 0x04,
.size = 2,
},
.saturation = {
.unit = UVC_PROCESSING_UNIT_ID,
.selector = 0x07,
.size = 2,
},
.sharpness = {
.unit = UVC_PROCESSING_UNIT_ID,
.selector = 0x08,
.size = 2,
},
.whiteBalance = {
.unit = UVC_PROCESSING_UNIT_ID,
.selector = 0x0A,
.size = 2,
},
.autoWhiteBalance = {
.unit = UVC_PROCESSING_UNIT_ID,
.selector = 0x0B,
.size = 1,
},
// // Logitech Pro 9000
// .absoluteFocus = {
// .unit = 0x09,
// .selector = 0x03,
// .size = 8,
// },
// Microsoft LifeCam
.absoluteFocus = {
.unit = UVC_INPUT_TERMINAL_ID,
.selector = 0x06, // CT_FOCUS_ABSOLUTE_CONTROL
.size = 2,
},
.autoFocus = {
.unit = UVC_INPUT_TERMINAL_ID,
.selector = 0x08, //CT_FOCUS_AUTO_CONTROL
.size = 1,
},
};
@implementation UVCCameraControl
@synthesize discreteExposureValues = _discreteExposureValues;
-(id) init {
self = [super init];
if(self) {
// these values are specific to MS LifeCam sensor
// find a way to populate this array device-specific
_discreteExposureValues = [[NSArray arrayWithObjects:
[NSNumber numberWithInt: 1],
[NSNumber numberWithInt: 2],
[NSNumber numberWithInt: 5],
[NSNumber numberWithInt: 10],
[NSNumber numberWithInt: 20],
[NSNumber numberWithInt: 39],
[NSNumber numberWithInt: 78],
[NSNumber numberWithInt: 156],
[NSNumber numberWithInt: 312],
[NSNumber numberWithInt: 625],
[NSNumber numberWithInt: 1250],
[NSNumber numberWithInt: 2500],
[NSNumber numberWithInt: 5000],
[NSNumber numberWithInt: 10000],
nil] retain];
}
return self;
}
- (id)initWithLocationID:(UInt32)locationID {
if( self = [self init] ) {
interface = NULL;
// Find All USB Devices, get their locationId and check if it matches the requested one
CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
io_iterator_t serviceIterator;
IOServiceGetMatchingServices( kIOMasterPortDefault, matchingDict, &serviceIterator );
io_service_t camera;
while( (camera = IOIteratorNext(serviceIterator)) ) {
// Get DeviceInterface
IOUSBDeviceInterface **deviceInterface = NULL;
IOCFPlugInInterface **plugInInterface = NULL;
SInt32 score;
kern_return_t kr = IOCreatePlugInInterfaceForService( camera, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score );
if( (kIOReturnSuccess != kr) || !plugInInterface ) {
NSLog( @"CameraControl Error: IOCreatePlugInInterfaceForService returned 0x%08x.", kr );
continue;
}
HRESULT res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &deviceInterface );
(*plugInInterface)->Release(plugInInterface);
if( res || deviceInterface == NULL ) {
NSLog( @"CameraControl Error: QueryInterface returned %d.\n", (int)res );
continue;
}
UInt32 currentLocationID = 0;
(*deviceInterface)->GetLocationID(deviceInterface, &currentLocationID);
if( currentLocationID == locationID ) {
// Yep, this is the USB Device that was requested!
interface = [self getControlInferaceWithDeviceInterface:deviceInterface];
return self;
}
} // end while
}
return self;
}
- (id)initWithVendorID:(long)vendorID productID:(long)productID {
if( self = [self init] ) {
interface = NULL;
// Find USB Device
CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
CFNumberRef numberRef;
numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendorID);
CFDictionarySetValue( matchingDict, CFSTR(kUSBVendorID), numberRef );
CFRelease(numberRef);
numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &productID);
CFDictionarySetValue( matchingDict, CFSTR(kUSBProductID), numberRef );
CFRelease(numberRef);
io_service_t camera = IOServiceGetMatchingService( kIOMasterPortDefault, matchingDict );
// Get DeviceInterface
IOUSBDeviceInterface **deviceInterface = NULL;
IOCFPlugInInterface **plugInInterface = NULL;
SInt32 score;
kern_return_t kr = IOCreatePlugInInterfaceForService( camera, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score );
if( (kIOReturnSuccess != kr) || !plugInInterface ) {
NSLog( @"CameraControl Error: IOCreatePlugInInterfaceForService returned 0x%08x.", kr );
return self;
}
HRESULT res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &deviceInterface );
(*plugInInterface)->Release(plugInInterface);
if( res || deviceInterface == NULL ) {
NSLog( @"CameraControl Error: QueryInterface returned %d.\n", (int)res );
return self;
}
interface = [self getControlInferaceWithDeviceInterface:deviceInterface];
}
return self;
}
- (IOUSBInterfaceInterface190 **)getControlInferaceWithDeviceInterface:(IOUSBDeviceInterface **)deviceInterface {
IOUSBInterfaceInterface190 **controlInterface;
io_iterator_t interfaceIterator;
IOUSBFindInterfaceRequest interfaceRequest;
interfaceRequest.bInterfaceClass = UVC_CONTROL_INTERFACE_CLASS;
interfaceRequest.bInterfaceSubClass = UVC_CONTROL_INTERFACE_SUBCLASS;
interfaceRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
interfaceRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare;
IOReturn success = (*deviceInterface)->CreateInterfaceIterator( deviceInterface, &interfaceRequest, &interfaceIterator );
if( success != kIOReturnSuccess ) {
return NULL;
}
io_service_t usbInterface;
HRESULT result;
if( (usbInterface = IOIteratorNext(interfaceIterator)) ) {
IOCFPlugInInterface **plugInInterface = NULL;
//Create an intermediate plug-in
SInt32 score;
kern_return_t kr = IOCreatePlugInInterfaceForService( usbInterface, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score );
//Release the usbInterface object after getting the plug-in
kr = IOObjectRelease(usbInterface);
if( (kr != kIOReturnSuccess) || !plugInInterface ) {
NSLog( @"CameraControl Error: Unable to create a plug-in (%08x)\n", kr );
return NULL;
}
//Now create the device interface for the interface
result = (*plugInInterface)->QueryInterface( plugInInterface, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID *) &controlInterface );
//No longer need the intermediate plug-in
(*plugInInterface)->Release(plugInInterface);
if( result || !controlInterface ) {
NSLog( @"CameraControl Error: Couldn’t create a device interface for the interface (%08x)", (int) result );
return NULL;
}
return controlInterface;
}
return NULL;
}
- (void)dealloc {
if( interface ) {
(*interface)->USBInterfaceClose(interface);
(*interface)->Release(interface);
}
[super dealloc];
}
- (BOOL)sendControlRequest:(IOUSBDevRequest)controlRequest {
if( !interface ){
NSLog( @"CameraControl Error: No interface to send request" );
return NO;
}
//Now open the interface. This will cause the pipes associated with
//the endpoints in the interface descriptor to be instantiated
kern_return_t kr = (*interface)->USBInterfaceOpen(interface);
if (kr != kIOReturnSuccess) {
NSLog( @"CameraControl Error: Unable to open interface (%08x)\n", kr );
return NO;
}
kr = (*interface)->ControlRequest( interface, 0, &controlRequest );
if( kr != kIOReturnSuccess ) {
kr = (*interface)->USBInterfaceClose(interface);
//NSLog( @"CameraControl Error: Control request failed: %08x", kr );
return NO;
}
kr = (*interface)->USBInterfaceClose(interface);
return YES;
}
- (BOOL)setData:(long)value withLength:(int)length forSelector:(int)selector at:(int)unitId {
IOUSBDevRequest controlRequest;
controlRequest.bmRequestType = USBmakebmRequestType( kUSBOut, kUSBClass, kUSBInterface );
controlRequest.bRequest = UVC_SET_CUR;
controlRequest.wValue = (selector << 8) | 0x00;
controlRequest.wIndex = (unitId << 8) | 0x00;
controlRequest.wLength = length;
controlRequest.wLenDone = 0;
controlRequest.pData = &value;
return [self sendControlRequest:controlRequest];
}
- (long)getDataFor:(int)type withLength:(int)length fromSelector:(int)selector at:(int)unitId {
long value = 0;
IOUSBDevRequest controlRequest;
controlRequest.bmRequestType = USBmakebmRequestType( kUSBIn, kUSBClass, kUSBInterface );
controlRequest.bRequest = type;
controlRequest.wValue = (selector << 8) | 0x00;
controlRequest.wIndex = (unitId << 8) | 0x00;
controlRequest.wLength = length;
controlRequest.wLenDone = 0;
controlRequest.pData = &value;
BOOL success = [self sendControlRequest:controlRequest];
return ( success ? value : 0 );
}
// Get Range (min, max)
- (uvc_range_t)getRangeForControl:(const uvc_control_info_t *)control {
uvc_range_t range = { 0, 0 };
range.min = (int)[self getDataFor:UVC_GET_MIN withLength:control->size fromSelector:control->selector at:control->unit];
range.max = (int)[self getDataFor:UVC_GET_MAX withLength:control->size fromSelector:control->selector at:control->unit];
return range;
}
// Used to de-/normalize values
- (float)mapValue:(float)value fromMin:(float)fromMin max:(float)fromMax toMin:(float)toMin max:(float)toMax {
return toMin + (toMax - toMin) * ((value - fromMin) / (fromMax - fromMin));
}
// Get a normalized value
- (float)getValueForControl:(const uvc_control_info_t *)control {
// TODO: Cache the range somewhere?
uvc_range_t range = [self getRangeForControl:control];
long intval = [self getDataFor:UVC_GET_CUR withLength:control->size fromSelector:control->selector at:control->unit];
return [self mapValue:intval fromMin:range.min max:range.max toMin:0 max:1];
}
-(int)nearestDiscreteValueFor:(int)value from:(NSArray*)discreteValues {
if(!discreteValues)
return value;
int distanceToBestValue = INT_MAX;
int bestValue = value;
for(NSNumber *number in discreteValues) {
int currentValue = number.intValue;
int currentDistance = abs(currentValue - value);
if(currentDistance < distanceToBestValue) {
distanceToBestValue = currentDistance;
bestValue = currentValue;
}
}
return bestValue;
}
// Set a normalized value
- (BOOL)setDiscreteValue:(float)value fromSet:(NSArray*)discreteValues forControl:(const uvc_control_info_t *)control {
// TODO: Cache the range somewhere?
int intval = 0;
if(discreteValues){
int index = (int)(value*(discreteValues.count-1));
intval = [[discreteValues objectAtIndex:index] intValue];
} else {
uvc_range_t range = [self getRangeForControl:control];
intval = [self mapValue:value fromMin:0 max:1 toMin:range.min max:range.max];
}
return [self setData:intval withLength:control->size forSelector:control->selector at:control->unit];
}
- (BOOL)setValue:(float)value forControl:(const uvc_control_info_t *)control {
return [self setDiscreteValue:value fromSet:nil forControl:control];
}
// ================================================================
// Set/Get the actual values for the camera
//
- (BOOL)setAutoExposure:(BOOL)enabled {
int intval = (enabled ? 0x08 : 0x01); // "auto exposure modes" ar NOT boolean (on|off) as it seems
return [self setData:intval
withLength:uvc_controls.autoExposure.size
forSelector:uvc_controls.autoExposure.selector
at:uvc_controls.autoExposure.unit];
}
- (BOOL)getAutoExposure {
long intval = [self getDataFor:UVC_GET_CUR
withLength:uvc_controls.autoExposure.size
fromSelector:uvc_controls.autoExposure.selector
at:uvc_controls.autoExposure.unit];
return ( intval == 0x08 ? YES : NO );
}
- (BOOL)setExposure:(float)value {
value = 1 - value;
return [self setDiscreteValue:value fromSet:self.discreteExposureValues forControl:(uvc_control_info_t *)&uvc_controls.exposure];
}
- (float)getExposure {
float value = [self getValueForControl:&uvc_controls.exposure];
return 1 - value;
}
- (BOOL)setGain:(float)value {
return [self setValue:value forControl:&uvc_controls.gain];
}
- (float)getGain {
return [self getValueForControl:&uvc_controls.gain];
}
- (BOOL)setBrightness:(float)value {
return [self setValue:value forControl:&uvc_controls.brightness];
}
- (float)getBrightness {
return [self getValueForControl:&uvc_controls.brightness];
}
- (BOOL)setContrast:(float)value {
return [self setValue:value forControl:&uvc_controls.contrast];
}
- (float)getContrast {
return [self getValueForControl:&uvc_controls.contrast];
}
- (BOOL)setSaturation:(float)value {
return [self setValue:value forControl:&uvc_controls.saturation];
}
- (float)getSaturation {
return [self getValueForControl:&uvc_controls.saturation];
}
- (BOOL)setSharpness:(float)value {
return [self setValue:value forControl:&uvc_controls.sharpness];
}
- (float)getSharpness {
return [self getValueForControl:&uvc_controls.sharpness];
}
- (BOOL)setAutoWhiteBalance:(BOOL)enabled {
int intval = (enabled ? 0x01 : 0x00);
return [self setData:intval
withLength:uvc_controls.autoWhiteBalance.size
forSelector:uvc_controls.autoWhiteBalance.selector
at:uvc_controls.autoWhiteBalance.unit];
}
- (BOOL)getAutoWhiteBalance {
long intval = [self getDataFor:UVC_GET_CUR
withLength:uvc_controls.autoWhiteBalance.size
fromSelector:uvc_controls.autoWhiteBalance.selector
at:uvc_controls.autoWhiteBalance.unit];
return ( intval ? YES : NO );
}
- (BOOL)setWhiteBalance:(float)value {
return [self setValue:value forControl:&uvc_controls.whiteBalance];
}
- (float)getWhiteBalance {
return [self getValueForControl:&uvc_controls.whiteBalance];
}
//-----focus
- (BOOL)setAutoFocus:(BOOL)enabled {
//int intval = (enabled ? 0x08 : 0x01); //that's how eposure does it
int intval = (enabled ? 0x01 : 0x00); //that's how white balance does it
printf("setAutoFocus = %i \n",enabled);
return [self setData:intval
withLength:uvc_controls.autoFocus.size
forSelector:uvc_controls.autoFocus.selector
at:uvc_controls.autoFocus.unit];
}
- (BOOL)getAutoFocus {
long intval = [self getDataFor:UVC_GET_CUR
withLength:uvc_controls.autoFocus.size
fromSelector:uvc_controls.autoFocus.selector
at:uvc_controls.autoFocus.unit];
//return ( intval == 0x08 ? YES : NO );
//return ( intval ? YES : NO );
return ( intval == 0x01 ? YES : NO );
}
- (BOOL)setAbsoluteFocus:(float)value {
printf("focus value %f \n",value);
//value = 1 - value;
return [self setValue:value forControl:&uvc_controls.absoluteFocus];
}
- (float)getAbsoluteFocus {
//float value = [self getValueForControl:&uvc_controls.absoluteFocus];
//return 1 - value;
return [self getValueForControl:&uvc_controls.absoluteFocus];
}
@end