├── 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 |
--------------------------------------------------------------------------------