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

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

Powered by Google App Engine
This is Rietveld 408576698