1 /*
2 * libjingle
3 * Copyright 2004 Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "talk/media/devices/macdevicemanager.h"
29
30 #include <CoreAudio/CoreAudio.h>
31 #include <QuickTime/QuickTime.h>
32
33 #include "talk/media/base/mediacommon.h"
34 #include "webrtc/base/logging.h"
35 #include "webrtc/base/stringutils.h"
36 #include "webrtc/base/thread.h"
37
38 class DeviceWatcherImpl;
39
40 namespace cricket {
41
Create()42 DeviceManagerInterface* DeviceManagerFactory::Create() {
43 return new MacDeviceManager();
44 }
45
46 class MacDeviceWatcher : public DeviceWatcher {
47 public:
48 explicit MacDeviceWatcher(DeviceManagerInterface* dm);
49 virtual ~MacDeviceWatcher();
50 virtual bool Start();
51 virtual void Stop();
52
53 private:
54 DeviceManagerInterface* manager_;
55 DeviceWatcherImpl* impl_;
56 };
57
58 static const char* kFilteredAudioDevicesName[] = {
59 NULL,
60 };
61 // TODO(tommyw): Try to get hold of a copy of Final Cut to understand why we
62 // crash while scanning their components on OS X.
63 static const char* const kFilteredVideoDevicesName[] = {
64 "DVCPRO HD", // Final cut
65 "Sonix SN9C201p", // Crashes in OpenAComponent and CloseComponent
66 NULL,
67 };
68 static const UInt32 kAudioDeviceNameLength = 64;
69 // Obj-C functions defined in macdevicemanagermm.mm
70 // TODO(ronghuawu): have a shared header for these function defines.
71 extern DeviceWatcherImpl* CreateDeviceWatcherCallback(
72 DeviceManagerInterface* dm);
73 extern void ReleaseDeviceWatcherCallback(DeviceWatcherImpl* impl);
74 extern bool GetAVFoundationVideoDevices(std::vector<Device>* out);
75 static bool GetAudioDeviceIDs(bool inputs, std::vector<AudioDeviceID>* out);
76 static bool GetAudioDeviceName(AudioDeviceID id, bool input, std::string* out);
77
MacDeviceManager()78 MacDeviceManager::MacDeviceManager() {
79 set_watcher(new MacDeviceWatcher(this));
80 }
81
~MacDeviceManager()82 MacDeviceManager::~MacDeviceManager() {
83 }
84
GetVideoCaptureDevices(std::vector<Device> * devices)85 bool MacDeviceManager::GetVideoCaptureDevices(std::vector<Device>* devices) {
86 devices->clear();
87 if (!GetAVFoundationVideoDevices(devices)) {
88 return false;
89 }
90 return FilterDevices(devices, kFilteredVideoDevicesName);
91 }
92
GetAudioDevices(bool input,std::vector<Device> * devs)93 bool MacDeviceManager::GetAudioDevices(bool input,
94 std::vector<Device>* devs) {
95 devs->clear();
96 std::vector<AudioDeviceID> dev_ids;
97 bool ret = GetAudioDeviceIDs(input, &dev_ids);
98 if (!ret) {
99 return false;
100 }
101 for (size_t i = 0; i < dev_ids.size(); ++i) {
102 std::string name;
103 if (GetAudioDeviceName(dev_ids[i], input, &name)) {
104 devs->push_back(Device(name, dev_ids[i]));
105 }
106 }
107 return FilterDevices(devs, kFilteredAudioDevicesName);
108 }
109
GetAudioDeviceIDs(bool input,std::vector<AudioDeviceID> * out_dev_ids)110 static bool GetAudioDeviceIDs(bool input,
111 std::vector<AudioDeviceID>* out_dev_ids) {
112 UInt32 propsize;
113 OSErr err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
114 &propsize, NULL);
115 if (0 != err) {
116 LOG(LS_ERROR) << "Couldn't get information about property, "
117 << "so no device list acquired.";
118 return false;
119 }
120
121 size_t num_devices = propsize / sizeof(AudioDeviceID);
122 rtc::scoped_ptr<AudioDeviceID[]> device_ids(
123 new AudioDeviceID[num_devices]);
124
125 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
126 &propsize, device_ids.get());
127 if (0 != err) {
128 LOG(LS_ERROR) << "Failed to get device ids, "
129 << "so no device listing acquired.";
130 return false;
131 }
132
133 for (size_t i = 0; i < num_devices; ++i) {
134 AudioDeviceID an_id = device_ids[i];
135 // find out the number of channels for this direction
136 // (input/output) on this device -
137 // we'll ignore anything with no channels.
138 err = AudioDeviceGetPropertyInfo(an_id, 0, input,
139 kAudioDevicePropertyStreams,
140 &propsize, NULL);
141 if (0 == err) {
142 unsigned num_channels = propsize / sizeof(AudioStreamID);
143 if (0 < num_channels) {
144 out_dev_ids->push_back(an_id);
145 }
146 } else {
147 LOG(LS_ERROR) << "No property info for stream property for device id "
148 << an_id << "(is_input == " << input
149 << "), so not including it in the list.";
150 }
151 }
152
153 return true;
154 }
155
GetAudioDeviceName(AudioDeviceID id,bool input,std::string * out_name)156 static bool GetAudioDeviceName(AudioDeviceID id,
157 bool input,
158 std::string* out_name) {
159 UInt32 nameLength = kAudioDeviceNameLength;
160 char name[kAudioDeviceNameLength + 1];
161 OSErr err = AudioDeviceGetProperty(id, 0, input,
162 kAudioDevicePropertyDeviceName,
163 &nameLength, name);
164 if (0 != err) {
165 LOG(LS_ERROR) << "No name acquired for device id " << id;
166 return false;
167 }
168
169 *out_name = name;
170 return true;
171 }
172
MacDeviceWatcher(DeviceManagerInterface * manager)173 MacDeviceWatcher::MacDeviceWatcher(DeviceManagerInterface* manager)
174 : DeviceWatcher(manager),
175 manager_(manager),
176 impl_(NULL) {
177 }
178
~MacDeviceWatcher()179 MacDeviceWatcher::~MacDeviceWatcher() {
180 }
181
Start()182 bool MacDeviceWatcher::Start() {
183 if (!impl_) {
184 impl_ = CreateDeviceWatcherCallback(manager_);
185 }
186 return impl_ != NULL;
187 }
188
Stop()189 void MacDeviceWatcher::Stop() {
190 if (impl_) {
191 ReleaseDeviceWatcherCallback(impl_);
192 impl_ = NULL;
193 }
194 }
195
196 }; // namespace cricket
197