From 556a64eab9fefaf5d9fc2673ab7b11cc013ba0a7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 18 Jun 2013 16:47:11 -0700 Subject: [PATCH] Adding UVCCameraControl. --- interface/CMakeLists.txt | 3 + .../include/UVCCameraControl.h | 83 ++++ .../include/UVCCameraControl.hpp | 14 + .../lib/libUVCCameraControl.a | Bin 0 -> 18448 bytes .../UVCCameraControl/src/UVCCameraControl.m | 391 ++++++++++++++++++ 5 files changed, 491 insertions(+) create mode 100644 interface/external/UVCCameraControl/include/UVCCameraControl.h create mode 100644 interface/external/UVCCameraControl/include/UVCCameraControl.hpp create mode 100644 interface/external/UVCCameraControl/lib/libUVCCameraControl.a create mode 100644 interface/external/UVCCameraControl/src/UVCCameraControl.m diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index d4f42160d9..aa2257fbf1 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -120,6 +120,8 @@ if (APPLE) find_library(QTKit QTKit) find_library(QuartzCore QuartzCore) + include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/external/UVCCameraControl/include) + target_link_libraries( ${TARGET_NAME} ${AppKit} @@ -134,6 +136,7 @@ if (APPLE) ${IOKit} ${QTKit} ${QuartzCore} + ${CMAKE_CURRENT_SOURCE_DIR}/external/UVCCameraControl/lib/libUVCCameraControl.a ${LIBOVR_LIBRARIES} ) else (APPLE) diff --git a/interface/external/UVCCameraControl/include/UVCCameraControl.h b/interface/external/UVCCameraControl/include/UVCCameraControl.h new file mode 100644 index 0000000000..7d109d1b98 --- /dev/null +++ b/interface/external/UVCCameraControl/include/UVCCameraControl.h @@ -0,0 +1,83 @@ +#import +#include + +#include +#include +#include +#include + + +#define UVC_INPUT_TERMINAL_ID 0x01 +#define UVC_PROCESSING_UNIT_ID 0x02 + +#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 diff --git a/interface/external/UVCCameraControl/include/UVCCameraControl.hpp b/interface/external/UVCCameraControl/include/UVCCameraControl.hpp new file mode 100644 index 0000000000..8c18cc69f9 --- /dev/null +++ b/interface/external/UVCCameraControl/include/UVCCameraControl.hpp @@ -0,0 +1,14 @@ +// +// UVCCameraControl.hpp +// UVCCameraControl +// +// Created by Philip Rosedale on 6/18/13. +// Copyright (c) 2013 Phobos Lab. All rights reserved. +// + +#ifndef UVCCameraControl_UVCCameraControl_hpp +#define UVCCameraControl_UVCCameraControl_hpp + +void configureCamera(); + +#endif diff --git a/interface/external/UVCCameraControl/lib/libUVCCameraControl.a b/interface/external/UVCCameraControl/lib/libUVCCameraControl.a new file mode 100644 index 0000000000000000000000000000000000000000..3dec0b7aab671c90d5860f38f9e8f3765fdad372 GIT binary patch literal 18448 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 literal 0 HcmV?d00001 diff --git a/interface/external/UVCCameraControl/src/UVCCameraControl.m b/interface/external/UVCCameraControl/src/UVCCameraControl.m new file mode 100644 index 0000000000..03800fec26 --- /dev/null +++ b/interface/external/UVCCameraControl/src/UVCCameraControl.m @@ -0,0 +1,391 @@ +#import "UVCCameraControl.h" + + +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, ¤tLocationID); + + 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: 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:(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