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