Commit dcda8c2e authored by François Beaufort's avatar François Beaufort Committed by Commit Bot

Reland "[PTZ] Add macOS support"

This reverts commit 1e32efc6.

Reason for revert:
Some tests used to fail in WebRTC Chromium Mac Tester bot.
Since macOS now supports detecting PTZ-capable cameras with this patch,
it is no longer correct to assume that pan_tilt_zoom_supported() will be
false for every enumerated camera, so let's remove the test named
VideoCaptureDeviceFactoryMacTest.ListDevicesWithNoPanTiltZoomSupport


Original change's description:
> Revert "[PTZ] Add macOS support"
>
> This reverts commit 6fc7c32c.
>
> Reason for revert: crbug.com/1128470
>
> Original change's description:
> > [PTZ] Add macOS support
> >
> > This CL makes sure a USB Webcam device on macOS properly advertises its
> > PTZ support. When supported, the pan, tilt, and zoom controls (ranges
> > and values) are available to websites that user has granted access to.
> >
> > Bug: 934063
> > Change-Id: I9d92d41fba0a6e9d518efb2501355d66cf77998f
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2368596
> > Reviewed-by: François Beaufort <beaufort.francois@gmail.com>
> > Reviewed-by: Guido Urdaneta <guidou@chromium.org>
> > Reviewed-by: Reilly Grant <reillyg@chromium.org>
> > Reviewed-by: Avi Drissman <avi@chromium.org>
> > Commit-Queue: François Beaufort <beaufort.francois@gmail.com>
> > Cr-Commit-Position: refs/heads/master@{#807387}
>
> TBR=avi@chromium.org,beaufort.francois@gmail.com,reillyg@chromium.org,guidou@chromium.org
>
> # Not skipping CQ checks because original CL landed > 1 day ago.
>
> Bug: 934063
> Change-Id: I88b1a41d28f5ad34f149f489b22eae6753e051dd
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2418577
> Reviewed-by: Markus Handell <handellm@google.com>
> Commit-Queue: Markus Handell <handellm@google.com>
> Cr-Commit-Position: refs/heads/master@{#808384}

TBR=avi@chromium.org,beaufort.francois@gmail.com,reillyg@chromium.org,guidou@chromium.org,handellm@google.com

# Not skipping CQ checks because this is a reland.

Bug: 934063
Change-Id: I82b757e1b5d3e6e62f520982ac5e4ef216849550
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2420031Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarMarkus Handell <handellm@google.com>
Reviewed-by: default avatarFrançois Beaufort <beaufort.francois@gmail.com>
Commit-Queue: François Beaufort <beaufort.francois@gmail.com>
Cr-Commit-Position: refs/heads/master@{#809727}
parent a1e91012
...@@ -120,10 +120,11 @@ void VideoCaptureDeviceFactoryMac::GetDevicesInfo( ...@@ -120,10 +120,11 @@ void VideoCaptureDeviceFactoryMac::GetDevicesInfo(
: VideoCaptureTransportType::OTHER_TRANSPORT; : VideoCaptureTransportType::OTHER_TRANSPORT;
const std::string model_id = VideoCaptureDeviceMac::GetDeviceModelId( const std::string model_id = VideoCaptureDeviceMac::GetDeviceModelId(
device_id, capture_api, device_transport_type); device_id, capture_api, device_transport_type);
bool pan_tilt_zoom_supported =
VideoCaptureDeviceMac::IsPanTiltZoomSupported(model_id);
VideoCaptureDeviceDescriptor descriptor( VideoCaptureDeviceDescriptor descriptor(
[[[capture_devices valueForKey:key] deviceName] UTF8String], device_id, [[[capture_devices valueForKey:key] deviceName] UTF8String], device_id,
model_id, capture_api, model_id, capture_api, pan_tilt_zoom_supported, device_transport_type);
/*pan_tilt_zoom_supported=*/false, device_transport_type);
if (IsDeviceBlocked(descriptor)) if (IsDeviceBlocked(descriptor))
continue; continue;
devices_info.emplace_back(descriptor); devices_info.emplace_back(descriptor);
......
...@@ -47,21 +47,6 @@ TEST_P(VideoCaptureDeviceFactoryMacTest, ListDevicesAVFoundation) { ...@@ -47,21 +47,6 @@ TEST_P(VideoCaptureDeviceFactoryMacTest, ListDevicesAVFoundation) {
})); }));
} }
TEST_P(VideoCaptureDeviceFactoryMacTest, ListDevicesWithNoPanTiltZoomSupport) {
RunTestCase(base::BindOnce([]() {
VideoCaptureDeviceFactoryMac video_capture_device_factory;
std::vector<VideoCaptureDeviceInfo> devices_info =
GetDevicesInfo(&video_capture_device_factory);
if (devices_info.empty()) {
DVLOG(1) << "No camera available. Exiting test.";
return;
}
for (const auto& device : devices_info)
EXPECT_FALSE(device.descriptor.pan_tilt_zoom_supported());
}));
}
INSTANTIATE_TEST_SUITE_P(, INSTANTIATE_TEST_SUITE_P(,
VideoCaptureDeviceFactoryMacTest, VideoCaptureDeviceFactoryMacTest,
::testing::Values(AVFoundationCaptureV2::kEnabled, ::testing::Values(AVFoundationCaptureV2::kEnabled,
......
...@@ -103,6 +103,8 @@ class VideoCaptureDeviceMac ...@@ -103,6 +103,8 @@ class VideoCaptureDeviceMac
VideoCaptureApi capture_api, VideoCaptureApi capture_api,
VideoCaptureTransportType transport_type); VideoCaptureTransportType transport_type);
static bool IsPanTiltZoomSupported(const std::string& device_model);
private: private:
void SetErrorState(VideoCaptureError error, void SetErrorState(VideoCaptureError error,
const base::Location& from_here, const base::Location& from_here,
......
...@@ -32,6 +32,9 @@ ...@@ -32,6 +32,9 @@
#include "media/capture/video/mac/video_capture_device_avfoundation_utils_mac.h" #include "media/capture/video/mac/video_capture_device_avfoundation_utils_mac.h"
#include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/size.h"
using ScopedIOUSBInterfaceInterface =
base::mac::ScopedIOPluginInterface<IOUSBInterfaceInterface220>;
@implementation DeviceNameAndTransportType @implementation DeviceNameAndTransportType
- (id)initWithName:(NSString*)deviceName transportType:(int32_t)transportType { - (id)initWithName:(NSString*)deviceName transportType:(int32_t)transportType {
...@@ -64,27 +67,59 @@ const size_t kVidPidSize = 4; ...@@ -64,27 +67,59 @@ const size_t kVidPidSize = 4;
// The following constants are extracted from the specification "Universal // The following constants are extracted from the specification "Universal
// Serial Bus Device Class Definition for Video Devices", Rev. 1.1 June 1, 2005. // Serial Bus Device Class Definition for Video Devices", Rev. 1.1 June 1, 2005.
// http://www.usb.org/developers/devclass_docs/USB_Video_Class_1_1.zip // http://www.usb.org/developers/devclass_docs/USB_Video_Class_1_1.zip
// CS_INTERFACE: Sec. A.4 "Video Class-Specific Descriptor Types". // Sec. A.4 "Video Class-Specific Descriptor Types".
const int kVcCsInterface = 0x24; const int kVcCsInterface = 0x24; // CS_INTERFACE
// VC_PROCESSING_UNIT: Sec. A.5 "Video Class-Specific VC Interface Descriptor // Sec. A.5 "Video Class-Specific VC Interface Descriptor Subtypes".
// Subtypes". const int kVcInputTerminal = 0x2; // VC_INPUT_TERMINAL
const int kVcProcessingUnit = 0x5; const int kVcProcessingUnit = 0x5; // VC_PROCESSING_UNIT
// SET_CUR: Sec. A.8 "Video Class-Specific Request Codes". // Sec. A.8 "Video Class-Specific Request Codes".
const int kVcRequestCodeSetCur = 0x1; const int kVcRequestCodeSetCur = 0x1; // SET_CUR
// PU_POWER_LINE_FREQUENCY_CONTROL: Sec. A.9.5 "Processing Unit Control const int kVcRequestCodeGetCur = 0x81; // GET_CUR
// Selectors". const int kVcRequestCodeGetMin = 0x82; // GET_MIN
const int kPuPowerLineFrequencyControl = 0x5; const int kVcRequestCodeGetMax = 0x83; // GET_MAX
// Sec. 4.2.2.3.5 Power Line Frequency Control. const int kVcRequestCodeGetRes = 0x84; // GET_RES
// Sec. A.9.4. "Camera Terminal Control Selectors".
const int kCtZoomAbsoluteControl = 0x0b; // CT_ZOOM_ABSOLUTE_CONTROL
const int kCtPanTiltAbsoluteControl = 0x0d; // CT_PANTILT_ABSOLUTE_CONTROL
// Sec. A.9.5 "Processing Unit Control Selectors".
const int kPuPowerLineFrequencyControl =
0x5; // PU_POWER_LINE_FREQUENCY_CONTROL
// Sec. 4.2.2.3.5 "Power Line Frequency Control".
const int k50Hz = 1; const int k50Hz = 1;
const int k60Hz = 2; const int k60Hz = 2;
const int kPuPowerLineFrequencyControlCommandSize = 1; const int kPuPowerLineFrequencyControlCommandSize = 1;
// Sec. 4.2.2.1.11 "Zoom (Absolute) Control".
const int kCtZoomAbsoluteControlCommandSize = 2;
// Sec. 4.2.2.1.13 "PanTilt (Absolute) Control".
const int kCtPanTiltAbsoluteControlCommandSize = 8;
// Addition to the IOUSB family of structures, with subtype and unit ID. // Addition to the IOUSB family of structures, with subtype and unit ID.
typedef struct IOUSBInterfaceDescriptor { // Sec. 3.7.2 "Class-Specific VC Interface Descriptor"
typedef struct VcCsInterfaceDescriptor {
IOUSBDescriptorHeader header; IOUSBDescriptorHeader header;
UInt8 bDescriptorSubType; UInt8 bDescriptorSubType;
UInt8 bUnitID; UInt8 bUnitID;
} IOUSBInterfaceDescriptor; } VcCsInterfaceDescriptor;
static bool FindDeviceWithVendorAndProductIds(int vendor_id,
int product_id,
io_iterator_t* usb_iterator) {
// Compose a search dictionary with vendor and product ID.
base::ScopedCFTypeRef<CFMutableDictionaryRef> query_dictionary(
IOServiceMatching(kIOUSBDeviceClassName));
CFDictionarySetValue(query_dictionary, CFSTR(kUSBVendorName),
base::mac::NSToCFCast(@(vendor_id)));
CFDictionarySetValue(query_dictionary, CFSTR(kUSBProductName),
base::mac::NSToCFCast(@(product_id)));
kern_return_t kr = IOServiceGetMatchingServices(
kIOMasterPortDefault, query_dictionary.release(), usb_iterator);
if (kr != kIOReturnSuccess) {
DLOG(ERROR) << "No devices found with specified Vendor and Product ID.";
return false;
}
return true;
}
// Tries to create a user-side device interface for a given USB device. Returns // Tries to create a user-side device interface for a given USB device. Returns
// true if interface was found and passes it back in |device_interface|. The // true if interface was found and passes it back in |device_interface|. The
...@@ -169,33 +204,29 @@ static void SetAntiFlickerInVideoControlInterface( ...@@ -169,33 +204,29 @@ static void SetAntiFlickerInVideoControlInterface(
const PowerLineFrequency frequency) { const PowerLineFrequency frequency) {
// Create, the control interface for the found plugin, and release // Create, the control interface for the found plugin, and release
// the intermediate plugin. // the intermediate plugin.
IOUSBInterfaceInterface** control_interface = NULL; ScopedIOUSBInterfaceInterface control_interface;
HRESULT res = HRESULT res =
(*plugin_interface) (*plugin_interface)
->QueryInterface(plugin_interface, ->QueryInterface(
CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), plugin_interface, CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
reinterpret_cast<LPVOID*>(&control_interface)); reinterpret_cast<LPVOID*>(control_interface.InitializeInto()));
if (!SUCCEEDED(res) || !control_interface) { if (!SUCCEEDED(res) || !control_interface) {
DLOG(ERROR) << "Couldn’t create control interface"; DLOG(ERROR) << "Couldn’t create control interface";
return; return;
} }
base::mac::ScopedIOPluginInterface<IOUSBInterfaceInterface>
control_interface_ref(control_interface);
// Find the device's unit ID presenting type 0x24 (kVcCsInterface) and // Find the device's unit ID presenting type 0x24 (kVcCsInterface) and
// subtype 0x5 (kVcProcessingUnit). Inside this unit is where we find the // subtype 0x5 (kVcProcessingUnit). Inside this unit is where we find the
// power line frequency removal setting, and this id is device dependent. // power line frequency removal setting, and this id is device dependent.
int real_unit_id = -1; int real_unit_id = -1;
IOUSBDescriptorHeader* descriptor = NULL; IOUSBDescriptorHeader* descriptor = NULL;
IOUSBInterfaceDescriptor* cs_descriptor = NULL; while ((descriptor =
IOUSBInterfaceInterface220** interface = (*control_interface)
reinterpret_cast<IOUSBInterfaceInterface220**>(control_interface); ->FindNextAssociatedDescriptor(control_interface.get(),
while ((descriptor = (*interface) descriptor, kVcCsInterface))) {
->FindNextAssociatedDescriptor(interface, descriptor, auto* cs_descriptor =
kUSBAnyDesc))) { reinterpret_cast<VcCsInterfaceDescriptor*>(descriptor);
cs_descriptor = reinterpret_cast<IOUSBInterfaceDescriptor*>(descriptor); if (cs_descriptor->bDescriptorSubType == kVcProcessingUnit) {
if ((descriptor->bDescriptorType == kVcCsInterface) &&
(cs_descriptor->bDescriptorSubType == kVcProcessingUnit)) {
real_unit_id = cs_descriptor->bUnitID; real_unit_id = cs_descriptor->bUnitID;
break; break;
} }
...@@ -254,22 +285,11 @@ static void SetAntiFlickerInUsbDevice(const int vendor_id, ...@@ -254,22 +285,11 @@ static void SetAntiFlickerInUsbDevice(const int vendor_id,
DVLOG(1) << "Setting Power Line Frequency to " << static_cast<int>(frequency) DVLOG(1) << "Setting Power Line Frequency to " << static_cast<int>(frequency)
<< " Hz, device " << std::hex << vendor_id << "-" << product_id; << " Hz, device " << std::hex << vendor_id << "-" << product_id;
// Compose a search dictionary with vendor and product ID. base::mac::ScopedIOObject<io_iterator_t> usb_iterator;
base::ScopedCFTypeRef<CFMutableDictionaryRef> query_dictionary( if (!FindDeviceWithVendorAndProductIds(vendor_id, product_id,
IOServiceMatching(kIOUSBDeviceClassName)); usb_iterator.InitializeInto())) {
CFDictionarySetValue(query_dictionary, CFSTR(kUSBVendorName),
base::mac::NSToCFCast(@(vendor_id)));
CFDictionarySetValue(query_dictionary, CFSTR(kUSBProductName),
base::mac::NSToCFCast(@(product_id)));
io_iterator_t usb_iterator;
kern_return_t kr = IOServiceGetMatchingServices(
kIOMasterPortDefault, query_dictionary.release(), &usb_iterator);
if (kr != kIOReturnSuccess) {
DLOG(ERROR) << "No devices found with specified Vendor and Product ID.";
return; return;
} }
base::mac::ScopedIOObject<io_iterator_t> usb_iterator_ref(usb_iterator);
while (io_service_t usb_device = IOIteratorNext(usb_iterator)) { while (io_service_t usb_device = IOIteratorNext(usb_iterator)) {
base::mac::ScopedIOObject<io_service_t> usb_device_ref(usb_device); base::mac::ScopedIOObject<io_service_t> usb_device_ref(usb_device);
...@@ -294,6 +314,287 @@ static void SetAntiFlickerInUsbDevice(const int vendor_id, ...@@ -294,6 +314,287 @@ static void SetAntiFlickerInUsbDevice(const int vendor_id,
} }
} }
// Create an empty IOUSBDevRequest for a USB device to either control or get
// information from pan, tilt, and zoom controls.
static IOUSBDevRequest CreateEmptyPanTiltZoomRequest(
IOUSBInterfaceInterface220** control_interface,
int unit_id,
int request_code,
int endpoint_direction,
int control_selector,
int control_command_size) {
DCHECK((endpoint_direction == kUSBIn) || (endpoint_direction == kUSBOut));
UInt8 interface_number;
(*control_interface)
->GetInterfaceNumber(control_interface, &interface_number);
IOUSBDevRequest command;
command.bmRequestType =
USBmakebmRequestType(endpoint_direction, kUSBClass, kUSBInterface);
command.bRequest = request_code;
command.wIndex = (unit_id << 8) | interface_number;
command.wValue = (control_selector << 8);
command.wLength = control_command_size;
command.wLenDone = 0;
return command;
}
// Send USB request to get information about pan and tilt controls.
// Returns true if the request is successful. To send the request, the interface
// must be opened already.
static bool SendPanTiltControlRequest(
IOUSBInterfaceInterface220** control_interface,
int unit_id,
int request_code,
int* pan_result,
int* tilt_result) {
IOUSBDevRequest command = CreateEmptyPanTiltZoomRequest(
control_interface, unit_id, request_code, kUSBIn,
kCtPanTiltAbsoluteControl, kCtPanTiltAbsoluteControlCommandSize);
int32_t data[2];
command.pData = &data;
IOReturn ret =
(*control_interface)->ControlRequest(control_interface, 0, &command);
DLOG_IF(ERROR, ret != kIOReturnSuccess)
<< "Control pan tilt request"
<< " failed (0x" << std::hex << ret << "), unit id: " << unit_id;
if (ret != kIOReturnSuccess)
return false;
*pan_result = USBToHostLong(data[0]);
*tilt_result = USBToHostLong(data[1]);
return true;
}
// Send USB request to get information about zoom control.
// Returns true if the request is successful. To send the request, the interface
// must be opened already.
static bool SendZoomControlRequest(
IOUSBInterfaceInterface220** control_interface,
int unit_id,
int request_code,
int* result) {
IOUSBDevRequest command = CreateEmptyPanTiltZoomRequest(
control_interface, unit_id, request_code, kUSBIn, kCtZoomAbsoluteControl,
kCtZoomAbsoluteControlCommandSize);
uint16_t data;
command.pData = &data;
IOReturn ret =
(*control_interface)->ControlRequest(control_interface, 0, &command);
DLOG_IF(ERROR, ret != kIOReturnSuccess)
<< "Control zoom request"
<< " failed (0x" << std::hex << ret << "), unit id: " << unit_id;
if (ret != kIOReturnSuccess)
return false;
*result = USBToHostLong(data);
return true;
}
// Retrieves the control range and current value of pan and tilt controls.
// The interface must be opened already.
static void GetPanTiltControlRangeAndCurrent(
IOUSBInterfaceInterface220** control_interface,
int unit_id,
mojom::Range* pan_range,
mojom::Range* tilt_range) {
int pan_max, pan_min, pan_step, pan_current;
int tilt_max, tilt_min, tilt_step, tilt_current;
if (!SendPanTiltControlRequest(control_interface, unit_id,
kVcRequestCodeGetMax, &pan_max, &tilt_max) ||
!SendPanTiltControlRequest(control_interface, unit_id,
kVcRequestCodeGetMin, &pan_min, &tilt_min) ||
!SendPanTiltControlRequest(control_interface, unit_id,
kVcRequestCodeGetRes, &pan_step, &tilt_step) ||
!SendPanTiltControlRequest(control_interface, unit_id,
kVcRequestCodeGetCur, &pan_current,
&tilt_current)) {
return;
}
pan_range->max = pan_max;
pan_range->min = pan_min;
pan_range->step = pan_step;
pan_range->current = pan_current;
tilt_range->max = tilt_max;
tilt_range->min = tilt_min;
tilt_range->step = tilt_step;
tilt_range->current = tilt_current;
}
// Retrieves the control range and current value of a zoom control. The
// interface must be opened already.
static void GetZoomControlRangeAndCurrent(
IOUSBInterfaceInterface220** control_interface,
int unit_id,
mojom::Range* zoom_range) {
int max, min, step, current;
if (!SendZoomControlRequest(control_interface, unit_id, kVcRequestCodeGetMax,
&max) ||
!SendZoomControlRequest(control_interface, unit_id, kVcRequestCodeGetMin,
&min) ||
!SendZoomControlRequest(control_interface, unit_id, kVcRequestCodeGetRes,
&step) ||
!SendZoomControlRequest(control_interface, unit_id, kVcRequestCodeGetCur,
&current)) {
return;
}
zoom_range->max = max;
zoom_range->min = min;
zoom_range->step = step;
zoom_range->current = current;
}
// Set pan and tilt values for a USB camera device. The interface must be opened
// already.
static void SetPanTiltInUsbDevice(
IOUSBInterfaceInterface220** control_interface,
int unit_id,
base::Optional<int> pan,
base::Optional<int> tilt) {
if (!pan.has_value() && !tilt.has_value())
return;
int pan_current, tilt_current;
if ((!pan.has_value() || !tilt.has_value()) &&
!SendPanTiltControlRequest(control_interface, unit_id,
kVcRequestCodeGetCur, &pan_current,
&tilt_current)) {
return;
}
uint32_t pan_tilt_data[2];
pan_tilt_data[0] =
CFSwapInt32HostToLittle((uint32_t)pan.value_or(pan_current));
pan_tilt_data[1] =
CFSwapInt32HostToLittle((uint32_t)tilt.value_or(tilt_current));
IOUSBDevRequest command = CreateEmptyPanTiltZoomRequest(
control_interface, unit_id, kVcRequestCodeSetCur, kUSBOut,
kCtPanTiltAbsoluteControl, kCtPanTiltAbsoluteControlCommandSize);
command.pData = pan_tilt_data;
IOReturn ret =
(*control_interface)->ControlRequest(control_interface, 0, &command);
DLOG_IF(ERROR, ret != kIOReturnSuccess)
<< "Control request"
<< " failed (0x" << std::hex << ret << "), unit id: " << unit_id
<< " pan value: " << pan.value_or(pan_current)
<< " tilt value: " << tilt.value_or(tilt_current);
DVLOG_IF(1, ret == kIOReturnSuccess)
<< "Setting pan value to " << pan.value_or(pan_current)
<< " and tilt value to " << tilt.value_or(tilt_current);
}
// Set zoom value for a USB camera device. The interface must be opened already.
static void SetZoomInUsbDevice(IOUSBInterfaceInterface220** control_interface,
int unit_id,
int zoom) {
IOUSBDevRequest command = CreateEmptyPanTiltZoomRequest(
control_interface, unit_id, kVcRequestCodeSetCur, kUSBOut,
kCtZoomAbsoluteControl, kCtZoomAbsoluteControlCommandSize);
command.pData = &zoom;
IOReturn ret =
(*control_interface)->ControlRequest(control_interface, 0, &command);
DLOG_IF(ERROR, ret != kIOReturnSuccess)
<< "Control request"
<< " failed (0x" << std::hex << ret << "), unit id: " << unit_id
<< " zoom value: " << zoom;
DVLOG_IF(1, ret == kIOReturnSuccess) << "Setting zoom value to " << zoom;
}
// Open the pan, tilt, zoom interface in a USB webcam identified by
// |device_model|. Returns interface when it is succcessfully opened. You have
// to close the interface manually when you're done.
static ScopedIOUSBInterfaceInterface OpenPanTiltZoomControlInterface(
std::string device_model,
int* unit_id) {
if (device_model.length() <= 2 * kVidPidSize) {
return ScopedIOUSBInterfaceInterface();
}
std::string vendor_id = device_model.substr(0, kVidPidSize);
std::string product_id = device_model.substr(kVidPidSize + 1);
int vendor_id_as_int, product_id_as_int;
if (!base::HexStringToInt(vendor_id, &vendor_id_as_int) ||
!base::HexStringToInt(product_id, &product_id_as_int)) {
return ScopedIOUSBInterfaceInterface();
}
base::mac::ScopedIOObject<io_iterator_t> usb_iterator;
if (!FindDeviceWithVendorAndProductIds(vendor_id_as_int, product_id_as_int,
usb_iterator.InitializeInto())) {
return ScopedIOUSBInterfaceInterface();
}
base::mac::ScopedIOPluginInterface<IOCFPlugInInterface>
video_control_interface;
while (io_service_t usb_device = IOIteratorNext(usb_iterator)) {
base::mac::ScopedIOObject<io_service_t> usb_device_ref(usb_device);
base::mac::ScopedIOPluginInterface<IOUSBDeviceInterface> device_interface;
if (!FindDeviceInterfaceInUsbDevice(vendor_id_as_int, product_id_as_int,
usb_device,
device_interface.InitializeInto())) {
continue;
}
if (FindVideoControlInterfaceInDeviceInterface(
device_interface, video_control_interface.InitializeInto())) {
break;
}
}
if (video_control_interface == nullptr) {
return ScopedIOUSBInterfaceInterface();
}
// Create the control interface for the found plugin, and release
// the intermediate plugin.
ScopedIOUSBInterfaceInterface control_interface;
HRESULT res =
(*video_control_interface)
->QueryInterface(
video_control_interface,
CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID220),
reinterpret_cast<LPVOID*>(control_interface.InitializeInto()));
if (!SUCCEEDED(res) || !control_interface) {
DLOG(ERROR) << "Couldn’t get control interface";
return ScopedIOUSBInterfaceInterface();
}
// Find the device's unit ID presenting type 0x24 (kVcCsInterface) and
// subtype 0x2 (kVcInputTerminal). Inside this unit is where we find the
// settings for pan, tilt, and zoom, and this id is device dependent.
IOUSBDescriptorHeader* descriptor = nullptr;
while ((descriptor =
(*control_interface)
->FindNextAssociatedDescriptor(control_interface.get(),
descriptor, kVcCsInterface))) {
auto* cs_descriptor =
reinterpret_cast<VcCsInterfaceDescriptor*>(descriptor);
if (cs_descriptor->bDescriptorSubType == kVcInputTerminal) {
*unit_id = cs_descriptor->bUnitID;
break;
}
}
DVLOG_IF(1, *unit_id == -1)
<< "This USB device doesn't seem to have a "
<< " VC_INPUT_TERMINAL. Pan, tilt, zoom are not available.";
if (*unit_id == -1)
return ScopedIOUSBInterfaceInterface();
if ((*control_interface)->USBInterfaceOpen(control_interface) !=
kIOReturnSuccess) {
DLOG(ERROR) << "Unable to open control interface";
return ScopedIOUSBInterfaceInterface();
}
return control_interface;
}
VideoCaptureDeviceMac::VideoCaptureDeviceMac( VideoCaptureDeviceMac::VideoCaptureDeviceMac(
const VideoCaptureDeviceDescriptor& device_descriptor) const VideoCaptureDeviceDescriptor& device_descriptor)
: device_descriptor_(device_descriptor), : device_descriptor_(device_descriptor),
...@@ -350,11 +651,11 @@ void VideoCaptureDeviceMac::AllocateAndStart( ...@@ -350,11 +651,11 @@ void VideoCaptureDeviceMac::AllocateAndStart(
device_descriptor_.transport_type); device_descriptor_.transport_type);
if (device_model.length() > 2 * kVidPidSize) { if (device_model.length() > 2 * kVidPidSize) {
std::string vendor_id = device_model.substr(0, kVidPidSize); std::string vendor_id = device_model.substr(0, kVidPidSize);
std::string model_id = device_model.substr(kVidPidSize + 1); std::string product_id = device_model.substr(kVidPidSize + 1);
int vendor_id_as_int, model_id_as_int; int vendor_id_as_int, product_id_as_int;
if (base::HexStringToInt(base::StringPiece(vendor_id), &vendor_id_as_int) && if (base::HexStringToInt(vendor_id, &vendor_id_as_int) &&
base::HexStringToInt(base::StringPiece(model_id), &model_id_as_int)) { base::HexStringToInt(product_id, &product_id_as_int)) {
SetAntiFlickerInUsbDevice(vendor_id_as_int, model_id_as_int, SetAntiFlickerInUsbDevice(vendor_id_as_int, product_id_as_int,
GetPowerLineFrequency(params)); GetPowerLineFrequency(params));
} }
} }
...@@ -405,6 +706,21 @@ void VideoCaptureDeviceMac::GetPhotoState(GetPhotoStateCallback callback) { ...@@ -405,6 +706,21 @@ void VideoCaptureDeviceMac::GetPhotoState(GetPhotoStateCallback callback) {
capture_format_.frame_size.width(), capture_format_.frame_size.width(), capture_format_.frame_size.width(), capture_format_.frame_size.width(),
capture_format_.frame_size.width(), 0 /* step */); capture_format_.frame_size.width(), 0 /* step */);
const std::string device_model = GetDeviceModelId(
device_descriptor_.device_id, device_descriptor_.capture_api,
device_descriptor_.transport_type);
int unit_id = -1;
ScopedIOUSBInterfaceInterface control_interface(
OpenPanTiltZoomControlInterface(device_model, &unit_id));
if (control_interface) {
GetPanTiltControlRangeAndCurrent(control_interface, unit_id,
photo_state->pan.get(),
photo_state->tilt.get());
GetZoomControlRangeAndCurrent(control_interface, unit_id,
photo_state->zoom.get());
(*control_interface)->USBInterfaceClose(control_interface);
}
std::move(callback).Run(std::move(photo_state)); std::move(callback).Run(std::move(photo_state));
} }
...@@ -420,6 +736,31 @@ void VideoCaptureDeviceMac::SetPhotoOptions(mojom::PhotoSettingsPtr settings, ...@@ -420,6 +736,31 @@ void VideoCaptureDeviceMac::SetPhotoOptions(mojom::PhotoSettingsPtr settings,
settings->has_fill_light_mode || settings->has_red_eye_reduction) { settings->has_fill_light_mode || settings->has_red_eye_reduction) {
return; return;
} }
if (settings->has_pan || settings->has_tilt || settings->has_zoom) {
const std::string device_model = GetDeviceModelId(
device_descriptor_.device_id, device_descriptor_.capture_api,
device_descriptor_.transport_type);
int unit_id = -1;
ScopedIOUSBInterfaceInterface control_interface(
OpenPanTiltZoomControlInterface(device_model, &unit_id));
if (!control_interface)
return;
if (settings->has_pan || settings->has_tilt) {
SetPanTiltInUsbDevice(
control_interface, unit_id,
settings->has_pan ? base::make_optional(settings->pan)
: base::nullopt,
settings->has_tilt ? base::make_optional(settings->tilt)
: base::nullopt);
}
if (settings->has_zoom) {
SetZoomInUsbDevice(control_interface, unit_id, settings->zoom);
}
(*control_interface)->USBInterfaceClose(control_interface);
}
std::move(callback).Run(true); std::move(callback).Run(true);
} }
...@@ -539,6 +880,41 @@ std::string VideoCaptureDeviceMac::GetDeviceModelId( ...@@ -539,6 +880,41 @@ std::string VideoCaptureDeviceMac::GetDeviceModelId(
return id_vendor + ":" + id_product; return id_vendor + ":" + id_product;
} }
// Check if the video capture device supports at least one of pan, tilt and zoom
// controls.
// static
bool VideoCaptureDeviceMac::IsPanTiltZoomSupported(
const std::string& device_model) {
int unit_id = -1;
ScopedIOUSBInterfaceInterface control_interface(
OpenPanTiltZoomControlInterface(device_model, &unit_id));
if (!control_interface)
return false;
bool result = false;
int zoom_max, zoom_min = 0;
if (SendZoomControlRequest(control_interface, unit_id, kVcRequestCodeGetMax,
&zoom_max) &&
SendZoomControlRequest(control_interface, unit_id, kVcRequestCodeGetMin,
&zoom_min) &&
zoom_min < zoom_max) {
result = true;
}
int pan_max, pan_min = 0;
int tilt_max, tilt_min = 0;
if (!result &&
SendPanTiltControlRequest(control_interface, unit_id,
kVcRequestCodeGetMax, &pan_max, &tilt_max) &&
SendPanTiltControlRequest(control_interface, unit_id,
kVcRequestCodeGetMin, &pan_min, &tilt_min) &&
((pan_min < pan_max || tilt_min < tilt_max))) {
result = true;
}
(*control_interface)->USBInterfaceClose(control_interface);
return result;
}
void VideoCaptureDeviceMac::SetErrorState(VideoCaptureError error, void VideoCaptureDeviceMac::SetErrorState(VideoCaptureError error,
const base::Location& from_here, const base::Location& from_here,
const std::string& reason) { const std::string& reason) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment