Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / media / video / capture / mac / video_capture_device_mac.mm
index 8ff066f..190f53d 100644 (file)
@@ -4,45 +4,96 @@
 
 #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/base/mac/avfoundation_glue.h"
 #import "media/video/capture/mac/platform_video_capturing_mac.h"
 #import "media/video/capture/mac/video_capture_device_avfoundation_mac.h"
 #import "media/video/capture/mac/video_capture_device_qtkit_mac.h"
 
-namespace media {
+@implementation DeviceNameAndTransportType
 
-const int kMinFrameRate = 1;
-const int kMaxFrameRate = 30;
+- (id)initWithName:(NSString*)deviceName transportType:(int32_t)transportType {
+  if (self = [super init]) {
+    deviceName_.reset([deviceName copy]);
+    transportType_ = transportType;
+  }
+  return self;
+}
 
-// In QT device identifiers, the USB VID and PID are stored in 4 bytes each.
-const size_t kVidPidSize = 4;
+- (NSString*)deviceName {
+  return deviceName_;
+}
 
-struct Resolution {
-  int width;
-  int height;
-};
+- (int32_t)transportType {
+  return transportType_;
+}
 
-const Resolution kQVGA = { 320, 240 },
-                 kVGA = { 640, 480 },
-                 kHD = { 1280, 720 };
+@end  // @implementation DeviceNameAndTransportType
+
+namespace media {
 
-const Resolution* const kWellSupportedResolutions[] = {
-   &kQVGA,
-   &kVGA,
-   &kHD,
+// 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;
+
+const struct Resolution {
+  const int width;
+  const int height;
+} kQVGA = { 320, 240 },
+  kVGA = { 640, 480 },
+  kHD = { 1280, 720 };
+
+const struct Resolution* const kWellSupportedResolutions[] = {
+  &kQVGA,
+  &kVGA,
+  &kHD,
 };
 
 // Rescaling the image to fix the pixel aspect ratio runs the risk of making
 // the aspect ratio worse, if QTKit selects a new source mode with a different
-// shape.  This constant ensures that we don't take this risk if the current
+// shape. This constant ensures that we don't take this risk if the current
 // 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;
@@ -63,36 +114,228 @@ void GetBestMatchSupportedResolution(int* width, int* height) {
   *height = matched_height;
 }
 
-void VideoCaptureDevice::GetDeviceNames(Names* device_names) {
-  // Loop through all available devices and add to |device_names|.
-  device_names->clear();
+// 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;
+}
 
-  NSDictionary* capture_devices;
-  if (AVFoundationGlue::IsAVFoundationSupported()) {
-    DVLOG(1) << "Enumerating video capture devices using AVFoundation";
-    capture_devices = [VideoCaptureDeviceAVFoundation deviceNames];
-  } else {
-    DVLOG(1) << "Enumerating video capture devices using QTKit";
-    capture_devices = [VideoCaptureDeviceQTKit deviceNames];
+// 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;
   }
-  for (NSString* key in capture_devices) {
-    Name name([[capture_devices valueForKey:key] UTF8String],
-              [key UTF8String]);
-    device_names->push_back(name);
+  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);
 }
 
-// static
-void VideoCaptureDevice::GetDeviceSupportedFormats(const Name& device,
-    VideoCaptureFormats* formats) {
-  NOTIMPLEMENTED();
+// 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);
+  }
 }
 
 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) {
+  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;
@@ -103,26 +346,16 @@ const std::string VideoCaptureDevice::Name::GetModel() const {
   return id_vendor + ":" + id_product;
 }
 
-VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) {
-  VideoCaptureDeviceMac* capture_device =
-      new VideoCaptureDeviceMac(device_name);
-  if (!capture_device->Init()) {
-    LOG(ERROR) << "Could not initialize VideoCaptureDevice.";
-    delete capture_device;
-    capture_device = NULL;
-  }
-  return capture_device;
-}
-
 VideoCaptureDeviceMac::VideoCaptureDeviceMac(const Name& device_name)
     : device_name_(device_name),
-      sent_frame_info_(false),
       tried_to_square_pixels_(false),
       task_runner_(base::MessageLoopProxy::current()),
       state_(kNotInitialized),
-      weak_factory_(this),
-      weak_this_(weak_factory_.GetWeakPtr()),
-      capture_device_(nil) {
+      capture_device_(nil),
+      weak_factory_(this) {
+  // Avoid reconfiguring AVFoundation or blacklisted devices.
+  final_resolution_selected_ = AVFoundationGlue::IsAVFoundationSupported() ||
+      device_name.is_blacklisted();
 }
 
 VideoCaptureDeviceMac::~VideoCaptureDeviceMac() {
@@ -139,14 +372,19 @@ void VideoCaptureDeviceMac::AllocateAndStart(
   }
   int width = params.requested_format.frame_size.width();
   int height = params.requested_format.frame_size.height();
-  int frame_rate = params.requested_format.frame_rate;
+  float frame_rate = params.requested_format.frame_rate;
 
-  // The OS API can scale captured frame to any size requested, which would lead
-  // to undesired aspect ratio change. Try to open the camera with a natively
+  // QTKit API can scale captured frame to any size requested, which would lead
+  // to undesired aspect ratio changes. Try to open the camera with a known
   // supported format and let the client crop/pad the captured frames.
-  GetBestMatchSupportedResolution(&width, &height);
+  if (!AVFoundationGlue::IsAVFoundationSupported())
+    GetBestMatchSupportedResolution(&width, &height);
 
   client_ = client.Pass();
+  if (device_name_.capture_api_type() == Name::AVFOUNDATION)
+    LogMessage("Using AVFoundation for device: " + device_name_.name());
+  else
+    LogMessage("Using QTKit for device: " + device_name_.name());
   NSString* deviceId =
       [NSString stringWithUTF8String:device_name_.id().c_str()];
 
@@ -165,20 +403,33 @@ void VideoCaptureDeviceMac::AllocateAndStart(
   capture_format_.frame_rate = frame_rate;
   capture_format_.pixel_format = PIXEL_FORMAT_UYVY;
 
-  if (width <= kVGA.width || height <= kVGA.height) {
-    // If the resolution is VGA or QVGA, set the capture resolution to the
-    // target size. Essentially all supported cameras offer at least VGA.
+  // QTKit: Set the capture resolution only if this is VGA or smaller, otherwise
+  // leave it unconfigured and start capturing: QTKit will produce frames at the
+  // native resolution, allowing us to identify cameras whose native resolution
+  // is too low for HD. This additional information comes at a cost in startup
+  // latency, because the webcam will need to be reopened if its default
+  // resolution is not HD or VGA.
+  // AVfoundation is configured for all resolutions.
+  if (AVFoundationGlue::IsAVFoundationSupported() || width <= kVGA.width ||
+      height <= kVGA.height) {
     if (!UpdateCaptureResolution())
       return;
   }
-  // For higher resolutions, we first open at the default resolution to find
-  // out if the request is larger than the camera's native resolution.
 
-  // If the resolution is HD, start capturing without setting a resolution.
-  // QTKit will produce frames at the native resolution, allowing us to
-  // identify cameras whose native resolution is too low for HD.  This
-  // additional information comes at a cost in startup latency, because the
-  // webcam will need to be reopened if its default resolution is not HD or VGA.
+  // 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.");
@@ -191,7 +442,6 @@ void VideoCaptureDeviceMac::AllocateAndStart(
 void VideoCaptureDeviceMac::StopAndDeAllocate() {
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(state_ == kCapturing || state_ == kError) << state_;
-  [capture_device_ stopCapture];
 
   [capture_device_ setCaptureDevice:nil];
   [capture_device_ setFrameReceiver:nil];
@@ -200,24 +450,12 @@ void VideoCaptureDeviceMac::StopAndDeAllocate() {
   tried_to_square_pixels_ = false;
 }
 
-bool VideoCaptureDeviceMac::Init() {
+bool VideoCaptureDeviceMac::Init(
+    VideoCaptureDevice::Name::CaptureApiType capture_api_type) {
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK_EQ(state_, kNotInitialized);
 
-  // TODO(mcasas): The following check might not be necessary; if the device has
-  // disappeared after enumeration and before coming here, opening would just
-  // fail but not necessarily produce a crash.
-  Names device_names;
-  GetDeviceNames(&device_names);
-  Names::iterator it = device_names.begin();
-  for (; it != device_names.end(); ++it) {
-    if (it->id() == device_name_.id())
-      break;
-  }
-  if (it == device_names.end())
-    return false;
-
-  if (AVFoundationGlue::IsAVFoundationSupported()) {
+  if (capture_api_type == Name::AVFOUNDATION) {
     capture_device_ =
         [[VideoCaptureDeviceAVFoundation alloc] initWithFrameReceiver:this];
   } else {
@@ -238,11 +476,10 @@ void VideoCaptureDeviceMac::ReceiveFrame(
     const VideoCaptureFormat& frame_format,
     int aspect_numerator,
     int aspect_denominator) {
-  // This method is safe to call from a device capture thread,
-  // i.e. any thread controlled by QTKit.
-
-  if (!sent_frame_info_) {
-    // Final resolution has not yet been selected.
+  // This method is safe to call from a device capture thread, i.e. any thread
+  // controlled by QTKit/AVFoundation.
+  if (!final_resolution_selected_) {
+    DCHECK(!AVFoundationGlue::IsAVFoundationSupported());
     if (capture_format_.frame_size.width() > kVGA.width ||
         capture_format_.frame_size.height() > kVGA.height) {
       // We are requesting HD.  Make sure that the picture is good, otherwise
@@ -267,72 +504,81 @@ void VideoCaptureDeviceMac::ReceiveFrame(
         change_to_vga = true;
       }
 
-      if (change_to_vga) {
+      if (change_to_vga)
         capture_format_.frame_size.SetSize(kVGA.width, kVGA.height);
-      }
     }
 
     if (capture_format_.frame_size == frame_format.frame_size &&
         !tried_to_square_pixels_ &&
         (aspect_numerator > kMaxPixelAspectRatio * aspect_denominator ||
          aspect_denominator > kMaxPixelAspectRatio * aspect_numerator)) {
-      // The requested size results in non-square PAR.
-      // Shrink the frame to 1:1 PAR (assuming QTKit selects the same input
-      // mode, which is not guaranteed).
+      // The requested size results in non-square PAR. Shrink the frame to 1:1
+      // PAR (assuming QTKit selects the same input mode, which is not
+      // guaranteed).
       int new_width = capture_format_.frame_size.width();
       int new_height = capture_format_.frame_size.height();
-      if (aspect_numerator < aspect_denominator) {
+      if (aspect_numerator < aspect_denominator)
         new_width = (new_width * aspect_numerator) / aspect_denominator;
-      } else {
+      else
         new_height = (new_height * aspect_denominator) / aspect_numerator;
-      }
       capture_format_.frame_size.SetSize(new_width, new_height);
       tried_to_square_pixels_ = true;
     }
 
     if (capture_format_.frame_size == frame_format.frame_size) {
-      sent_frame_info_ = true;
+      final_resolution_selected_ = true;
     } else {
       UpdateCaptureResolution();
-      // OnFrameInfo has not yet been called.  OnIncomingCapturedFrame must
-      // not be called until after OnFrameInfo, so we return early.
+      // Let the resolution update sink through QTKit and wait for next frame.
       return;
     }
   }
 
-  DCHECK_EQ(capture_format_.frame_size.width(),
-            frame_format.frame_size.width());
-  DCHECK_EQ(capture_format_.frame_size.height(),
-            frame_format.frame_size.height());
+  // QTKit capture source can change resolution if someone else reconfigures the
+  // camera, and that is fine: http://crbug.com/353620. In AVFoundation, this
+  // should not happen, it should resize internally.
+  if (!AVFoundationGlue::IsAVFoundationSupported()) {
+    capture_format_.frame_size = frame_format.frame_size;
+  } else if (capture_format_.frame_size != frame_format.frame_size) {
+    ReceiveError("Captured resolution " + frame_format.frame_size.ToString() +
+        ", and expected " + capture_format_.frame_size.ToString());
+    return;
+  }
 
-  client_->OnIncomingCapturedFrame(video_frame,
-                                   video_frame_length,
-                                   base::TimeTicks::Now(),
-                                   0,
-                                   capture_format_);
+  client_->OnIncomingCapturedData(video_frame,
+                                  video_frame_length,
+                                  capture_format_,
+                                  0,
+                                  base::TimeTicks::Now());
 }
 
 void VideoCaptureDeviceMac::ReceiveError(const std::string& reason) {
   task_runner_->PostTask(FROM_HERE,
-      base::Bind(&VideoCaptureDeviceMac::SetErrorState, weak_this_,
-          reason));
+                         base::Bind(&VideoCaptureDeviceMac::SetErrorState,
+                                    weak_factory_.GetWeakPtr(),
+                                    reason));
 }
 
 void VideoCaptureDeviceMac::SetErrorState(const std::string& reason) {
   DCHECK(task_runner_->BelongsToCurrentThread());
-  DLOG(ERROR) << reason;
   state_ = kError;
   client_->OnError(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("Could not configure capture device.");
-   return false;
- }
- return true;
 if (![capture_device_ setCaptureHeight:capture_format_.frame_size.height()
+                                   width:capture_format_.frame_size.width()
+                               frameRate:capture_format_.frame_rate]) {
+    ReceiveError("Could not configure capture device.");
+    return false;
 }
 return true;
 }
 
 } // namespace media