Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / media / capture / video / mac / video_capture_device_factory_mac.mm
blob3d7c795d03a637fbe56b86316753453635170884
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/capture/video/mac/video_capture_device_factory_mac.h"
7 #import <IOKit/audio/IOAudioTypes.h>
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/profiler/scoped_tracker.h"
12 #include "base/strings/string_util.h"
13 #include "base/task_runner_util.h"
14 #import "media/base/mac/avfoundation_glue.h"
15 #import "media/capture/video/mac/video_capture_device_avfoundation_mac.h"
16 #import "media/capture/video/mac/video_capture_device_decklink_mac.h"
17 #include "media/capture/video/mac/video_capture_device_mac.h"
18 #import "media/capture/video/mac/video_capture_device_qtkit_mac.h"
20 namespace media {
22 // In QTKit API, some devices are known to crash if VGA is requested, for them
23 // HD is the only supported resolution (see http://crbug.com/396812). In the
24 // AVfoundation case, we skip enumerating them altogether. These devices are
25 // identified by a characteristic trailing substring of uniqueId. At the moment
26 // these are just Blackmagic devices.
27 const struct NameAndVid {
28   const char* unique_id_signature;
29   const int capture_width;
30   const int capture_height;
31   const float capture_frame_rate;
32 } kBlacklistedCameras[] = {{"-01FDA82C8A9C", 1280, 720, 60.0f}};
34 static bool IsDeviceBlacklisted(const VideoCaptureDevice::Name& name) {
35   bool is_device_blacklisted = false;
36   for(size_t i = 0;
37     !is_device_blacklisted && i < arraysize(kBlacklistedCameras); ++i) {
38     is_device_blacklisted =
39         base::EndsWith(name.id(),
40                        kBlacklistedCameras[i].unique_id_signature,
41                        base::CompareCase::INSENSITIVE_ASCII);
42   }
43   DVLOG_IF(2, is_device_blacklisted) << "Blacklisted camera: " << name.name()
44                                      << ", id: " << name.id();
45   return is_device_blacklisted;
48 static scoped_ptr<media::VideoCaptureDevice::Names>
49 EnumerateDevicesUsingQTKit() {
50   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/458397 is
51   // fixed.
52   tracked_objects::ScopedTracker tracking_profile(
53       FROM_HERE_WITH_EXPLICIT_FUNCTION(
54           "458397 media::EnumerateDevicesUsingQTKit"));
56   scoped_ptr<VideoCaptureDevice::Names> device_names(
57       new VideoCaptureDevice::Names());
58   NSMutableDictionary* capture_devices =
59       [[[NSMutableDictionary alloc] init] autorelease];
60   [VideoCaptureDeviceQTKit getDeviceNames:capture_devices];
61   for (NSString* key in capture_devices) {
62     VideoCaptureDevice::Name name(
63         [[[capture_devices valueForKey:key] deviceName] UTF8String],
64         [key UTF8String], VideoCaptureDevice::Name::QTKIT);
65     if (IsDeviceBlacklisted(name))
66       name.set_is_blacklisted(true);
67     device_names->push_back(name);
68   }
69   return device_names.Pass();
72 static void RunDevicesEnumeratedCallback(
73     const base::Callback<void(scoped_ptr<media::VideoCaptureDevice::Names>)>&
74         callback,
75     scoped_ptr<media::VideoCaptureDevice::Names> device_names) {
76   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/458397 is
77   // fixed.
78   tracked_objects::ScopedTracker tracking_profile(
79       FROM_HERE_WITH_EXPLICIT_FUNCTION(
80           "458397 media::RunDevicesEnumeratedCallback"));
81   callback.Run(device_names.Pass());
84 // static
85 bool VideoCaptureDeviceFactoryMac::PlatformSupportsAVFoundation() {
86   return AVFoundationGlue::IsAVFoundationSupported();
89 VideoCaptureDeviceFactoryMac::VideoCaptureDeviceFactoryMac(
90     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
91     : ui_task_runner_(ui_task_runner) {
92   thread_checker_.DetachFromThread();
95 VideoCaptureDeviceFactoryMac::~VideoCaptureDeviceFactoryMac() {
98 scoped_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryMac::Create(
99     const VideoCaptureDevice::Name& device_name) {
100   DCHECK(thread_checker_.CalledOnValidThread());
101   DCHECK_NE(device_name.capture_api_type(),
102             VideoCaptureDevice::Name::API_TYPE_UNKNOWN);
104   scoped_ptr<VideoCaptureDevice> capture_device;
105   if (device_name.capture_api_type() == VideoCaptureDevice::Name::DECKLINK) {
106     capture_device.reset(new VideoCaptureDeviceDeckLinkMac(device_name));
107   } else {
108     VideoCaptureDeviceMac* device = new VideoCaptureDeviceMac(device_name);
109     capture_device.reset(device);
110     if (!device->Init(device_name.capture_api_type())) {
111       LOG(ERROR) << "Could not initialize VideoCaptureDevice.";
112       capture_device.reset();
113     }
114   }
115   return scoped_ptr<VideoCaptureDevice>(capture_device.Pass());
118 void VideoCaptureDeviceFactoryMac::GetDeviceNames(
119     VideoCaptureDevice::Names* device_names) {
120   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/458397 is
121   // fixed.
122   tracked_objects::ScopedTracker tracking_profile(
123       FROM_HERE_WITH_EXPLICIT_FUNCTION(
124           "458397 VideoCaptureDeviceFactoryMac::GetDeviceNames"));
125   DCHECK(thread_checker_.CalledOnValidThread());
126   // Loop through all available devices and add to |device_names|.
127   NSDictionary* capture_devices;
128   if (AVFoundationGlue::IsAVFoundationSupported()) {
129     DVLOG(1) << "Enumerating video capture devices using AVFoundation";
130     capture_devices = [VideoCaptureDeviceAVFoundation deviceNames];
131     // Enumerate all devices found by AVFoundation, translate the info for each
132     // to class Name and add it to |device_names|.
133     for (NSString* key in capture_devices) {
134       int transport_type = [[capture_devices valueForKey:key] transportType];
135       // Transport types are defined for Audio devices and reused for video.
136       VideoCaptureDevice::Name::TransportType device_transport_type =
137           (transport_type == kIOAudioDeviceTransportTypeBuiltIn ||
138            transport_type == kIOAudioDeviceTransportTypeUSB)
139               ? VideoCaptureDevice::Name::USB_OR_BUILT_IN
140               : VideoCaptureDevice::Name::OTHER_TRANSPORT;
141       VideoCaptureDevice::Name name(
142           [[[capture_devices valueForKey:key] deviceName] UTF8String],
143           [key UTF8String], VideoCaptureDevice::Name::AVFOUNDATION,
144           device_transport_type);
145       if (IsDeviceBlacklisted(name))
146         continue;
147       device_names->push_back(name);
148     }
149     // Also retrieve Blackmagic devices, if present, via DeckLink SDK API.
150     VideoCaptureDeviceDeckLinkMac::EnumerateDevices(device_names);
151   } else {
152     // We should not enumerate QTKit devices in Device Thread;
153     NOTREACHED();
154   }
157 void VideoCaptureDeviceFactoryMac::EnumerateDeviceNames(const base::Callback<
158     void(scoped_ptr<media::VideoCaptureDevice::Names>)>& callback) {
159   DCHECK(thread_checker_.CalledOnValidThread());
160   if (AVFoundationGlue::IsAVFoundationSupported()) {
161     scoped_ptr<VideoCaptureDevice::Names> device_names(
162         new VideoCaptureDevice::Names());
163     GetDeviceNames(device_names.get());
164     callback.Run(device_names.Pass());
165   } else {
166     DVLOG(1) << "Enumerating video capture devices using QTKit";
167     base::PostTaskAndReplyWithResult(
168         ui_task_runner_.get(), FROM_HERE,
169         base::Bind(&EnumerateDevicesUsingQTKit),
170         base::Bind(&RunDevicesEnumeratedCallback, callback));
171   }
174 void VideoCaptureDeviceFactoryMac::GetDeviceSupportedFormats(
175     const VideoCaptureDevice::Name& device,
176     VideoCaptureFormats* supported_formats) {
177   DCHECK(thread_checker_.CalledOnValidThread());
178   switch (device.capture_api_type()) {
179   case VideoCaptureDevice::Name::AVFOUNDATION:
180     DVLOG(1) << "Enumerating video capture capabilities, AVFoundation";
181     [VideoCaptureDeviceAVFoundation getDevice:device
182                              supportedFormats:supported_formats];
183     break;
184   case VideoCaptureDevice::Name::QTKIT:
185     // Blacklisted cameras provide their own supported format(s), otherwise no
186     // such information is provided for QTKit devices.
187     if (device.is_blacklisted()) {
188       for (size_t i = 0; i < arraysize(kBlacklistedCameras); ++i) {
189         if (base::EndsWith(device.id(),
190                            kBlacklistedCameras[i].unique_id_signature,
191                            base::CompareCase::INSENSITIVE_ASCII)) {
192           supported_formats->push_back(media::VideoCaptureFormat(
193               gfx::Size(kBlacklistedCameras[i].capture_width,
194                         kBlacklistedCameras[i].capture_height),
195               kBlacklistedCameras[i].capture_frame_rate,
196               media::PIXEL_FORMAT_UYVY));
197           break;
198         }
199       }
200     }
201     break;
202   case VideoCaptureDevice::Name::DECKLINK:
203     DVLOG(1) << "Enumerating video capture capabilities " << device.name();
204     VideoCaptureDeviceDeckLinkMac::EnumerateDeviceCapabilities(
205         device, supported_formats);
206     break;
207   default:
208     NOTREACHED();
209   }
212 // static
213 VideoCaptureDeviceFactory*
214 VideoCaptureDeviceFactory::CreateVideoCaptureDeviceFactory(
215     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
216   return new VideoCaptureDeviceFactoryMac(ui_task_runner);
219 }  // namespace media