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