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 |