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