Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / media / video / capture / mac / video_capture_device_factory_mac.mm
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.
4
5 #include "media/video/capture/mac/video_capture_device_factory_mac.h"
6
7 #import <IOKit/audio/IOAudioTypes.h>
8
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/strings/string_util.h"
12 #include "base/task_runner_util.h"
13 #import "media/base/mac/avfoundation_glue.h"
14 #import "media/video/capture/mac/video_capture_device_avfoundation_mac.h"
15 #include "media/video/capture/mac/video_capture_device_mac.h"
16 #import "media/video/capture/mac/video_capture_device_decklink_mac.h"
17 #import "media/video/capture/mac/video_capture_device_qtkit_mac.h"
18
19 namespace media {
20
21 // Some devices are not correctly supported in AVFoundation, f.i. Blackmagic,
22 // see http://crbug.com/347371. The devices are identified by a characteristic
23 // trailing substring of uniqueId and by (part of) the vendor's name.
24 // Blackmagic cameras are known to crash if VGA is requested , see
25 // http://crbug.com/396812; for them HD is the only supported resolution.
26 const struct NameAndVid {
27   const char* unique_id_signature;
28   const char* name;
29   const int capture_width;
30   const int capture_height;
31   const float capture_frame_rate;
32 } kBlacklistedCameras[] = {
33   { "-01FDA82C8A9C", "Blackmagic", 1280, 720, 60.0f } };
34
35 static scoped_ptr<media::VideoCaptureDevice::Names>
36 EnumerateDevicesUsingQTKit() {
37   scoped_ptr<VideoCaptureDevice::Names> device_names(
38         new VideoCaptureDevice::Names());
39   NSMutableDictionary* capture_devices =
40       [[[NSMutableDictionary alloc] init] autorelease];
41   [VideoCaptureDeviceQTKit getDeviceNames:capture_devices];
42   for (NSString* key in capture_devices) {
43     VideoCaptureDevice::Name name(
44         [[[capture_devices valueForKey:key] deviceName] UTF8String],
45         [key UTF8String], VideoCaptureDevice::Name::QTKIT);
46     for (size_t i = 0; i < arraysize(kBlacklistedCameras); ++i) {
47       if (name.id().find(kBlacklistedCameras[i].name) != std::string::npos) {
48         DVLOG(2) << "Found blacklisted camera: " << name.id();
49         name.set_is_blacklisted(true);
50         break;
51       }
52     }
53     device_names->push_back(name);
54   }
55   return device_names.Pass();
56 }
57
58 static void RunDevicesEnumeratedCallback(
59     const base::Callback<void(scoped_ptr<media::VideoCaptureDevice::Names>)>&
60         callback,
61     scoped_ptr<media::VideoCaptureDevice::Names> device_names) {
62   callback.Run(device_names.Pass());
63 }
64
65 // static
66 bool VideoCaptureDeviceFactoryMac::PlatformSupportsAVFoundation() {
67   return AVFoundationGlue::IsAVFoundationSupported();
68 }
69
70 VideoCaptureDeviceFactoryMac::VideoCaptureDeviceFactoryMac(
71     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
72     : ui_task_runner_(ui_task_runner) {
73   thread_checker_.DetachFromThread();
74 }
75
76 VideoCaptureDeviceFactoryMac::~VideoCaptureDeviceFactoryMac() {}
77
78 scoped_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryMac::Create(
79     const VideoCaptureDevice::Name& device_name) {
80   DCHECK(thread_checker_.CalledOnValidThread());
81   DCHECK_NE(device_name.capture_api_type(),
82             VideoCaptureDevice::Name::API_TYPE_UNKNOWN);
83
84   // Check device presence only for AVFoundation API, since it is too expensive
85   // and brittle for QTKit. The actual initialization at device level will fail
86   // subsequently if the device is not present.
87   if (AVFoundationGlue::IsAVFoundationSupported()) {
88     scoped_ptr<VideoCaptureDevice::Names> device_names(
89         new VideoCaptureDevice::Names());
90     GetDeviceNames(device_names.get());
91
92     VideoCaptureDevice::Names::iterator it = device_names->begin();
93     for (; it != device_names->end(); ++it) {
94       if (it->id() == device_name.id())
95         break;
96     }
97     if (it == device_names->end())
98       return scoped_ptr<VideoCaptureDevice>();
99   }
100
101   scoped_ptr<VideoCaptureDeviceMac> capture_device(
102       new VideoCaptureDeviceMac(device_name));
103   if (!capture_device->Init(device_name.capture_api_type())) {
104     LOG(ERROR) << "Could not initialize VideoCaptureDevice.";
105     capture_device.reset();
106   }
107   return scoped_ptr<VideoCaptureDevice>(capture_device.Pass());
108 }
109
110 void VideoCaptureDeviceFactoryMac::GetDeviceNames(
111     VideoCaptureDevice::Names* device_names) {
112   DCHECK(thread_checker_.CalledOnValidThread());
113   // Loop through all available devices and add to |device_names|.
114   NSDictionary* capture_devices;
115   if (AVFoundationGlue::IsAVFoundationSupported()) {
116     bool is_any_device_blacklisted = false;
117     DVLOG(1) << "Enumerating video capture devices using AVFoundation";
118     capture_devices = [VideoCaptureDeviceAVFoundation deviceNames];
119     // Enumerate all devices found by AVFoundation, translate the info for each
120     // to class Name and add it to |device_names|.
121     for (NSString* key in capture_devices) {
122       int transport_type = [[capture_devices valueForKey:key] transportType];
123       // Transport types are defined for Audio devices and reused for video.
124       VideoCaptureDevice::Name::TransportType device_transport_type =
125           (transport_type == kIOAudioDeviceTransportTypeBuiltIn ||
126               transport_type == kIOAudioDeviceTransportTypeUSB)
127           ? VideoCaptureDevice::Name::USB_OR_BUILT_IN
128           : VideoCaptureDevice::Name::OTHER_TRANSPORT;
129       VideoCaptureDevice::Name name(
130           [[[capture_devices valueForKey:key] deviceName] UTF8String],
131           [key UTF8String], VideoCaptureDevice::Name::AVFOUNDATION,
132           device_transport_type);
133       device_names->push_back(name);
134       for (size_t i = 0; i < arraysize(kBlacklistedCameras); ++i) {
135         is_any_device_blacklisted |= EndsWith(name.id(),
136             kBlacklistedCameras[i].unique_id_signature, false);
137         if (is_any_device_blacklisted)
138           break;
139       }
140     }
141     // If there is any device blacklisted in the system, walk the QTKit device
142     // list and add those devices with a blacklisted name to the |device_names|.
143     // AVFoundation and QTKit device lists partially overlap, so add a "QTKit"
144     // prefix to the latter ones to distinguish them from the AVFoundation ones.
145     if (is_any_device_blacklisted) {
146       capture_devices = [VideoCaptureDeviceQTKit deviceNames];
147       for (NSString* key in capture_devices) {
148         NSString* device_name = [[capture_devices valueForKey:key] deviceName];
149         for (size_t i = 0; i < arraysize(kBlacklistedCameras); ++i) {
150           if ([device_name rangeOfString:@(kBlacklistedCameras[i].name)
151                                  options:NSCaseInsensitiveSearch].length != 0) {
152             DVLOG(1) << "Enumerated blacklisted " << [device_name UTF8String];
153             VideoCaptureDevice::Name name(
154                 "QTKit " + std::string([device_name UTF8String]),
155                 [key UTF8String], VideoCaptureDevice::Name::QTKIT);
156             device_names->push_back(name);
157           }
158         }
159       }
160     }
161
162     // Also retrieve Blackmagic devices, if present, via DeckLink SDK API.
163     VideoCaptureDeviceDeckLinkMac::EnumerateDevices(device_names);
164   } else {
165     // We should not enumerate QTKit devices in Device Thread;
166     NOTREACHED();
167   }
168 }
169
170 void VideoCaptureDeviceFactoryMac::EnumerateDeviceNames(const base::Callback<
171     void(scoped_ptr<media::VideoCaptureDevice::Names>)>& callback) {
172   DCHECK(thread_checker_.CalledOnValidThread());
173   if (AVFoundationGlue::IsAVFoundationSupported()) {
174     scoped_ptr<VideoCaptureDevice::Names> device_names(
175         new VideoCaptureDevice::Names());
176     GetDeviceNames(device_names.get());
177     callback.Run(device_names.Pass());
178   } else {
179     DVLOG(1) << "Enumerating video capture devices using QTKit";
180     base::PostTaskAndReplyWithResult(
181         ui_task_runner_.get(),
182         FROM_HERE,
183         base::Bind(&EnumerateDevicesUsingQTKit),
184         base::Bind(&RunDevicesEnumeratedCallback, callback));
185   }
186 }
187
188 void VideoCaptureDeviceFactoryMac::GetDeviceSupportedFormats(
189     const VideoCaptureDevice::Name& device,
190     VideoCaptureFormats* supported_formats) {
191   DCHECK(thread_checker_.CalledOnValidThread());
192   switch (device.capture_api_type()) {
193   case VideoCaptureDevice::Name::AVFOUNDATION:
194     DVLOG(1) << "Enumerating video capture capabilities, AVFoundation";
195     [VideoCaptureDeviceAVFoundation getDevice:device
196                              supportedFormats:supported_formats];
197     break;
198   case VideoCaptureDevice::Name::QTKIT:
199     // Blacklisted cameras provide their own supported format(s), otherwise no
200     // such information is provided for QTKit.
201     if (device.is_blacklisted()) {
202       for (size_t i = 0; i < arraysize(kBlacklistedCameras); ++i) {
203         if (device.id().find(kBlacklistedCameras[i].name) !=
204             std::string::npos) {
205           supported_formats->push_back(media::VideoCaptureFormat(
206               gfx::Size(kBlacklistedCameras[i].capture_width,
207                         kBlacklistedCameras[i].capture_height),
208               kBlacklistedCameras[i].capture_frame_rate,
209               media::PIXEL_FORMAT_UYVY));
210           break;
211         }
212       }
213     }
214     break;
215   case VideoCaptureDevice::Name::DECKLINK:
216     DVLOG(1) << "Enumerating video capture capabilities " << device.name();
217     VideoCaptureDeviceDeckLinkMac::EnumerateDeviceCapabilities(
218         device, supported_formats);
219     break;
220   default:
221     NOTREACHED();
222   }
223 }
224
225 }  // namespace media