summaryrefslogtreecommitdiffstats
path: root/media/video
diff options
context:
space:
mode:
Diffstat (limited to 'media/video')
-rw-r--r--media/video/capture/mac/video_capture_device_mac.mm261
1 files changed, 261 insertions, 0 deletions
diff --git a/media/video/capture/mac/video_capture_device_mac.mm b/media/video/capture/mac/video_capture_device_mac.mm
index fd0a68d..fb47719 100644
--- a/media/video/capture/mac/video_capture_device_mac.mm
+++ b/media/video/capture/mac/video_capture_device_mac.mm
@@ -4,10 +4,17 @@
#include "media/video/capture/mac/video_capture_device_mac.h"
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/usb/USBSpec.h>
+
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/mac/scoped_ioobject.h"
+#include "base/mac/scoped_ioplugininterface.h"
+#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#import "media/video/capture/mac/avfoundation_glue.h"
#import "media/video/capture/mac/platform_video_capturing_mac.h"
@@ -41,6 +48,31 @@ const struct Resolution* const kWellSupportedResolutions[] = {
// aspect ratio is tolerable.
const float kMaxPixelAspectRatio = 1.15;
+// The following constants are extracted from the specification "Universal
+// 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
+// CS_INTERFACE: Sec. A.4 "Video Class-Specific Descriptor Types".
+const int kVcCsInterface = 0x24;
+// VC_PROCESSING_UNIT: Sec. A.5 "Video Class-Specific VC Interface Descriptor
+// Subtypes".
+const int kVcProcessingUnit = 0x5;
+// SET_CUR: Sec. A.8 "Video Class-Specific Request Codes".
+const int kVcRequestCodeSetCur = 0x1;
+// PU_POWER_LINE_FREQUENCY_CONTROL: Sec. A.9.5 "Processing Unit Control
+// Selectors".
+const int kPuPowerLineFrequencyControl = 0x5;
+// Sec. 4.2.2.3.5 Power Line Frequency Control.
+const int k50Hz = 1;
+const int k60Hz = 2;
+const int kPuPowerLineFrequencyControlCommandSize = 1;
+
+// Addition to the IOUSB family of structures, with subtype and unit ID.
+typedef struct IOUSBInterfaceDescriptor {
+ IOUSBDescriptorHeader header;
+ UInt8 bDescriptorSubType;
+ UInt8 bUnitID;
+} IOUSBInterfaceDescriptor;
+
// TODO(ronghuawu): Replace this with CapabilityList::GetBestMatchedCapability.
void GetBestMatchSupportedResolution(int* width, int* height) {
int min_diff = kint32max;
@@ -61,6 +93,219 @@ void GetBestMatchSupportedResolution(int* width, int* height) {
*height = matched_height;
}
+// 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
+// caller should release |device_interface|.
+static bool FindDeviceInterfaceInUsbDevice(
+ const int vendor_id,
+ const int product_id,
+ const io_service_t usb_device,
+ IOUSBDeviceInterface*** device_interface) {
+ // Create a plug-in, i.e. a user-side controller to manipulate USB device.
+ IOCFPlugInInterface** plugin;
+ SInt32 score; // Unused, but required for IOCreatePlugInInterfaceForService.
+ kern_return_t kr =
+ IOCreatePlugInInterfaceForService(usb_device,
+ kIOUSBDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugin,
+ &score);
+ if (kr != kIOReturnSuccess || !plugin) {
+ DLOG(ERROR) << "IOCreatePlugInInterfaceForService";
+ return false;
+ }
+ base::mac::ScopedIOPluginInterface<IOCFPlugInInterface> plugin_ref(plugin);
+
+ // Fetch the Device Interface from the plug-in.
+ HRESULT res =
+ (*plugin)->QueryInterface(plugin,
+ CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
+ reinterpret_cast<LPVOID*>(device_interface));
+ if (!SUCCEEDED(res) || !*device_interface) {
+ DLOG(ERROR) << "QueryInterface, couldn't create interface to USB";
+ return false;
+ }
+ return true;
+}
+
+// Tries to find a Video Control type interface inside a general USB device
+// interface |device_interface|, and returns it in |video_control_interface| if
+// found. The returned interface must be released in the caller.
+static bool FindVideoControlInterfaceInDeviceInterface(
+ IOUSBDeviceInterface** device_interface,
+ IOCFPlugInInterface*** video_control_interface) {
+ // Create an iterator to the list of Video-AVControl interfaces of the device,
+ // then get the first interface in the list.
+ io_iterator_t interface_iterator;
+ IOUSBFindInterfaceRequest interface_request = {
+ .bInterfaceClass = kUSBVideoInterfaceClass,
+ .bInterfaceSubClass = kUSBVideoControlSubClass,
+ .bInterfaceProtocol = kIOUSBFindInterfaceDontCare,
+ .bAlternateSetting = kIOUSBFindInterfaceDontCare
+ };
+ kern_return_t kr =
+ (*device_interface)->CreateInterfaceIterator(device_interface,
+ &interface_request,
+ &interface_iterator);
+ if (kr != kIOReturnSuccess) {
+ DLOG(ERROR) << "Could not create an iterator to the device's interfaces.";
+ return false;
+ }
+ base::mac::ScopedIOObject<io_iterator_t> iterator_ref(interface_iterator);
+
+ // There should be just one interface matching the class-subclass desired.
+ io_service_t found_interface;
+ found_interface = IOIteratorNext(interface_iterator);
+ if (!found_interface) {
+ DLOG(ERROR) << "Could not find a Video-AVControl interface in the device.";
+ return false;
+ }
+ base::mac::ScopedIOObject<io_service_t> found_interface_ref(found_interface);
+
+ // Create a user side controller (i.e. a "plug-in") for the found interface.
+ SInt32 score;
+ kr = IOCreatePlugInInterfaceForService(found_interface,
+ kIOUSBInterfaceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ video_control_interface,
+ &score);
+ if (kr != kIOReturnSuccess || !*video_control_interface) {
+ DLOG(ERROR) << "IOCreatePlugInInterfaceForService";
+ return false;
+ }
+ return true;
+}
+
+// Creates a control interface for |plugin_interface| and produces a command to
+// set the appropriate Power Line frequency for flicker removal.
+static void SetAntiFlickerInVideoControlInterface(
+ IOCFPlugInInterface** plugin_interface,
+ const int frequency) {
+ // Create, the control interface for the found plug-in, and release
+ // the intermediate plug-in.
+ IOUSBInterfaceInterface** control_interface = NULL;
+ HRESULT res = (*plugin_interface)->QueryInterface(
+ plugin_interface,
+ CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
+ reinterpret_cast<LPVOID*>(&control_interface));
+ if (!SUCCEEDED(res) || !control_interface ) {
+ DLOG(ERROR) << "Couldn’t create control interface";
+ return;
+ }
+ base::mac::ScopedIOPluginInterface<IOUSBInterfaceInterface>
+ control_interface_ref(control_interface);
+
+ // Find the device's unit ID presenting type 0x24 (kVcCsInterface) and
+ // subtype 0x5 (kVcProcessingUnit). Inside this unit is where we find the
+ // power line frequency removal setting, and this id is device dependent.
+ int real_unit_id = -1;
+ IOUSBDescriptorHeader* descriptor = NULL;
+ IOUSBInterfaceDescriptor* cs_descriptor = NULL;
+ IOUSBInterfaceInterface220** interface =
+ reinterpret_cast<IOUSBInterfaceInterface220**>(control_interface);
+ while ((descriptor = (*interface)->FindNextAssociatedDescriptor(
+ interface, descriptor, kUSBAnyDesc))) {
+ cs_descriptor =
+ reinterpret_cast<IOUSBInterfaceDescriptor*>(descriptor);
+ if ((descriptor->bDescriptorType == kVcCsInterface) &&
+ (cs_descriptor->bDescriptorSubType == kVcProcessingUnit)) {
+ real_unit_id = cs_descriptor->bUnitID;
+ break;
+ }
+ }
+ DVLOG_IF(1, real_unit_id == -1) << "This USB device doesn't seem to have a "
+ << " VC_PROCESSING_UNIT, anti-flicker not available";
+ if (real_unit_id == -1)
+ return;
+
+ if ((*control_interface)->USBInterfaceOpen(control_interface) !=
+ kIOReturnSuccess) {
+ DLOG(ERROR) << "Unable to open control interface";
+ return;
+ }
+
+ // Create the control request and launch it to the device's control interface.
+ // Note how the wIndex needs the interface number OR'ed in the lowest bits.
+ IOUSBDevRequest command;
+ command.bmRequestType = USBmakebmRequestType(kUSBOut,
+ kUSBClass,
+ kUSBInterface);
+ command.bRequest = kVcRequestCodeSetCur;
+ UInt8 interface_number;
+ (*control_interface)->GetInterfaceNumber(control_interface,
+ &interface_number);
+ command.wIndex = (real_unit_id << 8) | interface_number;
+ const int selector = kPuPowerLineFrequencyControl;
+ command.wValue = (selector << 8);
+ command.wLength = kPuPowerLineFrequencyControlCommandSize;
+ command.wLenDone = 0;
+ int power_line_flag_value = (frequency == 50) ? k50Hz : k60Hz;
+ command.pData = &power_line_flag_value;
+
+ IOReturn ret = (*control_interface)->ControlRequest(control_interface,
+ 0, &command);
+ DLOG_IF(ERROR, ret != kIOReturnSuccess) << "Anti-flicker control request"
+ << " failed (0x" << std::hex << ret << "), unit id: " << real_unit_id;
+ DVLOG_IF(1, ret == kIOReturnSuccess) << "Anti-flicker set to " << frequency
+ << "Hz";
+
+ (*control_interface)->USBInterfaceClose(control_interface);
+}
+
+// Sets the flicker removal in a USB webcam identified by |vendor_id| and
+// |product_id|, if available. The process includes first finding all USB
+// devices matching the specified |vendor_id| and |product_id|; for each
+// matching device, a device interface, and inside it a video control interface
+// are created. The latter is used to a send a power frequency setting command.
+static void SetAntiFlickerInUsbDevice(const int vendor_id,
+ const int product_id,
+ const int frequency) {
+ if (frequency == 0)
+ return;
+ DVLOG(1) << "Setting Power Line Frequency to " << frequency << " Hz, device "
+ << std::hex << vendor_id << "-" << product_id;
+
+ // Compose a search dictionary with vendor and product ID.
+ CFMutableDictionaryRef query_dictionary =
+ IOServiceMatching(kIOUSBDeviceClassName);
+ CFDictionarySetValue(query_dictionary, CFSTR(kUSBVendorName),
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendor_id));
+ CFDictionarySetValue(query_dictionary, CFSTR(kUSBProductName),
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &product_id));
+
+ io_iterator_t usb_iterator;
+ kern_return_t kr = IOServiceGetMatchingServices(kIOMasterPortDefault,
+ query_dictionary,
+ &usb_iterator);
+ if (kr != kIOReturnSuccess) {
+ DLOG(ERROR) << "No devices found with specified Vendor and Product ID.";
+ return;
+ }
+ base::mac::ScopedIOObject<io_iterator_t> usb_iterator_ref(usb_iterator);
+
+ while (io_service_t usb_device = IOIteratorNext(usb_iterator)) {
+ base::mac::ScopedIOObject<io_service_t> usb_device_ref(usb_device);
+
+ IOUSBDeviceInterface** device_interface = NULL;
+ if (!FindDeviceInterfaceInUsbDevice(vendor_id, product_id,
+ usb_device, &device_interface)) {
+ return;
+ }
+ base::mac::ScopedIOPluginInterface<IOUSBDeviceInterface>
+ device_interface_ref(device_interface);
+
+ IOCFPlugInInterface** video_control_interface = NULL;
+ if (!FindVideoControlInterfaceInDeviceInterface(device_interface,
+ &video_control_interface)) {
+ return;
+ }
+ base::mac::ScopedIOPluginInterface<IOCFPlugInInterface>
+ plugin_interface_ref(video_control_interface);
+
+ SetAntiFlickerInVideoControlInterface(video_control_interface, frequency);
+ }
+}
+
// TODO(mcasas): Remove the following static methods when they are no longer
// referenced from VideoCaptureDeviceFactory, i.e. when all OS platforms have
// splitted the VideoCaptureDevice into VideoCaptureDevice and
@@ -163,6 +408,22 @@ void VideoCaptureDeviceMac::AllocateAndStart(
if (!UpdateCaptureResolution())
return;
}
+
+ // Try setting the power line frequency removal (anti-flicker). The built-in
+ // cameras are normally suspended so the configuration must happen right
+ // before starting capture and during configuration.
+ const std::string& device_model = device_name_.GetModel();
+ if (device_model.length() > 2 * kVidPidSize) {
+ std::string vendor_id = device_model.substr(0, kVidPidSize);
+ std::string model_id = device_model.substr(kVidPidSize + 1);
+ int vendor_id_as_int, model_id_as_int;
+ if (base::HexStringToInt(base::StringPiece(vendor_id), &vendor_id_as_int) &&
+ base::HexStringToInt(base::StringPiece(model_id), &model_id_as_int)) {
+ SetAntiFlickerInUsbDevice(vendor_id_as_int, model_id_as_int,
+ GetPowerLineFrequencyForLocation());
+ }
+ }
+
if (![capture_device_ startCapture]) {
SetErrorState("Could not start capture device.");
return;