├── CHANGELOG.md ├── LICENSE ├── uvc-util.xcodeproj ├── xcshareddata │ └── xcschemes │ │ └── uvc-util.xcscheme └── project.pbxproj ├── .gitignore ├── src ├── UVCValue.h ├── UVCValue.m ├── UVCType.h ├── UVCController.h ├── uvc-util.m └── UVCController.m └── README.md /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [1.1.0] 10 | Baseline release to open source. 11 | 12 | ## [1.2.0] - 2021-04-25 13 | ### Fixed 14 | - Several issues reported with some camera's ProcessingUnit being available but unusable. Diagnosed as the use of a static unit id of 2 for the ProcessingUnit, whereas the UVC standard has a variable unit id present in the PU header. Added unit id map to UVCController with default unit ids for each handled unit type, overridden by unit id from the unit's header record. User who reported issues tested the change, confirmed success. 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jeffrey Frey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /uvc-util.xcodeproj/xcshareddata/xcschemes/uvc-util.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 57 | 58 | 59 | 60 | 66 | 68 | 74 | 75 | 76 | 77 | 79 | 80 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/xcode,objective-c,macos,vscode 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=xcode,objective-c,macos,vscode 4 | 5 | ### macOS ### 6 | # General 7 | .DS_Store 8 | .AppleDouble 9 | .LSOverride 10 | 11 | # Icon must end with two \r 12 | Icon 13 | 14 | 15 | # Thumbnails 16 | ._* 17 | 18 | # Files that might appear in the root of a volume 19 | .DocumentRevisions-V100 20 | .fseventsd 21 | .Spotlight-V100 22 | .TemporaryItems 23 | .Trashes 24 | .VolumeIcon.icns 25 | .com.apple.timemachine.donotpresent 26 | 27 | # Directories potentially created on remote AFP share 28 | .AppleDB 29 | .AppleDesktop 30 | Network Trash Folder 31 | Temporary Items 32 | .apdisk 33 | 34 | ### Objective-C ### 35 | # Xcode 36 | # 37 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 38 | 39 | ## User settings 40 | xcuserdata/ 41 | 42 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 43 | *.xcscmblueprint 44 | *.xccheckout 45 | 46 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 47 | build/ 48 | DerivedData/ 49 | *.moved-aside 50 | *.pbxuser 51 | !default.pbxuser 52 | *.mode1v3 53 | !default.mode1v3 54 | *.mode2v3 55 | !default.mode2v3 56 | *.perspectivev3 57 | !default.perspectivev3 58 | 59 | ## Obj-C/Swift specific 60 | *.hmap 61 | 62 | ## App packaging 63 | *.ipa 64 | *.dSYM.zip 65 | *.dSYM 66 | 67 | # CocoaPods 68 | # We recommend against adding the Pods directory to your .gitignore. However 69 | # you should judge for yourself, the pros and cons are mentioned at: 70 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 71 | # Pods/ 72 | # Add this line if you want to avoid checking in source code from the Xcode workspace 73 | # *.xcworkspace 74 | 75 | # Carthage 76 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 77 | # Carthage/Checkouts 78 | 79 | Carthage/Build/ 80 | 81 | # fastlane 82 | # It is recommended to not store the screenshots in the git repo. 83 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 84 | # For more information about the recommended setup visit: 85 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 86 | 87 | fastlane/report.xml 88 | fastlane/Preview.html 89 | fastlane/screenshots/**/*.png 90 | fastlane/test_output 91 | 92 | # Code Injection 93 | # After new code Injection tools there's a generated folder /iOSInjectionProject 94 | # https://github.com/johnno1962/injectionforxcode 95 | 96 | iOSInjectionProject/ 97 | 98 | ### Objective-C Patch ### 99 | 100 | ### vscode ### 101 | .vscode/* 102 | !.vscode/settings.json 103 | !.vscode/tasks.json 104 | !.vscode/launch.json 105 | !.vscode/extensions.json 106 | *.code-workspace 107 | 108 | ### Xcode ### 109 | # Xcode 110 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 111 | 112 | 113 | 114 | 115 | ## Gcc Patch 116 | /*.gcno 117 | 118 | ### Xcode Patch ### 119 | *.xcodeproj/* 120 | !*.xcodeproj/project.pbxproj 121 | !*.xcodeproj/xcshareddata/ 122 | !*.xcworkspace/contents.xcworkspacedata 123 | **/xcshareddata/WorkspaceSettings.xcsettings 124 | 125 | # End of https://www.toptal.com/developers/gitignore/api/xcode,objective-c,macos,vscode -------------------------------------------------------------------------------- /src/UVCValue.h: -------------------------------------------------------------------------------- 1 | // 2 | // UVCValue.h 3 | // 4 | // Structured byte-packed data containers for UVC controls. 5 | // 6 | // Copyright © 2016 7 | // Dr. Jeffrey Frey, IT-NSS 8 | // University of Delaware 9 | // 10 | // $Id$ 11 | // 12 | 13 | #import "UVCType.h" 14 | 15 | /*! 16 | @class UVCValue 17 | @abstract Structured byte-packed data container 18 | 19 | An instance of UVCValue combines the structural meta-data from a UVCType 20 | instance with a memory buffer of sufficient size to hold data of that 21 | type. 22 | 23 | Many of the methods provided by UVCType are duplicated in UVCValue, but 24 | lack the specification of an external buffer (since UVCValue itself contains 25 | the buffer in question). 26 | */ 27 | @interface UVCValue : NSObject 28 | { 29 | BOOL _isSwappedToUSBEndian; 30 | UVCType *_valueType; 31 | void *_valuePtr; 32 | } 33 | 34 | /*! 35 | @method uvcValueWithType: 36 | 37 | Returns an autoreleased instance of UVCValue which wraps a buffer sized 38 | according to [valueType byteSize] and uses valueType as its structural 39 | meta-data. 40 | */ 41 | + (UVCValue*) uvcValueWithType:(UVCType*)valueType; 42 | 43 | /*! 44 | @method valueType 45 | 46 | Returns the UVCType that acts as the structural meta-data for the 47 | receiver. 48 | */ 49 | - (UVCType*) valueType; 50 | 51 | /*! 52 | @method valuePtr 53 | 54 | Returns the base address of the receiver's memory buffer (where data 55 | structured according to the valueType should be stored). 56 | */ 57 | - (void*) valuePtr; 58 | 59 | /*! 60 | @method byteSize 61 | 62 | Returns the number of bytes occupied by the receiver's valueType. 63 | */ 64 | - (NSUInteger) byteSize; 65 | 66 | /*! 67 | @method pointerToFieldAtIndex: 68 | 69 | Calculates the base pointer of the given field within the receiver's 70 | memory buffer. 71 | 72 | Returns NULL if index is out of range. 73 | */ 74 | - (void*) pointerToFieldAtIndex:(NSUInteger)index; 75 | 76 | /*! 77 | @method pointerToFieldWithName: 78 | 79 | Calculates the base pointer of the given field (under a case-insensitive 80 | string comparison against fieldName) within the receiver's memory buffer. 81 | 82 | Returns NULL if index is out of range. 83 | */ 84 | - (void*) pointerToFieldWithName:(NSString*)fieldName; 85 | 86 | /*! 87 | @method isSwappedToUSBEndian 88 | 89 | Returns YES if the receiver's memory buffer has been byte-swapped to USB 90 | (little) endian. 91 | */ 92 | - (BOOL) isSwappedToUSBEndian; 93 | 94 | /*! 95 | @method byteSwapHostToUSBEndian 96 | 97 | If the receiver is currently in host endian order, byte swap all necessary 98 | component fields of the receiver's memory buffer (anything larger than 1 99 | byte) from the host endian to USB (little) endian. 100 | */ 101 | - (void) byteSwapHostToUSBEndian; 102 | 103 | /*! 104 | @method byteSwapUSBToHostEndian: 105 | 106 | If the receiver is currently byte-swapped to USB (little) endian, byte 107 | swap all necessary component fields of the receiver's memory buffer 108 | (anything larger than 1 byte) from USB (little) endian to host endian. 109 | */ 110 | - (void) byteSwapUSBToHostEndian; 111 | 112 | /*! 113 | @method scanCString:flags: 114 | 115 | Convenience method that calls 116 | 117 | [self scanCString:cString flags:flags minimum:NULL maximum:NULL stepSize:NULL defaultValue:NULL] 118 | */ 119 | - (BOOL) scanCString:(const char*)cString flags:(UVCTypeScanFlags)flags; 120 | 121 | /*! 122 | @method scanCString:flags:minimum:maximum: 123 | 124 | Convenience method that calls 125 | 126 | [self scanCString:cString flags:flags minimum:minimum maximum:maximum stepSize:NULL defaultValue:NULL] 127 | */ 128 | - (BOOL) scanCString:(const char*)cString flags:(UVCTypeScanFlags)flags minimum:(UVCValue*)minimum maximum:(UVCValue*)maximum; 129 | 130 | /*! 131 | @method scanCString:flags:minimum:maximum:stepSize: 132 | 133 | Convenience method that calls 134 | 135 | [self scanCString:cString flags:flags minimum:minimum maximum:maximum stepSize:stepSize defaultValue:NULL] 136 | */ 137 | - (BOOL) scanCString:(const char*)cString flags:(UVCTypeScanFlags)flags minimum:(UVCValue*)minimum maximum:(UVCValue*)maximum stepSize:(UVCValue*)stepSize; 138 | 139 | /*! 140 | @method scanCString:flags:minimum:maximum:stepSize:defaultValue: 141 | 142 | Send the scanCString:intoBuffer:flags:minimum:maximum:stepSize:defaultValue: message 143 | to the receiver's UVCType, using the receiver's valuePtr as the buffer. 144 | 145 | See UVCType's documentation for a description of the acceptable C string 146 | format. 147 | 148 | Returns YES if all component fields of the receiver's memory buffer were 149 | successfully set. 150 | */ 151 | - (BOOL) scanCString:(const char*)cString flags:(UVCTypeScanFlags)flags minimum:(UVCValue*)minimum maximum:(UVCValue*)maximum stepSize:(UVCValue*)stepSize defaultValue:(UVCValue*)defaultValue; 152 | 153 | /*! 154 | @method stringValue 155 | 156 | Returns a human-readable description of the receiver's data, as structured by its 157 | UVCType. 158 | 159 | Example: 160 | 161 | "{pan=3600,tilt=-360000}" 162 | 163 | */ 164 | - (NSString*) stringValue; 165 | 166 | /*! 167 | @method copyValue: 168 | 169 | If [otherValue valueType] matches the receiver's UVCType (same layout of atomic 170 | types) then the requisite number of bytes from [otherValue valuePtr] are copied 171 | to the receiver's memory buffer. 172 | */ 173 | - (BOOL) copyValue:(UVCValue*)otherValue; 174 | 175 | @end 176 | -------------------------------------------------------------------------------- /src/UVCValue.m: -------------------------------------------------------------------------------- 1 | // 2 | // UVCValue.m 3 | // 4 | // Structured byte-packed data containers for UVC controls. 5 | // 6 | // Copyright © 2016 7 | // Dr. Jeffrey Frey, IT-NSS 8 | // University of Delaware 9 | // 10 | // $Id$ 11 | // 12 | 13 | #import "UVCValue.h" 14 | 15 | #import 16 | 17 | @implementation UVCValue 18 | 19 | + (UVCValue*) uvcValueWithType:(UVCType*)valueType 20 | { 21 | UVCValue *newValue = nil; 22 | if ( valueType ) { 23 | NSUInteger extraBytes = [valueType byteSize]; 24 | 25 | newValue = class_createInstance(self, extraBytes); 26 | if ( newValue && (newValue = [newValue init]) ) { 27 | newValue->_valueType = [valueType retain]; 28 | newValue->_valuePtr = object_getIndexedIvars(newValue); 29 | memset(newValue->_valuePtr, 0, extraBytes); 30 | 31 | [newValue autorelease]; 32 | } 33 | } 34 | return newValue; 35 | } 36 | 37 | // 38 | 39 | - (void) dealloc 40 | { 41 | [_valueType release]; 42 | [super dealloc]; 43 | } 44 | 45 | // 46 | 47 | - (NSString*) description 48 | { 49 | NSMutableString *asString = [[NSMutableString alloc] initWithFormat:@"UVCValue@%p { type: %@; bytes: [", self, _valueType]; 50 | NSUInteger i = 0, iMax = [_valueType byteSize]; 51 | UInt8 *p = _valuePtr; 52 | 53 | while ( i < iMax ) { 54 | [asString appendFormat:@"%s%02hhx", i ? ":" : "", *p++]; 55 | i++; 56 | } 57 | [asString appendString:@"] }"]; 58 | 59 | NSString *outString = [[asString copy] autorelease]; 60 | [asString release]; 61 | 62 | return outString; 63 | } 64 | 65 | // 66 | 67 | - (BOOL) isEqual:(id)otherObject 68 | { 69 | if ( otherObject == self ) return YES; 70 | 71 | if ( [otherObject isKindOfClass:[UVCValue class]] ) { 72 | if ( [_valueType isEqual:[otherObject valueType]] ) { 73 | return (memcmp(_valuePtr, [otherObject valuePtr], [_valueType byteSize]) == 0) ? YES : NO; 74 | } 75 | } 76 | return NO; 77 | } 78 | 79 | // 80 | 81 | - (UVCType*) valueType 82 | { 83 | return _valueType; 84 | } 85 | 86 | // 87 | 88 | - (NSUInteger) byteSize 89 | { 90 | return [_valueType byteSize]; 91 | } 92 | 93 | // 94 | 95 | - (void*) valuePtr 96 | { 97 | return _valuePtr; 98 | } 99 | 100 | // 101 | 102 | - (void*) pointerToFieldAtIndex:(NSUInteger)index 103 | { 104 | NSUInteger offset = [_valueType offsetToFieldAtIndex:index]; 105 | 106 | if ( offset != kUVCTypeComponentTypeInvalid ) return (_valuePtr + offset); 107 | return NULL; 108 | } 109 | 110 | // 111 | 112 | - (void*) pointerToFieldWithName:(NSString*)fieldName 113 | { 114 | NSUInteger offset = [_valueType offsetToFieldWithName:fieldName]; 115 | 116 | if ( offset != kUVCTypeComponentTypeInvalid ) return (_valuePtr + offset); 117 | return NULL; 118 | } 119 | 120 | // 121 | 122 | - (BOOL) isSwappedToUSBEndian 123 | { 124 | return _isSwappedToUSBEndian; 125 | } 126 | 127 | // 128 | 129 | - (void) byteSwapHostToUSBEndian 130 | { 131 | if ( ! _isSwappedToUSBEndian ) { 132 | [_valueType byteSwapHostToUSBEndian:_valuePtr]; 133 | _isSwappedToUSBEndian = YES; 134 | } 135 | } 136 | 137 | // 138 | 139 | - (void) byteSwapUSBToHostEndian 140 | { 141 | if ( _isSwappedToUSBEndian ) { 142 | [_valueType byteSwapUSBToHostEndian:_valuePtr]; 143 | _isSwappedToUSBEndian = NO; 144 | } 145 | } 146 | 147 | // 148 | 149 | - (BOOL) scanCString:(const char*)cString 150 | flags:(UVCTypeScanFlags)flags 151 | { 152 | return [_valueType scanCString:cString 153 | intoBuffer:_valuePtr 154 | flags:flags 155 | ]; 156 | } 157 | 158 | // 159 | 160 | - (BOOL) scanCString:(const char*)cString 161 | flags:(UVCTypeScanFlags)flags 162 | minimum:(UVCValue*)minimum 163 | maximum:(UVCValue*)maximum 164 | { 165 | return [_valueType scanCString:cString 166 | intoBuffer:_valuePtr 167 | flags:flags 168 | minimum:(minimum ? [minimum valuePtr] : NULL) 169 | maximum:(maximum ? [maximum valuePtr] : NULL) 170 | ]; 171 | } 172 | 173 | // 174 | 175 | - (BOOL) scanCString:(const char*)cString 176 | flags:(UVCTypeScanFlags)flags 177 | minimum:(UVCValue*)minimum 178 | maximum:(UVCValue*)maximum 179 | stepSize:(UVCValue*)stepSize 180 | { 181 | return [_valueType scanCString:cString 182 | intoBuffer:_valuePtr 183 | flags:flags 184 | minimum:(minimum ? [minimum valuePtr] : NULL) 185 | maximum:(maximum ? [maximum valuePtr] : NULL) 186 | stepSize:(stepSize ? [stepSize valuePtr] : NULL) 187 | ]; 188 | } 189 | 190 | // 191 | 192 | - (BOOL) scanCString:(const char*)cString 193 | flags:(UVCTypeScanFlags)flags 194 | minimum:(UVCValue*)minimum 195 | maximum:(UVCValue*)maximum 196 | stepSize:(UVCValue*)stepSize 197 | defaultValue:(UVCValue*)defaultValue 198 | { 199 | return [_valueType scanCString:cString 200 | intoBuffer:_valuePtr 201 | flags:flags 202 | minimum:(minimum ? [minimum valuePtr] : NULL) 203 | maximum:(maximum ? [maximum valuePtr] : NULL) 204 | stepSize:(stepSize ? [stepSize valuePtr] : NULL) 205 | defaultValue:(defaultValue ? [defaultValue valuePtr] : NULL) 206 | ]; 207 | } 208 | 209 | // 210 | 211 | - (NSString*) stringValue 212 | { 213 | return [_valueType stringFromBuffer:_valuePtr]; 214 | } 215 | 216 | // 217 | 218 | - (BOOL) copyValue:(UVCValue*)otherValue 219 | { 220 | if ( [_valueType isEqual:[otherValue valueType]] ) { 221 | memcpy(_valuePtr, [otherValue valuePtr], [_valueType byteSize]); 222 | } 223 | return NO; 224 | } 225 | 226 | @end 227 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uvc-util 2 | USB Video Class (UVC) control management utility for Mac OS X 3 | 4 | This code arose from a need for a command-line utility on Mac OS X that could query and modify UVC camera controls (like contrast and brightness). It presently implements all Terminal and Processing Unit controls available under the [1.1 standard](http://www.cajunbot.com/wiki/images/8/85/USB_Video_Class_1.1.pdf "UVC 1.1 PDF"). Additional [1.5 standard](https://www.usb.org/sites/default/files/USB_Video_Class_1_5.zip) Terminal and Processing Unit controls were added for the 1.2 release of this software. 5 | 6 | Control values are implemented using a class (UVCType) that represents byte-packed data structures containing core atomic types (8-, 16-, 32-, and 64-bit integers). Multi-component types allow fields to be named. Another class (UVCValue) uses UVCType and a memory buffer to manage data structured according to that UVCType. Thus, the code knows how each implemented UVC control's data is structured, which allows for per-component byte-swapping when necessary, etc. 7 | 8 | Unlike other (GUI-based) utilities, this code only makes use of the IOKit to walk the USB bus, searching for UVC-compliant devices. 9 | 10 | ## Features 11 | 12 | THe program has built-in help, available via the `-h` or `--help` flag: 13 | 14 | ~~~~ 15 | usage: 16 | 17 | ./uvc-util {options/actions/target selection} 18 | 19 | Options: 20 | 21 | -h/--help Show this information 22 | -v/--version Show the version of the program 23 | -k/--keep-running Continue processing additional actions despite 24 | encountering errors 25 | 26 | Actions: 27 | 28 | -d/--list-devices Display a list of all UVC-capable devices 29 | -c/--list-controls Display a list of UVC controls implemented 30 | 31 | Available after a target device is selected: 32 | 33 | -c/--list-controls Display a list of UVC controls available for 34 | the target device 35 | 36 | -S Display available information for the given 37 | --show-control= UVC control: component fields for multi-value 38 | types, minimum, maximum, resolution, and default 39 | value when provided: 40 | 41 | pan-tilt-abs { 42 | type-description: { 43 | signed 32-bit integer pan; 44 | signed 32-bit integer tilt; 45 | }, 46 | minimum: {pan=-648000,tilt=-648000} 47 | maximum: {pan=648000,tilt=648000} 48 | step-size: {pan=3600,tilt=3600} 49 | default-value: {pan=0,tilt=0} 50 | } 51 | 52 | -g Get the value of a control. 53 | --get= 54 | 55 | -o Same as -g/--get, but ONLY the value of the control 56 | --get-value= is displayed (no label) 57 | 58 | -s = Set the value of a control; see below for a 59 | --set== description of 60 | 61 | Specifying for -s/--set: 62 | 63 | * The string "default" indicates the control should be reset to its default value(s) 64 | (if available) 65 | * The string "minimum" indicates the control should be reset to its minimum value(s) 66 | (if available) 67 | * The string "maximum" indicates the control should be reset to its maximum value(s) 68 | (if available) 69 | 70 | * Multi-component controls must provide a list of per-component values. The values may 71 | be specified either in the same sequence as shown by the -S/--show-control, or by naming 72 | each value. For example, the "pan-tilt-abs" control has two components, "pan" and 73 | "tilt" (in that order), so the following are equivalent: 74 | 75 | -s pan-tilt-abs="{-3600, 36000}" 76 | -s pan-tilt-abs="{tilt=0.52778, pan=-3600}" 77 | 78 | * Single-value controls should not use the brace notation, just the component value of the 79 | control, for example: 80 | 81 | -s brightness=0.5 82 | 83 | * Component values may be provided as fractional values (in the range [0,1]) if the control 84 | provides a value range (can be checked using -S/--show-control). The value "0.0" 85 | corresponds to the minimum, "1.0" to the maximum. 86 | 87 | * Component values may use the strings "default," "minimum," or "maximum" to indicate that 88 | the component's default, minimum, or maximum value should be used (if the control provides one, 89 | can be checked using -S/--show-control) 90 | 91 | -s pan-tilt-abs="{default,minimum}" 92 | -s pan-tilt-abs="{tilt=-648000,pan=default}" 93 | 94 | Methods for selecting the target device: 95 | 96 | -0 97 | --select-none 98 | 99 | Drop the selected target device 100 | 101 | -I 102 | --select-by-index= 103 | 104 | Index of the device in the list of all devices (zero-based) 105 | 106 | -V : 107 | --select-by-vendor-and-product-id=: 108 | 109 | Provide the hexadecimal- or integer-valued vendor and product identifier 110 | (Prefix hexadecimal values with "0x") 111 | 112 | -L 113 | --select-by-location-id= 114 | 115 | Provide the hexadecimal- or integer-valued USB locationID attribute 116 | (Prefix hexadecimal values with "0x") 117 | 118 | -N 119 | --select-by-name= 120 | 121 | Provide the USB product name (e.g. "AV.io HDMI Video") 122 | 123 | ~~~~ 124 | 125 | ## Build & Run 126 | 127 | The source package includes an XCode project file in the top-level directory. As time goes by — and more releases of XCode are made by Apple — any guarantee of compatibility decreases toward zero. 128 | 129 | As an alternative, the code can be built from the command line after XCode has been installed using the `gcc` command it installs on the system. From the `src` subdirectory of this project: 130 | 131 | ~~~~ 132 | gcc -o uvc-util -framework IOKit -framework Foundation uvc-util.m UVCController.m UVCType.m UVCValue.m 133 | ~~~~ 134 | 135 | The executable will be produced in the working directory and can be tested using 136 | 137 | ~~~~ 138 | ./uvc-util --list-devices 139 | ~~~~ 140 | -------------------------------------------------------------------------------- /src/UVCType.h: -------------------------------------------------------------------------------- 1 | // 2 | // UVCType.h 3 | // 4 | // Abstract data types for UVC controls. 5 | // 6 | // Copyright © 2016 7 | // Dr. Jeffrey Frey, IT-NSS 8 | // University of Delaware 9 | // 10 | // $Id$ 11 | // 12 | 13 | #import 14 | 15 | /*! 16 | @typedef UVCTypeComponentType 17 | 18 | Enumerates the atomic data types that the UVCType class implements. 19 | These are underlying types used by the UVC standard in control 20 | interfaces. 21 | */ 22 | typedef enum { 23 | kUVCTypeComponentTypeInvalid = 0, 24 | kUVCTypeComponentTypeBoolean, 25 | kUVCTypeComponentTypeSInt8, 26 | kUVCTypeComponentTypeUInt8, 27 | kUVCTypeComponentTypeBitmap8, 28 | kUVCTypeComponentTypeSInt16, 29 | kUVCTypeComponentTypeUInt16, 30 | kUVCTypeComponentTypeBitmap16, 31 | kUVCTypeComponentTypeSInt32, 32 | kUVCTypeComponentTypeUInt32, 33 | kUVCTypeComponentTypeBitmap32, 34 | kUVCTypeComponentTypeSInt64, 35 | kUVCTypeComponentTypeUInt64, 36 | kUVCTypeComponentTypeBitmap64, 37 | kUVCTypeComponentTypeMax 38 | } UVCTypeComponentType; 39 | 40 | /*! 41 | @function UVCTypeComponentByteSize 42 | 43 | Returns the number of bytes occupied by the given componentType or 44 | zero (0) if componentType was invalid. 45 | */ 46 | NSUInteger UVCTypeComponentByteSize(UVCTypeComponentType componentType); 47 | 48 | /*! 49 | @defined UVCTypeInvalidIndex 50 | 51 | Constant returned by UVCType to indicate that a field index was out 52 | of range. 53 | */ 54 | #define UVCTypeInvalidIndex NSUIntegerMax 55 | 56 | /*! 57 | @typedef UVCTypeScanFlags 58 | 59 | Enumerates bitmask components that alter the behavior of the scanCString:* 60 | methods of UVCType. 61 | 62 | The kUVCTypeScanFlagShowWarnings flag allows warning messages to be 63 | written to stderr as the routines parse a cString. Additionallly, the 64 | kUVCTypeScanFlagShowInfo flag produces more extensive output to stderr 65 | as the cString is processed (more like debugging information). 66 | */ 67 | typedef enum { 68 | kUVCTypeScanFlagShowWarnings = 1 << 0, 69 | kUVCTypeScanFlagShowInfo = 1 << 1 70 | } UVCTypeScanFlags; 71 | 72 | /*! 73 | @class UVCType 74 | @abstract Abstract data type comprised of UVCTypeComponentType atomic types 75 | 76 | Instances of the UVCType class represent the structured data brokered by 77 | UVC controls. A UVCType comprises one or more named data structure fields in 78 | a specific order, with each having an atomic type from the 79 | UVCTypeComponentType enumeration. 80 | 81 | The set of fields correlate to a packed C struct without word boundary padding; 82 | this also correlates directly to the format of UVC control data. 83 | 84 | In case this code were to be compiled on a big-endian host, byte-swapping 85 | routines are included which can reorder an external buffer (containing the 86 | UVC control data structured by the UVCType) to and from USB (little) endian. 87 | 88 | Methods are included to calculate relative byte offsets of the component fields, 89 | either by index of the name of the field. Also, the number of bytes occupied 90 | by the UVCType is available via the byteSize method. 91 | 92 | Methods are also provided to initialize an external buffer structured by a 93 | UVCType using textual input (from a C string). 94 | */ 95 | @interface UVCType : NSObject 96 | { 97 | NSUInteger _fieldCount; 98 | BOOL _needsNoByteSwap; 99 | void *_fields; 100 | } 101 | 102 | /*! 103 | @method uvcTypeWithCString: 104 | 105 | Returns an autoreleased instance of UVCType initialized with the component field(s) 106 | described by a C string. The C string must begin and end with curly braces and 107 | include one or more named types. Each named type follows the syntax: 108 | 109 | [type] [name]; 110 | 111 | where [name] is the component name (alphanumeric characters and '-') and the [type] 112 | is one of: 113 | 114 | [type] description 115 | B UInt8, accepting only 0 and 1 116 | S1 SInt8 / char 117 | U1 UInt8 / unsigned char 118 | M1 UInt8 / unsigned char as a bitmap 119 | S2 SInt16 / short 120 | U2 UInt16 / unsigned short 121 | M2 UInt16 / unsigned short as a bitmap 122 | S4 SInt32 / int 123 | U4 UInt32 / unsigned int 124 | M4 UInt32 / unsigned int as a bitmap 125 | S8 SInt64 / long long int 126 | U8 UInt64 / unsigned long long int 127 | M8 UInt64 / unsigned long long int as a bitmap 128 | 129 | For types with a single field, the [name] can be omitted: 130 | 131 | {S2} 132 | 133 | Other examples: 134 | 135 | { S2 pan; S2 tilt; } 136 | 137 | */ 138 | + (UVCType*) uvcTypeWithCString:(const char*)typeDescription; 139 | 140 | /*! 141 | @method uvcTypeWithFieldNamesAndTypes: 142 | 143 | Returns an autoreleased instance of UVCType initialized with an arbitrary length sequence 144 | of component field names and types, followed by nil or NULL. For example: 145 | 146 | [uvcTypeWithFieldNamesAndTypes:@"pan", kUVCTypeComponentTypeSInt16, @"tilt", kUVCTypeComponentTypeSInt16, nil]; 147 | 148 | */ 149 | + (UVCType*) uvcTypeWithFieldNamesAndTypes:(NSString*)name,...; 150 | 151 | /*! 152 | @method uvcTypeWithFieldCount:names:types: 153 | 154 | Returns an autoreleased instance of UVCType initialized using two C arrays of component 155 | field names (as NSString instances) and types. The arrays must have at least count elements 156 | present. 157 | */ 158 | + (UVCType*) uvcTypeWithFieldCount:(NSUInteger)count names:(NSString**)names types:(UVCTypeComponentType*)types; 159 | 160 | /*! 161 | @method fieldCount 162 | 163 | Returns the number of component fields in the structure represented by the receiver. 164 | */ 165 | - (NSUInteger) fieldCount; 166 | 167 | /*! 168 | @method fieldNameAtIndex: 169 | 170 | Returns the name associated with the component field at the given index. 171 | 172 | Returns nil if index is out of range. 173 | */ 174 | - (NSString*) fieldNameAtIndex:(NSUInteger)index; 175 | 176 | /*! 177 | @method fieldTypeAtIndex: 178 | 179 | Returns the type associated with the component field at the given index. 180 | 181 | Returns kUVCTypeComponentTypeInvalid if index is out of range. 182 | */ 183 | - (UVCTypeComponentType) fieldTypeAtIndex:(NSUInteger)index; 184 | 185 | /*! 186 | @method indexOfFieldWithName: 187 | 188 | If one of the receiver's component fields is named the same as fieldName (under a case-insensitive 189 | string comparison) returns the index of that field. Otherwise, UVCTypeInvalidIndex is returned. 190 | */ 191 | - (NSUInteger) indexOfFieldWithName:(NSString*)fieldName; 192 | 193 | /*! 194 | @method byteSize 195 | 196 | Returns the number of bytes that data structured according to the receiver's component field 197 | types would occupy. 198 | */ 199 | - (NSUInteger) byteSize; 200 | 201 | /*! 202 | @method offsetToFieldAtIndex: 203 | 204 | Returns the relative offset (in bytes) at which the given component field would be found in a 205 | buffer structured according to the receiver's component field types. 206 | 207 | Returns UVCTypeInvalidIndex if index is out of range. 208 | */ 209 | - (NSUInteger) offsetToFieldAtIndex:(NSUInteger)index; 210 | 211 | /*! 212 | @method offsetToFieldWithName: 213 | 214 | Returns the relative offset (in bytes) at which the given component field (identified by 215 | case-insensitive string comparison against fieldName) would be found in a buffer structured 216 | according to the receiver's component field types. 217 | 218 | Returns UVCTypeInvalidIndex if index is out of range. 219 | */ 220 | - (NSUInteger) offsetToFieldWithName:(NSString*)fieldName; 221 | 222 | /*! 223 | @method byteSwapHostToUSBEndian: 224 | 225 | Given an external buffer structured according to the receiver's component field types, 226 | byte swap all necessary component fields (anything larger than 1 byte) from the host endian 227 | to USB (little) endian. 228 | */ 229 | - (void) byteSwapHostToUSBEndian:(void*)buffer; 230 | 231 | /*! 232 | @method byteSwapUSBToHostEndian: 233 | 234 | Given an external buffer structured according to the receiver's component field types, 235 | byte swap all necessary component fields (anything larger than 1 byte) from USB (little) 236 | endian to host endian. 237 | */ 238 | - (void) byteSwapUSBToHostEndian:(void*)buffer; 239 | 240 | /*! 241 | @method scanCString:intoBuffer:flags: 242 | 243 | Convenience method that calls 244 | 245 | [self scanCString:cString intoBuffer:buffer flags:flags minimum:NULL maximum:NULL stepSize:NULL defaultValue:NULL] 246 | */ 247 | - (BOOL) scanCString:(const char*)cString intoBuffer:(void*)buffer flags:(UVCTypeScanFlags)flags; 248 | 249 | /*! 250 | @method scanCString:intoBuffer:flags:minimum:maximum: 251 | 252 | Convenience method that calls 253 | 254 | [self scanCString:cString intoBuffer:buffer flags:flags minimum:minimum maximum:maximum stepSize:NULL defaultValue:NULL] 255 | */ 256 | - (BOOL) scanCString:(const char*)cString intoBuffer:(void*)buffer flags:(UVCTypeScanFlags)flags minimum:(void*)minimum maximum:(void*)maximum; 257 | 258 | /*! 259 | @method scanCString:intoBuffer:flags:minimum:maximum:stepSize: 260 | 261 | Convenience method that calls 262 | 263 | [self scanCString:cString intoBuffer:buffer flags:flags minimum:minimum maximum:maximum stepSize:stepSize defaultValue:NULL] 264 | */ 265 | - (BOOL) scanCString:(const char*)cString intoBuffer:(void*)buffer flags:(UVCTypeScanFlags)flags minimum:(void*)minimum maximum:(void*)maximum stepSize:(void*)stepSize; 266 | 267 | /*! 268 | @method scanCString:intoBuffer:flags:minimum:maximum:stepSize:defaultValue: 269 | 270 | The arguments buffer, minimum, maximum, stepSize, and defaultValue are all assumed to be at least as 271 | large as the receiver's [self byteSize] method would return. If any of minimum, maximum, stepSize, or 272 | defaultValue are non-NULL, they are assumed to contain the corresponding limit data read from the UVC 273 | device. 274 | 275 | Parses cString and attempts to fill-in the provided buffer according to the receiver's component fields. 276 | The component values must be contained within curly braces, and values should be delimited using a 277 | comma. Whitespace is permissible around words and commas. 278 | 279 | Use of a floating-point (fractional) value for a component requires that minimum and maximum are non-NULL. 280 | The fractional value maps to the correponding value in that range, with 0.0f being the minimum. 281 | 282 | The words "default," "minimum," and "maximum" are permissible on components so long as minimum, 283 | maximum, or defaultValue are non-NULL. 284 | 285 | Receivers with a single component may omit the curly braces. 286 | 287 | If the words "default," "minimum," or "maximum" are the entirety of cString, then the corresponding 288 | value is set for all component fields, provided the corresponding pointer argument is non-NULL. 289 | 290 | The flags bitmask controls this function's behavior. At this time, the only flags present enable 291 | informative output to stderr as the parsing progresses. 292 | 293 | Returns YES if all component fields of the receiver were successfully set in the provided buffer. 294 | 295 | Examples: 296 | 297 | Type Value 298 | {S2} "25" 299 | {S2} "{=25}" (same as previous) 300 | {S2 x; S2 y;} "{0,1}" 301 | {S2 x; S2 y;} "{y=1,x=0}" (same as previous) 302 | {S2 w; S2 h;} "{h=minimum,w=default}" (provided minimum and defaultValue are non-NULL) 303 | {S2 w; S2 h;} "{default,minimum}" (same as previous) 304 | 305 | */ 306 | - (BOOL) scanCString:(const char*)cString intoBuffer:(void*)buffer flags:(UVCTypeScanFlags)flags minimum:(void*)minimum maximum:(void*)maximum stepSize:(void*)stepSize defaultValue:(void*)defaultValue; 307 | 308 | /*! 309 | @method stringFromBuffer: 310 | 311 | Create a formatted textual description of the data in the external buffer structured according to 312 | the receiver's component field types. E.g. 313 | 314 | "{pan=3600,tilt=-360000}" 315 | 316 | */ 317 | - (NSString*) stringFromBuffer:(void*)buffer; 318 | 319 | /*! 320 | @method typeSummaryString 321 | 322 | Returns a human-readable description of the receiver's component field types. 323 | */ 324 | - (NSString*) typeSummaryString; 325 | 326 | @end 327 | -------------------------------------------------------------------------------- /src/UVCController.h: -------------------------------------------------------------------------------- 1 | // 2 | // UVCController.h 3 | // 4 | // USB Video Class (UVC) interface to UVC-compatible video devices. 5 | // 6 | // Copyright © 2016 7 | // Dr. Jeffrey Frey, IT-NSS 8 | // University of Delaware 9 | // 10 | // $Id$ 11 | // 12 | 13 | #import 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #import "UVCValue.h" 21 | 22 | // 23 | // Forward-declare the UVCControl class: 24 | // 25 | @class UVCControl; 26 | 27 | /*! 28 | @class UVCController 29 | @abstract USB Video Class (UVC) device control wrapper. 30 | 31 | An instance of this class is used to interact with the software controls on a 32 | USB video capture device. 33 | 34 | The class performs extensive checking of the USB device when an instance is 35 | instantiated. The vendor- and product-id; USB location id; interface index; 36 | version of the UVC specification implemented; and the control enablement bit 37 | vectors are all explored and retained when available. 38 | */ 39 | @interface UVCController : NSObject 40 | { 41 | NSString *_deviceName; 42 | UInt32 _locationId; 43 | UInt16 _vendorId, _productId; 44 | 45 | // All necessary functionality comes from USB standard 2.2.0: 46 | IOUSBInterfaceInterface220 **_controllerInterface; 47 | 48 | BOOL _isInterfaceOpen; 49 | BOOL _shouldNotCloseInterface; 50 | uint8_t _videoInterfaceIndex; 51 | NSMutableDictionary *_controls; 52 | NSMutableDictionary *_unitIds; 53 | UInt16 _uvcVersion; 54 | NSData *_terminalControlsAvailable; 55 | NSData *_processingUnitControlsAvailable; 56 | } 57 | 58 | /*! 59 | @method uvcControllers 60 | 61 | Scan the USB bus and locate all video devices that appear to be UVC-compliant. 62 | Returns an NSArray containing all such devices, or nil if no devices were 63 | present. 64 | */ 65 | + (NSArray*) uvcControllers; 66 | 67 | /*! 68 | @method uvcControllerWithService: 69 | 70 | Returns an autoreleased instance of the class which wraps the given device from 71 | the I/O Registry. The caller retains ownership of the reference ioService and is 72 | responsible for releasing it. 73 | 74 | If the device referenced by ioService is not UVC-compliant, nil is returned. 75 | */ 76 | + (id) uvcControllerWithService:(io_service_t)ioService; 77 | 78 | /*! 79 | @method uvcControllerWithLocationId: 80 | 81 | Attempts to locate a USB device with the given locationID property. If the 82 | device is found (and appears to be UVC-compliant) an autoreleased instance of 83 | the class is returned. Otherwise, nil is returned. 84 | 85 | Note that the locationID should uniquely identify a single device. 86 | */ 87 | + (id) uvcControllerWithLocationId:(UInt32)locationId; 88 | 89 | /*! 90 | @method uvcControllerWithVendorId:productId: 91 | 92 | Attempts to locate a USB device with the given vendor and product identifier 93 | properties. If a device is found (and appears to be UVC-compliant) an autoreleased 94 | instance of the class is returned. Otherwise, nil is returned. 95 | 96 | Note that this merely chooses the first USB device found in the I/O Registry with 97 | the given vendor and product identifier. If there are multiple devices, the 98 | uvcControllers method should be used to retrieve an array of all UVC-compliant 99 | devices. 100 | */ 101 | + (id) uvcControllerWithVendorId:(UInt16)vendorId productId:(UInt16)productId; 102 | 103 | /*! 104 | @method deviceName 105 | 106 | Returns the name of the USB device. 107 | */ 108 | - (NSString*) deviceName; 109 | 110 | /*! 111 | @method locationId 112 | 113 | Returns the 32-bit USB locationId of the device on this system. 114 | */ 115 | - (UInt32) locationId; 116 | 117 | /*! 118 | @method vendorId 119 | 120 | Returns the 16-bit USB vendor identifier for the device. 121 | */ 122 | - (UInt16) vendorId; 123 | 124 | /*! 125 | @method productId 126 | 127 | Returns the 16-bit USB product identifier for the device. 128 | */ 129 | - (UInt16) productId; 130 | 131 | /*! 132 | @method uvcVersion 133 | 134 | Returns the version of the UVC specification which the device 135 | implements (as a binary-coded decimal value, e.g. 0x0210 = 2.10). 136 | */ 137 | - (UInt16) uvcVersion; 138 | 139 | /*! 140 | @method isInterfaceOpen 141 | 142 | Returns YES if the device interface is open. The interface must be 143 | open in order to send/receive control requests. 144 | */ 145 | - (BOOL) isInterfaceOpen; 146 | 147 | /*! 148 | @method setIsInterfaceOpen: 149 | 150 | Force the device interface into an open- or closed-state. 151 | */ 152 | - (void) setIsInterfaceOpen:(BOOL)isInterfaceOpen; 153 | 154 | /*! 155 | @method controlStrings 156 | 157 | Returns the array of all control names to which this class responds. 158 | */ 159 | + (NSArray*) controlStrings; 160 | 161 | /*! 162 | @method controlStrings 163 | 164 | Returns the array of all control names to which this class responds. 165 | */ 166 | - (NSArray*) controlStrings; 167 | 168 | /*! 169 | @method controlWithName: 170 | 171 | Attempt to retrieve a UVCControl wrapper for the given controlName. If 172 | the receiver has previously instantiated the control, the cached copy is 173 | returned. If not, the capability data pulled from the device is 174 | consulted (if it exists) to determine whether or not the control is 175 | available. If it is (or the device returned no such capability 176 | information) a new UVCControl object is instantiated. If successfully 177 | instantiated, the new control is cached and returned to the caller. 178 | */ 179 | - (UVCControl*) controlWithName:(NSString*)controlName; 180 | 181 | @end 182 | 183 | /*! 184 | @typedef uvc_capabilities_t 185 | 186 | Flags used internally by UVCControl to indicate what operations/fields the 187 | control supports. 188 | */ 189 | typedef NSUInteger uvc_capabilities_t; 190 | 191 | /*! 192 | @class UVCControl 193 | @abstract Wrapper for individual UVC controls. 194 | 195 | Each instance of UVCController manages a collection of UVC controls that the device 196 | has available. Each control is represented by an instance of the UVCControl 197 | class, which abstracts the control meta-data and interaction with the control. 198 | */ 199 | @interface UVCControl : NSObject 200 | { 201 | UVCController *_parentController; 202 | NSUInteger _controlIndex; 203 | NSString *_controlName; 204 | uvc_capabilities_t _capabilities; 205 | UVCValue *_currentValue; 206 | UVCValue *_minimum, *_maximum, *_stepSize; 207 | UVCValue *_defaultValue; 208 | } 209 | 210 | /*! 211 | @method supportsGetValue 212 | 213 | Returns YES if the value of this control can be read. 214 | */ 215 | - (BOOL) supportsGetValue; 216 | 217 | /*! 218 | @method supportsSetValue 219 | 220 | Returns YES if the value of this control can be modified. 221 | */ 222 | - (BOOL) supportsSetValue; 223 | 224 | /*! 225 | @method hasRange 226 | 227 | Returns YES if this control provides a range for its values. 228 | */ 229 | - (BOOL) hasRange; 230 | 231 | /*! 232 | @method hasStepSize 233 | 234 | Returns YES if this control provides a step-size (resolution) 235 | for its values. 236 | */ 237 | - (BOOL) hasStepSize; 238 | 239 | /*! 240 | @method hasDefaultValue 241 | 242 | Returns YES if this control provides a default value. 243 | */ 244 | - (BOOL) hasDefaultValue; 245 | 246 | /*! 247 | @method controlName 248 | 249 | Returns the textual name of the control. This is the same string used 250 | to reference the control is the controlWithName: method of UVCController. 251 | */ 252 | - (NSString*) controlName; 253 | 254 | /*! 255 | @method currentValue 256 | 257 | Attempts to read the current value of the control from the device. If 258 | successful, the returned reference to the receiver's UVCValue object 259 | contains the current value. 260 | 261 | Returns nil if the control could not be read. 262 | 263 | The return 264 | */ 265 | - (UVCValue*) currentValue; 266 | 267 | /*! 268 | @method minimum 269 | 270 | Returns the minimum value(s) provided by the device for the receiver control 271 | or nil if the device provided no minimum. 272 | */ 273 | - (UVCValue*) minimum; 274 | 275 | /*! 276 | @method maximum 277 | 278 | Returns the maximum value(s) provided by the device for the receiver control 279 | or nil if the device provided no maximum. 280 | */ 281 | - (UVCValue*) maximum; 282 | 283 | /*! 284 | @method stepSize 285 | 286 | Returns the step size (resolution) value(s) provided by the device for the 287 | receiver control or nil if the device provided no step size. 288 | */ 289 | - (UVCValue*) stepSize; 290 | 291 | /*! 292 | @method defaultValue 293 | 294 | Returns the default value(s) provided by the device for the receiver control 295 | or nil if the device provided no defaults. 296 | */ 297 | - (UVCValue*) defaultValue; 298 | 299 | /*! 300 | @method resetToDefaultValue 301 | 302 | If the receiver control has a default value (provided by the device) attempt 303 | to set the control to the defaults. 304 | 305 | Returns YES if a default value was present and was successfully written to 306 | the device. 307 | */ 308 | - (BOOL) resetToDefaultValue; 309 | 310 | /*! 311 | @method setCurrentValueFromCString:flags: 312 | 313 | Attempts to parse cString using the receiver's native UVCType, filling-in 314 | the currentValue UVCValue object with the parsed values. See the UVCType 315 | documentation for a description of the acceptable formats, etc. 316 | 317 | Returns YES if currentValue was successfully set. 318 | */ 319 | - (BOOL) setCurrentValueFromCString:(const char*)cString flags:(UVCTypeScanFlags)flags; 320 | 321 | /*! 322 | @method readIntoCurrentValue 323 | 324 | Attempts to read the receiver control's value from the device, storing the 325 | value in the receiver's UVCValue object. The UVCValue object can be accessed 326 | using the currentValue method. 327 | 328 | Returns YES if successful. 329 | */ 330 | - (BOOL) readIntoCurrentValue; 331 | 332 | /*! 333 | @method writeFromCurrentValue 334 | 335 | Attempts to write the value stored in the receiver's UVCValue object to the receiver 336 | control on the device. The UVCValue object can be accessed using the currentValue 337 | method; its data buffer can be modified by external software agents prior to calling 338 | this method. 339 | 340 | Returns YES if successful. 341 | */ 342 | - (BOOL) writeFromCurrentValue; 343 | 344 | /*! 345 | @method summaryString 346 | 347 | Returns an autorelease string that summarizes the structure and attributes of 348 | the receiver control; should be adequately human-readable. 349 | */ 350 | - (NSString*) summaryString; 351 | 352 | @end 353 | 354 | // 355 | // Control names, Terminal 356 | // 357 | FOUNDATION_EXPORT NSString *UVCTerminalControlScanningMode; 358 | FOUNDATION_EXPORT NSString *UVCTerminalControlAutoExposureMode; 359 | FOUNDATION_EXPORT NSString *UVCTerminalControlAutoExposurePriority; 360 | FOUNDATION_EXPORT NSString *UVCTerminalControlExposureTimeAbsolute; 361 | FOUNDATION_EXPORT NSString *UVCTerminalControlExposureTimeRelative; 362 | FOUNDATION_EXPORT NSString *UVCTerminalControlFocusAbsolute; 363 | FOUNDATION_EXPORT NSString *UVCTerminalControlFocusRelative; 364 | FOUNDATION_EXPORT NSString *UVCTerminalControlAutoFocus; 365 | FOUNDATION_EXPORT NSString *UVCTerminalControlIrisAbsolute; 366 | FOUNDATION_EXPORT NSString *UVCTerminalControlIrisRelative; 367 | FOUNDATION_EXPORT NSString *UVCTerminalControlZoomAbsolute; 368 | FOUNDATION_EXPORT NSString *UVCTerminalControlZoomRelative; 369 | FOUNDATION_EXPORT NSString *UVCTerminalControlPanTiltAbsolute; 370 | FOUNDATION_EXPORT NSString *UVCTerminalControlPanTiltRelative; 371 | FOUNDATION_EXPORT NSString *UVCTerminalControlRollAbsolute; 372 | FOUNDATION_EXPORT NSString *UVCTerminalControlRollRelative; 373 | FOUNDATION_EXPORT NSString *UVCTerminalControlFocusAuto; 374 | FOUNDATION_EXPORT NSString *UVCTerminalControlPrivacy; 375 | FOUNDATION_EXPORT NSString *UVCTerminalControlFocusSimple; 376 | FOUNDATION_EXPORT NSString *UVCTerminalControlWindow; 377 | FOUNDATION_EXPORT NSString *UVCTerminalControlRegionOfInterest; 378 | 379 | // 380 | // Control names, Processing Unit 381 | // 382 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlBacklightCompensation; 383 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlBrightness; 384 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlContrast; 385 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlGain; 386 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlPowerLineFrequency; 387 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlHue; 388 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlSaturation; 389 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlSharpness; 390 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlGamma; 391 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlWhiteBalanceTemperature; 392 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlAutoWhiteBalanceTemperature; 393 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlWhiteBalanceComponent; 394 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlAutoWhiteBalanceComponent; 395 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlDigitalMultiplier; 396 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlDigitalMultiplierLimit; 397 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlAutoHue; 398 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlAnalogVideoStandard; 399 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlAnalogLockStatus; 400 | FOUNDATION_EXPORT NSString *UVCProcessingUnitControlAutoContrast; 401 | -------------------------------------------------------------------------------- /uvc-util.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3B79C7811D245ED5004A5C35 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B79C7801D245ED5004A5C35 /* IOKit.framework */; }; 11 | 3BB7CF8E1D2ED005009D6F42 /* uvc-util.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB7CF871D2ED005009D6F42 /* uvc-util.m */; }; 12 | 3BB7CF8F1D2ED005009D6F42 /* uvc-util.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB7CF871D2ED005009D6F42 /* uvc-util.m */; }; 13 | 3BB7CF901D2ED005009D6F42 /* UVCController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB7CF891D2ED005009D6F42 /* UVCController.m */; }; 14 | 3BB7CF911D2ED005009D6F42 /* UVCController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB7CF891D2ED005009D6F42 /* UVCController.m */; }; 15 | 3BB7CF921D2ED005009D6F42 /* UVCType.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB7CF8B1D2ED005009D6F42 /* UVCType.m */; }; 16 | 3BB7CF931D2ED005009D6F42 /* UVCType.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB7CF8B1D2ED005009D6F42 /* UVCType.m */; }; 17 | 3BB7CF941D2ED005009D6F42 /* UVCValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB7CF8D1D2ED005009D6F42 /* UVCValue.m */; }; 18 | 3BB7CF951D2ED005009D6F42 /* UVCValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB7CF8D1D2ED005009D6F42 /* UVCValue.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXCopyFilesBuildPhase section */ 22 | 3B79C7731D245EAD004A5C35 /* CopyFiles */ = { 23 | isa = PBXCopyFilesBuildPhase; 24 | buildActionMask = 2147483647; 25 | dstPath = /usr/share/man/man1/; 26 | dstSubfolderSpec = 0; 27 | files = ( 28 | ); 29 | runOnlyForDeploymentPostprocessing = 1; 30 | }; 31 | 3B7C3E191D2C384100122FB8 /* CopyFiles */ = { 32 | isa = PBXCopyFilesBuildPhase; 33 | buildActionMask = 2147483647; 34 | dstPath = /usr/share/man/man1/; 35 | dstSubfolderSpec = 0; 36 | files = ( 37 | ); 38 | runOnlyForDeploymentPostprocessing = 1; 39 | }; 40 | /* End PBXCopyFilesBuildPhase section */ 41 | 42 | /* Begin PBXFileReference section */ 43 | 3B79C7751D245EAD004A5C35 /* uvc-util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "uvc-util"; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 3B79C7801D245ED5004A5C35 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = ../../../../System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; 45 | 3B7C3E1B1D2C384100122FB8 /* uvc-util-10_9 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "uvc-util-10_9"; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 3BB7CF871D2ED005009D6F42 /* uvc-util.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "uvc-util.m"; path = "src/uvc-util.m"; sourceTree = SOURCE_ROOT; }; 47 | 3BB7CF881D2ED005009D6F42 /* UVCController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UVCController.h; path = src/UVCController.h; sourceTree = SOURCE_ROOT; }; 48 | 3BB7CF891D2ED005009D6F42 /* UVCController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UVCController.m; path = src/UVCController.m; sourceTree = SOURCE_ROOT; }; 49 | 3BB7CF8A1D2ED005009D6F42 /* UVCType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UVCType.h; path = src/UVCType.h; sourceTree = SOURCE_ROOT; }; 50 | 3BB7CF8B1D2ED005009D6F42 /* UVCType.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UVCType.m; path = src/UVCType.m; sourceTree = SOURCE_ROOT; }; 51 | 3BB7CF8C1D2ED005009D6F42 /* UVCValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UVCValue.h; path = src/UVCValue.h; sourceTree = SOURCE_ROOT; }; 52 | 3BB7CF8D1D2ED005009D6F42 /* UVCValue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UVCValue.m; path = src/UVCValue.m; sourceTree = SOURCE_ROOT; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | 3B79C7721D245EAD004A5C35 /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | 3B79C7811D245ED5004A5C35 /* IOKit.framework in Frameworks */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | 3B7C3E181D2C384100122FB8 /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | /* End PBXFrameworksBuildPhase section */ 72 | 73 | /* Begin PBXGroup section */ 74 | 3B79C76C1D245EAD004A5C35 = { 75 | isa = PBXGroup; 76 | children = ( 77 | 3B79C77F1D245EB7004A5C35 /* Frameworks */, 78 | 3B79C7771D245EAD004A5C35 /* src */, 79 | 3B79C7761D245EAD004A5C35 /* Products */, 80 | ); 81 | sourceTree = ""; 82 | }; 83 | 3B79C7761D245EAD004A5C35 /* Products */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 3B79C7751D245EAD004A5C35 /* uvc-util */, 87 | 3B7C3E1B1D2C384100122FB8 /* uvc-util-10_9 */, 88 | ); 89 | name = Products; 90 | sourceTree = ""; 91 | }; 92 | 3B79C7771D245EAD004A5C35 /* src */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 3BB7CF871D2ED005009D6F42 /* uvc-util.m */, 96 | 3BB7CF881D2ED005009D6F42 /* UVCController.h */, 97 | 3BB7CF891D2ED005009D6F42 /* UVCController.m */, 98 | 3BB7CF8A1D2ED005009D6F42 /* UVCType.h */, 99 | 3BB7CF8B1D2ED005009D6F42 /* UVCType.m */, 100 | 3BB7CF8C1D2ED005009D6F42 /* UVCValue.h */, 101 | 3BB7CF8D1D2ED005009D6F42 /* UVCValue.m */, 102 | ); 103 | name = src; 104 | path = "uvc-util"; 105 | sourceTree = ""; 106 | }; 107 | 3B79C77F1D245EB7004A5C35 /* Frameworks */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 3B79C7801D245ED5004A5C35 /* IOKit.framework */, 111 | ); 112 | name = Frameworks; 113 | sourceTree = ""; 114 | }; 115 | /* End PBXGroup section */ 116 | 117 | /* Begin PBXNativeTarget section */ 118 | 3B79C7741D245EAD004A5C35 /* uvc-util */ = { 119 | isa = PBXNativeTarget; 120 | buildConfigurationList = 3B79C77C1D245EAD004A5C35 /* Build configuration list for PBXNativeTarget "uvc-util" */; 121 | buildPhases = ( 122 | 3B79C7711D245EAD004A5C35 /* Sources */, 123 | 3B79C7721D245EAD004A5C35 /* Frameworks */, 124 | 3B79C7731D245EAD004A5C35 /* CopyFiles */, 125 | ); 126 | buildRules = ( 127 | ); 128 | dependencies = ( 129 | ); 130 | name = "uvc-util"; 131 | productName = "uvc-util"; 132 | productReference = 3B79C7751D245EAD004A5C35 /* uvc-util */; 133 | productType = "com.apple.product-type.tool"; 134 | }; 135 | 3B7C3E1A1D2C384100122FB8 /* uvc-util-10_9 */ = { 136 | isa = PBXNativeTarget; 137 | buildConfigurationList = 3B7C3E1F1D2C384100122FB8 /* Build configuration list for PBXNativeTarget "uvc-util-10_9" */; 138 | buildPhases = ( 139 | 3B7C3E171D2C384100122FB8 /* Sources */, 140 | 3B7C3E181D2C384100122FB8 /* Frameworks */, 141 | 3B7C3E191D2C384100122FB8 /* CopyFiles */, 142 | ); 143 | buildRules = ( 144 | ); 145 | dependencies = ( 146 | ); 147 | name = "uvc-util-10_9"; 148 | productName = "uvc-util-10_9"; 149 | productReference = 3B7C3E1B1D2C384100122FB8 /* uvc-util-10_9 */; 150 | productType = "com.apple.product-type.tool"; 151 | }; 152 | /* End PBXNativeTarget section */ 153 | 154 | /* Begin PBXProject section */ 155 | 3B79C76D1D245EAD004A5C35 /* Project object */ = { 156 | isa = PBXProject; 157 | attributes = { 158 | LastUpgradeCheck = 0730; 159 | ORGANIZATIONNAME = "IT-NSS University of Delaware"; 160 | TargetAttributes = { 161 | 3B79C7741D245EAD004A5C35 = { 162 | CreatedOnToolsVersion = 7.3; 163 | }; 164 | 3B7C3E1A1D2C384100122FB8 = { 165 | CreatedOnToolsVersion = 7.3; 166 | }; 167 | }; 168 | }; 169 | buildConfigurationList = 3B79C7701D245EAD004A5C35 /* Build configuration list for PBXProject "uvc-util" */; 170 | compatibilityVersion = "Xcode 3.2"; 171 | developmentRegion = English; 172 | hasScannedForEncodings = 0; 173 | knownRegions = ( 174 | en, 175 | ); 176 | mainGroup = 3B79C76C1D245EAD004A5C35; 177 | productRefGroup = 3B79C7761D245EAD004A5C35 /* Products */; 178 | projectDirPath = ""; 179 | projectRoot = ""; 180 | targets = ( 181 | 3B79C7741D245EAD004A5C35 /* uvc-util */, 182 | 3B7C3E1A1D2C384100122FB8 /* uvc-util-10_9 */, 183 | ); 184 | }; 185 | /* End PBXProject section */ 186 | 187 | /* Begin PBXSourcesBuildPhase section */ 188 | 3B79C7711D245EAD004A5C35 /* Sources */ = { 189 | isa = PBXSourcesBuildPhase; 190 | buildActionMask = 2147483647; 191 | files = ( 192 | 3BB7CF941D2ED005009D6F42 /* UVCValue.m in Sources */, 193 | 3BB7CF901D2ED005009D6F42 /* UVCController.m in Sources */, 194 | 3BB7CF8E1D2ED005009D6F42 /* uvc-util.m in Sources */, 195 | 3BB7CF921D2ED005009D6F42 /* UVCType.m in Sources */, 196 | ); 197 | runOnlyForDeploymentPostprocessing = 0; 198 | }; 199 | 3B7C3E171D2C384100122FB8 /* Sources */ = { 200 | isa = PBXSourcesBuildPhase; 201 | buildActionMask = 2147483647; 202 | files = ( 203 | 3BB7CF951D2ED005009D6F42 /* UVCValue.m in Sources */, 204 | 3BB7CF911D2ED005009D6F42 /* UVCController.m in Sources */, 205 | 3BB7CF8F1D2ED005009D6F42 /* uvc-util.m in Sources */, 206 | 3BB7CF931D2ED005009D6F42 /* UVCType.m in Sources */, 207 | ); 208 | runOnlyForDeploymentPostprocessing = 0; 209 | }; 210 | /* End PBXSourcesBuildPhase section */ 211 | 212 | /* Begin XCBuildConfiguration section */ 213 | 3B79C77A1D245EAD004A5C35 /* Debug */ = { 214 | isa = XCBuildConfiguration; 215 | buildSettings = { 216 | ALWAYS_SEARCH_USER_PATHS = NO; 217 | CLANG_ANALYZER_NONNULL = YES; 218 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 219 | CLANG_CXX_LIBRARY = "libc++"; 220 | CLANG_ENABLE_MODULES = YES; 221 | CLANG_ENABLE_OBJC_ARC = YES; 222 | CLANG_WARN_BOOL_CONVERSION = YES; 223 | CLANG_WARN_CONSTANT_CONVERSION = YES; 224 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 225 | CLANG_WARN_EMPTY_BODY = YES; 226 | CLANG_WARN_ENUM_CONVERSION = YES; 227 | CLANG_WARN_INT_CONVERSION = YES; 228 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 229 | CLANG_WARN_UNREACHABLE_CODE = YES; 230 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 231 | CODE_SIGN_IDENTITY = "-"; 232 | COPY_PHASE_STRIP = NO; 233 | DEBUG_INFORMATION_FORMAT = dwarf; 234 | ENABLE_STRICT_OBJC_MSGSEND = YES; 235 | ENABLE_TESTABILITY = YES; 236 | GCC_C_LANGUAGE_STANDARD = gnu99; 237 | GCC_DYNAMIC_NO_PIC = NO; 238 | GCC_NO_COMMON_BLOCKS = YES; 239 | GCC_OPTIMIZATION_LEVEL = 0; 240 | GCC_PREPROCESSOR_DEFINITIONS = ( 241 | "DEBUG=1", 242 | "$(inherited)", 243 | ); 244 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 245 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 246 | GCC_WARN_UNDECLARED_SELECTOR = YES; 247 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 248 | GCC_WARN_UNUSED_FUNCTION = YES; 249 | GCC_WARN_UNUSED_VARIABLE = YES; 250 | MACOSX_DEPLOYMENT_TARGET = 10.11; 251 | MTL_ENABLE_DEBUG_INFO = YES; 252 | ONLY_ACTIVE_ARCH = YES; 253 | SDKROOT = macosx; 254 | }; 255 | name = Debug; 256 | }; 257 | 3B79C77B1D245EAD004A5C35 /* Release */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | ALWAYS_SEARCH_USER_PATHS = NO; 261 | CLANG_ANALYZER_NONNULL = YES; 262 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 263 | CLANG_CXX_LIBRARY = "libc++"; 264 | CLANG_ENABLE_MODULES = YES; 265 | CLANG_ENABLE_OBJC_ARC = YES; 266 | CLANG_WARN_BOOL_CONVERSION = YES; 267 | CLANG_WARN_CONSTANT_CONVERSION = YES; 268 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 269 | CLANG_WARN_EMPTY_BODY = YES; 270 | CLANG_WARN_ENUM_CONVERSION = YES; 271 | CLANG_WARN_INT_CONVERSION = YES; 272 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 273 | CLANG_WARN_UNREACHABLE_CODE = YES; 274 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 275 | CODE_SIGN_IDENTITY = "-"; 276 | COPY_PHASE_STRIP = NO; 277 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 278 | ENABLE_NS_ASSERTIONS = NO; 279 | ENABLE_STRICT_OBJC_MSGSEND = YES; 280 | GCC_C_LANGUAGE_STANDARD = gnu99; 281 | GCC_NO_COMMON_BLOCKS = YES; 282 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 283 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 284 | GCC_WARN_UNDECLARED_SELECTOR = YES; 285 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 286 | GCC_WARN_UNUSED_FUNCTION = YES; 287 | GCC_WARN_UNUSED_VARIABLE = YES; 288 | MACOSX_DEPLOYMENT_TARGET = 10.11; 289 | MTL_ENABLE_DEBUG_INFO = NO; 290 | SDKROOT = macosx; 291 | }; 292 | name = Release; 293 | }; 294 | 3B79C77D1D245EAD004A5C35 /* Debug */ = { 295 | isa = XCBuildConfiguration; 296 | buildSettings = { 297 | CLANG_ENABLE_OBJC_ARC = NO; 298 | PRODUCT_NAME = "$(TARGET_NAME)"; 299 | }; 300 | name = Debug; 301 | }; 302 | 3B79C77E1D245EAD004A5C35 /* Release */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | CLANG_ENABLE_OBJC_ARC = NO; 306 | PRODUCT_NAME = "$(TARGET_NAME)"; 307 | }; 308 | name = Release; 309 | }; 310 | 3B7C3E201D2C384100122FB8 /* Debug */ = { 311 | isa = XCBuildConfiguration; 312 | buildSettings = { 313 | CLANG_ENABLE_OBJC_ARC = NO; 314 | CLANG_LINK_OBJC_RUNTIME = NO; 315 | MACOSX_DEPLOYMENT_TARGET = 10.9; 316 | PRODUCT_NAME = "$(TARGET_NAME)"; 317 | SDKROOT = macosx10.9; 318 | }; 319 | name = Debug; 320 | }; 321 | 3B7C3E211D2C384100122FB8 /* Release */ = { 322 | isa = XCBuildConfiguration; 323 | buildSettings = { 324 | CLANG_ENABLE_OBJC_ARC = NO; 325 | CLANG_LINK_OBJC_RUNTIME = NO; 326 | MACOSX_DEPLOYMENT_TARGET = 10.9; 327 | PRODUCT_NAME = "$(TARGET_NAME)"; 328 | SDKROOT = macosx10.9; 329 | }; 330 | name = Release; 331 | }; 332 | /* End XCBuildConfiguration section */ 333 | 334 | /* Begin XCConfigurationList section */ 335 | 3B79C7701D245EAD004A5C35 /* Build configuration list for PBXProject "uvc-util" */ = { 336 | isa = XCConfigurationList; 337 | buildConfigurations = ( 338 | 3B79C77A1D245EAD004A5C35 /* Debug */, 339 | 3B79C77B1D245EAD004A5C35 /* Release */, 340 | ); 341 | defaultConfigurationIsVisible = 0; 342 | defaultConfigurationName = Release; 343 | }; 344 | 3B79C77C1D245EAD004A5C35 /* Build configuration list for PBXNativeTarget "uvc-util" */ = { 345 | isa = XCConfigurationList; 346 | buildConfigurations = ( 347 | 3B79C77D1D245EAD004A5C35 /* Debug */, 348 | 3B79C77E1D245EAD004A5C35 /* Release */, 349 | ); 350 | defaultConfigurationIsVisible = 0; 351 | defaultConfigurationName = Release; 352 | }; 353 | 3B7C3E1F1D2C384100122FB8 /* Build configuration list for PBXNativeTarget "uvc-util-10_9" */ = { 354 | isa = XCConfigurationList; 355 | buildConfigurations = ( 356 | 3B7C3E201D2C384100122FB8 /* Debug */, 357 | 3B7C3E211D2C384100122FB8 /* Release */, 358 | ); 359 | defaultConfigurationIsVisible = 0; 360 | defaultConfigurationName = Release; 361 | }; 362 | /* End XCConfigurationList section */ 363 | }; 364 | rootObject = 3B79C76D1D245EAD004A5C35 /* Project object */; 365 | } 366 | -------------------------------------------------------------------------------- /src/uvc-util.m: -------------------------------------------------------------------------------- 1 | // 2 | // uvc-util.m 3 | // 4 | // The utility program that makes use of the UVCController class to handle 5 | // introspection and interaction with software controls exposed by UVC-compliant 6 | // USB video devices. 7 | // 8 | // Copyright © 2016 9 | // Dr. Jeffrey Frey, IT-NSS 10 | // University of Delaware 11 | // 12 | // $Id$ 13 | // 14 | 15 | #import 16 | #include 17 | 18 | #import "UVCController.h" 19 | #import "UVCValue.h" 20 | 21 | // 22 | 23 | #if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9) 24 | #define UVC_UTIL_COMPAT_VERSION "pre-10.9" 25 | #elif (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10) 26 | #define UVC_UTIL_COMPAT_VERSION "10.9" 27 | #elif (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11) 28 | #define UVC_UTIL_COMPAT_VERSION "10.10" 29 | #else 30 | #define UVC_UTIL_COMPAT_VERSION "10.11" 31 | #endif 32 | 33 | static NumVersion UVCUtilVersion = { 34 | .majorRev = 1, 35 | .minorAndBugRev = 0x20, 36 | .stage = finalStage, 37 | .nonRelRev = 0 38 | }; 39 | 40 | const char* 41 | UVCUtilVersionString(void) 42 | { 43 | static char versionString[64]; 44 | BOOL ready = NO; 45 | 46 | if ( ! ready ) { 47 | const char *format; 48 | 49 | switch ( UVCUtilVersion.stage ) { 50 | 51 | case developStage: 52 | format = "%1$hhd.%2$1hhx.%3$1hhxdev%4$hhd (for Mac OS X %5$s)"; 53 | break; 54 | 55 | case alphaStage: 56 | format = "%1$hhd.%2$1hhx.%3$1hhxa%4$hhd (for Mac OS X %5$s)"; 57 | break; 58 | 59 | case betaStage: 60 | format = "%1$hhd.%2$1hhx.%3$1hhxb%4$hhd (for Mac OS X %5$s)"; 61 | break; 62 | 63 | case finalStage: 64 | format = ( UVCUtilVersion.minorAndBugRev &0xF ) ? "%1$hhd.%2$1hhx.%3$1hhx (for Mac OS X %5$s)" : "%1$hhd.%2$1hhx (for Mac OS X %5$s)"; 65 | break; 66 | 67 | } 68 | snprintf(versionString, sizeof(versionString), format, 69 | UVCUtilVersion.majorRev, 70 | ((UVCUtilVersion.minorAndBugRev & 0xF0) >> 4), 71 | (UVCUtilVersion.minorAndBugRev & 0xF), 72 | UVCUtilVersion.nonRelRev, 73 | UVC_UTIL_COMPAT_VERSION 74 | ); 75 | } 76 | 77 | return (const char*)versionString; 78 | } 79 | 80 | // 81 | 82 | static struct option uvcUtilOptions[] = { 83 | { "list-devices", no_argument, NULL, 'd' }, 84 | { "list-controls", no_argument, NULL, 'c' }, 85 | { "show-control", required_argument, NULL, 'S' }, 86 | { "set", required_argument, NULL, 's' }, 87 | { "get", required_argument, NULL, 'g' }, 88 | { "get-value", required_argument, NULL, 'o' }, 89 | { "reset-all", no_argument, NULL, 'r' }, 90 | { "select-none", no_argument, NULL, '0' }, 91 | { "select-by-vendor-and-product-id", required_argument, NULL, 'V' }, 92 | { "select-by-location-id", required_argument, NULL, 'L' }, 93 | { "select-by-name", required_argument, NULL, 'N' }, 94 | { "select-by-index", required_argument, NULL, 'I' }, 95 | { "keep-running", no_argument, NULL, 'k' }, 96 | { "help", no_argument, NULL, 'h' }, 97 | { "version", no_argument, NULL, 'v' }, 98 | // We don't publish the existence of the --debug/-D flag: 99 | { "debug", no_argument, NULL, 'D' }, 100 | { NULL, 0, NULL, 0 } 101 | }; 102 | 103 | // 104 | 105 | void 106 | usage( 107 | const char *exe 108 | ) 109 | { 110 | printf( 111 | "usage:\n" 112 | "\n" 113 | " %s {options/actions/target selection}\n" 114 | "\n" 115 | " Options:\n" 116 | "\n" 117 | " -h/--help Show this information\n" 118 | " -v/--version Show the version of the program\n" 119 | " -k/--keep-running Continue processing additional actions despite\n" 120 | " encountering errors\n" 121 | "\n" 122 | " Actions:\n" 123 | "\n" 124 | " -d/--list-devices Display a list of all UVC-capable devices\n" 125 | " -c/--list-controls Display a list of UVC controls implemented\n" 126 | "\n" 127 | " Available after a target device is selected:\n" 128 | "\n" 129 | " -c/--list-controls Display a list of UVC controls available for\n" 130 | " the target device\n" 131 | "\n" 132 | " -S (|*) Display available information for the given\n" 133 | " --show-control=(|*) UVC control (or all controls for \"*\"). Component\n" 134 | " fields for multi-component controls, minimum, maximum,\n" 135 | " resolution, and default value when provided:\n" 136 | "\n" 137 | " pan-tilt-abs {\n" 138 | " type-description: {\n" 139 | " signed 32-bit integer pan;\n" 140 | " signed 32-bit integer tilt;\n" 141 | " },\n" 142 | " minimum: {pan=-648000,tilt=-648000}\n" 143 | " maximum: {pan=648000,tilt=648000}\n" 144 | " step-size: {pan=3600,tilt=3600}\n" 145 | " default-value: {pan=0,tilt=0}\n" 146 | " }\n" 147 | "\n" 148 | " -g Get the value of a control.\n" 149 | " --get=\n" 150 | "\n" 151 | " -o Same as -g/--get, but ONLY the value of the control\n" 152 | " --get-value= is displayed (no label)\n" 153 | "\n" 154 | " -s = Set the value of a control; see below for a\n" 155 | " --set== description of \n" 156 | "\n" 157 | " -r/--reset-all Reset all controls with a default value to that value\n" 158 | "\n" 159 | " Specifying for -s/--set:\n" 160 | "\n" 161 | " * The string \"default\" indicates the control should be reset to its default value(s)\n" 162 | " (if available)\n" 163 | 164 | " * The string \"minimum\" indicates the control should be reset to its minimum value(s)\n" 165 | " (if available)\n" 166 | 167 | " * The string \"maximum\" indicates the control should be reset to its maximum value(s)\n" 168 | " (if available)\n" 169 | "\n" 170 | " * Multi-component controls must provide a list of per-component values. The values may\n" 171 | " be specified either in the same sequence as shown by the -S/--show-control, or by naming\n" 172 | " each value. For example, the \"pan-tilt-abs\" control has two components, \"pan\" and\n" 173 | " \"tilt\" (in that order), so the following are equivalent:\n" 174 | "\n" 175 | " -s pan-tilt-abs=\"{-3600, 36000}\"\n" 176 | " -s pan-tilt-abs=\"{tilt=0.52778, pan=-3600}\"\n" 177 | "\n" 178 | " * Single-value controls should not use the brace notation, just the component value of the\n" 179 | " control, for example:\n" 180 | "\n" 181 | " -s brightness=0.5\n" 182 | "\n" 183 | " * Component values may be provided as fractional values (in the range [0,1]) if the control\n" 184 | " provides a value range (can be checked using -S/--show-control). The value \"0.0\"\n" 185 | " corresponds to the minimum, \"1.0\" to the maximum.\n" 186 | "\n" 187 | " * Component values may use the strings \"default,\" \"minimum,\" or \"maximum\" to indicate that\n" 188 | " the component's default, minimum, or maximum value should be used (if the control provides one,\n" 189 | " can be checked using -S/--show-control)\n" 190 | "\n" 191 | " -s pan-tilt-abs=\"{default,minimum}\"\n" 192 | " -s pan-tilt-abs=\"{tilt=-648000,pan=default}\"\n" 193 | "\n" 194 | " Methods for selecting the target device:\n" 195 | "\n" 196 | " -0\n" 197 | " --select-none\n" 198 | "\n" 199 | " Drop the selected target device\n" 200 | "\n" 201 | " -I \n" 202 | " --select-by-index=\n" 203 | "\n" 204 | " Index of the device in the list of all devices (zero-based)\n" 205 | "\n" 206 | " -V :\n" 207 | " --select-by-vendor-and-product-id=:\n" 208 | "\n" 209 | " Provide the hexadecimal- or integer-valued vendor and product identifier\n" 210 | " (Prefix hexadecimal values with \"0x\")\n" 211 | "\n" 212 | " -L \n" 213 | " --select-by-location-id=\n" 214 | "\n" 215 | " Provide the hexadecimal- or integer-valued USB locationID attribute\n" 216 | " (Prefix hexadecimal values with \"0x\")\n" 217 | "\n" 218 | " -N \n" 219 | " --select-by-name=\n" 220 | "\n" 221 | " Provide the USB product name (e.g. \"AV.io HDMI Video\")\n" 222 | "\n", 223 | exe 224 | ); 225 | } 226 | 227 | // 228 | 229 | UVCController* 230 | UVCUtilGetControllerWithName( 231 | NSArray *uvcDevices, 232 | NSString *name 233 | ) 234 | { 235 | NSEnumerator *eDevices = [uvcDevices objectEnumerator]; 236 | UVCController *controller; 237 | 238 | while ( (controller = [eDevices nextObject]) ) { 239 | if ( [name compare:[controller deviceName] options:NSCaseInsensitiveSearch] == NSOrderedSame ) return controller; 240 | } 241 | return nil; 242 | } 243 | 244 | // 245 | 246 | UVCController* 247 | UVCUtilGetControllerWithVendorAndProductId( 248 | NSArray *uvcDevices, 249 | unsigned short vendorId, 250 | unsigned short productId 251 | ) 252 | { 253 | NSEnumerator *eDevices = [uvcDevices objectEnumerator]; 254 | UVCController *controller; 255 | 256 | while ( (controller = [eDevices nextObject]) ) { 257 | if ( ([controller vendorId] == vendorId) && ([controller productId] == productId) ) return controller; 258 | } 259 | return nil; 260 | } 261 | 262 | // 263 | 264 | UVCController* 265 | UVCUtilGetControllerWithLocationId( 266 | NSArray *uvcDevices, 267 | unsigned locationId 268 | ) 269 | { 270 | NSEnumerator *eDevices = [uvcDevices objectEnumerator]; 271 | UVCController *controller; 272 | 273 | while ( (controller = [eDevices nextObject]) ) { 274 | if ( [controller locationId] == locationId ) return controller; 275 | } 276 | return nil; 277 | } 278 | 279 | // 280 | 281 | int 282 | main( 283 | int argc, 284 | char* argv[] 285 | ) 286 | { 287 | const char *exe = argv[0]; 288 | int rc = 0; 289 | NSArray *uvcDevices = nil; 290 | UVCController *targetDevice = nil; 291 | int optCh; 292 | BOOL exitOnErrors = YES; 293 | UVCTypeScanFlags uvcScanFlags = kUVCTypeScanFlagShowWarnings; 294 | 295 | // 296 | // No CLI arguments, we've got nothing to do: 297 | // 298 | if ( argc == 1 ) { 299 | usage(argv[0]); 300 | return 0; 301 | } 302 | 303 | @autoreleasepool { 304 | while ( (optCh = getopt_long(argc, argv, "dcS:s:g:o:r0V:L:N:I:khfFvD", uvcUtilOptions, NULL)) != -1 ) { 305 | switch ( optCh ) { 306 | 307 | case 'h': { 308 | usage(exe); 309 | break; 310 | } 311 | 312 | case 'v': { 313 | printf("%s\n", UVCUtilVersionString()); 314 | printf("Build timestamp %s %s\n", __TIME__, __DATE__); 315 | break; 316 | } 317 | 318 | case 'k': { 319 | exitOnErrors = NO; 320 | break; 321 | } 322 | 323 | case 'D': { 324 | uvcScanFlags |= kUVCTypeScanFlagShowInfo; 325 | break; 326 | } 327 | 328 | case 'd': { 329 | if ( ! uvcDevices ) uvcDevices = [[UVCController uvcControllers] retain]; 330 | if ( uvcDevices && [uvcDevices count] ) { 331 | NSEnumerator *eDevices = [uvcDevices objectEnumerator]; 332 | UVCController *device; 333 | unsigned long deviceIndex = 0; 334 | 335 | printf("------------ -------------- ------------ ------------ ------------------------------------------------\n"); 336 | printf("%-12s %-14s %-12s %-12s %s\n", "Index", "Vend:Prod", "LocationID", "UVC Version", "Device name"); 337 | printf("------------ -------------- ------------ ------------ ------------------------------------------------\n"); 338 | while ( (device = [eDevices nextObject]) ) { 339 | UInt16 uvcVersion = [device uvcVersion]; 340 | char versionStr[8]; 341 | 342 | snprintf(versionStr, sizeof(versionStr), "%d.%02x", (short)(uvcVersion >> 8), (uvcVersion &0xFF)); 343 | printf("%-12lu 0x%04x:0x%04x 0x%08x %-5s %s\n", deviceIndex++, [device vendorId], [device productId], [device locationId], versionStr, [[device deviceName] cStringUsingEncoding:NSASCIIStringEncoding]); 344 | } 345 | printf("------------ -------------- ------------ ------------ ------------------------------------------------\n"); 346 | } else { 347 | fprintf(stderr, "ERROR: no UVC-capable devices available\n"); 348 | rc = ENODEV; 349 | if ( exitOnErrors ) goto cleanupAndExit; 350 | } 351 | break; 352 | } 353 | 354 | case 'c': { 355 | if ( targetDevice ) { 356 | NSArray *controlNames = [UVCController controlStrings]; 357 | 358 | if ( controlNames && [controlNames count] ) { 359 | NSEnumerator *eNames = [controlNames objectEnumerator]; 360 | NSString *name; 361 | 362 | printf("UVC controls implemented by this device:\n"); 363 | while ( (name = [eNames nextObject]) ) { 364 | UVCControl *control = [targetDevice controlWithName:name]; 365 | 366 | if ( control ) printf(" %s\n", [name cStringUsingEncoding:NSASCIIStringEncoding]); 367 | } 368 | } else { 369 | fprintf(stderr, "WARNING: no controls implemented by this device\n"); 370 | } 371 | } else { 372 | NSArray *controlNames = [UVCController controlStrings]; 373 | 374 | if ( controlNames ) { 375 | NSEnumerator *eNames = [controlNames objectEnumerator]; 376 | NSString *name; 377 | 378 | printf("UVC controls implemented by this program:\n"); 379 | while ( (name = [eNames nextObject]) ) printf(" %s\n", [name cStringUsingEncoding:NSASCIIStringEncoding]); 380 | } 381 | } 382 | break; 383 | } 384 | 385 | case '0': { 386 | targetDevice = nil; 387 | break; 388 | } 389 | 390 | case 'I': { 391 | if ( optarg && *optarg ) { 392 | char *endPtr = NULL; 393 | unsigned long deviceIndex = strtoul(optarg, &endPtr, 10); 394 | 395 | if ( endPtr > optarg ) { 396 | if ( ! uvcDevices ) uvcDevices = [[UVCController uvcControllers] retain]; 397 | if ( uvcDevices ) { 398 | if ( deviceIndex < [uvcDevices count] ) { 399 | targetDevice = [uvcDevices objectAtIndex:deviceIndex]; 400 | if ( ! targetDevice ) { 401 | fprintf(stderr, "ERROR: no UVC-capable device with the name \"%s\"\n", optarg); 402 | rc = ENODEV; 403 | if ( exitOnErrors ) goto cleanupAndExit; 404 | } 405 | } else { 406 | fprintf(stderr, "ERROR: invalid device index: %lu\n", deviceIndex); 407 | rc = EINVAL; 408 | if ( exitOnErrors ) goto cleanupAndExit; 409 | } 410 | } else { 411 | fprintf(stderr, "ERROR: no UVC-capable devices available\n"); 412 | rc = ENODEV; 413 | if ( exitOnErrors ) goto cleanupAndExit; 414 | } 415 | } else { 416 | fprintf(stderr, "ERROR: invalid device index: %s\n", optarg); 417 | rc = EINVAL; 418 | if ( exitOnErrors ) goto cleanupAndExit; 419 | } 420 | } else { 421 | fprintf(stderr, "ERROR: missing argument to -I/--select-by-index\n"); 422 | rc = EINVAL; 423 | if ( exitOnErrors ) goto cleanupAndExit; 424 | } 425 | break; 426 | } 427 | 428 | case 'N': { 429 | if ( optarg && *optarg ) { 430 | if ( ! uvcDevices ) uvcDevices = [[UVCController uvcControllers] retain]; 431 | if ( uvcDevices ) { 432 | targetDevice = UVCUtilGetControllerWithName(uvcDevices, [NSString stringWithCString:optarg encoding:NSASCIIStringEncoding]); 433 | if ( ! targetDevice ) { 434 | fprintf(stderr, "ERROR: no UVC-capable device with the name \"%s\"\n", optarg); 435 | rc = ENODEV; 436 | if ( exitOnErrors ) goto cleanupAndExit; 437 | } 438 | } else { 439 | fprintf(stderr, "ERROR: no UVC-capable devices available\n"); 440 | rc = ENODEV; 441 | if ( exitOnErrors ) goto cleanupAndExit; 442 | } 443 | } else { 444 | fprintf(stderr, "ERROR: missing argument to -N/--select-by-name\n"); 445 | rc = EINVAL; 446 | if ( exitOnErrors ) goto cleanupAndExit; 447 | } 448 | break; 449 | } 450 | 451 | case 'V': { 452 | if ( optarg && *optarg ) { 453 | unsigned short vendorId, productId; 454 | int nChar; 455 | 456 | if ( sscanf(optarg, "%hi:%n", &vendorId, &nChar) == 1 ) { 457 | if ( sscanf(optarg + nChar, "%hi", &productId) == 1 ) { 458 | if ( ! uvcDevices ) uvcDevices = [[UVCController uvcControllers] retain]; 459 | if ( uvcDevices && [uvcDevices count] ) { 460 | targetDevice = UVCUtilGetControllerWithVendorAndProductId(uvcDevices, vendorId, productId); 461 | if ( ! targetDevice ) { 462 | fprintf(stderr, "ERROR: no UVC-capable device with vendor:product = 0x%04hx:0x%04hx\n", vendorId, productId); 463 | rc = ENODEV; 464 | if ( exitOnErrors ) goto cleanupAndExit; 465 | } 466 | } else { 467 | fprintf(stderr, "ERROR: no UVC-capable devices available\n"); 468 | rc = ENODEV; 469 | if ( exitOnErrors ) goto cleanupAndExit; 470 | } 471 | } else { 472 | fprintf(stderr, "ERROR: invalid product id: %s\n", optarg + nChar); 473 | rc = EINVAL; 474 | if ( exitOnErrors) goto cleanupAndExit; 475 | } 476 | } else { 477 | fprintf(stderr, "ERROR: invalid vendor id: %s\n", optarg); 478 | rc = EINVAL; 479 | if ( exitOnErrors) goto cleanupAndExit; 480 | } 481 | } else { 482 | fprintf(stderr, "ERROR: missing argument to -V/--select-by-vendor-and-product-id\n"); 483 | rc = EINVAL; 484 | if ( exitOnErrors ) goto cleanupAndExit; 485 | } 486 | break; 487 | } 488 | 489 | case 'L': { 490 | if ( optarg && *optarg ) { 491 | unsigned locationId; 492 | 493 | if ( sscanf(optarg, "%i", &locationId) == 1 ) { 494 | if ( ! uvcDevices ) uvcDevices = [[UVCController uvcControllers] retain]; 495 | if ( uvcDevices && [uvcDevices count] ) { 496 | targetDevice = UVCUtilGetControllerWithLocationId(uvcDevices, locationId); 497 | if ( ! targetDevice ) { 498 | fprintf(stderr, "ERROR: no UVC-capable device with location = 0x%08x\n", locationId); 499 | rc = ENODEV; 500 | if ( exitOnErrors ) goto cleanupAndExit; 501 | } 502 | } else { 503 | fprintf(stderr, "ERROR: no UVC-capable devices available\n"); 504 | rc = ENODEV; 505 | if ( exitOnErrors ) goto cleanupAndExit; 506 | } 507 | } else { 508 | fprintf(stderr, "ERROR: invalid location id: %s\n", optarg); 509 | rc = EINVAL; 510 | if ( exitOnErrors) goto cleanupAndExit; 511 | } 512 | } else { 513 | fprintf(stderr, "ERROR: missing argument to -L/--select-by-location-id\n"); 514 | rc = EINVAL; 515 | if ( exitOnErrors ) goto cleanupAndExit; 516 | } 517 | break; 518 | } 519 | 520 | case 'S': { 521 | if ( targetDevice ) { 522 | if ( optarg && *optarg ) { 523 | if ( (*optarg == '*') && (*(optarg + 1) == '\0') ) { 524 | NSArray *controlNames = [UVCController controlStrings]; 525 | 526 | if ( controlNames && [controlNames count] ) { 527 | NSEnumerator *eNames = [controlNames objectEnumerator]; 528 | NSString *name; 529 | 530 | while ( (name = [eNames nextObject]) ) { 531 | UVCControl *control = [targetDevice controlWithName:name]; 532 | 533 | if ( control ) printf("%s\n", [[control summaryString] cStringUsingEncoding:NSASCIIStringEncoding]); 534 | } 535 | } else { 536 | fprintf(stderr, "WARNING: no controls implemented by this device\n"); 537 | } 538 | } else { 539 | long controlNameLen = strlen(optarg); 540 | 541 | if ( controlNameLen ) { 542 | char controlName[controlNameLen + 1]; 543 | long i = 0; 544 | 545 | while ( i < controlNameLen ) { 546 | controlName[i] = tolower(optarg[i]); 547 | i++; 548 | } 549 | controlName[i] = '\0'; 550 | 551 | UVCControl *control = [targetDevice controlWithName:[NSString stringWithCString:controlName encoding:NSASCIIStringEncoding]]; 552 | 553 | if ( control ) { 554 | printf("%s\n", [[control summaryString] cStringUsingEncoding:NSASCIIStringEncoding]); 555 | } else { 556 | fprintf(stderr, "ERROR: invalid control name: %s\n", controlName); 557 | rc = ENOENT; 558 | if ( exitOnErrors ) goto cleanupAndExit; 559 | } 560 | } 561 | } 562 | } else { 563 | fprintf(stderr, "ERROR: missing argument to -S/--show option\n"); 564 | rc = EINVAL; 565 | if ( exitOnErrors ) goto cleanupAndExit; 566 | } 567 | } else { 568 | fprintf(stderr, "ERROR: no target device selected\n"); 569 | rc = ENODEV; 570 | if ( exitOnErrors ) goto cleanupAndExit; 571 | } 572 | break; 573 | } 574 | 575 | case 'o': 576 | case 'g': { 577 | if ( targetDevice ) { 578 | if ( optarg && *optarg ) { 579 | UVCControl *control = [targetDevice controlWithName:[NSString stringWithCString:optarg encoding:NSASCIIStringEncoding]]; 580 | 581 | if ( control ) { 582 | UVCValue *currentValue = [control currentValue]; 583 | 584 | if ( currentValue ) { 585 | if ( optCh == 'o' ) { 586 | printf("%s\n", [[currentValue stringValue] cStringUsingEncoding:NSASCIIStringEncoding]); 587 | } else { 588 | printf("%s = %s\n", [[control controlName] cStringUsingEncoding:NSASCIIStringEncoding], [[currentValue stringValue] cStringUsingEncoding:NSASCIIStringEncoding]); 589 | } 590 | } else { 591 | fprintf(stderr, "ERROR: unable to read value of control: %s\n", optarg); 592 | rc = EACCES; 593 | if ( exitOnErrors ) goto cleanupAndExit; 594 | } 595 | } else { 596 | fprintf(stderr, "ERROR: invalid control name: %s\n", optarg); 597 | rc = ENOENT; 598 | if ( exitOnErrors ) goto cleanupAndExit; 599 | } 600 | } else { 601 | fprintf(stderr, "ERROR: missing argument to -g/--get/-o/--get-value option\n"); 602 | rc = EINVAL; 603 | if ( exitOnErrors ) goto cleanupAndExit; 604 | } 605 | } else { 606 | fprintf(stderr, "ERROR: no target device selected\n"); 607 | rc = ENODEV; 608 | if ( exitOnErrors ) goto cleanupAndExit; 609 | } 610 | break; 611 | } 612 | 613 | case 'r': { 614 | if ( targetDevice ) { 615 | NSArray *controlNames = [UVCController controlStrings]; 616 | 617 | if ( controlNames && [controlNames count] ) { 618 | NSEnumerator *eNames = [controlNames objectEnumerator]; 619 | NSString *name; 620 | 621 | while ( (name = [eNames nextObject]) ) { 622 | UVCControl *control = [targetDevice controlWithName:name]; 623 | 624 | if ( control && [control hasDefaultValue] ) { 625 | if ( ! [control resetToDefaultValue] ) { 626 | fprintf(stderr, "ERROR: unable to write default value to control %s\n", [name cStringUsingEncoding:NSASCIIStringEncoding]); 627 | rc = EACCES; 628 | if ( exitOnErrors ) goto cleanupAndExit; 629 | } 630 | } 631 | } 632 | } else { 633 | fprintf(stderr, "WARNING: no controls implemented by this device\n"); 634 | } 635 | } else { 636 | fprintf(stderr, "ERROR: no target device selected\n"); 637 | rc = ENODEV; 638 | if ( exitOnErrors ) goto cleanupAndExit; 639 | } 640 | break; 641 | } 642 | 643 | case 's': { 644 | if ( targetDevice ) { 645 | if ( optarg && *optarg ) { 646 | const char *valuePtr = strchr(optarg, '='); 647 | 648 | if ( valuePtr ) { 649 | long controlNameLen = valuePtr - optarg; 650 | 651 | valuePtr++; 652 | if ( controlNameLen ) { 653 | char controlName[controlNameLen + 1]; 654 | long i = 0; 655 | 656 | while ( i < controlNameLen ) { 657 | controlName[i] = tolower(optarg[i]); 658 | i++; 659 | } 660 | controlName[i] = '\0'; 661 | 662 | UVCControl *control = [targetDevice controlWithName:[NSString stringWithCString:controlName encoding:NSASCIIStringEncoding]]; 663 | 664 | if ( control ) { 665 | if ( [control setCurrentValueFromCString:valuePtr flags:uvcScanFlags] ) { 666 | if ( ! [control writeFromCurrentValue] ) { 667 | fprintf(stderr, "ERROR: unable to write new value to control %s\n", controlName); 668 | rc = EACCES; 669 | if ( exitOnErrors ) goto cleanupAndExit; 670 | } 671 | } else { 672 | fprintf(stderr, "ERROR: invalid value for control %s: %s\n", controlName, valuePtr); 673 | rc = EINVAL; 674 | if ( exitOnErrors ) goto cleanupAndExit; 675 | } 676 | } else { 677 | fprintf(stderr, "ERROR: invalid control name: %s\n", controlName); 678 | rc = ENOENT; 679 | if ( exitOnErrors ) goto cleanupAndExit; 680 | } 681 | } else { 682 | fprintf(stderr, "ERROR: missing control name: %s\n", optarg); 683 | rc = EINVAL; 684 | if ( exitOnErrors ) goto cleanupAndExit; 685 | } 686 | } else { 687 | fprintf(stderr, "ERROR: no value provided with control name: %s\n", optarg); 688 | rc = EINVAL; 689 | if ( exitOnErrors ) goto cleanupAndExit; 690 | } 691 | } else { 692 | fprintf(stderr, "ERROR: missing argument to -s/--set option\n"); 693 | rc = EINVAL; 694 | if ( exitOnErrors ) goto cleanupAndExit; 695 | } 696 | } else { 697 | fprintf(stderr, "ERROR: no target device selected\n"); 698 | rc = ENODEV; 699 | if ( exitOnErrors ) goto cleanupAndExit; 700 | } 701 | break; 702 | } 703 | 704 | } 705 | } 706 | } 707 | 708 | cleanupAndExit: 709 | if ( uvcDevices ) [uvcDevices release]; 710 | return rc; 711 | } 712 | -------------------------------------------------------------------------------- /src/UVCController.m: -------------------------------------------------------------------------------- 1 | // 2 | // UVCController.h 3 | // 4 | // USB Video Controller (UVC) interface to UVC-compatible video devices. 5 | // 6 | // Copyright © 2016 7 | // Dr. Jeffrey Frey, IT-NSS 8 | // University of Delaware 9 | // 10 | // $Id$ 11 | // 12 | 13 | //#define DEBUG_WRITE_UVC_HEADER_TO_FILE 14 | 15 | #import "UVCController.h" 16 | 17 | // 18 | // UVC descriptor codes: 19 | // 20 | #define CS_INTERFACE 0x24 21 | 22 | #define VC_HEADER 0x01 23 | #define VC_INPUT_TERMINAL 0x02 24 | #define VC_OUTPUT_TERMINAL 0x03 25 | #define VC_SELECTOR_UNIT 0x04 26 | #define VC_PROCESSING_UNIT 0x05 27 | #define VC_EXTENSION_UNIT 0x06 28 | 29 | // On newer versions of Mac OS X, the kIOMasterPortDefault enum has been 30 | // replaced by kIOMainPortDefault. 31 | #if (MAC_OS_X_VERSION_MAX_ALLOWED < 120000) // Before macOS 12 Monterey 32 | #define kIOMainPortDefault kIOMasterPortDefault 33 | #endif 34 | 35 | // 36 | // UVC descriptor data type definitions: 37 | // 38 | typedef struct { 39 | UInt8 bLength; 40 | UInt8 bDescriptorType; 41 | UInt8 bDescriptorSubType; 42 | } __attribute__((packed)) UVC_Descriptor_Prefix; 43 | 44 | typedef struct { 45 | UInt8 bLength; 46 | UInt8 bDescriptorType; 47 | UInt8 bDescriptorSubType; 48 | UInt16 bcdUVC; 49 | UInt16 wTotalLength; 50 | UInt32 dwClockFrequency; 51 | UInt8 bInCollection; 52 | UInt8 baInterfaceNr1; 53 | } __attribute__((packed)) UVC_VC_Interface_Header_Descriptor; 54 | 55 | typedef struct { 56 | UInt8 bLength; 57 | UInt8 bDescriptorType; 58 | UInt8 bDescriptorSubType; 59 | UInt8 bTerminalId; 60 | UInt16 wTerminalType; 61 | UInt8 bAssocTerminal; 62 | UInt8 iTerminal; 63 | UInt16 wObjectiveFocalLengthMin; 64 | UInt16 wObjectiveFocalLengthMax; 65 | UInt16 wOcularFocalLength; 66 | UInt8 bControlSize; 67 | UInt8 bmControls[]; 68 | } __attribute__((packed)) UVC_VC_Terminal_Header_Descriptor; 69 | 70 | /*! 71 | @enum UVC Terminal control enablement bit values 72 | 73 | The UVC_VC_Terminal_Header_Descriptor contains a variable-length 74 | bmControls field that expresses the controls which that unit 75 | implements. The field is a bitmap, with bit zero (0) being 76 | the least-significant bit in the first byte -- which happens to 77 | correspond to kUVCTerminalControlEnableScanningMode. 78 | */ 79 | enum { 80 | kUVCTerminalControlEnableScanningMode = 0, 81 | kUVCTerminalControlEnableAutoExposureMode = 1, 82 | kUVCTerminalControlEnableAutoExposurePriority = 2, 83 | kUVCTerminalControlEnableExposureTimeAbsolute = 3, 84 | kUVCTerminalControlEnableExposureTimeRelative = 4, 85 | kUVCTerminalControlEnableFocusAbsolute = 5, 86 | kUVCTerminalControlEnableFocusRelative = 6, 87 | kUVCTerminalControlEnableIrisAbsolute = 7, 88 | kUVCTerminalControlEnableIrisRelative = 8, 89 | kUVCTerminalControlEnableZoomAbsolute = 9, 90 | kUVCTerminalControlEnableZoomRelative = 10, 91 | kUVCTerminalControlEnablePanTiltAbsolute = 11, 92 | kUVCTerminalControlEnablePanTiltRelative = 12, 93 | kUVCTerminalControlEnableRollAbsolute = 13, 94 | kUVCTerminalControlEnableRollRelative = 14, 95 | kUVCTerminalControlEnableFocusAuto = 17, 96 | kUVCTerminalControlEnablePrivacy = 18, 97 | kUVCTerminalControlEnableFocusSimple = 19, 98 | kUVCTerminalControlEnableWindow = 20, 99 | kUVCTerminalControlEnableRegionOfInterest = 21 100 | }; 101 | 102 | typedef struct { 103 | UInt8 bLength; 104 | UInt8 bDescriptorType; 105 | UInt8 bDescriptorSubType; 106 | UInt8 bUnitId; 107 | UInt8 bSourceId; 108 | UInt16 wMaxMultiplier; 109 | UInt8 bControlSize; 110 | UInt8 bmControls[]; 111 | } __attribute__((packed)) UVC_PU_Header_Descriptor; 112 | 113 | /*! 114 | @enum UVC Processing Unit control enablement bit values 115 | 116 | The UVC_PU_Header_Descriptor contains a variable-length 117 | bmControls field that expresses the controls which that unit 118 | implements. The field is a bitmap, with bit zero (0) being 119 | the least-significant bit in the first byte -- which happens to 120 | correspond to kUVCProcessingUnitControlEnableBrightness. 121 | */ 122 | enum { 123 | kUVCProcessingUnitControlEnableBrightness = 0, 124 | kUVCProcessingUnitControlEnableContrast = 1, 125 | kUVCProcessingUnitControlEnableHue = 2, 126 | kUVCProcessingUnitControlEnableSaturation = 3, 127 | kUVCProcessingUnitControlEnableSharpness = 4, 128 | kUVCProcessingUnitControlEnableGamma = 5, 129 | kUVCProcessingUnitControlEnableWhiteBalanceTemperature = 6, 130 | kUVCProcessingUnitControlEnableWhiteBalanceComponent = 7, 131 | kUVCProcessingUnitControlEnableBacklightCompensation = 8, 132 | kUVCProcessingUnitControlEnableGain = 9, 133 | kUVCProcessingUnitControlEnablePowerLineFrequency = 10, 134 | kUVCProcessingUnitControlEnableAutoHue = 11, 135 | kUVCProcessingUnitControlEnableAutoWhiteBalanceTemperature = 12, 136 | kUVCProcessingUnitControlEnableAutoWhiteBalanceComponent = 13, 137 | kUVCProcessingUnitControlEnableDigitalMultiplier = 14, 138 | kUVCProcessingUnitControlEnableDigitalMultiplierLimit = 15, 139 | kUVCProcessingUnitControlEnableAnalogVideoStandard = 16, 140 | kUVCProcessingUnitControlEnableAnalogVideoLockStatus = 17, 141 | kUVCProcessingUnitControlEnableAutoContrast = 18 142 | }; 143 | 144 | // 145 | // UVC request opcodes: 146 | // 147 | #define UVC_SET_CUR 0x01 148 | #define UVC_GET_CUR 0x81 149 | #define UVC_GET_MIN 0x82 150 | #define UVC_GET_MAX 0x83 151 | #define UVC_GET_RES 0x84 152 | #define UVC_GET_LEN 0x85 153 | #define UVC_GET_INFO 0x86 154 | #define UVC_GET_DEF 0x87 155 | 156 | // 157 | // Terminal controls: 158 | // 159 | #define UVC_INPUT_TERMINAL_ID 0x01 160 | 161 | #define CT_SCANNING_MODE_CONTROL 0x01 162 | #define CT_AE_MODE_CONTROL 0x02 163 | #define CT_AE_PRIORITY_CONTROL 0x03 164 | #define CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04 165 | #define CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05 166 | #define CT_FOCUS_ABSOLUTE_CONTROL 0x06 167 | #define CT_FOCUS_RELATIVE_CONTROL 0x07 168 | #define CT_FOCUS_AUTO_CONTROL 0x08 169 | #define CT_IRIS_ABSOLUTE_CONTROL 0x09 170 | #define CT_IRIS_RELATIVE_CONTROL 0x0a 171 | #define CT_ZOOM_ABSOLUTE_CONTROL 0x0b 172 | #define CT_ZOOM_RELATIVE_CONTROL 0x0c 173 | #define CT_PANTILT_ABSOLUTE_CONTROL 0x0d 174 | #define CT_PANTILT_RELATIVE_CONTROL 0x0e 175 | #define CT_ROLL_ABSOLUTE_CONTROL 0x0f 176 | #define CT_ROLL_RELATIVE_CONTROL 0x10 177 | #define CT_PRIVACY_CONTROL 0x11 178 | #define CT_FOCUS_SIMPLE_CONTROL 0x12 179 | #define CT_WINDOW_CONTROL 0x13 180 | #define CT_REGION_OF_INTEREST_CONTROL 0x14 181 | 182 | // 183 | // Processing-unit controls: 184 | // 185 | #define UVC_PROCESSING_UNIT_ID 0x02 186 | 187 | #define PU_BACKLIGHT_COMPENSATION_CONTROL 0x01 188 | #define PU_BRIGHTNESS_CONTROL 0x02 189 | #define PU_CONTRAST_CONTROL 0x03 190 | #define PU_GAIN_CONTROL 0x04 191 | #define PU_POWER_LINE_FREQUENCY_CONTROL 0x05 192 | #define PU_HUE_CONTROL 0x06 193 | #define PU_SATURATION_CONTROL 0x07 194 | #define PU_SHARPNESS_CONTROL 0x08 195 | #define PU_GAMMA_CONTROL 0x09 196 | #define PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0a 197 | #define PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0b 198 | #define PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0c 199 | #define PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0d 200 | #define PU_DIGITAL_MULTIPLIER_CONTROL 0x0e 201 | #define PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x0f 202 | #define PU_HUE_AUTO_CONTROL 0x10 203 | #define PU_ANALOG_VIDEO_STANDARD_CONTROL 0x11 204 | #define PU_ANALOG_LOCK_STATUS_CONTROL 0x12 205 | #define PU_CONTRAST_AUTO_CONTROL 0x13 206 | 207 | /*! 208 | @enum UVC control capabilities 209 | 210 | The UVC standard allows introspection of control's capabilities, 211 | including: 212 | 213 | - supports get (read) 214 | - supports set (write) 215 | - get/set disabled due to automatic control by device 216 | - device may alter the value of the control 217 | - alteration of control value requires non-trivial amount of time 218 | 219 | The UVC standard only encompasses bits 0 - 7. This API makes use 220 | of higher-position bits to cache/express other capabilities: 221 | 222 | - GET_MIN and GET_MAX supported 223 | - GET_RES supported 224 | - GET_DEF supported 225 | 226 | */ 227 | enum { 228 | /* Bits 0 - 7 come from the UVC standard: */ 229 | kUVCControlSupportsGet = 1 << 0, 230 | kUVCControlSupportsSet = 1 << 1, 231 | kUVCControlDisabledDueToAutomaticMode = 1 << 2, 232 | kUVCControlAutoUpdateControl = 1 << 3, 233 | kUVCControlAsynchronousControl = 1 << 4, 234 | /* Bits 8 and up are ours: */ 235 | kUVCControlHasRange = 1 << 8, 236 | kUVCControlHasStepSize = 1 << 9, 237 | kUVCControlHasDefaultValue = 1 << 10 238 | }; 239 | 240 | /*! 241 | @typedef UVCControllerPostProcessCallback 242 | 243 | A callback function that may alter the value read from a control prior 244 | to its being returned to the calling program. This is typically used 245 | to perform byte-swapping of the (little endian) USB-native value to the 246 | system endian. 247 | */ 248 | typedef void (*UVCControllerPostProcessCallback)(void *data, int size); 249 | 250 | /*! 251 | @typedef UVCControllerPreProcessCallback 252 | 253 | A callback function that may alter the value to be written to a control. 254 | This is typically used to perform byte-swapping of the system endian value 255 | to the (little endian) USB-native value. 256 | */ 257 | typedef void (*UVCControllerPreProcessCallback)(void *data, int size); 258 | 259 | /*! 260 | @typedef uvc_control_t 261 | 262 | Each UVC control is defined via one of these data structures. Since the standard 263 | declares each control's expected data size and structure, the data structure 264 | associates the declared selector-id with the type description string and a cached 265 | "compiled" type representation. 266 | */ 267 | typedef struct { 268 | int unitType; 269 | NSString *unitTypeStr; 270 | int selector; 271 | const char *uvcTypeDescription; 272 | UVCType *uvcType; 273 | } uvc_control_t; 274 | 275 | /*! 276 | @defined UVC_CONTROL_INIT 277 | 278 | Macro that expands a list of UVCControllerControls field values into a struct 279 | initializer statement. 280 | */ 281 | #define UVC_CONTROL_INIT(U,S,T) { .unitType = (U), .unitTypeStr = @#U, .selector = (S), .uvcTypeDescription = (T), .uvcType = nil } 282 | 283 | /*! 284 | @constant UVCControllerControls 285 | 286 | List of all the UVC controls this API supports. Each control consists of: 287 | 288 | - control selector (e.g. CT_SCANNING_MODE_CONTROL, PU_BACKLIGHT_COMPENSATION_CONTROL) 289 | - UVCType type description string 290 | - cached UVCType instance 291 | 292 | The index of each control in this array is important!!! It determines the value that 293 | should be assigned to the control's textual name in the controlMapping dictionary 294 | (see the controlMapping method). 295 | */ 296 | uvc_control_t UVCControllerControls[] = { 297 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_SCANNING_MODE_CONTROL, "{B}"), 298 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_AE_MODE_CONTROL, "{M1}"), 299 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_AE_PRIORITY_CONTROL, "{U1}"), 300 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, "{U4}"), 301 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_EXPOSURE_TIME_RELATIVE_CONTROL, "{S1}"), 302 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_FOCUS_ABSOLUTE_CONTROL, "{U2}"), 303 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_FOCUS_RELATIVE_CONTROL, "{S1 focus-relative; U1 focus-speed}"), 304 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_FOCUS_AUTO_CONTROL, "{B}"), 305 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_IRIS_ABSOLUTE_CONTROL, "{U2}"), 306 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_IRIS_RELATIVE_CONTROL, "{S1}"), 307 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_ZOOM_ABSOLUTE_CONTROL, "{U2}"), 308 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_ZOOM_RELATIVE_CONTROL, "{S1 zoom; B digital-zoom; U1 speed}"), 309 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_PANTILT_ABSOLUTE_CONTROL, "{S4 pan; S4 tilt}"), 310 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_PANTILT_RELATIVE_CONTROL, "{S1 pan-relative; U1 pan-speed; S1 tilt-relative; U1 tilt-speed}"), 311 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_ROLL_ABSOLUTE_CONTROL, "{S2}"), 312 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_ROLL_RELATIVE_CONTROL, "{S1 roll-relative; U1 roll-speed}"), 313 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_PRIVACY_CONTROL, "{B}"), 314 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_FOCUS_SIMPLE_CONTROL, "{U1}"), 315 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_WINDOW_CONTROL, "{U2 window-top; U2 window-left; U2 window-bottom; U2 window-right; U2 num-steps; M2 num-steps-units}"), 316 | UVC_CONTROL_INIT(UVC_INPUT_TERMINAL_ID, CT_REGION_OF_INTEREST_CONTROL, "{U2 roi-top; U2 roi-left; U2 roi-bottom; U2 roi-right; M2 auto-controls}"), 317 | // 318 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_BACKLIGHT_COMPENSATION_CONTROL, "{U2}"), 319 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_BRIGHTNESS_CONTROL, "{S2}"), 320 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_CONTRAST_CONTROL, "{U2}"), 321 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_GAIN_CONTROL, "{U2}"), 322 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_POWER_LINE_FREQUENCY_CONTROL, "{U1}"), 323 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_HUE_CONTROL, "{S2}"), 324 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_SATURATION_CONTROL, "{U2}"), 325 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_SHARPNESS_CONTROL, "{U2}"), 326 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_GAMMA_CONTROL, "{U2}"), 327 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_WHITE_BALANCE_TEMPERATURE_CONTROL, "{U2}"), 328 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, "{B}"), 329 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_WHITE_BALANCE_COMPONENT_CONTROL, "{U2 blue; U2 red}"), 330 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, "{B}"), 331 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_DIGITAL_MULTIPLIER_CONTROL, "{U2}"), 332 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL, "{U2}"), 333 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_HUE_AUTO_CONTROL,"{B}"), 334 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_ANALOG_VIDEO_STANDARD_CONTROL, "{U1}"), 335 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_ANALOG_LOCK_STATUS_CONTROL, "{U1}"), 336 | UVC_CONTROL_INIT(UVC_PROCESSING_UNIT_ID, PU_CONTRAST_AUTO_CONTROL, "{U1}") 337 | }; 338 | 339 | /*! 340 | @defined UVCInvalidControlIndex 341 | 342 | Returned by control-lookup function(s) to indicate a control not implemented by 343 | this API. 344 | */ 345 | #define UVCInvalidControlIndex ((NSUInteger)-1) 346 | 347 | // 348 | #if 0 349 | #pragma mark - 350 | #endif 351 | // 352 | 353 | /*! 354 | @class UVCControl(UVCControlPrivate) 355 | @abstract Non-public methods of the UVCControl class. 356 | 357 | UVCControl instances are only ever initialized by the UVCController class, so the 358 | initializer method(s) can be private. 359 | */ 360 | @interface UVCControl(UVCControlPrivate) 361 | 362 | /*! 363 | @method initControlWithName:parentController:controlIndex: 364 | 365 | Initialize a newly-allocated instance of UVCControl. The controlName is copied; 366 | the parentController is sent a retain message; and the controlIndex indicates 367 | this control's position in the UVCControllerControls array. 368 | */ 369 | - (id) initControlWithName:(NSString*)controlName parentController:(UVCController*)parentController controlIndex:(NSUInteger)controlIndex; 370 | 371 | @end 372 | 373 | // 374 | #if 0 375 | #pragma mark - 376 | #endif 377 | // 378 | 379 | /*! 380 | @class UVCController(UVCControllerPrivate) 381 | @abstract Non-public methods of the UVCController class. 382 | 383 | Class and instance methods of UVCController that should not be accessible 384 | outside this source file. 385 | */ 386 | @interface UVCController(UVCControllerPrivate) 387 | 388 | /*! 389 | @method controlMapping 390 | 391 | Returns a constant NSDictionary which maps control name strings to an index in 392 | the UVCControllerControls array. 393 | */ 394 | + (NSDictionary*) controlMapping; 395 | /*! 396 | @method controlMapping 397 | 398 | Convenience method which calls the controlMapping class method. 399 | */ 400 | - (NSDictionary*) controlMapping; 401 | 402 | /*! 403 | @method terminalControlEnableMapping 404 | 405 | Returns a constant NSDictionary which maps Terminal unit control name strings to 406 | the control's enablement bit in the UVC_VC_Terminal_Header_Descriptor. 407 | */ 408 | + (NSDictionary*) terminalControlEnableMapping; 409 | /*! 410 | @method terminalControlEnableMapping 411 | 412 | Convenience method which calls the terminalControlEnableMapping class method. 413 | */ 414 | - (NSDictionary*) terminalControlEnableMapping; 415 | 416 | /*! 417 | @method processingUnitControlEnableMapping 418 | 419 | Returns a constant NSDictionary which maps Processing Unit control name strings to 420 | the control's enablement bit in the UVC_PU_Header_Descriptor. 421 | */ 422 | + (NSDictionary*) processingUnitControlEnableMapping; 423 | /*! 424 | @method processingUnitControlEnableMapping 425 | 426 | Convenience method which calls the processingUnitControlEnableMapping class method. 427 | */ 428 | - (NSDictionary*) processingUnitControlEnableMapping; 429 | 430 | /*! 431 | @method controlIndexForString: 432 | 433 | Looks-up the controlString in the controlMapping NSDictionary. If found, returns 434 | the control's index in the UVCControllerControls array. 435 | 436 | If the control is not found, returns UVCInvalidControlIndex. 437 | */ 438 | + (NSUInteger) controlIndexForString:(NSString*)controlString; 439 | /*! 440 | @method controlIndexForString: 441 | 442 | Convenience method which calls the controlIndexForString: class method. 443 | */ 444 | - (NSUInteger) controlIndexForString:(NSString*)controlString; 445 | 446 | /*! 447 | @method controlIsNotAvailable: 448 | 449 | If the findControllerInterfaceForServiceObject: method was able to pull control 450 | enablement data from the device descriptors, then this method will check the 451 | enablement bitmask to determine whether or not the device claims the control 452 | is implemented. 453 | 454 | If no enablement data was found, this method always returns NO; the controlWithName: 455 | method will then always at least TRY to instantiate the control (and maybe fail). 456 | */ 457 | - (BOOL) controlIsNotAvailable:(NSString*)controlString; 458 | 459 | /*! 460 | @method initWithLocationId:vendorId:productId:ioServiceObject: 461 | 462 | Designated initializer for this class. Expects the locationId, vendorId, and 463 | productId to already have been read from the ioServiceObject's property list. 464 | 465 | Caches a copy of the device's textual name. 466 | 467 | Calls the findControllerInterfaceForServiceObject: method to fill-in the USB 468 | interface callback list, etc. 469 | 470 | Returns nil if any aspect of initialization fails. 471 | */ 472 | - (id) initWithLocationId:(UInt32)locationId vendorId:(UInt16)vendorId productId:(UInt16)productId ioServiceObject:(io_service_t)ioServiceObject; 473 | 474 | /*! 475 | @method findControllerInterfaceForServiceObject: 476 | 477 | Given the ioServiceObject, locate the correct UVC-compliant device interface object 478 | which this class can control. 479 | 480 | Also determines the appropriate interface index for use in control request parameter 481 | blocks. 482 | 483 | If successful, the interface's descriptor list is walked in an attempt to find 484 | control enablement bitmasks; if found, they are retained as NSData instances which 485 | are later consulted to determine which controls are enabled/disabled. 486 | 487 | Returns YES if all operations are successful. 488 | 489 | This method consumes one reference to ioServiceObject; the caller should retain 490 | the object before calling this method if it needs to keep ioServiceObject valid 491 | afterwards. 492 | */ 493 | - (BOOL) findControllerInterfaceForServiceObject:(io_service_t)ioServiceObject; 494 | 495 | /*! 496 | @method sendControlRequest: 497 | 498 | Lowest-level mechanism for delivering USB requests to the receiver's device. 499 | Returns YES if the request is successful. 500 | 501 | In order to send the request, the interface must be open. If the receiver has 502 | not been explicitly opened (via setIsInterfaceOpen:) then this method will 503 | open the interface, deliver the request, and then close the interface. 504 | */ 505 | - (BOOL) sendControlRequest:(IOUSBDevRequest)controlRequest; 506 | 507 | /*! 508 | @method setData:withLength:forSelector:atUnitId: 509 | 510 | Constructs an IOUSBDevRequest parameter block around the given arguments with the UVC_SET_CUR 511 | opcode and calls sendControlRequest: to write the length bytes of data at value to the 512 | device. 513 | 514 | Returns YES if successful. 515 | */ 516 | - (BOOL) setData:(void*)value withLength:(int)length forSelector:(int)selector atUnitId:(int)unitId; 517 | 518 | /*! 519 | @method getData:ofType:withLength:fromSelector:atUnitId: 520 | 521 | Constructs an IOUSBDevRequest parameter block around the given arguments with the given 522 | opcode (type) and calls sendControlRequest: to read length bytes of data into value from 523 | the device. 524 | 525 | Returns YES if successful. 526 | */ 527 | - (BOOL) getData:(void*)value ofType:(int)type withLength:(int)length fromSelector:(int)selector atUnitId:(int)unitId; 528 | 529 | /*! 530 | @method getLowValue:highValue:stepSize:defaultValue:updateCapabilitiesBitmask:forControl: 531 | 532 | For the given control (by controlId, index in the UVCControllerControls array) attempt to read 533 | the minimum, maximum, resolution, and default values. So long as the control uses a 1-, 2-, or 534 | 4-byte data size, the values are set (for any parameter that is not NULL). 535 | 536 | The capabilities bitmask is updated to indicate if the range, resolution (step size), or 537 | default values were available. 538 | */ 539 | - (void) getLowValue:(UVCValue**)lowValue highValue:(UVCValue**)highValue stepSize:(UVCValue**)stepSize defaultValue:(UVCValue**)defaultValue updateCapabilitiesBitmask:(uvc_capabilities_t*)capabilities forControl:(NSUInteger)controlId; 540 | 541 | 542 | - (BOOL) getValue:(UVCValue*)value forControl:(NSUInteger)controlId; 543 | - (BOOL) setValue:(UVCValue*)value forControl:(NSUInteger)controlId; 544 | 545 | @end 546 | 547 | @implementation UVCController(UVCControllerPrivate) 548 | 549 | + (NSDictionary*) controlMapping 550 | { 551 | static NSDictionary *sharedControlMapping = nil; 552 | 553 | if ( ! sharedControlMapping ) { 554 | sharedControlMapping = [[NSDictionary alloc] initWithObjectsAndKeys: 555 | [NSNumber numberWithInt:0], UVCTerminalControlScanningMode, 556 | [NSNumber numberWithInt:1], UVCTerminalControlAutoExposureMode, 557 | [NSNumber numberWithInt:2], UVCTerminalControlAutoExposurePriority, 558 | [NSNumber numberWithInt:3], UVCTerminalControlExposureTimeAbsolute, 559 | [NSNumber numberWithInt:4], UVCTerminalControlExposureTimeRelative, 560 | [NSNumber numberWithInt:5], UVCTerminalControlFocusAbsolute, 561 | [NSNumber numberWithInt:6], UVCTerminalControlFocusRelative, 562 | [NSNumber numberWithInt:7], UVCTerminalControlAutoFocus, 563 | [NSNumber numberWithInt:8], UVCTerminalControlIrisAbsolute, 564 | [NSNumber numberWithInt:9], UVCTerminalControlIrisRelative, 565 | [NSNumber numberWithInt:10], UVCTerminalControlZoomAbsolute, 566 | [NSNumber numberWithInt:11], UVCTerminalControlZoomRelative, 567 | [NSNumber numberWithInt:12], UVCTerminalControlPanTiltAbsolute, 568 | [NSNumber numberWithInt:13], UVCTerminalControlPanTiltRelative, 569 | [NSNumber numberWithInt:14], UVCTerminalControlRollAbsolute, 570 | [NSNumber numberWithInt:15], UVCTerminalControlRollRelative, 571 | [NSNumber numberWithInt:16], UVCTerminalControlPrivacy, 572 | [NSNumber numberWithInt:17], UVCTerminalControlFocusSimple, 573 | [NSNumber numberWithInt:18], UVCTerminalControlWindow, 574 | [NSNumber numberWithInt:19], UVCTerminalControlRegionOfInterest, 575 | 576 | [NSNumber numberWithInt:20], UVCProcessingUnitControlBacklightCompensation, 577 | [NSNumber numberWithInt:21], UVCProcessingUnitControlBrightness, 578 | [NSNumber numberWithInt:22], UVCProcessingUnitControlContrast, 579 | [NSNumber numberWithInt:23], UVCProcessingUnitControlGain, 580 | [NSNumber numberWithInt:24], UVCProcessingUnitControlPowerLineFrequency, 581 | [NSNumber numberWithInt:25], UVCProcessingUnitControlHue, 582 | [NSNumber numberWithInt:26], UVCProcessingUnitControlSaturation, 583 | [NSNumber numberWithInt:27], UVCProcessingUnitControlSharpness, 584 | [NSNumber numberWithInt:28], UVCProcessingUnitControlGamma, 585 | [NSNumber numberWithInt:29], UVCProcessingUnitControlWhiteBalanceTemperature, 586 | [NSNumber numberWithInt:30], UVCProcessingUnitControlAutoWhiteBalanceTemperature, 587 | [NSNumber numberWithInt:31], UVCProcessingUnitControlWhiteBalanceComponent, 588 | [NSNumber numberWithInt:32], UVCProcessingUnitControlAutoWhiteBalanceComponent, 589 | [NSNumber numberWithInt:33], UVCProcessingUnitControlDigitalMultiplier, 590 | [NSNumber numberWithInt:34], UVCProcessingUnitControlDigitalMultiplierLimit, 591 | [NSNumber numberWithInt:35], UVCProcessingUnitControlAutoHue, 592 | [NSNumber numberWithInt:36], UVCProcessingUnitControlAnalogVideoStandard, 593 | [NSNumber numberWithInt:37], UVCProcessingUnitControlAnalogLockStatus, 594 | [NSNumber numberWithInt:38], UVCProcessingUnitControlAutoContrast, 595 | nil 596 | ]; 597 | } 598 | return sharedControlMapping; 599 | } 600 | - (NSDictionary*) controlMapping 601 | { 602 | return [[self class] controlMapping]; 603 | } 604 | 605 | // 606 | 607 | + (NSDictionary*) terminalControlEnableMapping 608 | { 609 | static NSDictionary *sharedTerminalControlEnableMapping = nil; 610 | 611 | if ( ! sharedTerminalControlEnableMapping ) { 612 | sharedTerminalControlEnableMapping = [[NSDictionary alloc] initWithObjectsAndKeys: 613 | [NSNumber numberWithInt:kUVCTerminalControlEnableScanningMode], UVCTerminalControlScanningMode, 614 | [NSNumber numberWithInt:kUVCTerminalControlEnableAutoExposureMode], UVCTerminalControlAutoExposureMode, 615 | [NSNumber numberWithInt:kUVCTerminalControlEnableAutoExposurePriority], UVCTerminalControlAutoExposurePriority, 616 | [NSNumber numberWithInt:kUVCTerminalControlEnableExposureTimeAbsolute], UVCTerminalControlExposureTimeAbsolute, 617 | [NSNumber numberWithInt:kUVCTerminalControlEnableExposureTimeRelative], UVCTerminalControlExposureTimeRelative, 618 | [NSNumber numberWithInt:kUVCTerminalControlEnableFocusAbsolute], UVCTerminalControlFocusAbsolute, 619 | [NSNumber numberWithInt:kUVCTerminalControlEnableFocusRelative], UVCTerminalControlFocusRelative, 620 | [NSNumber numberWithInt:kUVCTerminalControlEnableIrisAbsolute], UVCTerminalControlIrisAbsolute, 621 | [NSNumber numberWithInt:kUVCTerminalControlEnableIrisRelative], UVCTerminalControlIrisRelative, 622 | [NSNumber numberWithInt:kUVCTerminalControlEnableZoomAbsolute], UVCTerminalControlZoomAbsolute, 623 | [NSNumber numberWithInt:kUVCTerminalControlEnableZoomRelative], UVCTerminalControlZoomRelative, 624 | [NSNumber numberWithInt:kUVCTerminalControlEnablePanTiltAbsolute], UVCTerminalControlPanTiltAbsolute, 625 | [NSNumber numberWithInt:kUVCTerminalControlEnablePanTiltRelative], UVCTerminalControlPanTiltRelative, 626 | [NSNumber numberWithInt:kUVCTerminalControlEnableRollAbsolute], UVCTerminalControlRollAbsolute, 627 | [NSNumber numberWithInt:kUVCTerminalControlEnableRollRelative], UVCTerminalControlRollRelative, 628 | [NSNumber numberWithInt:kUVCTerminalControlEnableFocusAuto], UVCTerminalControlAutoFocus, 629 | [NSNumber numberWithInt:kUVCTerminalControlEnablePrivacy], UVCTerminalControlPrivacy, 630 | [NSNumber numberWithInt:kUVCTerminalControlEnableFocusSimple], UVCTerminalControlFocusSimple, 631 | [NSNumber numberWithInt:kUVCTerminalControlEnableWindow], UVCTerminalControlWindow, 632 | [NSNumber numberWithInt:kUVCTerminalControlEnableRegionOfInterest], UVCTerminalControlRegionOfInterest, 633 | nil 634 | ]; 635 | } 636 | return sharedTerminalControlEnableMapping; 637 | } 638 | - (NSDictionary*) terminalControlEnableMapping 639 | { 640 | return [[self class] terminalControlEnableMapping]; 641 | } 642 | 643 | // 644 | 645 | + (NSDictionary*) processingUnitControlEnableMapping 646 | { 647 | static NSDictionary *sharedProcessingUnitControlEnableMapping = nil; 648 | 649 | if ( ! sharedProcessingUnitControlEnableMapping ) { 650 | sharedProcessingUnitControlEnableMapping = [[NSDictionary alloc] initWithObjectsAndKeys: 651 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableBrightness], UVCProcessingUnitControlBrightness, 652 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableContrast], UVCProcessingUnitControlContrast, 653 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableHue], UVCProcessingUnitControlHue, 654 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableSaturation], UVCProcessingUnitControlSaturation, 655 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableSharpness], UVCProcessingUnitControlSharpness, 656 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableGamma], UVCProcessingUnitControlGamma, 657 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableWhiteBalanceTemperature], UVCProcessingUnitControlWhiteBalanceTemperature, 658 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableWhiteBalanceComponent], UVCProcessingUnitControlWhiteBalanceComponent, 659 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableBacklightCompensation], UVCProcessingUnitControlBacklightCompensation, 660 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableGain], UVCProcessingUnitControlGain, 661 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnablePowerLineFrequency], UVCProcessingUnitControlPowerLineFrequency, 662 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableAutoHue], UVCProcessingUnitControlAutoHue, 663 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableAutoWhiteBalanceTemperature], UVCProcessingUnitControlAutoWhiteBalanceTemperature, 664 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableAutoWhiteBalanceComponent], UVCProcessingUnitControlAutoWhiteBalanceComponent, 665 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableDigitalMultiplier], UVCProcessingUnitControlDigitalMultiplier, 666 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableDigitalMultiplierLimit], UVCProcessingUnitControlDigitalMultiplierLimit, 667 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableAnalogVideoStandard], UVCProcessingUnitControlAnalogVideoStandard, 668 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableAnalogVideoLockStatus], UVCProcessingUnitControlAnalogLockStatus, 669 | [NSNumber numberWithInt:kUVCProcessingUnitControlEnableAutoContrast], UVCProcessingUnitControlAutoContrast, 670 | nil 671 | ]; 672 | } 673 | return sharedProcessingUnitControlEnableMapping; 674 | } 675 | - (NSDictionary*) processingUnitControlEnableMapping 676 | { 677 | return [[self class] processingUnitControlEnableMapping]; 678 | } 679 | 680 | // 681 | 682 | + (NSUInteger) controlIndexForString:(NSString*)controlString 683 | { 684 | NSDictionary *controlMapping = [self controlMapping]; 685 | NSUInteger index = UVCInvalidControlIndex; 686 | 687 | if ( controlMapping ) { 688 | NSNumber *indexObj = [controlMapping objectForKey:controlString]; 689 | 690 | if ( indexObj ) index = [indexObj unsignedIntegerValue]; 691 | } 692 | return index; 693 | } 694 | - (NSUInteger) controlIndexForString:(NSString*)controlString 695 | { 696 | return [[self class] controlIndexForString:controlString]; 697 | } 698 | 699 | // 700 | 701 | - (BOOL) controlIsNotAvailable:(NSString*)controlString 702 | { 703 | NSUInteger controlIndex = [self controlIndexForString:controlString]; 704 | 705 | if ( controlIndex != UVCInvalidControlIndex ) { 706 | switch ( UVCControllerControls[controlIndex].unitType ) { 707 | 708 | case UVC_INPUT_TERMINAL_ID: { 709 | // If we weren't able to get control enablement from the interface 710 | // descriptor, don't prevent the code from TRYING to access the 711 | // control and failing: 712 | if ( ! _terminalControlsAvailable ) return NO; 713 | // Get the bit index for the given control: 714 | NSNumber *bitIndexObj = [[self terminalControlEnableMapping] objectForKey:controlString]; 715 | 716 | if ( bitIndexObj ) { 717 | // Check the enablement bitvector: 718 | NSUInteger bitIndex = [bitIndexObj unsignedIntegerValue], byteIndex; 719 | UInt8 byte; 720 | 721 | byteIndex = bitIndex / 8; 722 | bitIndex = bitIndex % 8; 723 | if ( byteIndex < [_terminalControlsAvailable length] ) { 724 | [_terminalControlsAvailable getBytes:&byte range:NSMakeRange(byteIndex, 1)]; 725 | } else { 726 | return YES; 727 | } 728 | if ( (byte & (1 << bitIndex)) != 0 ) return NO; 729 | } 730 | break; 731 | } 732 | 733 | case UVC_PROCESSING_UNIT_ID: { 734 | // If we weren't able to get control enablement from the interface 735 | // descriptor, don't prevent the code from TRYING to access the 736 | // control and failing: 737 | if ( ! _processingUnitControlsAvailable ) return NO; 738 | // Get the bit index for the given control: 739 | NSNumber *bitIndexObj = [[self processingUnitControlEnableMapping] objectForKey:controlString]; 740 | 741 | if ( bitIndexObj ) { 742 | // Check the enablement bitvector: 743 | NSUInteger bitIndex = [bitIndexObj unsignedIntegerValue], byteIndex; 744 | UInt8 byte; 745 | 746 | byteIndex = bitIndex / 8; 747 | bitIndex = bitIndex % 8; 748 | if ( byteIndex < [_processingUnitControlsAvailable length] ) { 749 | [_processingUnitControlsAvailable getBytes:&byte range:NSMakeRange(byteIndex, 1)]; 750 | } else { 751 | return YES; 752 | } 753 | if ( (byte & (1 << bitIndex)) != 0 ) return NO; 754 | } 755 | break; 756 | } 757 | 758 | } 759 | } 760 | return YES; 761 | } 762 | 763 | // 764 | 765 | - (id) initWithLocationId:(UInt32)locationId 766 | vendorId:(UInt16)vendorId 767 | productId:(UInt16)productId 768 | ioServiceObject:(io_service_t)ioServiceObject 769 | { 770 | if ( (self = [self init]) ) { 771 | io_name_t nameBuffer; 772 | 773 | _locationId = locationId; 774 | _vendorId = vendorId; 775 | _productId = productId; 776 | 777 | if ( IORegistryEntryGetName(ioServiceObject, nameBuffer) == KERN_SUCCESS ) { 778 | _deviceName = [[NSString stringWithUTF8String:nameBuffer] retain]; 779 | } 780 | _unitIds = [[NSMutableDictionary alloc] initWithObjectsAndKeys: 781 | [NSNumber numberWithInt:UVC_INPUT_TERMINAL_ID], @"UVC_INPUT_TERMINAL_ID", 782 | [NSNumber numberWithInt:UVC_PROCESSING_UNIT_ID], @"UVC_PROCESSING_UNIT_ID", 783 | nil 784 | ]; 785 | if ( [self findControllerInterfaceForServiceObject:ioServiceObject] ) { 786 | _controls = [[NSMutableDictionary alloc] init]; 787 | } else { 788 | [self release]; 789 | self = nil; 790 | } 791 | } 792 | return self; 793 | } 794 | 795 | // 796 | 797 | - (BOOL) findControllerInterfaceForServiceObject:(io_service_t)ioServiceObject 798 | { 799 | IOUSBDeviceInterface **deviceInterface = NULL; 800 | IOCFPlugInInterface **plugInInterface = NULL; 801 | SInt32 score; 802 | kern_return_t krc = IOCreatePlugInInterfaceForService(ioServiceObject, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score); 803 | 804 | if ( (krc != kIOReturnSuccess) || ! plugInInterface ) return NO; 805 | 806 | IOReturn hrc = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*)&deviceInterface); 807 | 808 | IODestroyPlugInInterface(plugInInterface); 809 | if ( (hrc != 0) || ! deviceInterface ) return NO; 810 | 811 | // 812 | // Find any video control interfaces to this device: 813 | // 814 | io_iterator_t interfaceIter; 815 | IOUSBFindInterfaceRequest interfaceRequest = { 816 | .bInterfaceClass = kUSBVideoInterfaceClass, 817 | .bInterfaceSubClass = kUSBVideoControlSubClass, 818 | .bInterfaceProtocol = kIOUSBFindInterfaceDontCare, 819 | .bAlternateSetting = kIOUSBFindInterfaceDontCare 820 | }; 821 | 822 | hrc = (*deviceInterface)->CreateInterfaceIterator(deviceInterface, &interfaceRequest, &interfaceIter); 823 | (*deviceInterface)->Release(deviceInterface); 824 | 825 | if( (hrc != 0) || ! interfaceIter ) return NO; 826 | 827 | io_service_t usbInterface = IOIteratorNext(interfaceIter); 828 | 829 | IOObjectRelease(interfaceIter); 830 | if ( ! usbInterface ) return NO; 831 | 832 | // Create an intermediate plug-in to use to grab an interface to the device: 833 | krc = IOCreatePlugInInterfaceForService(usbInterface, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score); 834 | IOObjectRelease(usbInterface); 835 | if ( (krc != kIOReturnSuccess) || ! plugInInterface ) return NO; 836 | 837 | // Now create the device interface for the interface: 838 | hrc = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID *)&_controllerInterface); 839 | IODestroyPlugInInterface(plugInInterface); 840 | if ( (hrc != 0) || ! _controllerInterface ) return NO; 841 | 842 | hrc = (*_controllerInterface)->GetInterfaceNumber(_controllerInterface, &_videoInterfaceIndex); 843 | 844 | // 845 | // Check with the interface for the UVC config descriptors: 846 | // 847 | IOUSBDescriptorHeader *interfaceDescriptor = NULL; 848 | if ( (interfaceDescriptor = (*_controllerInterface)->FindNextAssociatedDescriptor(_controllerInterface, NULL, CS_INTERFACE)) ) { 849 | // 850 | // UVC leads the descriptor with a VC Interface Header that provides 851 | // the version of UVC implemented and a list of the available streaming 852 | // interfaces (by number). More importantly, it provides the full byte 853 | // length of the descriptor, which aids in walking the Unit/Terminal 854 | // descriptors that follow it. 855 | // 856 | UVC_Descriptor_Prefix *descriptorPrefix = (UVC_Descriptor_Prefix*)interfaceDescriptor; 857 | 858 | switch ( descriptorPrefix->bDescriptorSubType ) { 859 | 860 | case VC_HEADER: { 861 | UVC_VC_Interface_Header_Descriptor *vcHeader = (UVC_VC_Interface_Header_Descriptor*)interfaceDescriptor; 862 | void *basePtr = (void*)vcHeader; 863 | void *endPtr = basePtr + vcHeader->wTotalLength; 864 | 865 | #ifdef DEBUG_WRITE_UVC_HEADER_TO_FILE 866 | // Dump the header to a file for debugging: 867 | char headerFName[64]; 868 | 869 | snprintf(headerFName, sizeof(headerFName), "uvc-header-%hhu.bin", vcHeader->baInterfaceNr1); 870 | 871 | FILE *headerFPtr = fopen(headerFName, "w"); 872 | 873 | if ( headerFPtr ) { 874 | fwrite(basePtr, vcHeader->wTotalLength, 1, headerFPtr); 875 | fclose(headerFPtr); 876 | } 877 | #endif 878 | 879 | // Grab the version of the UVC standard this device implements: 880 | _uvcVersion = NSSwapLittleShortToHost(vcHeader->bcdUVC); 881 | 882 | // 883 | // basePtr and endPtr are setup to allow us to easily walk the embedded 884 | // Unit/Terminal descriptors 885 | // 886 | basePtr += vcHeader->bLength; 887 | while ( basePtr < endPtr ) { 888 | descriptorPrefix = (UVC_Descriptor_Prefix*)basePtr; 889 | if ( descriptorPrefix->bDescriptorType == CS_INTERFACE ) { 890 | switch ( descriptorPrefix->bDescriptorSubType ) { 891 | 892 | case VC_INPUT_TERMINAL: { 893 | UVC_VC_Terminal_Header_Descriptor *terminalHeader = (UVC_VC_Terminal_Header_Descriptor*)basePtr; 894 | 895 | if ( terminalHeader->bControlSize > 0 ) { 896 | _terminalControlsAvailable = [[NSData alloc] initWithBytes:&terminalHeader->bmControls[0] length:terminalHeader->bControlSize]; 897 | } 898 | break; 899 | } 900 | 901 | case VC_PROCESSING_UNIT: { 902 | UVC_PU_Header_Descriptor *puHeader = (UVC_PU_Header_Descriptor*)basePtr; 903 | 904 | if ( puHeader->bControlSize > 0 ) { 905 | [_unitIds setObject:[NSNumber numberWithInt:puHeader->bUnitId] forKey:@"UVC_PROCESSING_UNIT_ID"]; 906 | _processingUnitControlsAvailable = [[NSData alloc] initWithBytes:&puHeader->bmControls[0] length:puHeader->bControlSize]; 907 | } 908 | break; 909 | } 910 | 911 | } 912 | } 913 | basePtr += descriptorPrefix->bLength; 914 | 915 | } 916 | break; 917 | } 918 | } 919 | } 920 | 921 | return YES; 922 | } 923 | 924 | // 925 | 926 | - (BOOL) sendControlRequest:(IOUSBDevRequest)controlRequest 927 | { 928 | // 929 | // Open the interface. This will cause the pipes associated with the endpoints in 930 | // the interface descriptor to be instantiated. Then send a control request. 931 | // 932 | IOReturn rc = 0; 933 | 934 | if ( ! [self isInterfaceOpen] ) { 935 | [self setIsInterfaceOpen:YES]; 936 | if ( ! [self isInterfaceOpen] ) return NO; 937 | } 938 | rc = (*_controllerInterface)->ControlRequest(_controllerInterface, 0, &controlRequest); 939 | return ( rc == kIOReturnSuccess ); 940 | } 941 | 942 | // 943 | 944 | - (BOOL) setData:(void*)value 945 | withLength:(int)length 946 | forSelector:(int)selector 947 | atUnitId:(int)unitId 948 | { 949 | IOUSBDevRequest controlRequest = { 950 | .bmRequestType = USBmakebmRequestType(kUSBOut, kUSBClass, kUSBInterface), 951 | .bRequest = UVC_SET_CUR, 952 | .wValue = (selector << 8), 953 | .wIndex = (unitId << 8) | _videoInterfaceIndex, 954 | .wLength = length, 955 | .wLenDone = 0, 956 | .pData = value 957 | }; 958 | return [self sendControlRequest:controlRequest]; 959 | } 960 | 961 | // 962 | 963 | - (BOOL) getData:(void*)value 964 | ofType:(int)type 965 | withLength:(int)length 966 | fromSelector:(int)selector 967 | atUnitId:(int)unitId 968 | { 969 | IOUSBDevRequest controlRequest = { 970 | .bmRequestType = USBmakebmRequestType(kUSBIn, kUSBClass, kUSBInterface), 971 | .bRequest = type, 972 | .wValue = (selector << 8), 973 | .wIndex = (unitId << 8) | _videoInterfaceIndex, 974 | .wLength = length, 975 | .wLenDone = 0, 976 | .pData = value 977 | }; 978 | return [self sendControlRequest:controlRequest]; 979 | } 980 | 981 | // 982 | 983 | - (BOOL) capabilities:(NSUInteger*)capabilities 984 | forControl:(NSUInteger)controlId 985 | { 986 | uvc_control_t *control = &UVCControllerControls[controlId]; 987 | uint8_t scratch; 988 | 989 | if ( [self getData:&scratch ofType:UVC_GET_INFO withLength:1 fromSelector:control->selector atUnitId:[[_unitIds objectForKey:control->unitTypeStr] intValue]] ) { 990 | *capabilities = scratch; 991 | return YES; 992 | } 993 | return NO; 994 | } 995 | 996 | // 997 | 998 | - (void) getLowValue:(UVCValue**)lowValue 999 | highValue:(UVCValue**)highValue 1000 | stepSize:(UVCValue**)stepSize 1001 | defaultValue:(UVCValue**)defaultValue 1002 | updateCapabilitiesBitmask:(uvc_capabilities_t*)capabilities 1003 | forControl:(NSUInteger)controlId 1004 | { 1005 | uvc_control_t *control = &UVCControllerControls[controlId]; 1006 | int unitId = [[_unitIds objectForKey:control->unitTypeStr] intValue]; 1007 | 1008 | if ( *lowValue && *highValue ) { 1009 | if ( [self getData:[*lowValue valuePtr] ofType:UVC_GET_MIN withLength:(int)[*lowValue byteSize] fromSelector:control->selector atUnitId:unitId] && 1010 | [self getData:[*highValue valuePtr] ofType:UVC_GET_MAX withLength:(int)[*highValue byteSize] fromSelector:control->selector atUnitId:unitId] 1011 | ) { 1012 | *capabilities |= kUVCControlHasRange; 1013 | [(*lowValue = [*lowValue retain]) byteSwapUSBToHostEndian]; 1014 | [(*highValue = [*highValue retain]) byteSwapUSBToHostEndian]; 1015 | } else { 1016 | *lowValue = nil; 1017 | *highValue = nil; 1018 | } 1019 | } 1020 | if ( *stepSize ) { 1021 | if ( [self getData:[*stepSize valuePtr] ofType:UVC_GET_RES withLength:(int)[*stepSize byteSize] fromSelector:control->selector atUnitId:unitId]) { 1022 | *capabilities |= kUVCControlHasStepSize; 1023 | [(*stepSize = [*stepSize retain]) byteSwapUSBToHostEndian]; 1024 | } else { 1025 | *stepSize = nil; 1026 | } 1027 | } 1028 | if ( *defaultValue ) { 1029 | if ( [self getData:[*defaultValue valuePtr] ofType:UVC_GET_DEF withLength:(int)[*defaultValue byteSize] fromSelector:control->selector atUnitId:unitId]) { 1030 | *capabilities |= kUVCControlHasDefaultValue; 1031 | [(*defaultValue = [*defaultValue retain]) byteSwapUSBToHostEndian]; 1032 | } else { 1033 | *defaultValue = nil; 1034 | } 1035 | } 1036 | } 1037 | 1038 | // 1039 | 1040 | - (BOOL) getValue:(UVCValue*)value 1041 | forControl:(NSUInteger)controlId 1042 | { 1043 | uvc_control_t *control = &UVCControllerControls[controlId]; 1044 | 1045 | if ( [self getData:[value valuePtr] ofType:UVC_GET_CUR withLength:(int)[value byteSize] fromSelector:control->selector atUnitId:[[_unitIds objectForKey:control->unitTypeStr] intValue]] ) { 1046 | [value byteSwapUSBToHostEndian]; 1047 | return YES; 1048 | } 1049 | return NO; 1050 | } 1051 | 1052 | // 1053 | 1054 | - (BOOL) setValue:(UVCValue*)value 1055 | forControl:(NSUInteger)controlId 1056 | { 1057 | uvc_control_t *control = &UVCControllerControls[controlId]; 1058 | BOOL rc = NO; 1059 | 1060 | [value byteSwapHostToUSBEndian]; 1061 | rc = [self setData:[value valuePtr] withLength:(int)[value byteSize] forSelector:control->selector atUnitId:[[_unitIds objectForKey:control->unitTypeStr] intValue]]; 1062 | [value byteSwapUSBToHostEndian]; 1063 | return rc; 1064 | } 1065 | 1066 | @end 1067 | 1068 | // 1069 | #if 0 1070 | #pragma mark - 1071 | #endif 1072 | // 1073 | 1074 | @implementation UVCController 1075 | 1076 | + (NSArray*) controlStrings 1077 | { 1078 | static NSArray *sharedControlStrings = nil; 1079 | 1080 | if ( ! sharedControlStrings ) { 1081 | NSDictionary *controlMapping = [self controlMapping]; 1082 | 1083 | if ( controlMapping ) sharedControlStrings = [controlMapping allKeys]; 1084 | } 1085 | return sharedControlStrings; 1086 | } 1087 | - (NSArray*) controlStrings 1088 | { 1089 | return [[self class] controlStrings]; 1090 | } 1091 | 1092 | // 1093 | 1094 | + (NSArray*) uvcControllers 1095 | { 1096 | NSMutableArray *newControllers = nil; 1097 | 1098 | // Find a USB Device with the given locationId: 1099 | CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName); 1100 | io_iterator_t deviceIter; 1101 | 1102 | if ( IOServiceGetMatchingServices(kIOMainPortDefault, matchingDict, &deviceIter) == KERN_SUCCESS ) { 1103 | io_service_t device; 1104 | 1105 | while ( (device = IOIteratorNext(deviceIter)) ) { 1106 | UVCController *newController = [UVCController uvcControllerWithService:device]; 1107 | 1108 | IOObjectRelease(device); 1109 | if ( newController ) { 1110 | if ( ! newControllers ) newControllers = [[NSMutableArray alloc] init]; 1111 | [newControllers addObject:newController]; 1112 | } 1113 | IOObjectRelease(device); 1114 | } 1115 | IOObjectRelease(deviceIter); 1116 | } 1117 | if ( newControllers ) { 1118 | NSArray *outArray = [newControllers copy]; 1119 | 1120 | [newControllers release]; 1121 | return [outArray autorelease]; 1122 | } 1123 | return nil; 1124 | } 1125 | 1126 | // 1127 | 1128 | + (id) uvcControllerWithService:(io_service_t)ioService 1129 | { 1130 | CFNumberRef vendorIdObj = IORegistryEntrySearchCFProperty(ioService, kIOUSBPlane, CFSTR(kUSBVendorID), kCFAllocatorDefault, 0); 1131 | CFNumberRef productIdObj = IORegistryEntrySearchCFProperty(ioService, kIOUSBPlane, CFSTR(kUSBProductID), kCFAllocatorDefault, 0); 1132 | CFNumberRef locationIdObj = IORegistryEntrySearchCFProperty(ioService, kIOUSBPlane, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0); 1133 | UInt16 vendorId = -1, productId = -1; 1134 | UInt32 locationId = -1; 1135 | 1136 | if ( vendorIdObj ) { 1137 | CFNumberGetValue(vendorIdObj, kCFNumberSInt16Type, &vendorId); 1138 | CFRelease(vendorIdObj); 1139 | } 1140 | if ( productIdObj ) { 1141 | CFNumberGetValue(productIdObj, kCFNumberSInt16Type, &productId); 1142 | CFRelease(productIdObj); 1143 | } 1144 | if ( locationIdObj ) { 1145 | CFNumberGetValue(locationIdObj, kCFNumberSInt32Type, &locationId); 1146 | CFRelease(locationIdObj); 1147 | } 1148 | // We assume that the caller doesn't want to lose ioService in the course of finding the 1149 | // controller interface, so increase the refcount on it: 1150 | IOObjectRetain(ioService); 1151 | return [[[UVCController alloc] initWithLocationId:locationId vendorId:vendorId productId:productId ioServiceObject:ioService] autorelease]; 1152 | } 1153 | 1154 | // 1155 | 1156 | + (id) uvcControllerWithLocationId:(UInt32)locationId 1157 | { 1158 | UVCController *newController = nil; 1159 | 1160 | // Find a USB Device with the given locationId: 1161 | CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName); 1162 | CFMutableDictionaryRef propertiesDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, 1163 | &kCFTypeDictionaryKeyCallBacks, 1164 | &kCFTypeDictionaryValueCallBacks); 1165 | CFNumberRef usbLocationIdObj = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &locationId); 1166 | 1167 | CFDictionarySetValue(propertiesDict, CFSTR(kUSBDevicePropertyLocationID), usbLocationIdObj); 1168 | CFRelease(usbLocationIdObj); 1169 | CFDictionarySetValue(matchingDict, CFSTR(kIOPropertyMatchKey), propertiesDict); 1170 | CFRelease(propertiesDict); 1171 | 1172 | io_service_t device = IOServiceGetMatchingService(kIOMainPortDefault, matchingDict); 1173 | 1174 | if ( device ) { 1175 | CFNumberRef vendorIdObj = IORegistryEntrySearchCFProperty(device, kIOUSBPlane, CFSTR(kUSBVendorID), kCFAllocatorDefault, 0); 1176 | CFNumberRef productIdObj = IORegistryEntrySearchCFProperty(device, kIOUSBPlane, CFSTR(kUSBProductID), kCFAllocatorDefault, 0); 1177 | UInt16 vendorId = -1, productId = -1; 1178 | 1179 | if ( vendorIdObj ) { 1180 | CFNumberGetValue(vendorIdObj, kCFNumberSInt16Type, &vendorId); 1181 | CFRelease(vendorIdObj); 1182 | } 1183 | if ( productIdObj ) { 1184 | CFNumberGetValue(productIdObj, kCFNumberSInt16Type, &productId); 1185 | CFRelease(productIdObj); 1186 | } 1187 | 1188 | // Our reference to the "device" object will be dropped in the course of finding the controller interface: 1189 | newController = [[[UVCController alloc] initWithLocationId:locationId vendorId:vendorId productId:productId ioServiceObject:device] autorelease]; 1190 | } 1191 | return newController; 1192 | } 1193 | 1194 | // 1195 | 1196 | + (id) uvcControllerWithVendorId:(UInt16)vendorId 1197 | productId:(UInt16)productId 1198 | { 1199 | UVCController *newController = nil; 1200 | 1201 | // Find a USB Device with the given vendor and product ids: 1202 | CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName); 1203 | CFMutableDictionaryRef propertiesDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, 1204 | &kCFTypeDictionaryKeyCallBacks, 1205 | &kCFTypeDictionaryValueCallBacks); 1206 | CFNumberRef vendorIdObj = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt16Type, &vendorId); 1207 | CFNumberRef productIdObj = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt16Type, &productId); 1208 | 1209 | CFDictionarySetValue(propertiesDict, CFSTR(kUSBVendorID), vendorIdObj); 1210 | CFRelease(vendorIdObj); 1211 | CFDictionarySetValue(propertiesDict, CFSTR(kUSBProductID), productIdObj); 1212 | CFRelease(productIdObj); 1213 | CFDictionarySetValue(matchingDict, CFSTR(kIOPropertyMatchKey), propertiesDict); 1214 | CFRelease(propertiesDict); 1215 | 1216 | io_service_t device = IOServiceGetMatchingService(kIOMainPortDefault, matchingDict); 1217 | 1218 | if ( device ) { 1219 | CFNumberRef locationIdObj = IORegistryEntrySearchCFProperty(device, kIOUSBPlane, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0); 1220 | UInt32 locationId = -1; 1221 | 1222 | if ( locationIdObj ) { 1223 | CFNumberGetValue(locationIdObj, kCFNumberSInt32Type, &locationId); 1224 | CFRelease(locationIdObj); 1225 | } 1226 | 1227 | // Our reference to the "device" object will be dropped in the course of finding the controller interface: 1228 | newController = [[[UVCController alloc] initWithLocationId:locationId vendorId:vendorId productId:productId ioServiceObject:device] autorelease]; 1229 | } 1230 | return newController; 1231 | } 1232 | 1233 | // 1234 | 1235 | - (void) dealloc 1236 | { 1237 | if ( _terminalControlsAvailable ) [_terminalControlsAvailable release]; 1238 | if ( _processingUnitControlsAvailable ) [_processingUnitControlsAvailable release]; 1239 | if ( _controls ) [_controls release]; 1240 | if ( _unitIds ) [_unitIds release]; 1241 | if ( _controllerInterface ) { 1242 | [self setIsInterfaceOpen:NO]; 1243 | (*_controllerInterface)->Release(_controllerInterface); 1244 | } 1245 | if ( _deviceName ) [_deviceName release]; 1246 | [super dealloc]; 1247 | } 1248 | 1249 | // 1250 | 1251 | - (NSString*) description 1252 | { 1253 | return [NSString stringWithFormat:@"UVCController@%p { \"%@\"; vendor-id=0x%04x; product-id=0x%04x; location-id=0x%08x; uvc-version: 0x%04x interface-index: %d%s }", 1254 | self, 1255 | _deviceName, 1256 | _vendorId, _productId, 1257 | _locationId, 1258 | _uvcVersion, 1259 | _videoInterfaceIndex, 1260 | (_isInterfaceOpen ? " ; is-open" : "") 1261 | ]; 1262 | } 1263 | 1264 | // 1265 | 1266 | - (NSString*) deviceName 1267 | { 1268 | return _deviceName; 1269 | } 1270 | 1271 | // 1272 | 1273 | - (UInt32) locationId 1274 | { 1275 | return _locationId; 1276 | } 1277 | 1278 | // 1279 | 1280 | - (UInt16) vendorId 1281 | { 1282 | return _vendorId; 1283 | } 1284 | 1285 | // 1286 | 1287 | - (UInt16) productId 1288 | { 1289 | return _productId; 1290 | } 1291 | 1292 | // 1293 | 1294 | - (UInt16) uvcVersion 1295 | { 1296 | return _uvcVersion; 1297 | } 1298 | 1299 | // 1300 | 1301 | - (BOOL) isInterfaceOpen 1302 | { 1303 | return _isInterfaceOpen; 1304 | } 1305 | - (void) setIsInterfaceOpen:(BOOL)isInterfaceOpen 1306 | { 1307 | if ( isInterfaceOpen != _isInterfaceOpen ) { 1308 | IOReturn rc; 1309 | 1310 | if ( isInterfaceOpen ) { 1311 | rc = (*_controllerInterface)->USBInterfaceOpen(_controllerInterface); 1312 | if ( rc == kIOReturnSuccess ) { 1313 | _shouldNotCloseInterface = _isInterfaceOpen = YES; 1314 | } 1315 | else if ( rc == kIOReturnExclusiveAccess) { 1316 | _isInterfaceOpen = YES; 1317 | _shouldNotCloseInterface = NO; 1318 | } 1319 | } else if ( ! _shouldNotCloseInterface ) { 1320 | rc = (*_controllerInterface)->USBInterfaceClose(_controllerInterface); 1321 | if ( rc == kIOReturnSuccess ) _shouldNotCloseInterface = _isInterfaceOpen = NO; 1322 | } 1323 | } 1324 | } 1325 | 1326 | // 1327 | 1328 | - (UVCControl*) controlWithName:(NSString*)controlName 1329 | { 1330 | UVCControl *theControl = [_controls objectForKey:controlName]; 1331 | 1332 | if ( ! theControl ) { 1333 | if ( ! [self controlIsNotAvailable:controlName] ) { 1334 | NSUInteger controlIndex = [self controlIndexForString:controlName]; 1335 | 1336 | if ( controlIndex != UVCInvalidControlIndex ) { 1337 | theControl = [[UVCControl alloc] initControlWithName:controlName parentController:self controlIndex:controlIndex]; 1338 | 1339 | if ( theControl ) { 1340 | [_controls setObject:theControl forKey:controlName]; 1341 | } else { 1342 | [_controls setObject:[NSNull null] forKey:controlName]; 1343 | } 1344 | [theControl release]; 1345 | } 1346 | } else { 1347 | [_controls setObject:[NSNull null] forKey:controlName]; 1348 | } 1349 | } 1350 | if ( [theControl isMemberOfClass:[NSNull class]] ) return nil; 1351 | return theControl; 1352 | } 1353 | 1354 | @end 1355 | 1356 | // 1357 | #if 0 1358 | #pragma mark - 1359 | #endif 1360 | // 1361 | 1362 | @implementation UVCControl(UVCControlPrivate) 1363 | 1364 | - (id) initControlWithName:(NSString*)controlName 1365 | parentController:(UVCController*)parentController 1366 | controlIndex:(NSUInteger)controlIndex 1367 | { 1368 | if ( (self = [super init]) ) { 1369 | if ( ! [parentController capabilities:&_capabilities forControl:controlIndex] ) { 1370 | [self release]; 1371 | self = nil; 1372 | } else { 1373 | _parentController = [parentController retain]; 1374 | _controlName = [controlName copy]; 1375 | _controlIndex = controlIndex; 1376 | 1377 | uvc_control_t *controlInfo = &UVCControllerControls[controlIndex]; 1378 | 1379 | if ( controlInfo->uvcType == nil ) { 1380 | if ( (controlInfo->uvcType = [UVCType uvcTypeWithCString:controlInfo->uvcTypeDescription]) == nil ) { 1381 | fprintf(stderr, "FATAL ERROR: unable to instantiate UVCType for description %s !!!\n", controlInfo->uvcTypeDescription); 1382 | exit(EFAULT); 1383 | } 1384 | controlInfo->uvcType = [controlInfo->uvcType retain]; 1385 | } 1386 | 1387 | _currentValue = [[UVCValue uvcValueWithType:controlInfo->uvcType] retain]; 1388 | 1389 | _minimum = [UVCValue uvcValueWithType:controlInfo->uvcType]; 1390 | _maximum = [UVCValue uvcValueWithType:controlInfo->uvcType]; 1391 | _stepSize = [UVCValue uvcValueWithType:controlInfo->uvcType]; 1392 | _defaultValue = [UVCValue uvcValueWithType:controlInfo->uvcType]; 1393 | 1394 | [_parentController getLowValue:&_minimum highValue:&_maximum stepSize:&_stepSize defaultValue:&_defaultValue updateCapabilitiesBitmask:&_capabilities forControl:controlIndex]; 1395 | } 1396 | } 1397 | return self; 1398 | } 1399 | 1400 | @end 1401 | 1402 | // 1403 | 1404 | @implementation UVCControl : NSObject 1405 | 1406 | - (void) dealloc 1407 | { 1408 | if ( _controlName ) [_controlName release]; 1409 | if ( _parentController ) [_parentController release]; 1410 | if ( _currentValue ) [_currentValue release]; 1411 | if ( _minimum ) [_minimum release]; 1412 | if ( _maximum ) [_maximum release]; 1413 | if ( _stepSize ) [_stepSize release]; 1414 | if ( _defaultValue ) [_defaultValue release]; 1415 | [super dealloc]; 1416 | } 1417 | 1418 | // 1419 | 1420 | - (NSString*) description 1421 | { 1422 | NSMutableString *outString = [[NSMutableString alloc] initWithFormat:@"UVCControl[%@]@%p { capabilities: %012x, byte-size: %lu", 1423 | _controlName, self, 1424 | (unsigned int)_capabilities, 1425 | (unsigned long)[_currentValue byteSize] 1426 | ]; 1427 | 1428 | if ( [self hasRange] ) { 1429 | [outString appendFormat:@"; range: [%@,%@]", _minimum, _maximum]; 1430 | } 1431 | if ( [self hasStepSize] ) { 1432 | [outString appendFormat:@"; step-size: %@", _stepSize]; 1433 | } 1434 | if ( [self hasDefaultValue] ) { 1435 | [outString appendFormat:@"; default-value: %@", _defaultValue]; 1436 | } 1437 | [outString appendString:@" }"]; 1438 | 1439 | NSString *returnString = [outString copy]; 1440 | 1441 | [outString release]; 1442 | return returnString; 1443 | } 1444 | 1445 | // 1446 | 1447 | - (BOOL) supportsGetValue 1448 | { 1449 | return ((_capabilities & kUVCControlSupportsGet) != 0); 1450 | } 1451 | - (BOOL) supportsSetValue 1452 | { 1453 | return ((_capabilities & kUVCControlSupportsSet) != 0); 1454 | } 1455 | - (BOOL) hasRange 1456 | { 1457 | return ((_capabilities & kUVCControlHasRange) != 0 ); 1458 | } 1459 | - (BOOL) hasStepSize 1460 | { 1461 | return ((_capabilities & kUVCControlHasStepSize) != 0 ); 1462 | } 1463 | - (BOOL) hasDefaultValue 1464 | { 1465 | return ((_capabilities & kUVCControlHasDefaultValue) != 0 ); 1466 | } 1467 | 1468 | // 1469 | 1470 | - (NSString*) controlName 1471 | { 1472 | return _controlName; 1473 | } 1474 | 1475 | // 1476 | 1477 | - (UVCValue*) currentValue 1478 | { 1479 | if ( [self readIntoCurrentValue] ) return _currentValue; 1480 | return nil; 1481 | } 1482 | - (UVCValue*) minimum { return _minimum; } 1483 | - (UVCValue*) maximum { return _maximum; } 1484 | - (UVCValue*) stepSize { return _stepSize; } 1485 | - (UVCValue*) defaultValue { return _defaultValue; } 1486 | 1487 | // 1488 | 1489 | - (BOOL) resetToDefaultValue 1490 | { 1491 | if ( [self hasDefaultValue] ) return [_parentController setValue:_defaultValue forControl:_controlIndex]; 1492 | return NO; 1493 | } 1494 | 1495 | // 1496 | 1497 | - (BOOL) setCurrentValueFromCString:(const char*)cString 1498 | flags:(UVCTypeScanFlags)flags 1499 | { 1500 | return [_currentValue scanCString:cString flags:flags minimum:_minimum maximum:_maximum stepSize:_stepSize defaultValue:_defaultValue]; 1501 | } 1502 | 1503 | // 1504 | 1505 | - (BOOL) readIntoCurrentValue 1506 | { 1507 | return [_parentController getValue:_currentValue forControl:_controlIndex]; 1508 | } 1509 | 1510 | // 1511 | 1512 | - (BOOL) writeFromCurrentValue 1513 | { 1514 | return [_parentController setValue:_currentValue forControl:_controlIndex]; 1515 | } 1516 | 1517 | // 1518 | 1519 | - (NSString*) summaryString 1520 | { 1521 | NSMutableString *asString = [[NSMutableString alloc] initWithFormat:@"%@ {\n type-description: {\n%@ },", 1522 | _controlName, [[_currentValue valueType] typeSummaryString]]; 1523 | if ( [self hasRange] ) { 1524 | [asString appendFormat:@"\n minimum: %@", [_minimum stringValue]]; 1525 | [asString appendFormat:@"\n maximum: %@", [_maximum stringValue]]; 1526 | } 1527 | if ( [self hasStepSize] ) { 1528 | [asString appendFormat:@"\n step-size: %@", [_stepSize stringValue]]; 1529 | } 1530 | if ( [self hasDefaultValue] ) { 1531 | [asString appendFormat:@"\n default-value: %@", [_defaultValue stringValue]]; 1532 | } 1533 | 1534 | UVCValue *curValue = [self currentValue]; 1535 | if ( curValue ) [asString appendFormat:@"\n current-value: %@", [curValue stringValue]]; 1536 | 1537 | [asString appendString:@"\n}"]; 1538 | 1539 | NSString *outString = [[asString copy] autorelease]; 1540 | [asString release]; 1541 | 1542 | return outString; 1543 | } 1544 | 1545 | @end 1546 | 1547 | // 1548 | 1549 | NSString *UVCTerminalControlScanningMode = @"scanning-mode"; 1550 | NSString *UVCTerminalControlAutoExposureMode = @"auto-exposure-mode"; 1551 | NSString *UVCTerminalControlAutoExposurePriority = @"auto-exposure-priority"; 1552 | NSString *UVCTerminalControlExposureTimeAbsolute = @"exposure-time-abs"; 1553 | NSString *UVCTerminalControlExposureTimeRelative = @"exposure-time-rel"; 1554 | NSString *UVCTerminalControlFocusAbsolute = @"focus-abs"; 1555 | NSString *UVCTerminalControlFocusRelative = @"focus-rel"; 1556 | NSString *UVCTerminalControlAutoFocus = @"auto-focus"; 1557 | NSString *UVCTerminalControlIrisAbsolute = @"iris-abs"; 1558 | NSString *UVCTerminalControlIrisRelative = @"iris-rel"; 1559 | NSString *UVCTerminalControlZoomAbsolute = @"zoom-abs"; 1560 | NSString *UVCTerminalControlZoomRelative = @"zoom-rel"; 1561 | NSString *UVCTerminalControlPanTiltAbsolute = @"pan-tilt-abs"; 1562 | NSString *UVCTerminalControlPanTiltRelative = @"pan-tilt-rel"; 1563 | NSString *UVCTerminalControlRollAbsolute = @"roll-abs"; 1564 | NSString *UVCTerminalControlRollRelative = @"roll-rel"; 1565 | NSString *UVCTerminalControlPrivacy = @"privacy"; 1566 | NSString *UVCTerminalControlFocusSimple = @"focus-simple"; 1567 | NSString *UVCTerminalControlWindow = @"window"; 1568 | NSString *UVCTerminalControlRegionOfInterest = @"region-of-interest"; 1569 | 1570 | // 1571 | 1572 | NSString *UVCProcessingUnitControlBacklightCompensation = @"backlight-compensation"; 1573 | NSString *UVCProcessingUnitControlBrightness = @"brightness"; 1574 | NSString *UVCProcessingUnitControlContrast = @"contrast"; 1575 | NSString *UVCProcessingUnitControlGain = @"gain"; 1576 | NSString *UVCProcessingUnitControlPowerLineFrequency = @"power-line-frequency"; 1577 | NSString *UVCProcessingUnitControlHue = @"hue"; 1578 | NSString *UVCProcessingUnitControlSaturation = @"saturation"; 1579 | NSString *UVCProcessingUnitControlSharpness = @"sharpness"; 1580 | NSString *UVCProcessingUnitControlGamma = @"gamma"; 1581 | NSString *UVCProcessingUnitControlWhiteBalanceTemperature = @"white-balance-temp"; 1582 | NSString *UVCProcessingUnitControlAutoWhiteBalanceTemperature = @"auto-white-balance-temp"; 1583 | NSString *UVCProcessingUnitControlWhiteBalanceComponent = @"white-balance-component"; 1584 | NSString *UVCProcessingUnitControlAutoWhiteBalanceComponent = @"auto-white-balance-component"; 1585 | NSString *UVCProcessingUnitControlDigitalMultiplier = @"digital-multiplier"; 1586 | NSString *UVCProcessingUnitControlDigitalMultiplierLimit = @"digital-multiplier-limit"; 1587 | NSString *UVCProcessingUnitControlAutoHue = @"auto-hue"; 1588 | NSString *UVCProcessingUnitControlAnalogVideoStandard = @"analog-video-standard"; 1589 | NSString *UVCProcessingUnitControlAnalogLockStatus = @"analog-lock-status"; 1590 | NSString *UVCProcessingUnitControlAutoContrast = @"auto-contrast"; 1591 | 1592 | --------------------------------------------------------------------------------