Added source files for UVCCameraControl.

This commit is contained in:
Andrzej Kapolka 2013-06-19 10:18:48 -07:00
parent cc9f8bc717
commit 1757f32fd0
2 changed files with 466 additions and 0 deletions

View file

@ -0,0 +1,83 @@
#import <Foundation/Foundation.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>
#define UVC_INPUT_TERMINAL_ID 0x01
#define UVC_PROCESSING_UNIT_ID 0x03
#define UVC_CONTROL_INTERFACE_CLASS 14
#define UVC_CONTROL_INTERFACE_SUBCLASS 1
#define UVC_SET_CUR 0x01
#define UVC_GET_CUR 0x81
#define UVC_GET_MIN 0x82
#define UVC_GET_MAX 0x83
typedef struct {
int min, max;
} uvc_range_t;
typedef struct {
int unit;
int selector;
int size;
} uvc_control_info_t;
typedef struct {
uvc_control_info_t autoExposure;
uvc_control_info_t exposure;
uvc_control_info_t brightness;
uvc_control_info_t contrast;
uvc_control_info_t gain;
uvc_control_info_t saturation;
uvc_control_info_t sharpness;
uvc_control_info_t whiteBalance;
uvc_control_info_t autoWhiteBalance;
} uvc_controls_t ;
@interface UVCCameraControl : NSObject {
long dataBuffer;
IOUSBInterfaceInterface190 **interface;
}
- (id)initWithLocationID:(UInt32)locationID;
- (id)initWithVendorID:(long)vendorID productID:(long)productID;
- (IOUSBInterfaceInterface190 **)getControlInferaceWithDeviceInterface:(IOUSBDeviceInterface **)deviceInterface;
- (BOOL)sendControlRequest:(IOUSBDevRequest)controlRequest;
- (BOOL)setData:(long)value withLength:(int)length forSelector:(int)selector at:(int)unitID;
- (long)getDataFor:(int)type withLength:(int)length fromSelector:(int)selector at:(int)unitID;
- (uvc_range_t)getRangeForControl:(const uvc_control_info_t *)control;
- (float)mapValue:(float)value fromMin:(float)fromMin max:(float)fromMax toMin:(float)toMin max:(float)toMax;
- (float)getValueForControl:(const uvc_control_info_t *)control;
- (BOOL)setValue:(float)value forControl:(const uvc_control_info_t *)control;
- (BOOL)setAutoExposure:(BOOL)enabled;
- (BOOL)getAutoExposure;
- (BOOL)setExposure:(float)value;
- (float)getExposure;
- (BOOL)setGain:(float)value;
- (float)getGain;
- (BOOL)setBrightness:(float)value;
- (float)getBrightness;
- (BOOL)setContrast:(float)value;
- (float)getContrast;
- (BOOL)setSaturation:(float)value;
- (float)getSaturation;
- (BOOL)setSharpness:(float)value;
- (float)getSharpness;
- (BOOL)setAutoWhiteBalance:(BOOL)enabled;
- (BOOL)getAutoWhiteBalance;
- (BOOL)setWhiteBalance:(float)value;
- (float)getWhiteBalance;
@end

View file

@ -0,0 +1,383 @@
#import "UVCCameraControl.h"
#import "UVCCameraControl.hpp"
void configureCamera(int vendorID, int productID, int autoExposure, float exposure, float contrast, float saturation, float sharpness, int autoWhiteBalance, float whiteBalance) {
UVCCameraControl* cameraControl = [[UVCCameraControl alloc] initWithVendorID:0x05ac productID:0x8510];
[cameraControl setAutoExposure:autoExposure];
[cameraControl setExposure:exposure];
[cameraControl setContrast:contrast];
[cameraControl setSaturation:saturation];
[cameraControl setSharpness:sharpness];
[cameraControl setAutoWhiteBalance:autoWhiteBalance];
[cameraControl setWhiteBalance:whiteBalance];
}
const uvc_controls_t uvc_controls = {
.autoExposure = {
.unit = UVC_INPUT_TERMINAL_ID,
.selector = 0x02,
.size = 1,
},
.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,
},
};
@implementation UVCCameraControl
- (id)initWithLocationID:(UInt32)locationID {
if( self = [super 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 = [super 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: Couldnt create a device interface for the interface (%08x)", (int) result );
return NULL;
}
return controlInterface;
}
return NULL;
}
- (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)->ControlRequest( interface, 0, &controlRequest );
if( kr != kIOReturnSuccess ) {
NSLog( @"CameraControl Error: Control request failed: %08x", kr );
return NO;
}
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:(uvc_control_info_t *)control {
uvc_range_t range = { 0, 0 };
range.min = [self getDataFor:UVC_GET_MIN withLength:control->size fromSelector:control->selector at:control->unit];
range.max = [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:(uvc_control_info_t *)control {
// TODO: Cache the range somewhere?
uvc_range_t range = [self getRangeForControl:control];
int 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];
}
// Set a normalized value
- (BOOL)setValue:(float)value forControl:(uvc_control_info_t *)control {
// TODO: Cache the range somewhere?
uvc_range_t range = [self getRangeForControl:control];
int 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];
}
// ================================================================
// 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 {
int 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 setValue:value forControl:&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 {
int 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];
}
@end