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 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 78 MacDeviceManager::MacDeviceManager() { 79 set_watcher(new MacDeviceWatcher(this)); 80 } 81 82 MacDeviceManager::~MacDeviceManager() { 83 } 84 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 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 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 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 173 MacDeviceWatcher::MacDeviceWatcher(DeviceManagerInterface* manager) 174 : DeviceWatcher(manager), 175 manager_(manager), 176 impl_(NULL) { 177 } 178 179 MacDeviceWatcher::~MacDeviceWatcher() { 180 } 181 182 bool MacDeviceWatcher::Start() { 183 if (!impl_) { 184 impl_ = CreateDeviceWatcherCallback(manager_); 185 } 186 return impl_ != NULL; 187 } 188 189 void MacDeviceWatcher::Stop() { 190 if (impl_) { 191 ReleaseDeviceWatcherCallback(impl_); 192 impl_ = NULL; 193 } 194 } 195 196 }; // namespace cricket 197