| Index: media/capture/video/mac/video_capture_device_mac.mm
|
| diff --git a/media/capture/video/mac/video_capture_device_mac.mm b/media/capture/video/mac/video_capture_device_mac.mm
|
| deleted file mode 100644
|
| index 6ae6b8c1d8c8ef7a6ed3e71551f2842c00b25af0..0000000000000000000000000000000000000000
|
| --- a/media/capture/video/mac/video_capture_device_mac.mm
|
| +++ /dev/null
|
| @@ -1,490 +0,0 @@
|
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "media/capture/video/mac/video_capture_device_mac.h"
|
| -
|
| -#include <IOKit/IOCFPlugIn.h>
|
| -#include <IOKit/usb/IOUSBLib.h>
|
| -#include <IOKit/usb/USBSpec.h>
|
| -#include <stddef.h>
|
| -#include <stdint.h>
|
| -
|
| -#include <limits>
|
| -#include <utility>
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/location.h"
|
| -#include "base/logging.h"
|
| -#include "base/mac/scoped_ioobject.h"
|
| -#include "base/mac/scoped_ioplugininterface.h"
|
| -#include "base/macros.h"
|
| -#include "base/single_thread_task_runner.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "base/threading/thread_task_runner_handle.h"
|
| -#include "base/time/time.h"
|
| -#import "media/base/mac/avfoundation_glue.h"
|
| -#include "media/base/timestamp_constants.h"
|
| -#import "media/capture/video/mac/video_capture_device_avfoundation_mac.h"
|
| -#include "ui/gfx/geometry/size.h"
|
| -
|
| -@implementation DeviceNameAndTransportType
|
| -
|
| -- (id)initWithName:(NSString*)deviceName transportType:(int32_t)transportType {
|
| - if (self = [super init]) {
|
| - deviceName_.reset([deviceName copy]);
|
| - transportType_ = transportType;
|
| - }
|
| - return self;
|
| -}
|
| -
|
| -- (NSString*)deviceName {
|
| - return deviceName_;
|
| -}
|
| -
|
| -- (int32_t)transportType {
|
| - return transportType_;
|
| -}
|
| -
|
| -@end // @implementation DeviceNameAndTransportType
|
| -
|
| -namespace media {
|
| -
|
| -// Mac specific limits for minimum and maximum frame rate.
|
| -const float kMinFrameRate = 1.0f;
|
| -const float kMaxFrameRate = 30.0f;
|
| -
|
| -// In device identifiers, the USB VID and PID are stored in 4 bytes each.
|
| -const size_t kVidPidSize = 4;
|
| -
|
| -// 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;
|
| -
|
| -// 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 plugin, 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 plugin.
|
| - 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 "plugin") 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 PowerLineFrequency frequency) {
|
| - // Create, the control interface for the found plugin, and release
|
| - // the intermediate plugin.
|
| - 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 == PowerLineFrequency::FREQUENCY_50HZ) ? 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 "
|
| - << static_cast<int>(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 PowerLineFrequency frequency) {
|
| - if (frequency == PowerLineFrequency::FREQUENCY_DEFAULT)
|
| - return;
|
| - DVLOG(1) << "Setting Power Line Frequency to " << static_cast<int>(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);
|
| - }
|
| -}
|
| -
|
| -const std::string VideoCaptureDevice::Name::GetModel() const {
|
| - // Skip the AVFoundation's not USB nor built-in devices.
|
| - if (capture_api_type() == AVFOUNDATION && transport_type() != USB_OR_BUILT_IN)
|
| - return "";
|
| - if (capture_api_type() == DECKLINK)
|
| - return "";
|
| - // Both PID and VID are 4 characters.
|
| - if (unique_id_.size() < 2 * kVidPidSize)
|
| - return "";
|
| -
|
| - // The last characters of device id is a concatenation of VID and then PID.
|
| - const size_t vid_location = unique_id_.size() - 2 * kVidPidSize;
|
| - std::string id_vendor = unique_id_.substr(vid_location, kVidPidSize);
|
| - const size_t pid_location = unique_id_.size() - kVidPidSize;
|
| - std::string id_product = unique_id_.substr(pid_location, kVidPidSize);
|
| -
|
| - return id_vendor + ":" + id_product;
|
| -}
|
| -
|
| -VideoCaptureDeviceMac::VideoCaptureDeviceMac(const Name& device_name)
|
| - : device_name_(device_name),
|
| - task_runner_(base::ThreadTaskRunnerHandle::Get()),
|
| - state_(kNotInitialized),
|
| - capture_device_(nil),
|
| - weak_factory_(this) {
|
| -}
|
| -
|
| -VideoCaptureDeviceMac::~VideoCaptureDeviceMac() {
|
| - DCHECK(task_runner_->BelongsToCurrentThread());
|
| -}
|
| -
|
| -void VideoCaptureDeviceMac::AllocateAndStart(
|
| - const VideoCaptureParams& params,
|
| - std::unique_ptr<VideoCaptureDevice::Client> client) {
|
| - DCHECK(task_runner_->BelongsToCurrentThread());
|
| - if (state_ != kIdle) {
|
| - return;
|
| - }
|
| -
|
| - client_ = std::move(client);
|
| - if (device_name_.capture_api_type() == Name::AVFOUNDATION)
|
| - LogMessage("Using AVFoundation for device: " + device_name_.name());
|
| -
|
| - NSString* deviceId =
|
| - [NSString stringWithUTF8String:device_name_.id().c_str()];
|
| -
|
| - [capture_device_ setFrameReceiver:this];
|
| -
|
| - if (![capture_device_ setCaptureDevice:deviceId]) {
|
| - SetErrorState(FROM_HERE, "Could not open capture device.");
|
| - return;
|
| - }
|
| -
|
| - capture_format_.frame_size = params.requested_format.frame_size;
|
| - capture_format_.frame_rate =
|
| - std::max(kMinFrameRate,
|
| - std::min(params.requested_format.frame_rate, kMaxFrameRate));
|
| - // Leave the pixel format selection to AVFoundation. The pixel format
|
| - // will be passed to |ReceiveFrame|.
|
| - capture_format_.pixel_format = PIXEL_FORMAT_UNKNOWN;
|
| -
|
| - 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,
|
| - GetPowerLineFrequency(params));
|
| - }
|
| - }
|
| -
|
| - if (![capture_device_ startCapture]) {
|
| - SetErrorState(FROM_HERE, "Could not start capture device.");
|
| - return;
|
| - }
|
| -
|
| - state_ = kCapturing;
|
| -}
|
| -
|
| -void VideoCaptureDeviceMac::StopAndDeAllocate() {
|
| - DCHECK(task_runner_->BelongsToCurrentThread());
|
| - DCHECK(state_ == kCapturing || state_ == kError) << state_;
|
| -
|
| - [capture_device_ setCaptureDevice:nil];
|
| - [capture_device_ setFrameReceiver:nil];
|
| - client_.reset();
|
| - state_ = kIdle;
|
| -}
|
| -
|
| -void VideoCaptureDeviceMac::TakePhoto(TakePhotoCallback callback) {
|
| - DCHECK(task_runner_->BelongsToCurrentThread());
|
| - DCHECK(state_ == kCapturing) << state_;
|
| -
|
| - if (photo_callback_) // Only one picture can be in flight at a time.
|
| - return;
|
| -
|
| - photo_callback_.reset(new TakePhotoCallback(std::move(callback)));
|
| - [capture_device_ takePhoto];
|
| -}
|
| -
|
| -bool VideoCaptureDeviceMac::Init(
|
| - VideoCaptureDevice::Name::CaptureApiType capture_api_type) {
|
| - DCHECK(task_runner_->BelongsToCurrentThread());
|
| - DCHECK_EQ(state_, kNotInitialized);
|
| -
|
| - if (capture_api_type != Name::AVFOUNDATION)
|
| - return false;
|
| -
|
| - capture_device_.reset(
|
| - [[VideoCaptureDeviceAVFoundation alloc] initWithFrameReceiver:this]);
|
| -
|
| - if (!capture_device_)
|
| - return false;
|
| -
|
| - state_ = kIdle;
|
| - return true;
|
| -}
|
| -
|
| -void VideoCaptureDeviceMac::ReceiveFrame(const uint8_t* video_frame,
|
| - int video_frame_length,
|
| - const VideoCaptureFormat& frame_format,
|
| - int aspect_numerator,
|
| - int aspect_denominator,
|
| - base::TimeDelta timestamp) {
|
| - if (capture_format_.frame_size != frame_format.frame_size) {
|
| - ReceiveError(FROM_HERE,
|
| - "Captured resolution " + frame_format.frame_size.ToString() +
|
| - ", and expected " + capture_format_.frame_size.ToString());
|
| - return;
|
| - }
|
| -
|
| - client_->OnIncomingCapturedData(video_frame, video_frame_length, frame_format,
|
| - 0, base::TimeTicks::Now(), timestamp);
|
| -}
|
| -
|
| -void VideoCaptureDeviceMac::OnPhotoTaken(const uint8_t* image_data,
|
| - size_t image_length,
|
| - const std::string& mime_type) {
|
| - DCHECK(photo_callback_);
|
| - if (!image_data || !image_length) {
|
| - OnPhotoError();
|
| - return;
|
| - }
|
| -
|
| - photo_callback_->Run(mojo::String::From(mime_type),
|
| - mojo::Array<uint8_t>(std::vector<uint8_t>(
|
| - image_data, image_data + image_length)));
|
| - photo_callback_.reset();
|
| -}
|
| -
|
| -void VideoCaptureDeviceMac::OnPhotoError() {
|
| - DLOG(ERROR) << __FUNCTION__ << " error taking picture";
|
| - photo_callback_.reset();
|
| -}
|
| -
|
| -void VideoCaptureDeviceMac::ReceiveError(
|
| - const tracked_objects::Location& from_here,
|
| - const std::string& reason) {
|
| - task_runner_->PostTask(
|
| - FROM_HERE, base::Bind(&VideoCaptureDeviceMac::SetErrorState,
|
| - weak_factory_.GetWeakPtr(), from_here, reason));
|
| -}
|
| -
|
| -void VideoCaptureDeviceMac::SetErrorState(
|
| - const tracked_objects::Location& from_here,
|
| - const std::string& reason) {
|
| - DCHECK(task_runner_->BelongsToCurrentThread());
|
| - state_ = kError;
|
| - client_->OnError(from_here, reason);
|
| -}
|
| -
|
| -void VideoCaptureDeviceMac::LogMessage(const std::string& message) {
|
| - DCHECK(task_runner_->BelongsToCurrentThread());
|
| - if (client_)
|
| - client_->OnLog(message);
|
| -}
|
| -
|
| -bool VideoCaptureDeviceMac::UpdateCaptureResolution() {
|
| - if (![capture_device_ setCaptureHeight:capture_format_.frame_size.height()
|
| - width:capture_format_.frame_size.width()
|
| - frameRate:capture_format_.frame_rate]) {
|
| - ReceiveError(FROM_HERE, "Could not configure capture device.");
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -} // namespace media
|
|
|