| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (c) 2004 The WebRTC project authors. All Rights Reserved. | |
| 3 * | |
| 4 * Use of this source code is governed by a BSD-style license | |
| 5 * that can be found in the LICENSE file in the root of the source | |
| 6 * tree. An additional intellectual property rights grant can be found | |
| 7 * in the file PATENTS. All contributing project authors may | |
| 8 * be found in the AUTHORS file in the root of the source tree. | |
| 9 */ | |
| 10 | |
| 11 #include "webrtc/media/devices/linuxdevicemanager.h" | |
| 12 | |
| 13 #include <unistd.h> | |
| 14 #include "webrtc/base/fileutils.h" | |
| 15 #include "webrtc/base/linux.h" | |
| 16 #include "webrtc/base/logging.h" | |
| 17 #include "webrtc/base/pathutils.h" | |
| 18 #include "webrtc/base/physicalsocketserver.h" | |
| 19 #include "webrtc/base/stream.h" | |
| 20 #include "webrtc/base/stringutils.h" | |
| 21 #include "webrtc/base/thread.h" | |
| 22 #include "webrtc/media/base/mediacommon.h" | |
| 23 #include "webrtc/media/devices/libudevsymboltable.h" | |
| 24 #include "webrtc/media/devices/v4llookup.h" | |
| 25 #include "webrtc/sound/platformsoundsystem.h" | |
| 26 #include "webrtc/sound/platformsoundsystemfactory.h" | |
| 27 #include "webrtc/sound/sounddevicelocator.h" | |
| 28 #include "webrtc/sound/soundsysteminterface.h" | |
| 29 | |
| 30 namespace cricket { | |
| 31 | |
| 32 DeviceManagerInterface* DeviceManagerFactory::Create() { | |
| 33 return new LinuxDeviceManager(); | |
| 34 } | |
| 35 | |
| 36 class LinuxDeviceWatcher | |
| 37 : public DeviceWatcher, | |
| 38 private rtc::Dispatcher { | |
| 39 public: | |
| 40 explicit LinuxDeviceWatcher(DeviceManagerInterface* dm); | |
| 41 virtual ~LinuxDeviceWatcher(); | |
| 42 virtual bool Start(); | |
| 43 virtual void Stop(); | |
| 44 | |
| 45 private: | |
| 46 virtual uint32_t GetRequestedEvents(); | |
| 47 virtual void OnPreEvent(uint32_t ff); | |
| 48 virtual void OnEvent(uint32_t ff, int err); | |
| 49 virtual int GetDescriptor(); | |
| 50 virtual bool IsDescriptorClosed(); | |
| 51 | |
| 52 DeviceManagerInterface* manager_; | |
| 53 LibUDevSymbolTable libudev_; | |
| 54 struct udev* udev_; | |
| 55 struct udev_monitor* udev_monitor_; | |
| 56 bool registered_; | |
| 57 }; | |
| 58 | |
| 59 static const char* const kFilteredAudioDevicesName[] = { | |
| 60 #if defined(CHROMEOS) | |
| 61 "surround40:", | |
| 62 "surround41:", | |
| 63 "surround50:", | |
| 64 "surround51:", | |
| 65 "surround71:", | |
| 66 "iec958:", // S/PDIF | |
| 67 #endif | |
| 68 NULL, | |
| 69 }; | |
| 70 static const char* kFilteredVideoDevicesName[] = { | |
| 71 NULL, | |
| 72 }; | |
| 73 | |
| 74 LinuxDeviceManager::LinuxDeviceManager() | |
| 75 : sound_system_(new rtc::PlatformSoundSystemFactory()) { | |
| 76 set_watcher(new LinuxDeviceWatcher(this)); | |
| 77 } | |
| 78 | |
| 79 LinuxDeviceManager::~LinuxDeviceManager() { | |
| 80 } | |
| 81 | |
| 82 bool LinuxDeviceManager::GetAudioDevices(bool input, | |
| 83 std::vector<Device>* devs) { | |
| 84 devs->clear(); | |
| 85 if (!sound_system_.get()) { | |
| 86 return false; | |
| 87 } | |
| 88 rtc::SoundSystemInterface::SoundDeviceLocatorList list; | |
| 89 bool success; | |
| 90 if (input) { | |
| 91 success = sound_system_->EnumerateCaptureDevices(&list); | |
| 92 } else { | |
| 93 success = sound_system_->EnumeratePlaybackDevices(&list); | |
| 94 } | |
| 95 if (!success) { | |
| 96 LOG(LS_ERROR) << "Can't enumerate devices"; | |
| 97 sound_system_.release(); | |
| 98 return false; | |
| 99 } | |
| 100 // We have to start the index at 1 because webrtc VoiceEngine puts the default | |
| 101 // device at index 0, but Enumerate(Capture|Playback)Devices does not include | |
| 102 // a locator for the default device. | |
| 103 int index = 1; | |
| 104 for (rtc::SoundSystemInterface::SoundDeviceLocatorList::iterator i = list.begi
n(); | |
| 105 i != list.end(); | |
| 106 ++i, ++index) { | |
| 107 devs->push_back(Device((*i)->name(), index)); | |
| 108 } | |
| 109 rtc::SoundSystemInterface::ClearSoundDeviceLocatorList(&list); | |
| 110 sound_system_.release(); | |
| 111 return FilterDevices(devs, kFilteredAudioDevicesName); | |
| 112 } | |
| 113 | |
| 114 static const std::string kVideoMetaPathK2_4("/proc/video/dev/"); | |
| 115 static const std::string kVideoMetaPathK2_6("/sys/class/video4linux/"); | |
| 116 | |
| 117 enum MetaType { M2_4, M2_6, NONE }; | |
| 118 | |
| 119 static void ScanDeviceDirectory(const std::string& devdir, | |
| 120 std::vector<Device>* devices) { | |
| 121 rtc::scoped_ptr<rtc::DirectoryIterator> directoryIterator( | |
| 122 rtc::Filesystem::IterateDirectory()); | |
| 123 | |
| 124 if (directoryIterator->Iterate(rtc::Pathname(devdir))) { | |
| 125 do { | |
| 126 std::string filename = directoryIterator->Name(); | |
| 127 std::string device_name = devdir + filename; | |
| 128 if (!directoryIterator->IsDots()) { | |
| 129 if (filename.find("video") == 0 && | |
| 130 V4LLookup::IsV4L2Device(device_name)) { | |
| 131 devices->push_back(Device(device_name, device_name)); | |
| 132 } | |
| 133 } | |
| 134 } while (directoryIterator->Next()); | |
| 135 } | |
| 136 } | |
| 137 | |
| 138 static std::string GetVideoDeviceNameK2_6(const std::string& device_meta_path) { | |
| 139 std::string device_name; | |
| 140 | |
| 141 rtc::scoped_ptr<rtc::FileStream> device_meta_stream( | |
| 142 rtc::Filesystem::OpenFile(device_meta_path, "r")); | |
| 143 | |
| 144 if (device_meta_stream) { | |
| 145 if (device_meta_stream->ReadLine(&device_name) != rtc::SR_SUCCESS) { | |
| 146 LOG(LS_ERROR) << "Failed to read V4L2 device meta " << device_meta_path; | |
| 147 } | |
| 148 device_meta_stream->Close(); | |
| 149 } | |
| 150 | |
| 151 return device_name; | |
| 152 } | |
| 153 | |
| 154 static std::string Trim(const std::string& s, const std::string& drop = " \t") { | |
| 155 std::string::size_type first = s.find_first_not_of(drop); | |
| 156 std::string::size_type last = s.find_last_not_of(drop); | |
| 157 | |
| 158 if (first == std::string::npos || last == std::string::npos) | |
| 159 return std::string(""); | |
| 160 | |
| 161 return s.substr(first, last - first + 1); | |
| 162 } | |
| 163 | |
| 164 static std::string GetVideoDeviceNameK2_4(const std::string& device_meta_path) { | |
| 165 rtc::ConfigParser::MapVector all_values; | |
| 166 | |
| 167 rtc::ConfigParser config_parser; | |
| 168 rtc::FileStream* file_stream = | |
| 169 rtc::Filesystem::OpenFile(device_meta_path, "r"); | |
| 170 | |
| 171 if (file_stream == NULL) return ""; | |
| 172 | |
| 173 config_parser.Attach(file_stream); | |
| 174 config_parser.Parse(&all_values); | |
| 175 | |
| 176 for (rtc::ConfigParser::MapVector::iterator i = all_values.begin(); | |
| 177 i != all_values.end(); ++i) { | |
| 178 rtc::ConfigParser::SimpleMap::iterator device_name_i = | |
| 179 i->find("name"); | |
| 180 | |
| 181 if (device_name_i != i->end()) { | |
| 182 return device_name_i->second; | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 return ""; | |
| 187 } | |
| 188 | |
| 189 static std::string GetVideoDeviceName(MetaType meta, | |
| 190 const std::string& device_file_name) { | |
| 191 std::string device_meta_path; | |
| 192 std::string device_name; | |
| 193 std::string meta_file_path; | |
| 194 | |
| 195 if (meta == M2_6) { | |
| 196 meta_file_path = kVideoMetaPathK2_6 + device_file_name + "/name"; | |
| 197 | |
| 198 LOG(LS_INFO) << "Trying " + meta_file_path; | |
| 199 device_name = GetVideoDeviceNameK2_6(meta_file_path); | |
| 200 | |
| 201 if (device_name.empty()) { | |
| 202 meta_file_path = kVideoMetaPathK2_6 + device_file_name + "/model"; | |
| 203 | |
| 204 LOG(LS_INFO) << "Trying " << meta_file_path; | |
| 205 device_name = GetVideoDeviceNameK2_6(meta_file_path); | |
| 206 } | |
| 207 } else { | |
| 208 meta_file_path = kVideoMetaPathK2_4 + device_file_name; | |
| 209 LOG(LS_INFO) << "Trying " << meta_file_path; | |
| 210 device_name = GetVideoDeviceNameK2_4(meta_file_path); | |
| 211 } | |
| 212 | |
| 213 if (device_name.empty()) { | |
| 214 device_name = "/dev/" + device_file_name; | |
| 215 LOG(LS_ERROR) | |
| 216 << "Device name not found, defaulting to device path " << device_name; | |
| 217 } | |
| 218 | |
| 219 LOG(LS_INFO) << "Name for " << device_file_name << " is " << device_name; | |
| 220 | |
| 221 return Trim(device_name); | |
| 222 } | |
| 223 | |
| 224 static void ScanV4L2Devices(std::vector<Device>* devices) { | |
| 225 LOG(LS_INFO) << ("Enumerating V4L2 devices"); | |
| 226 | |
| 227 MetaType meta; | |
| 228 std::string metadata_dir; | |
| 229 | |
| 230 rtc::scoped_ptr<rtc::DirectoryIterator> directoryIterator( | |
| 231 rtc::Filesystem::IterateDirectory()); | |
| 232 | |
| 233 // Try and guess kernel version | |
| 234 if (directoryIterator->Iterate(kVideoMetaPathK2_6)) { | |
| 235 meta = M2_6; | |
| 236 metadata_dir = kVideoMetaPathK2_6; | |
| 237 } else if (directoryIterator->Iterate(kVideoMetaPathK2_4)) { | |
| 238 meta = M2_4; | |
| 239 metadata_dir = kVideoMetaPathK2_4; | |
| 240 } else { | |
| 241 meta = NONE; | |
| 242 } | |
| 243 | |
| 244 if (meta != NONE) { | |
| 245 LOG(LS_INFO) << "V4L2 device metadata found at " << metadata_dir; | |
| 246 | |
| 247 do { | |
| 248 std::string filename = directoryIterator->Name(); | |
| 249 | |
| 250 if (filename.find("video") == 0) { | |
| 251 std::string device_path = "/dev/" + filename; | |
| 252 | |
| 253 if (V4LLookup::IsV4L2Device(device_path)) { | |
| 254 devices->push_back( | |
| 255 Device(GetVideoDeviceName(meta, filename), device_path)); | |
| 256 } | |
| 257 } | |
| 258 } while (directoryIterator->Next()); | |
| 259 } else { | |
| 260 LOG(LS_ERROR) << "Unable to detect v4l2 metadata directory"; | |
| 261 } | |
| 262 | |
| 263 if (devices->size() == 0) { | |
| 264 LOG(LS_INFO) << "Plan B. Scanning all video devices in /dev directory"; | |
| 265 ScanDeviceDirectory("/dev/", devices); | |
| 266 } | |
| 267 | |
| 268 LOG(LS_INFO) << "Total V4L2 devices found : " << devices->size(); | |
| 269 } | |
| 270 | |
| 271 bool LinuxDeviceManager::GetVideoCaptureDevices(std::vector<Device>* devices) { | |
| 272 devices->clear(); | |
| 273 ScanV4L2Devices(devices); | |
| 274 return FilterDevices(devices, kFilteredVideoDevicesName); | |
| 275 } | |
| 276 | |
| 277 LinuxDeviceWatcher::LinuxDeviceWatcher(DeviceManagerInterface* dm) | |
| 278 : DeviceWatcher(dm), | |
| 279 manager_(dm), | |
| 280 udev_(NULL), | |
| 281 udev_monitor_(NULL), | |
| 282 registered_(false) { | |
| 283 } | |
| 284 | |
| 285 LinuxDeviceWatcher::~LinuxDeviceWatcher() { | |
| 286 } | |
| 287 | |
| 288 static rtc::PhysicalSocketServer* CurrentSocketServer() { | |
| 289 rtc::SocketServer* ss = | |
| 290 rtc::ThreadManager::Instance()->WrapCurrentThread()->socketserver(); | |
| 291 return reinterpret_cast<rtc::PhysicalSocketServer*>(ss); | |
| 292 } | |
| 293 | |
| 294 bool LinuxDeviceWatcher::Start() { | |
| 295 // We deliberately return true in the failure paths here because libudev is | |
| 296 // not a critical component of a Linux system so it may not be present/usable, | |
| 297 // and we don't want to halt LinuxDeviceManager initialization in such a case. | |
| 298 if (!libudev_.Load() || IsWrongLibUDevAbiVersion(libudev_.GetDllHandle())) { | |
| 299 LOG(LS_WARNING) | |
| 300 << "libudev not present/usable; LinuxDeviceWatcher disabled"; | |
| 301 return true; | |
| 302 } | |
| 303 udev_ = libudev_.udev_new()(); | |
| 304 if (!udev_) { | |
| 305 LOG_ERR(LS_ERROR) << "udev_new()"; | |
| 306 return true; | |
| 307 } | |
| 308 // The second argument here is the event source. It can be either "kernel" or | |
| 309 // "udev", but "udev" is the only correct choice. Apps listen on udev and the | |
| 310 // udev daemon in turn listens on the kernel. | |
| 311 udev_monitor_ = libudev_.udev_monitor_new_from_netlink()(udev_, "udev"); | |
| 312 if (!udev_monitor_) { | |
| 313 LOG_ERR(LS_ERROR) << "udev_monitor_new_from_netlink()"; | |
| 314 return true; | |
| 315 } | |
| 316 // We only listen for changes in the video devices. Audio devices are more or | |
| 317 // less unimportant because receiving device change notifications really only | |
| 318 // matters for broadcasting updated send/recv capabilities based on whether | |
| 319 // there is at least one device available, and almost all computers have at | |
| 320 // least one audio device. Also, PulseAudio device notifications don't come | |
| 321 // from the udev daemon, they come from the PulseAudio daemon, so we'd only | |
| 322 // want to listen for audio device changes from udev if using ALSA. For | |
| 323 // simplicity, we don't bother with any audio stuff at all. | |
| 324 if (libudev_.udev_monitor_filter_add_match_subsystem_devtype()( | |
| 325 udev_monitor_, "video4linux", NULL) < 0) { | |
| 326 LOG_ERR(LS_ERROR) << "udev_monitor_filter_add_match_subsystem_devtype()"; | |
| 327 return true; | |
| 328 } | |
| 329 if (libudev_.udev_monitor_enable_receiving()(udev_monitor_) < 0) { | |
| 330 LOG_ERR(LS_ERROR) << "udev_monitor_enable_receiving()"; | |
| 331 return true; | |
| 332 } | |
| 333 CurrentSocketServer()->Add(this); | |
| 334 registered_ = true; | |
| 335 return true; | |
| 336 } | |
| 337 | |
| 338 void LinuxDeviceWatcher::Stop() { | |
| 339 if (registered_) { | |
| 340 CurrentSocketServer()->Remove(this); | |
| 341 registered_ = false; | |
| 342 } | |
| 343 if (udev_monitor_) { | |
| 344 libudev_.udev_monitor_unref()(udev_monitor_); | |
| 345 udev_monitor_ = NULL; | |
| 346 } | |
| 347 if (udev_) { | |
| 348 libudev_.udev_unref()(udev_); | |
| 349 udev_ = NULL; | |
| 350 } | |
| 351 libudev_.Unload(); | |
| 352 } | |
| 353 | |
| 354 uint32_t LinuxDeviceWatcher::GetRequestedEvents() { | |
| 355 return rtc::DE_READ; | |
| 356 } | |
| 357 | |
| 358 void LinuxDeviceWatcher::OnPreEvent(uint32_t ff) { | |
| 359 // Nothing to do. | |
| 360 } | |
| 361 | |
| 362 void LinuxDeviceWatcher::OnEvent(uint32_t ff, int err) { | |
| 363 udev_device* device = libudev_.udev_monitor_receive_device()(udev_monitor_); | |
| 364 if (!device) { | |
| 365 // Probably the socket connection to the udev daemon was terminated (perhaps | |
| 366 // the daemon crashed or is being restarted?). | |
| 367 LOG_ERR(LS_WARNING) << "udev_monitor_receive_device()"; | |
| 368 // Stop listening to avoid potential livelock (an fd with EOF in it is | |
| 369 // always considered readable). | |
| 370 CurrentSocketServer()->Remove(this); | |
| 371 registered_ = false; | |
| 372 return; | |
| 373 } | |
| 374 // Else we read the device successfully. | |
| 375 | |
| 376 // Since we already have our own filesystem-based device enumeration code, we | |
| 377 // simply re-enumerate rather than inspecting the device event. | |
| 378 libudev_.udev_device_unref()(device); | |
| 379 manager_->SignalDevicesChange(); | |
| 380 } | |
| 381 | |
| 382 int LinuxDeviceWatcher::GetDescriptor() { | |
| 383 return libudev_.udev_monitor_get_fd()(udev_monitor_); | |
| 384 } | |
| 385 | |
| 386 bool LinuxDeviceWatcher::IsDescriptorClosed() { | |
| 387 // If it is closed then we will just get an error in | |
| 388 // udev_monitor_receive_device and unregister, so we don't need to check for | |
| 389 // it separately. | |
| 390 return false; | |
| 391 } | |
| 392 | |
| 393 }; // namespace cricket | |
| OLD | NEW |