Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(56)

Side by Side Diff: content/browser/renderer_host/media/media_devices_manager.cc

Issue 2350693002: Remove device enumeration, caching and monitoring from MediaStreamManager. (Closed)
Patch Set: latest hta@ comments Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/renderer_host/media/media_devices_manager.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <algorithm>
11 #include <string>
12
13 #include "base/command_line.h"
14 #include "base/location.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/task_runner_util.h"
17 #include "base/threading/thread_checker.h"
18 #include "base/threading/thread_task_runner_handle.h"
19 #include "build/build_config.h"
20 #include "content/browser/renderer_host/media/media_stream_manager.h"
21 #include "content/browser/renderer_host/media/video_capture_manager.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "media/audio/audio_device_description.h"
24 #include "media/audio/audio_manager.h"
25 #include "media/base/media_switches.h"
26
27 #if defined(OS_MACOSX)
28 #include "base/bind_helpers.h"
29 #include "base/profiler/scoped_tracker.h"
30 #include "base/single_thread_task_runner.h"
31 #include "content/browser/browser_main_loop.h"
32 #include "media/device_monitors/device_monitor_mac.h"
33 #endif
34
35 namespace content {
36
37 namespace {
38
39 // Private helper method to generate a string for the log message that lists the
40 // human readable names of |devices|.
41 std::string GetLogMessageString(MediaDeviceType device_type,
42 const MediaDeviceInfoArray& device_infos) {
43 std::string output_string =
44 base::StringPrintf("Getting devices of type %d:\n", device_type);
45 if (device_infos.empty())
46 return output_string + "No devices found.";
47 for (const auto& device_info : device_infos)
48 output_string += " " + device_info.label + "\n";
49 return output_string;
50 }
51
52 MediaDeviceInfoArray EnumerateAudioDevicesOnDeviceThread(
53 media::AudioManager* audio_manager,
54 bool is_input) {
55 DCHECK(audio_manager->GetTaskRunner()->BelongsToCurrentThread());
56
57 MediaDeviceInfoArray snapshot;
58 media::AudioDeviceNames device_names;
59 if (is_input)
60 audio_manager->GetAudioInputDeviceNames(&device_names);
61 else
62 audio_manager->GetAudioOutputDeviceNames(&device_names);
63
64 for (const media::AudioDeviceName& name : device_names) {
65 snapshot.emplace_back(
66 name.unique_id, name.device_name,
67 is_input ? audio_manager->GetGroupIDInput(name.unique_id)
68 : audio_manager->GetGroupIDOutput(name.unique_id));
69 }
70
71 return snapshot;
72 }
73
74 MediaDeviceInfoArray GetFakeAudioDevices(bool is_input) {
75 MediaDeviceInfoArray result;
76 if (is_input) {
77 result.emplace_back(media::AudioDeviceDescription::kDefaultDeviceId,
78 "Fake Default Audio Input",
79 "fake_group_audio_input_default");
80 result.emplace_back("fake_audio_input_1", "Fake Audio Input 1",
81 "fake_group_audio_input_1");
82 result.emplace_back("fake_audio_input_2", "Fake Audio Input 2",
83 "fake_group_audio_input_2");
84 } else {
85 result.emplace_back(media::AudioDeviceDescription::kDefaultDeviceId,
86 "Fake Default Audio Output",
87 "fake_group_audio_output_default");
88 result.emplace_back("fake_audio_output_1", "Fake Audio Output 1",
89 "fake_group_audio_output_1");
90 result.emplace_back("fake_audio_output_2", "Fake Audio Output 2",
91 "fake_group_audio_output_2");
92 }
93
94 return result;
95 }
96
97 } // namespace
98
99 struct MediaDevicesManager::EnumerationRequest {
100 EnumerationRequest(const BoolDeviceTypes& requested_types,
101 const EnumerationCallback& callback)
102 : callback(callback) {
103 requested = requested_types;
104 has_seen_result.fill(false);
105 }
106
107 BoolDeviceTypes requested;
108 BoolDeviceTypes has_seen_result;
109 EnumerationCallback callback;
110 };
111
112 // This class helps manage the consistency of cached enumeration results.
113 // It uses a sequence number for each invalidation and enumeration.
114 // A cache is considered valid if the sequence number for the last enumeration
115 // is greater than the sequence number for the last invalidation.
116 // The advantage of using invalidations over directly issuing enumerations upon
117 // each system notification is that some platforms issue multiple notifications
118 // on each device change. The cost of performing multiple redundant
119 // invalidations is significantly lower than the cost of issuing multiple
120 // redundant enumerations.
121 class MediaDevicesManager::CacheInfo {
122 public:
123 CacheInfo()
124 : current_event_sequence_(0),
125 seq_last_update_(0),
126 seq_last_invalidation_(0),
127 is_update_ongoing_(false) {}
128
129 void InvalidateCache() {
130 DCHECK(thread_checker_.CalledOnValidThread());
131 seq_last_invalidation_ = NewEventSequence();
132 }
133
134 bool IsLastUpdateValid() const {
135 DCHECK(thread_checker_.CalledOnValidThread());
136 return seq_last_update_ > seq_last_invalidation_ && !is_update_ongoing_;
137 }
138
139 void UpdateStarted() {
140 DCHECK(thread_checker_.CalledOnValidThread());
141 DCHECK(!is_update_ongoing_);
142 seq_last_update_ = NewEventSequence();
143 is_update_ongoing_ = true;
144 }
145
146 void UpdateCompleted() {
147 DCHECK(thread_checker_.CalledOnValidThread());
148 DCHECK(is_update_ongoing_);
149 is_update_ongoing_ = false;
150 }
151
152 bool is_update_ongoing() const {
153 DCHECK(thread_checker_.CalledOnValidThread());
154 return is_update_ongoing_;
155 }
156
157 private:
158 int64_t NewEventSequence() {
159 DCHECK(thread_checker_.CalledOnValidThread());
160 return ++current_event_sequence_;
161 }
162
163 int64_t current_event_sequence_;
164 int64_t seq_last_update_;
165 int64_t seq_last_invalidation_;
166 bool is_update_ongoing_;
167 base::ThreadChecker thread_checker_;
168 };
169
170 MediaDevicesManager::MediaDevicesManager(
171 media::AudioManager* audio_manager,
172 const scoped_refptr<VideoCaptureManager>& video_capture_manager,
173 MediaStreamManager* media_stream_manager)
174 : use_fake_devices_(base::CommandLine::ForCurrentProcess()->HasSwitch(
175 switches::kUseFakeDeviceForMediaStream)),
176 audio_manager_(audio_manager),
177 video_capture_manager_(video_capture_manager),
178 media_stream_manager_(media_stream_manager),
179 cache_infos_(NUM_MEDIA_DEVICE_TYPES),
180 monitoring_started_(false),
181 weak_factory_(this) {
182 DCHECK_CURRENTLY_ON(BrowserThread::IO);
183 DCHECK(audio_manager_);
184 DCHECK(video_capture_manager_.get());
185 cache_policies_.fill(CachePolicy::NO_CACHE);
186 has_seen_result_.fill(false);
187 }
188
189 MediaDevicesManager::~MediaDevicesManager() {
190 DCHECK_CURRENTLY_ON(BrowserThread::IO);
191 }
192
193 void MediaDevicesManager::EnumerateDevices(
194 const BoolDeviceTypes& requested_types,
195 const EnumerationCallback& callback) {
196 DCHECK_CURRENTLY_ON(BrowserThread::IO);
197 StartMonitoring();
198
199 requests_.emplace_back(requested_types, callback);
200 bool all_results_cached = true;
201 for (size_t i = 0; i < NUM_MEDIA_DEVICE_TYPES; ++i) {
202 if (requested_types[i] && cache_policies_[i] == CachePolicy::NO_CACHE) {
203 all_results_cached = false;
204 DoEnumerateDevices(static_cast<MediaDeviceType>(i));
205 }
206 }
207
208 if (all_results_cached)
209 ProcessRequests();
210 }
211
212 void MediaDevicesManager::SetCachePolicy(MediaDeviceType type,
213 CachePolicy policy) {
214 DCHECK_CURRENTLY_ON(BrowserThread::IO);
215 DCHECK(IsValidMediaDeviceType(type));
216 if (cache_policies_[type] == policy)
217 return;
218
219 cache_policies_[type] = policy;
220 // If the new policy is SYSTEM_MONITOR, issue an enumeration to populate the
221 // cache.
222 if (policy == CachePolicy::SYSTEM_MONITOR) {
223 cache_infos_[type].InvalidateCache();
224 DoEnumerateDevices(type);
225 }
226 }
227
228 void MediaDevicesManager::StartMonitoring() {
229 DCHECK_CURRENTLY_ON(BrowserThread::IO);
230 if (monitoring_started_)
231 return;
232
233 if (!base::SystemMonitor::Get())
234 return;
235
236 monitoring_started_ = true;
237 base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
238
239 for (size_t i = 0; i < NUM_MEDIA_DEVICE_TYPES; ++i) {
240 DCHECK(cache_policies_[i] != CachePolicy::SYSTEM_MONITOR);
241 SetCachePolicy(static_cast<MediaDeviceType>(i),
242 CachePolicy::SYSTEM_MONITOR);
243 }
244
245 #if defined(OS_MACOSX)
246 BrowserThread::PostTask(
247 BrowserThread::UI, FROM_HERE,
248 base::Bind(&MediaDevicesManager::StartMonitoringOnUIThread,
249 base::Unretained(this)));
250
251 // TODO(guidou): Remove this statement once the Mac device monitor is fixed to
252 // correctly report device-change events for output-only audio devices.
253 // See http://crbug.com/648173.
254 SetCachePolicy(MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, CachePolicy::NO_CACHE);
255 #endif
256 }
257
258 #if defined(OS_MACOSX)
259 void MediaDevicesManager::StartMonitoringOnUIThread() {
260 DCHECK_CURRENTLY_ON(BrowserThread::UI);
261 // TODO(erikchen): Remove ScopedTracker below once crbug.com/458404 is fixed.
262 tracked_objects::ScopedTracker tracking_profile1(
263 FROM_HERE_WITH_EXPLICIT_FUNCTION(
264 "458404 MediaDevicesManager::GetBrowserMainLoop"));
265 BrowserMainLoop* browser_main_loop = content::BrowserMainLoop::GetInstance();
266 if (!browser_main_loop)
267 return;
268
269 // TODO(erikchen): Remove ScopedTracker below once crbug.com/458404 is
270 // fixed.
271 tracked_objects::ScopedTracker tracking_profile2(
272 FROM_HERE_WITH_EXPLICIT_FUNCTION(
273 "458404 MediaDevicesManager::GetTaskRunner"));
274 const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
275 audio_manager_->GetTaskRunner();
276 // TODO(erikchen): Remove ScopedTracker below once crbug.com/458404 is
277 // fixed.
278 tracked_objects::ScopedTracker tracking_profile3(
279 FROM_HERE_WITH_EXPLICIT_FUNCTION(
280 "458404 MediaDevicesManager::DeviceMonitorMac::StartMonitoring"));
281 browser_main_loop->device_monitor_mac()->StartMonitoring(task_runner);
282 }
283 #endif
284
285 void MediaDevicesManager::StopMonitoring() {
286 DCHECK_CURRENTLY_ON(BrowserThread::IO);
287 if (!monitoring_started_)
288 return;
289 base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
290 monitoring_started_ = false;
291 for (size_t i = 0; i < NUM_MEDIA_DEVICE_TYPES; ++i)
292 SetCachePolicy(static_cast<MediaDeviceType>(i), CachePolicy::NO_CACHE);
293 }
294
295 bool MediaDevicesManager::IsMonitoringStarted() {
296 DCHECK_CURRENTLY_ON(BrowserThread::IO);
297 return monitoring_started_;
298 }
299
300 void MediaDevicesManager::OnDevicesChanged(
301 base::SystemMonitor::DeviceType device_type) {
302 DCHECK_CURRENTLY_ON(BrowserThread::IO);
303 switch (device_type) {
304 case base::SystemMonitor::DEVTYPE_AUDIO:
305 HandleDevicesChanged(MEDIA_DEVICE_TYPE_AUDIO_INPUT);
306 HandleDevicesChanged(MEDIA_DEVICE_TYPE_AUDIO_OUTPUT);
307 break;
308 case base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE:
309 HandleDevicesChanged(MEDIA_DEVICE_TYPE_VIDEO_INPUT);
310 break;
311 default:
312 break; // Uninteresting device change.
313 }
314 }
315
316 MediaDeviceInfoArray MediaDevicesManager::GetCachedDeviceInfo(
317 MediaDeviceType type) {
318 DCHECK_CURRENTLY_ON(BrowserThread::IO);
319 return current_snapshot_[type];
320 }
321
322 void MediaDevicesManager::DoEnumerateDevices(MediaDeviceType type) {
323 DCHECK_CURRENTLY_ON(BrowserThread::IO);
324 DCHECK(IsValidMediaDeviceType(type));
325 CacheInfo& cache_info = cache_infos_[type];
326 if (cache_info.is_update_ongoing())
327 return;
328
329 cache_info.UpdateStarted();
330 switch (type) {
331 case MEDIA_DEVICE_TYPE_AUDIO_INPUT:
332 EnumerateAudioDevices(true /* is_input */);
333 break;
334 case MEDIA_DEVICE_TYPE_VIDEO_INPUT:
335 video_capture_manager_->EnumerateDevices(
336 base::Bind(&MediaDevicesManager::VideoInputDevicesEnumerated,
337 weak_factory_.GetWeakPtr()));
338 break;
339 case MEDIA_DEVICE_TYPE_AUDIO_OUTPUT:
340 EnumerateAudioDevices(false /* is_input */);
341 break;
342 default:
343 NOTREACHED();
344 }
345 }
346
347 void MediaDevicesManager::EnumerateAudioDevices(bool is_input) {
348 DCHECK_CURRENTLY_ON(BrowserThread::IO);
349 MediaDeviceType type =
350 is_input ? MEDIA_DEVICE_TYPE_AUDIO_INPUT : MEDIA_DEVICE_TYPE_AUDIO_OUTPUT;
351 if (use_fake_devices_) {
352 base::ThreadTaskRunnerHandle::Get()->PostTask(
353 FROM_HERE, base::Bind(&MediaDevicesManager::DevicesEnumerated,
354 weak_factory_.GetWeakPtr(), type,
355 GetFakeAudioDevices(is_input)));
356 return;
357 }
358 base::PostTaskAndReplyWithResult(
359 audio_manager_->GetTaskRunner(), FROM_HERE,
360 base::Bind(&EnumerateAudioDevicesOnDeviceThread, audio_manager_,
361 is_input),
362 base::Bind(&MediaDevicesManager::DevicesEnumerated,
363 weak_factory_.GetWeakPtr(), type));
364 }
365
366 void MediaDevicesManager::VideoInputDevicesEnumerated(
367 const media::VideoCaptureDeviceDescriptors& descriptors) {
368 DCHECK_CURRENTLY_ON(BrowserThread::IO);
369 MediaDeviceInfoArray snapshot;
370 for (const auto& descriptor : descriptors) {
371 snapshot.emplace_back(descriptor.device_id, descriptor.GetNameAndModel(),
372 std::string());
373 }
374 DevicesEnumerated(MEDIA_DEVICE_TYPE_VIDEO_INPUT, snapshot);
375 }
376
377 void MediaDevicesManager::DevicesEnumerated(
378 MediaDeviceType type,
379 const MediaDeviceInfoArray& snapshot) {
380 DCHECK_CURRENTLY_ON(BrowserThread::IO);
381 DCHECK(IsValidMediaDeviceType(type));
382 UpdateSnapshot(type, snapshot);
383 cache_infos_[type].UpdateCompleted();
384 has_seen_result_[type] = true;
385
386 std::string log_message =
387 "New device enumeration result:\n" + GetLogMessageString(type, snapshot);
388 MediaStreamManager::SendMessageToNativeLog(log_message);
389
390 if (cache_policies_[type] == CachePolicy::NO_CACHE) {
391 for (auto& request : requests_)
392 request.has_seen_result[type] = true;
393 }
394
395 // Note that IsLastUpdateValid is always true when policy is NO_CACHE.
396 if (cache_infos_[type].IsLastUpdateValid()) {
397 ProcessRequests();
398 } else {
399 DoEnumerateDevices(type);
400 }
401 }
402
403 void MediaDevicesManager::UpdateSnapshot(
404 MediaDeviceType type,
405 const MediaDeviceInfoArray& new_snapshot) {
406 DCHECK_CURRENTLY_ON(BrowserThread::IO);
407 DCHECK(IsValidMediaDeviceType(type));
408
409 // Only cache the device list when the device list has been changed.
410 bool need_update_device_change_subscribers = false;
411 MediaDeviceInfoArray& old_snapshot = current_snapshot_[type];
412
413 if (old_snapshot.size() != new_snapshot.size() ||
414 !std::equal(new_snapshot.begin(), new_snapshot.end(),
415 old_snapshot.begin())) {
416 if (type == MEDIA_DEVICE_TYPE_AUDIO_INPUT ||
417 type == MEDIA_DEVICE_TYPE_VIDEO_INPUT) {
418 NotifyMediaStreamManager(type, new_snapshot);
419 }
420
421 // Do not notify device-change subscribers after the first enumeration
422 // result, since it is not due to an actual device change.
423 need_update_device_change_subscribers =
424 has_seen_result_[type] &&
425 (old_snapshot.size() != 0 || new_snapshot.size() != 0);
426 current_snapshot_[type] = new_snapshot;
427 }
428
429 if (need_update_device_change_subscribers)
430 NotifyDeviceChangeSubscribers(type, new_snapshot);
431 }
432
433 void MediaDevicesManager::ProcessRequests() {
434 DCHECK_CURRENTLY_ON(BrowserThread::IO);
435 requests_.erase(std::remove_if(requests_.begin(), requests_.end(),
436 [this](const EnumerationRequest& request) {
437 if (IsEnumerationRequestReady(request)) {
438 request.callback.Run(current_snapshot_);
439 return true;
440 }
441 return false;
442 }),
443 requests_.end());
444 }
445
446 bool MediaDevicesManager::IsEnumerationRequestReady(
447 const EnumerationRequest& request_info) {
448 DCHECK_CURRENTLY_ON(BrowserThread::IO);
449 bool is_ready = true;
450 for (size_t i = 0; i < NUM_MEDIA_DEVICE_TYPES; ++i) {
451 if (!request_info.requested[i])
452 continue;
453 switch (cache_policies_[i]) {
454 case CachePolicy::SYSTEM_MONITOR:
455 if (!cache_infos_[i].IsLastUpdateValid())
456 is_ready = false;
457 break;
458 case CachePolicy::NO_CACHE:
459 if (!request_info.has_seen_result[i])
460 is_ready = false;
461 break;
462 default:
463 NOTREACHED();
464 }
465 }
466 return is_ready;
467 }
468
469 void MediaDevicesManager::HandleDevicesChanged(MediaDeviceType type) {
470 DCHECK_CURRENTLY_ON(BrowserThread::IO);
471 DCHECK(IsValidMediaDeviceType(type));
472 cache_infos_[type].InvalidateCache();
473 DoEnumerateDevices(type);
474 }
475
476 void MediaDevicesManager::NotifyMediaStreamManager(
477 MediaDeviceType type,
478 const MediaDeviceInfoArray& new_snapshot) {
479 DCHECK_CURRENTLY_ON(BrowserThread::IO);
480 DCHECK(type == MEDIA_DEVICE_TYPE_AUDIO_INPUT ||
481 type == MEDIA_DEVICE_TYPE_VIDEO_INPUT);
482
483 if (!media_stream_manager_)
484 return;
485
486 for (const auto& old_device_info : current_snapshot_[type]) {
487 auto it = std::find_if(new_snapshot.begin(), new_snapshot.end(),
488 [&old_device_info](const MediaDeviceInfo& info) {
489 return info.device_id == old_device_info.device_id;
490 });
491
492 // If a device was removed, notify the MediaStreamManager to stop all
493 // streams using that device.
494 if (it == new_snapshot.end())
495 media_stream_manager_->StopRemovedDevice(type, old_device_info);
496 }
497
498 media_stream_manager_->NotifyDevicesChanged(type, new_snapshot);
499 }
500
501 void MediaDevicesManager::NotifyDeviceChangeSubscribers(
502 MediaDeviceType type,
503 const MediaDeviceInfoArray& snapshot) {
504 DCHECK_CURRENTLY_ON(BrowserThread::IO);
505 DCHECK(IsValidMediaDeviceType(type));
506
507 // TODO(guidou): Use device types instead of stream types, and remove the
508 // call to MediaStreamManager once handling of device-change subscriptions
509 // is removed from MediaStreamManager. See http://crbug.com/334244.
510 // |permission_type| is used by MediaStreamManager to decide which permission
511 // to check before forwarding the event to the renderer process. Since there
512 // is no separate permission for audio output devices yet, use
513 // MEDIA_DEVICE_AUDIO_CAPTURE for both audio input and output.
514 MediaStreamType permission_type = type == MEDIA_DEVICE_TYPE_VIDEO_INPUT
515 ? MEDIA_DEVICE_VIDEO_CAPTURE
516 : MEDIA_DEVICE_AUDIO_CAPTURE;
517 if (media_stream_manager_)
518 media_stream_manager_->NotifyDeviceChangeSubscribers(permission_type);
519 }
520
521 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698