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