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 38class DeviceWatcherImpl; 39 40namespace cricket { 41 42DeviceManagerInterface* DeviceManagerFactory::Create() { 43 return new MacDeviceManager(); 44} 45 46class 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 58static 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. 63static const char* const kFilteredVideoDevicesName[] = { 64 "DVCPRO HD", // Final cut 65 "Sonix SN9C201p", // Crashes in OpenAComponent and CloseComponent 66 NULL, 67}; 68static const UInt32 kAudioDeviceNameLength = 64; 69// Obj-C functions defined in macdevicemanagermm.mm 70// TODO(ronghuawu): have a shared header for these function defines. 71extern DeviceWatcherImpl* CreateDeviceWatcherCallback( 72 DeviceManagerInterface* dm); 73extern void ReleaseDeviceWatcherCallback(DeviceWatcherImpl* impl); 74extern bool GetAVFoundationVideoDevices(std::vector<Device>* out); 75static bool GetAudioDeviceIDs(bool inputs, std::vector<AudioDeviceID>* out); 76static bool GetAudioDeviceName(AudioDeviceID id, bool input, std::string* out); 77 78MacDeviceManager::MacDeviceManager() { 79 set_watcher(new MacDeviceWatcher(this)); 80} 81 82MacDeviceManager::~MacDeviceManager() { 83} 84 85bool MacDeviceManager::GetVideoCaptureDevices(std::vector<Device>* devices) { 86 devices->clear(); 87 if (!GetAVFoundationVideoDevices(devices)) { 88 return false; 89 } 90 return FilterDevices(devices, kFilteredVideoDevicesName); 91} 92 93bool 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 110static 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 156static 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 173MacDeviceWatcher::MacDeviceWatcher(DeviceManagerInterface* manager) 174 : DeviceWatcher(manager), 175 manager_(manager), 176 impl_(NULL) { 177} 178 179MacDeviceWatcher::~MacDeviceWatcher() { 180} 181 182bool MacDeviceWatcher::Start() { 183 if (!impl_) { 184 impl_ = CreateDeviceWatcherCallback(manager_); 185 } 186 return impl_ != NULL; 187} 188 189void MacDeviceWatcher::Stop() { 190 if (impl_) { 191 ReleaseDeviceWatcherCallback(impl_); 192 impl_ = NULL; 193 } 194} 195 196}; // namespace cricket 197