1 /* 2 * libjingle 3 * Copyright 2004--2008, 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/session/phone/devicemanager.h" 29 30 #if WIN32 31 #include <atlbase.h> 32 #include <dbt.h> 33 #include <strmif.h> // must come before ks.h 34 #include <ks.h> 35 #include <ksmedia.h> 36 #define INITGUID // For PKEY_AudioEndpoint_GUID 37 #include <mmdeviceapi.h> 38 #include <functiondiscoverykeys_devpkey.h> 39 #include <uuids.h> 40 #include "talk/base/win32.h" // ToUtf8 41 #include "talk/base/win32window.h" 42 #elif OSX 43 #include <CoreAudio/CoreAudio.h> 44 #include <QuickTime/QuickTime.h> 45 #elif LINUX 46 #include <libudev.h> 47 #include <unistd.h> 48 #include "talk/base/linux.h" 49 #include "talk/base/fileutils.h" 50 #include "talk/base/pathutils.h" 51 #include "talk/base/physicalsocketserver.h" 52 #include "talk/base/stream.h" 53 #include "talk/session/phone/libudevsymboltable.h" 54 #include "talk/session/phone/v4llookup.h" 55 #include "talk/sound/platformsoundsystem.h" 56 #include "talk/sound/platformsoundsystemfactory.h" 57 #include "talk/sound/sounddevicelocator.h" 58 #include "talk/sound/soundsysteminterface.h" 59 #endif 60 61 #include "talk/base/logging.h" 62 #include "talk/base/stringutils.h" 63 #include "talk/session/phone/mediaengine.h" 64 65 namespace cricket { 66 // Initialize to empty string. 67 const std::string DeviceManager::kDefaultDeviceName; 68 69 #ifdef WIN32 70 class DeviceWatcher : public talk_base::Win32Window { 71 public: 72 explicit DeviceWatcher(DeviceManager* dm); 73 bool Start(); 74 void Stop(); 75 76 private: 77 HDEVNOTIFY Register(REFGUID guid); 78 void Unregister(HDEVNOTIFY notify); 79 virtual bool OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT& result); 80 81 DeviceManager* manager_; 82 HDEVNOTIFY audio_notify_; 83 HDEVNOTIFY video_notify_; 84 }; 85 #elif defined(LINUX) 86 class DeviceWatcher : private talk_base::Dispatcher { 87 public: 88 explicit DeviceWatcher(DeviceManager* dm); 89 bool Start(); 90 void Stop(); 91 92 private: 93 virtual uint32 GetRequestedEvents(); 94 virtual void OnPreEvent(uint32 ff); 95 virtual void OnEvent(uint32 ff, int err); 96 virtual int GetDescriptor(); 97 virtual bool IsDescriptorClosed(); 98 99 DeviceManager* manager_; 100 LibUDevSymbolTable libudev_; 101 struct udev* udev_; 102 struct udev_monitor* udev_monitor_; 103 bool registered_; 104 }; 105 #define LATE(sym) LATESYM_GET(LibUDevSymbolTable, &libudev_, sym) 106 #elif defined(OSX) 107 class DeviceWatcher { 108 public: 109 explicit DeviceWatcher(DeviceManager* dm); 110 bool Start(); 111 void Stop(); 112 private: 113 DeviceManager* manager_; 114 void* impl_; 115 }; 116 #elif defined(IOS) || defined(ANDROID) 117 // We don't use DeviceWatcher on iOS or Android, so just stub out a noop class. 118 class DeviceWatcher { 119 public: 120 explicit DeviceWatcher(DeviceManager* dm) {} 121 bool Start() { return true; } 122 void Stop() {} 123 }; 124 #endif 125 126 #if !defined(LINUX) && !defined(IOS) 127 static bool ShouldDeviceBeIgnored(const std::string& device_name); 128 #endif 129 #ifndef OSX 130 static bool GetVideoDevices(std::vector<Device>* out); 131 #endif 132 #if WIN32 133 static const wchar_t kFriendlyName[] = L"FriendlyName"; 134 static const wchar_t kDevicePath[] = L"DevicePath"; 135 static const char kUsbDevicePathPrefix[] = "\\\\?\\usb"; 136 static bool GetDevices(const CLSID& catid, std::vector<Device>* out); 137 static bool GetCoreAudioDevices(bool input, std::vector<Device>* devs); 138 static bool GetWaveDevices(bool input, std::vector<Device>* devs); 139 #elif OSX 140 static const int kVideoDeviceOpenAttempts = 3; 141 static const UInt32 kAudioDeviceNameLength = 64; 142 // Obj-C functions defined in devicemanager-mac.mm 143 extern void* CreateDeviceWatcherCallback(DeviceManager* dm); 144 extern void ReleaseDeviceWatcherCallback(void* impl); 145 extern bool GetQTKitVideoDevices(std::vector<Device>* out); 146 static bool GetAudioDeviceIDs(bool inputs, std::vector<AudioDeviceID>* out); 147 static bool GetAudioDeviceName(AudioDeviceID id, bool input, std::string* out); 148 #endif 149 150 DeviceManager::DeviceManager() 151 : initialized_(false), 152 #if defined(WIN32) 153 need_couninitialize_(false), 154 #endif 155 watcher_(new DeviceWatcher(this)) 156 #ifdef LINUX 157 , sound_system_(new PlatformSoundSystemFactory()) 158 #endif 159 { 160 } 161 162 DeviceManager::~DeviceManager() { 163 if (initialized_) { 164 Terminate(); 165 } 166 delete watcher_; 167 } 168 169 bool DeviceManager::Init() { 170 if (!initialized_) { 171 #if defined(WIN32) 172 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); 173 need_couninitialize_ = SUCCEEDED(hr); 174 if (FAILED(hr)) { 175 LOG(LS_ERROR) << "CoInitialize failed, hr=" << hr; 176 if (hr != RPC_E_CHANGED_MODE) { 177 return false; 178 } 179 } 180 #endif 181 if (!watcher_->Start()) { 182 return false; 183 } 184 initialized_ = true; 185 } 186 return true; 187 } 188 189 void DeviceManager::Terminate() { 190 if (initialized_) { 191 watcher_->Stop(); 192 #if defined(WIN32) 193 if (need_couninitialize_) { 194 CoUninitialize(); 195 need_couninitialize_ = false; 196 } 197 #endif 198 initialized_ = false; 199 } 200 } 201 202 int DeviceManager::GetCapabilities() { 203 std::vector<Device> devices; 204 int caps = MediaEngine::VIDEO_RECV; 205 if (GetAudioInputDevices(&devices) && !devices.empty()) { 206 caps |= MediaEngine::AUDIO_SEND; 207 } 208 if (GetAudioOutputDevices(&devices) && !devices.empty()) { 209 caps |= MediaEngine::AUDIO_RECV; 210 } 211 if (GetVideoCaptureDevices(&devices) && !devices.empty()) { 212 caps |= MediaEngine::VIDEO_SEND; 213 } 214 return caps; 215 } 216 217 bool DeviceManager::GetAudioInputDevices(std::vector<Device>* devices) { 218 return GetAudioDevicesByPlatform(true, devices); 219 } 220 221 bool DeviceManager::GetAudioOutputDevices(std::vector<Device>* devices) { 222 return GetAudioDevicesByPlatform(false, devices); 223 } 224 225 bool DeviceManager::GetAudioInputDevice(const std::string& name, Device* out) { 226 return GetAudioDevice(true, name, out); 227 } 228 229 bool DeviceManager::GetAudioOutputDevice(const std::string& name, Device* out) { 230 return GetAudioDevice(false, name, out); 231 } 232 233 #ifdef OSX 234 static bool FilterDevice(const Device& d) { 235 return ShouldDeviceBeIgnored(d.name); 236 } 237 #endif 238 239 bool DeviceManager::GetVideoCaptureDevices(std::vector<Device>* devices) { 240 devices->clear(); 241 #ifdef OSX 242 if (GetQTKitVideoDevices(devices)) { 243 // Now filter out any known incompatible devices 244 devices->erase(remove_if(devices->begin(), devices->end(), FilterDevice), 245 devices->end()); 246 return true; 247 } 248 return false; 249 #else 250 return GetVideoDevices(devices); 251 #endif 252 } 253 254 bool DeviceManager::GetDefaultVideoCaptureDevice(Device* device) { 255 bool ret = false; 256 #if WIN32 257 // If there are multiple capture devices, we want the first USB one. 258 // This avoids issues with defaulting to virtual cameras or grabber cards. 259 std::vector<Device> devices; 260 ret = (GetVideoDevices(&devices) && !devices.empty()); 261 if (ret) { 262 *device = devices[0]; 263 for (size_t i = 0; i < devices.size(); ++i) { 264 if (strnicmp(devices[i].id.c_str(), kUsbDevicePathPrefix, 265 ARRAY_SIZE(kUsbDevicePathPrefix) - 1) == 0) { 266 *device = devices[i]; 267 break; 268 } 269 } 270 } 271 #else 272 // We just return the first device. 273 std::vector<Device> devices; 274 ret = (GetVideoCaptureDevices(&devices) && !devices.empty()); 275 if (ret) { 276 *device = devices[0]; 277 } 278 #endif 279 return ret; 280 } 281 282 bool DeviceManager::GetVideoCaptureDevice(const std::string& name, 283 Device* out) { 284 // If the name is empty, return the default device. 285 if (name.empty() || name == kDefaultDeviceName) { 286 return GetDefaultVideoCaptureDevice(out); 287 } 288 289 std::vector<Device> devices; 290 if (!GetVideoCaptureDevices(&devices)) { 291 return false; 292 } 293 294 for (std::vector<Device>::const_iterator it = devices.begin(); 295 it != devices.end(); ++it) { 296 if (name == it->name) { 297 *out = *it; 298 return true; 299 } 300 } 301 302 return false; 303 } 304 305 bool DeviceManager::GetAudioDevice(bool is_input, const std::string& name, 306 Device* out) { 307 // If the name is empty, return the default device id. 308 if (name.empty() || name == kDefaultDeviceName) { 309 *out = Device(name, -1); 310 return true; 311 } 312 313 std::vector<Device> devices; 314 bool ret = is_input ? GetAudioInputDevices(&devices) : 315 GetAudioOutputDevices(&devices); 316 if (ret) { 317 ret = false; 318 for (size_t i = 0; i < devices.size(); ++i) { 319 if (devices[i].name == name) { 320 *out = devices[i]; 321 ret = true; 322 break; 323 } 324 } 325 } 326 return ret; 327 } 328 329 bool DeviceManager::GetAudioDevicesByPlatform(bool input, 330 std::vector<Device>* devs) { 331 devs->clear(); 332 333 #if defined(LINUX) 334 if (!sound_system_.get()) { 335 return false; 336 } 337 SoundSystemInterface::SoundDeviceLocatorList list; 338 bool success; 339 if (input) { 340 success = sound_system_->EnumerateCaptureDevices(&list); 341 } else { 342 success = sound_system_->EnumeratePlaybackDevices(&list); 343 } 344 if (!success) { 345 LOG(LS_ERROR) << "Can't enumerate devices"; 346 sound_system_.release(); 347 return false; 348 } 349 int index = 0; 350 for (SoundSystemInterface::SoundDeviceLocatorList::iterator i = list.begin(); 351 i != list.end(); 352 ++i, ++index) { 353 devs->push_back(Device((*i)->name(), index)); 354 } 355 SoundSystemInterface::ClearSoundDeviceLocatorList(&list); 356 sound_system_.release(); 357 return true; 358 359 #elif defined(WIN32) 360 if (talk_base::IsWindowsVistaOrLater()) { 361 return GetCoreAudioDevices(input, devs); 362 } else { 363 return GetWaveDevices(input, devs); 364 } 365 366 #elif defined(OSX) 367 std::vector<AudioDeviceID> dev_ids; 368 bool ret = GetAudioDeviceIDs(input, &dev_ids); 369 if (ret) { 370 for (size_t i = 0; i < dev_ids.size(); ++i) { 371 std::string name; 372 if (GetAudioDeviceName(dev_ids[i], input, &name)) { 373 devs->push_back(Device(name, dev_ids[i])); 374 } 375 } 376 } 377 return ret; 378 379 #else 380 return false; 381 #endif 382 } 383 384 #if defined(WIN32) 385 bool GetVideoDevices(std::vector<Device>* devices) { 386 return GetDevices(CLSID_VideoInputDeviceCategory, devices); 387 } 388 389 bool GetDevices(const CLSID& catid, std::vector<Device>* devices) { 390 HRESULT hr; 391 392 // CComPtr is a scoped pointer that will be auto released when going 393 // out of scope. CoUninitialize must not be called before the 394 // release. 395 CComPtr<ICreateDevEnum> sys_dev_enum; 396 CComPtr<IEnumMoniker> cam_enum; 397 if (FAILED(hr = sys_dev_enum.CoCreateInstance(CLSID_SystemDeviceEnum)) || 398 FAILED(hr = sys_dev_enum->CreateClassEnumerator(catid, &cam_enum, 0))) { 399 LOG(LS_ERROR) << "Failed to create device enumerator, hr=" << hr; 400 return false; 401 } 402 403 // Only enum devices if CreateClassEnumerator returns S_OK. If there are no 404 // devices available, S_FALSE will be returned, but enumMk will be NULL. 405 if (hr == S_OK) { 406 CComPtr<IMoniker> mk; 407 while (cam_enum->Next(1, &mk, NULL) == S_OK) { 408 CComPtr<IPropertyBag> bag; 409 if (SUCCEEDED(mk->BindToStorage(NULL, NULL, 410 __uuidof(bag), reinterpret_cast<void**>(&bag)))) { 411 CComVariant name, path; 412 std::string name_str, path_str; 413 if (SUCCEEDED(bag->Read(kFriendlyName, &name, 0)) && 414 name.vt == VT_BSTR) { 415 name_str = talk_base::ToUtf8(name.bstrVal); 416 if (!ShouldDeviceBeIgnored(name_str)) { 417 // Get the device id if one exists. 418 if (SUCCEEDED(bag->Read(kDevicePath, &path, 0)) && 419 path.vt == VT_BSTR) { 420 path_str = talk_base::ToUtf8(path.bstrVal); 421 } 422 423 devices->push_back(Device(name_str, path_str)); 424 } 425 } 426 } 427 mk = NULL; 428 } 429 } 430 431 return true; 432 } 433 434 HRESULT GetStringProp(IPropertyStore* bag, PROPERTYKEY key, std::string* out) { 435 out->clear(); 436 PROPVARIANT var; 437 PropVariantInit(&var); 438 439 HRESULT hr = bag->GetValue(key, &var); 440 if (SUCCEEDED(hr)) { 441 if (var.pwszVal) 442 *out = talk_base::ToUtf8(var.pwszVal); 443 else 444 hr = E_FAIL; 445 } 446 447 PropVariantClear(&var); 448 return hr; 449 } 450 451 // Adapted from http://msdn.microsoft.com/en-us/library/dd370812(v=VS.85).aspx 452 HRESULT CricketDeviceFromImmDevice(IMMDevice* device, Device* out) { 453 CComPtr<IPropertyStore> props; 454 455 HRESULT hr = device->OpenPropertyStore(STGM_READ, &props); 456 if (FAILED(hr)) { 457 return hr; 458 } 459 460 // Get the endpoint's name and id. 461 std::string name, guid; 462 hr = GetStringProp(props, PKEY_Device_FriendlyName, &name); 463 if (SUCCEEDED(hr)) { 464 hr = GetStringProp(props, PKEY_AudioEndpoint_GUID, &guid); 465 466 if (SUCCEEDED(hr)) { 467 out->name = name; 468 out->id = guid; 469 } 470 } 471 return hr; 472 } 473 474 bool GetCoreAudioDevices(bool input, std::vector<Device>* devs) { 475 HRESULT hr = S_OK; 476 CComPtr<IMMDeviceEnumerator> enumerator; 477 478 hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, 479 __uuidof(IMMDeviceEnumerator), reinterpret_cast<void**>(&enumerator)); 480 if (SUCCEEDED(hr)) { 481 CComPtr<IMMDeviceCollection> devices; 482 hr = enumerator->EnumAudioEndpoints((input ? eCapture : eRender), 483 DEVICE_STATE_ACTIVE, &devices); 484 if (SUCCEEDED(hr)) { 485 unsigned int count; 486 hr = devices->GetCount(&count); 487 488 if (SUCCEEDED(hr)) { 489 for (unsigned int i = 0; i < count; i++) { 490 CComPtr<IMMDevice> device; 491 492 // Get pointer to endpoint number i. 493 hr = devices->Item(i, &device); 494 if (FAILED(hr)) { 495 break; 496 } 497 498 Device dev; 499 hr = CricketDeviceFromImmDevice(device, &dev); 500 if (SUCCEEDED(hr)) { 501 devs->push_back(dev); 502 } else { 503 LOG(LS_WARNING) << "Unable to query IMM Device, skipping. HR=" 504 << hr; 505 hr = S_FALSE; 506 } 507 } 508 } 509 } 510 } 511 512 if (!SUCCEEDED(hr)) { 513 LOG(LS_WARNING) << "GetCoreAudioDevices failed with hr " << hr; 514 return false; 515 } 516 return true; 517 } 518 519 bool GetWaveDevices(bool input, std::vector<Device>* devs) { 520 // Note, we don't use the System Device Enumerator interface here since it 521 // adds lots of pseudo-devices to the list, such as DirectSound and Wave 522 // variants of the same device. 523 if (input) { 524 int num_devs = waveInGetNumDevs(); 525 for (int i = 0; i < num_devs; ++i) { 526 WAVEINCAPS caps; 527 if (waveInGetDevCaps(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR && 528 caps.wChannels > 0) { 529 devs->push_back(Device(talk_base::ToUtf8(caps.szPname), 530 talk_base::ToString(i))); 531 } 532 } 533 } else { 534 int num_devs = waveOutGetNumDevs(); 535 for (int i = 0; i < num_devs; ++i) { 536 WAVEOUTCAPS caps; 537 if (waveOutGetDevCaps(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR && 538 caps.wChannels > 0) { 539 devs->push_back(Device(talk_base::ToUtf8(caps.szPname), i)); 540 } 541 } 542 } 543 return true; 544 } 545 546 DeviceWatcher::DeviceWatcher(DeviceManager* manager) 547 : manager_(manager), audio_notify_(NULL), video_notify_(NULL) { 548 } 549 550 bool DeviceWatcher::Start() { 551 if (!Create(NULL, _T("libjingle DeviceWatcher Window"), 552 0, 0, 0, 0, 0, 0)) { 553 return false; 554 } 555 556 audio_notify_ = Register(KSCATEGORY_AUDIO); 557 if (!audio_notify_) { 558 Stop(); 559 return false; 560 } 561 562 video_notify_ = Register(KSCATEGORY_VIDEO); 563 if (!video_notify_) { 564 Stop(); 565 return false; 566 } 567 568 return true; 569 } 570 571 void DeviceWatcher::Stop() { 572 UnregisterDeviceNotification(video_notify_); 573 video_notify_ = NULL; 574 UnregisterDeviceNotification(audio_notify_); 575 audio_notify_ = NULL; 576 Destroy(); 577 } 578 579 HDEVNOTIFY DeviceWatcher::Register(REFGUID guid) { 580 DEV_BROADCAST_DEVICEINTERFACE dbdi; 581 dbdi.dbcc_size = sizeof(dbdi); 582 dbdi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; 583 dbdi.dbcc_classguid = guid; 584 dbdi.dbcc_name[0] = '\0'; 585 return RegisterDeviceNotification(handle(), &dbdi, 586 DEVICE_NOTIFY_WINDOW_HANDLE); 587 } 588 589 void DeviceWatcher::Unregister(HDEVNOTIFY handle) { 590 UnregisterDeviceNotification(handle); 591 } 592 593 bool DeviceWatcher::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, 594 LRESULT& result) { 595 if (uMsg == WM_DEVICECHANGE) { 596 if (wParam == DBT_DEVICEARRIVAL || 597 wParam == DBT_DEVICEREMOVECOMPLETE) { 598 DEV_BROADCAST_DEVICEINTERFACE* dbdi = 599 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(lParam); 600 if (dbdi->dbcc_classguid == KSCATEGORY_AUDIO || 601 dbdi->dbcc_classguid == KSCATEGORY_VIDEO) { 602 manager_->OnDevicesChange(); 603 } 604 } 605 result = 0; 606 return true; 607 } 608 609 return false; 610 } 611 #elif defined(OSX) 612 static bool GetAudioDeviceIDs(bool input, 613 std::vector<AudioDeviceID>* out_dev_ids) { 614 UInt32 propsize; 615 OSErr err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, 616 &propsize, NULL); 617 if (0 != err) { 618 LOG(LS_ERROR) << "Couldn't get information about property, " 619 << "so no device list acquired."; 620 return false; 621 } 622 623 size_t num_devices = propsize / sizeof(AudioDeviceID); 624 talk_base::scoped_array<AudioDeviceID> device_ids( 625 new AudioDeviceID[num_devices]); 626 627 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, 628 &propsize, device_ids.get()); 629 if (0 != err) { 630 LOG(LS_ERROR) << "Failed to get device ids, " 631 << "so no device listing acquired."; 632 return false; 633 } 634 635 for (size_t i = 0; i < num_devices; ++i) { 636 AudioDeviceID an_id = device_ids[i]; 637 // find out the number of channels for this direction 638 // (input/output) on this device - 639 // we'll ignore anything with no channels. 640 err = AudioDeviceGetPropertyInfo(an_id, 0, input, 641 kAudioDevicePropertyStreams, 642 &propsize, NULL); 643 if (0 == err) { 644 unsigned num_channels = propsize / sizeof(AudioStreamID); 645 if (0 < num_channels) { 646 out_dev_ids->push_back(an_id); 647 } 648 } else { 649 LOG(LS_ERROR) << "No property info for stream property for device id " 650 << an_id << "(is_input == " << input 651 << "), so not including it in the list."; 652 } 653 } 654 655 return true; 656 } 657 658 static bool GetAudioDeviceName(AudioDeviceID id, 659 bool input, 660 std::string* out_name) { 661 UInt32 nameLength = kAudioDeviceNameLength; 662 char name[kAudioDeviceNameLength + 1]; 663 OSErr err = AudioDeviceGetProperty(id, 0, input, 664 kAudioDevicePropertyDeviceName, 665 &nameLength, name); 666 if (0 != err) { 667 LOG(LS_ERROR) << "No name acquired for device id " << id; 668 return false; 669 } 670 671 *out_name = name; 672 return true; 673 } 674 675 DeviceWatcher::DeviceWatcher(DeviceManager* manager) 676 : manager_(manager), impl_(NULL) { 677 } 678 679 bool DeviceWatcher::Start() { 680 if (!impl_) { 681 impl_ = CreateDeviceWatcherCallback(manager_); 682 } 683 return impl_ != NULL; 684 } 685 686 void DeviceWatcher::Stop() { 687 if (impl_) { 688 ReleaseDeviceWatcherCallback(impl_); 689 impl_ = NULL; 690 } 691 } 692 693 #elif defined(LINUX) 694 static const std::string kVideoMetaPathK2_4("/proc/video/dev/"); 695 static const std::string kVideoMetaPathK2_6("/sys/class/video4linux/"); 696 697 enum MetaType { M2_4, M2_6, NONE }; 698 699 static void ScanDeviceDirectory(const std::string& devdir, 700 std::vector<Device>* devices) { 701 talk_base::scoped_ptr<talk_base::DirectoryIterator> directoryIterator( 702 talk_base::Filesystem::IterateDirectory()); 703 704 if (directoryIterator->Iterate(talk_base::Pathname(devdir))) { 705 do { 706 std::string filename = directoryIterator->Name(); 707 std::string device_name = devdir + filename; 708 if (!directoryIterator->IsDots()) { 709 if (filename.find("video") == 0 && 710 V4LLookup::IsV4L2Device(device_name)) { 711 devices->push_back(Device(device_name, device_name)); 712 } 713 } 714 } while (directoryIterator->Next()); 715 } 716 } 717 718 static std::string GetVideoDeviceNameK2_6(const std::string& device_meta_path) { 719 std::string device_name; 720 721 talk_base::scoped_ptr<talk_base::FileStream> device_meta_stream( 722 talk_base::Filesystem::OpenFile(device_meta_path, "r")); 723 724 if (device_meta_stream.get() != NULL) { 725 if (device_meta_stream->ReadLine(&device_name) != talk_base::SR_SUCCESS) { 726 LOG(LS_ERROR) << "Failed to read V4L2 device meta " << device_meta_path; 727 } 728 device_meta_stream->Close(); 729 } 730 731 return device_name; 732 } 733 734 static std::string Trim(const std::string& s, const std::string& drop = " \t") { 735 std::string::size_type first = s.find_first_not_of(drop); 736 std::string::size_type last = s.find_last_not_of(drop); 737 738 if (first == std::string::npos || last == std::string::npos) 739 return std::string(""); 740 741 return s.substr(first, last - first + 1); 742 } 743 744 static std::string GetVideoDeviceNameK2_4(const std::string& device_meta_path) { 745 talk_base::ConfigParser::MapVector all_values; 746 747 talk_base::ConfigParser config_parser; 748 talk_base::FileStream* file_stream = 749 talk_base::Filesystem::OpenFile(device_meta_path, "r"); 750 751 if (file_stream == NULL) return ""; 752 753 config_parser.Attach(file_stream); 754 config_parser.Parse(&all_values); 755 756 for (talk_base::ConfigParser::MapVector::iterator i = all_values.begin(); 757 i != all_values.end(); ++i) { 758 talk_base::ConfigParser::SimpleMap::iterator device_name_i = 759 i->find("name"); 760 761 if (device_name_i != i->end()) { 762 return device_name_i->second; 763 } 764 } 765 766 return ""; 767 } 768 769 static std::string GetVideoDeviceName(MetaType meta, 770 const std::string& device_file_name) { 771 std::string device_meta_path; 772 std::string device_name; 773 std::string meta_file_path; 774 775 if (meta == M2_6) { 776 meta_file_path = kVideoMetaPathK2_6 + device_file_name + "/name"; 777 778 LOG(LS_INFO) << "Trying " + meta_file_path; 779 device_name = GetVideoDeviceNameK2_6(meta_file_path); 780 781 if (device_name.empty()) { 782 meta_file_path = kVideoMetaPathK2_6 + device_file_name + "/model"; 783 784 LOG(LS_INFO) << "Trying " << meta_file_path; 785 device_name = GetVideoDeviceNameK2_6(meta_file_path); 786 } 787 } else { 788 meta_file_path = kVideoMetaPathK2_4 + device_file_name; 789 LOG(LS_INFO) << "Trying " << meta_file_path; 790 device_name = GetVideoDeviceNameK2_4(meta_file_path); 791 } 792 793 if (device_name.empty()) { 794 device_name = "/dev/" + device_file_name; 795 LOG(LS_ERROR) 796 << "Device name not found, defaulting to device path " << device_name; 797 } 798 799 LOG(LS_INFO) << "Name for " << device_file_name << " is " << device_name; 800 801 return Trim(device_name); 802 } 803 804 static void ScanV4L2Devices(std::vector<Device>* devices) { 805 LOG(LS_INFO) << ("Enumerating V4L2 devices"); 806 807 MetaType meta; 808 std::string metadata_dir; 809 810 talk_base::scoped_ptr<talk_base::DirectoryIterator> directoryIterator( 811 talk_base::Filesystem::IterateDirectory()); 812 813 // Try and guess kernel version 814 if (directoryIterator->Iterate(kVideoMetaPathK2_6)) { 815 meta = M2_6; 816 metadata_dir = kVideoMetaPathK2_6; 817 } else if (directoryIterator->Iterate(kVideoMetaPathK2_4)) { 818 meta = M2_4; 819 metadata_dir = kVideoMetaPathK2_4; 820 } else { 821 meta = NONE; 822 } 823 824 if (meta != NONE) { 825 LOG(LS_INFO) << "V4L2 device metadata found at " << metadata_dir; 826 827 do { 828 std::string filename = directoryIterator->Name(); 829 830 if (filename.find("video") == 0) { 831 std::string device_path = "/dev/" + filename; 832 833 if (V4LLookup::IsV4L2Device(device_path)) { 834 devices->push_back( 835 Device(GetVideoDeviceName(meta, filename), device_path)); 836 } 837 } 838 } while (directoryIterator->Next()); 839 } else { 840 LOG(LS_ERROR) << "Unable to detect v4l2 metadata directory"; 841 } 842 843 if (devices->size() == 0) { 844 LOG(LS_INFO) << "Plan B. Scanning all video devices in /dev directory"; 845 ScanDeviceDirectory("/dev/", devices); 846 } 847 848 LOG(LS_INFO) << "Total V4L2 devices found : " << devices->size(); 849 } 850 851 static bool GetVideoDevices(std::vector<Device>* devices) { 852 ScanV4L2Devices(devices); 853 return true; 854 } 855 856 DeviceWatcher::DeviceWatcher(DeviceManager* dm) 857 : manager_(dm), udev_(NULL), udev_monitor_(NULL), registered_(false) {} 858 859 bool DeviceWatcher::Start() { 860 // We deliberately return true in the failure paths here because libudev is 861 // not a critical component of a Linux system so it may not be present/usable, 862 // and we don't want to halt DeviceManager initialization in such a case. 863 if (!libudev_.Load()) { 864 LOG(LS_WARNING) << "libudev not present/usable; DeviceWatcher disabled"; 865 return true; 866 } 867 udev_ = LATE(udev_new)(); 868 if (!udev_) { 869 LOG_ERR(LS_ERROR) << "udev_new()"; 870 return true; 871 } 872 // The second argument here is the event source. It can be either "kernel" or 873 // "udev", but "udev" is the only correct choice. Apps listen on udev and the 874 // udev daemon in turn listens on the kernel. 875 udev_monitor_ = LATE(udev_monitor_new_from_netlink)(udev_, "udev"); 876 if (!udev_monitor_) { 877 LOG_ERR(LS_ERROR) << "udev_monitor_new_from_netlink()"; 878 return true; 879 } 880 // We only listen for changes in the video devices. Audio devices are more or 881 // less unimportant because receiving device change notifications really only 882 // matters for broadcasting updated send/recv capabilities based on whether 883 // there is at least one device available, and almost all computers have at 884 // least one audio device. Also, PulseAudio device notifications don't come 885 // from the udev daemon, they come from the PulseAudio daemon, so we'd only 886 // want to listen for audio device changes from udev if using ALSA. For 887 // simplicity, we don't bother with any audio stuff at all. 888 if (LATE(udev_monitor_filter_add_match_subsystem_devtype)(udev_monitor_, 889 "video4linux", 890 NULL) < 0) { 891 LOG_ERR(LS_ERROR) << "udev_monitor_filter_add_match_subsystem_devtype()"; 892 return true; 893 } 894 if (LATE(udev_monitor_enable_receiving)(udev_monitor_) < 0) { 895 LOG_ERR(LS_ERROR) << "udev_monitor_enable_receiving()"; 896 return true; 897 } 898 static_cast<talk_base::PhysicalSocketServer*>( 899 talk_base::Thread::Current()->socketserver())->Add(this); 900 registered_ = true; 901 return true; 902 } 903 904 void DeviceWatcher::Stop() { 905 if (registered_) { 906 static_cast<talk_base::PhysicalSocketServer*>( 907 talk_base::Thread::Current()->socketserver())->Remove(this); 908 registered_ = false; 909 } 910 if (udev_monitor_) { 911 LATE(udev_monitor_unref)(udev_monitor_); 912 udev_monitor_ = NULL; 913 } 914 if (udev_) { 915 LATE(udev_unref)(udev_); 916 udev_ = NULL; 917 } 918 libudev_.Unload(); 919 } 920 921 uint32 DeviceWatcher::GetRequestedEvents() { 922 return talk_base::DE_READ; 923 } 924 925 void DeviceWatcher::OnPreEvent(uint32 ff) { 926 // Nothing to do. 927 } 928 929 void DeviceWatcher::OnEvent(uint32 ff, int err) { 930 udev_device* device = LATE(udev_monitor_receive_device)(udev_monitor_); 931 if (!device) { 932 // Probably the socket connection to the udev daemon was terminated (perhaps 933 // the daemon crashed or is being restarted?). 934 LOG_ERR(LS_WARNING) << "udev_monitor_receive_device()"; 935 // Stop listening to avoid potential livelock (an fd with EOF in it is 936 // always considered readable). 937 static_cast<talk_base::PhysicalSocketServer*>( 938 talk_base::Thread::Current()->socketserver())->Remove(this); 939 registered_ = false; 940 return; 941 } 942 // Else we read the device successfully. 943 944 // Since we already have our own filesystem-based device enumeration code, we 945 // simply re-enumerate rather than inspecting the device event. 946 LATE(udev_device_unref)(device); 947 manager_->OnDevicesChange(); 948 } 949 950 int DeviceWatcher::GetDescriptor() { 951 return LATE(udev_monitor_get_fd)(udev_monitor_); 952 } 953 954 bool DeviceWatcher::IsDescriptorClosed() { 955 // If it is closed then we will just get an error in 956 // udev_monitor_receive_device and unregister, so we don't need to check for 957 // it separately. 958 return false; 959 } 960 961 #endif 962 963 // TODO: Try to get hold of a copy of Final Cut to understand why we 964 // crash while scanning their components on OS X. 965 #if !defined(LINUX) && !defined(IOS) 966 static bool ShouldDeviceBeIgnored(const std::string& device_name) { 967 static const char* const kFilteredDevices[] = { 968 "Google Camera Adapter", // Our own magiccams 969 #ifdef WIN32 970 "Asus virtual Camera", // Bad Asus desktop virtual cam 971 "Bluetooth Video", // Bad Sony viao bluetooth sharing driver 972 #elif OSX 973 "DVCPRO HD", // Final cut 974 "Sonix SN9C201p", // Crashes in OpenAComponent and CloseComponent 975 #endif 976 }; 977 978 for (int i = 0; i < ARRAY_SIZE(kFilteredDevices); ++i) { 979 if (strnicmp(device_name.c_str(), kFilteredDevices[i], 980 strlen(kFilteredDevices[i])) == 0) { 981 LOG(LS_INFO) << "Ignoring device " << device_name; 982 return true; 983 } 984 } 985 return false; 986 } 987 #endif 988 989 }; // namespace cricket 990