summaryrefslogtreecommitdiffstats
path: root/media/video
diff options
context:
space:
mode:
authormcasas@chromium.org <mcasas@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-28 19:22:48 +0000
committermcasas@chromium.org <mcasas@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-28 19:22:48 +0000
commitb2ad22b896e93fd325d9311d6a45518565458302 (patch)
treeedd558a8e1d8bf6b2493a713ee5ab02d28a21861 /media/video
parentff19b7b6e3d2c174cf696af3b421a2e010fb0834 (diff)
downloadchromium_src-b2ad22b896e93fd325d9311d6a45518565458302.zip
chromium_src-b2ad22b896e93fd325d9311d6a45518565458302.tar.gz
chromium_src-b2ad22b896e93fd325d9311d6a45518565458302.tar.bz2
Mac Video Capture: Connect anti-flicker (power line frequency removal)
Neither QTKit nor AVFoundation have capabilities for setting the Power Line frequency with the goal of flicker removal. This can be done via one level lower API, USB and IOKit [1], in a cumbersome onion-peeling process of finding the device, interface etc. Tested: setting worked in a Creative Senz3d, a Logitech C910, a Logitech C9000 and a Microsoft Lifecam 5000. Setting also worked in the built-in Facetime of my Mac with the catch that this must happen right after configuration and before capture, since in normal conditions this device is suspended and would ignore the commands. BUG=357599 [1] https://developer.apple.com/library/mac/documentation/devicedrivers/conceptual/USBBook/USBDeviceInterfaces/USBDevInterfaces.html Review URL: https://codereview.chromium.org/299033005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@273316 0039d316-1c4b-4281-b951-d872f2087c98
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;