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/devicemanager.h"
29
30#include "talk/base/fileutils.h"
31#include "talk/base/logging.h"
32#include "talk/base/pathutils.h"
33#include "talk/base/stringutils.h"
34#include "talk/base/thread.h"
35#include "talk/base/windowpicker.h"
36#include "talk/base/windowpickerfactory.h"
37#include "talk/media/base/mediacommon.h"
38#include "talk/media/devices/deviceinfo.h"
39#include "talk/media/devices/filevideocapturer.h"
40
41#if !defined(IOS)
42
43#if defined(HAVE_WEBRTC_VIDEO)
44#include "talk/media/webrtc/webrtcvideocapturer.h"
45#endif
46
47
48#if defined(HAVE_WEBRTC_VIDEO)
49#define VIDEO_CAPTURER_NAME WebRtcVideoCapturer
50#endif
51
52
53#endif
54
55namespace {
56
57bool StringMatchWithWildcard(
58    const std::pair<const std::basic_string<char>, cricket::VideoFormat> key,
59    const std::string& val) {
60  return talk_base::string_match(val.c_str(), key.first.c_str());
61}
62
63}  // namespace
64
65namespace cricket {
66
67// Initialize to empty string.
68const char DeviceManagerInterface::kDefaultDeviceName[] = "";
69
70
71class DefaultVideoCapturerFactory : public VideoCapturerFactory {
72 public:
73  DefaultVideoCapturerFactory() {}
74  virtual ~DefaultVideoCapturerFactory() {}
75
76  VideoCapturer* Create(const Device& device) {
77#if defined(VIDEO_CAPTURER_NAME)
78    VIDEO_CAPTURER_NAME* return_value = new VIDEO_CAPTURER_NAME;
79    if (!return_value->Init(device)) {
80      delete return_value;
81      return NULL;
82    }
83    return return_value;
84#else
85    return NULL;
86#endif
87  }
88};
89
90DeviceManager::DeviceManager()
91    : initialized_(false),
92      device_video_capturer_factory_(new DefaultVideoCapturerFactory),
93      window_picker_(talk_base::WindowPickerFactory::CreateWindowPicker()) {
94}
95
96DeviceManager::~DeviceManager() {
97  if (initialized()) {
98    Terminate();
99  }
100}
101
102bool DeviceManager::Init() {
103  if (!initialized()) {
104    if (!watcher()->Start()) {
105      return false;
106    }
107    set_initialized(true);
108  }
109  return true;
110}
111
112void DeviceManager::Terminate() {
113  if (initialized()) {
114    watcher()->Stop();
115    set_initialized(false);
116  }
117}
118
119int DeviceManager::GetCapabilities() {
120  std::vector<Device> devices;
121  int caps = VIDEO_RECV;
122  if (GetAudioInputDevices(&devices) && !devices.empty()) {
123    caps |= AUDIO_SEND;
124  }
125  if (GetAudioOutputDevices(&devices) && !devices.empty()) {
126    caps |= AUDIO_RECV;
127  }
128  if (GetVideoCaptureDevices(&devices) && !devices.empty()) {
129    caps |= VIDEO_SEND;
130  }
131  return caps;
132}
133
134bool DeviceManager::GetAudioInputDevices(std::vector<Device>* devices) {
135  return GetAudioDevices(true, devices);
136}
137
138bool DeviceManager::GetAudioOutputDevices(std::vector<Device>* devices) {
139  return GetAudioDevices(false, devices);
140}
141
142bool DeviceManager::GetAudioInputDevice(const std::string& name, Device* out) {
143  return GetAudioDevice(true, name, out);
144}
145
146bool DeviceManager::GetAudioOutputDevice(const std::string& name, Device* out) {
147  return GetAudioDevice(false, name, out);
148}
149
150bool DeviceManager::GetVideoCaptureDevices(std::vector<Device>* devices) {
151  devices->clear();
152#if defined(ANDROID) || defined(IOS)
153  // On Android and iOS, we treat the camera(s) as a single device. Even if
154  // there are multiple cameras, that's abstracted away at a higher level.
155  Device dev("camera", "1");    // name and ID
156  devices->push_back(dev);
157  return true;
158#else
159  return false;
160#endif
161}
162
163bool DeviceManager::GetVideoCaptureDevice(const std::string& name,
164                                          Device* out) {
165  // If the name is empty, return the default device.
166  if (name.empty() || name == kDefaultDeviceName) {
167    return GetDefaultVideoCaptureDevice(out);
168  }
169
170  std::vector<Device> devices;
171  if (!GetVideoCaptureDevices(&devices)) {
172    return false;
173  }
174
175  for (std::vector<Device>::const_iterator it = devices.begin();
176      it != devices.end(); ++it) {
177    if (name == it->name) {
178      *out = *it;
179      return true;
180    }
181  }
182
183  // If |name| is a valid name for a file, return a file video capturer device.
184  if (talk_base::Filesystem::IsFile(name)) {
185    *out = FileVideoCapturer::CreateFileVideoCapturerDevice(name);
186    return true;
187  }
188
189  return false;
190}
191
192void DeviceManager::SetVideoCaptureDeviceMaxFormat(
193    const std::string& usb_id,
194    const VideoFormat& max_format) {
195  max_formats_[usb_id] = max_format;
196}
197
198void DeviceManager::ClearVideoCaptureDeviceMaxFormat(
199    const std::string& usb_id) {
200  max_formats_.erase(usb_id);
201}
202
203VideoCapturer* DeviceManager::CreateVideoCapturer(const Device& device) const {
204#if defined(IOS)
205  LOG_F(LS_ERROR) << " should never be called!";
206  return NULL;
207#else
208  // TODO(hellner): Throw out the creation of a file video capturer once the
209  // refactoring is completed.
210  if (FileVideoCapturer::IsFileVideoCapturerDevice(device)) {
211    FileVideoCapturer* capturer = new FileVideoCapturer;
212    if (!capturer->Init(device)) {
213      delete capturer;
214      return NULL;
215    }
216    LOG(LS_INFO) << "Created file video capturer " << device.name;
217    capturer->set_repeat(talk_base::kForever);
218    return capturer;
219  }
220  VideoCapturer* capturer = device_video_capturer_factory_->Create(device);
221  if (!capturer) {
222    return NULL;
223  }
224  LOG(LS_INFO) << "Created VideoCapturer for " << device.name;
225  VideoFormat video_format;
226  bool has_max = GetMaxFormat(device, &video_format);
227  capturer->set_enable_camera_list(has_max);
228  if (has_max) {
229    capturer->ConstrainSupportedFormats(video_format);
230  }
231  return capturer;
232#endif
233}
234
235bool DeviceManager::GetWindows(
236    std::vector<talk_base::WindowDescription>* descriptions) {
237  if (!window_picker_) {
238    return false;
239  }
240  return window_picker_->GetWindowList(descriptions);
241}
242
243VideoCapturer* DeviceManager::CreateWindowCapturer(talk_base::WindowId window) {
244#if defined(WINDOW_CAPTURER_NAME)
245  WINDOW_CAPTURER_NAME* window_capturer = new WINDOW_CAPTURER_NAME();
246  if (!window_capturer->Init(window)) {
247    delete window_capturer;
248    return NULL;
249  }
250  return window_capturer;
251#else
252  return NULL;
253#endif
254}
255
256bool DeviceManager::GetDesktops(
257    std::vector<talk_base::DesktopDescription>* descriptions) {
258  if (!window_picker_) {
259    return false;
260  }
261  return window_picker_->GetDesktopList(descriptions);
262}
263
264VideoCapturer* DeviceManager::CreateDesktopCapturer(
265    talk_base::DesktopId desktop) {
266#if defined(DESKTOP_CAPTURER_NAME)
267  DESKTOP_CAPTURER_NAME* desktop_capturer = new DESKTOP_CAPTURER_NAME();
268  if (!desktop_capturer->Init(desktop.index())) {
269    delete desktop_capturer;
270    return NULL;
271  }
272  return desktop_capturer;
273#else
274  return NULL;
275#endif
276}
277
278bool DeviceManager::GetAudioDevices(bool input,
279                                    std::vector<Device>* devs) {
280  devs->clear();
281#if defined(ANDROID)
282  // Under Android, 0 is always required for the playout device and 0 is the
283  // default for the recording device.
284  devs->push_back(Device("default-device", 0));
285  return true;
286#else
287  // Other platforms either have their own derived class implementation
288  // (desktop) or don't use device manager for audio devices (iOS).
289  return false;
290#endif
291}
292
293bool DeviceManager::GetAudioDevice(bool is_input, const std::string& name,
294                                   Device* out) {
295  // If the name is empty, return the default device id.
296  if (name.empty() || name == kDefaultDeviceName) {
297    *out = Device(name, -1);
298    return true;
299  }
300
301  std::vector<Device> devices;
302  bool ret = is_input ? GetAudioInputDevices(&devices) :
303                        GetAudioOutputDevices(&devices);
304  if (ret) {
305    ret = false;
306    for (size_t i = 0; i < devices.size(); ++i) {
307      if (devices[i].name == name) {
308        *out = devices[i];
309        ret = true;
310        break;
311      }
312    }
313  }
314  return ret;
315}
316
317bool DeviceManager::GetDefaultVideoCaptureDevice(Device* device) {
318  bool ret = false;
319  // We just return the first device.
320  std::vector<Device> devices;
321  ret = (GetVideoCaptureDevices(&devices) && !devices.empty());
322  if (ret) {
323    *device = devices[0];
324  }
325  return ret;
326}
327
328bool DeviceManager::IsInWhitelist(const std::string& key,
329                                  VideoFormat* video_format) const {
330  std::map<std::string, VideoFormat>::const_iterator found =
331      std::search_n(max_formats_.begin(), max_formats_.end(), 1, key,
332                    StringMatchWithWildcard);
333  if (found == max_formats_.end()) {
334    return false;
335  }
336  *video_format = found->second;
337  return true;
338}
339
340bool DeviceManager::GetMaxFormat(const Device& device,
341                                 VideoFormat* video_format) const {
342  // Match USB ID if available. Failing that, match device name.
343  std::string usb_id;
344  if (GetUsbId(device, &usb_id) && IsInWhitelist(usb_id, video_format)) {
345      return true;
346  }
347  return IsInWhitelist(device.name, video_format);
348}
349
350bool DeviceManager::ShouldDeviceBeIgnored(const std::string& device_name,
351    const char* const exclusion_list[]) {
352  // If exclusion_list is empty return directly.
353  if (!exclusion_list)
354    return false;
355
356  int i = 0;
357  while (exclusion_list[i]) {
358    if (strnicmp(device_name.c_str(), exclusion_list[i],
359        strlen(exclusion_list[i])) == 0) {
360      LOG(LS_INFO) << "Ignoring device " << device_name;
361      return true;
362    }
363    ++i;
364  }
365  return false;
366}
367
368bool DeviceManager::FilterDevices(std::vector<Device>* devices,
369    const char* const exclusion_list[]) {
370  if (!devices) {
371    return false;
372  }
373
374  for (std::vector<Device>::iterator it = devices->begin();
375       it != devices->end(); ) {
376    if (ShouldDeviceBeIgnored(it->name, exclusion_list)) {
377      it = devices->erase(it);
378    } else {
379      ++it;
380    }
381  }
382  return true;
383}
384
385}  // namespace cricket
386