IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / browser / renderer_host / media / media_stream_manager.cc
blob8a865fc2903a495faf5e9eb90a614de71607d4db
1 // Copyright (c) 2012 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.
5 #include "content/browser/renderer_host/media/media_stream_manager.h"
7 #include <list>
8 #include <vector>
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/compiler_specific.h"
13 #include "base/logging.h"
14 #include "base/rand_util.h"
15 #include "base/run_loop.h"
16 #include "base/threading/thread.h"
17 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
18 #include "content/browser/renderer_host/media/device_request_message_filter.h"
19 #include "content/browser/renderer_host/media/media_stream_requester.h"
20 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
21 #include "content/browser/renderer_host/media/video_capture_manager.h"
22 #include "content/browser/renderer_host/media/web_contents_capture_util.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/content_browser_client.h"
25 #include "content/public/browser/media_device_id.h"
26 #include "content/public/browser/media_observer.h"
27 #include "content/public/browser/media_request_state.h"
28 #include "content/public/common/content_switches.h"
29 #include "content/public/common/media_stream_request.h"
30 #include "media/audio/audio_manager_base.h"
31 #include "media/audio/audio_parameters.h"
32 #include "media/base/channel_layout.h"
33 #include "url/gurl.h"
35 #if defined(OS_WIN)
36 #include "base/win/scoped_com_initializer.h"
37 #endif
39 namespace content {
41 namespace {
42 // Creates a random label used to identify requests.
43 std::string RandomLabel() {
44 // An earlier PeerConnection spec,
45 // http://dev.w3.org/2011/webrtc/editor/webrtc.html, specified the
46 // MediaStream::label alphabet as containing 36 characters from
47 // range: U+0021, U+0023 to U+0027, U+002A to U+002B, U+002D to U+002E,
48 // U+0030 to U+0039, U+0041 to U+005A, U+005E to U+007E.
49 // Here we use a safe subset.
50 static const char kAlphabet[] = "0123456789"
51 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
53 std::string label(36, ' ');
54 for (size_t i = 0; i < label.size(); ++i) {
55 int random_char = base::RandGenerator(sizeof(kAlphabet) - 1);
56 label[i] = kAlphabet[random_char];
58 return label;
61 void ParseStreamType(const StreamOptions& options,
62 MediaStreamType* audio_type,
63 MediaStreamType* video_type) {
64 *audio_type = MEDIA_NO_SERVICE;
65 *video_type = MEDIA_NO_SERVICE;
66 if (options.audio_requested) {
67 std::string audio_stream_source;
68 bool mandatory = false;
69 if (options.GetFirstAudioConstraintByName(kMediaStreamSource,
70 &audio_stream_source,
71 &mandatory)) {
72 DCHECK(mandatory);
73 // This is tab or screen capture.
74 if (audio_stream_source == kMediaStreamSourceTab) {
75 *audio_type = content::MEDIA_TAB_AUDIO_CAPTURE;
76 } else if (audio_stream_source == kMediaStreamSourceSystem) {
77 *audio_type = content::MEDIA_LOOPBACK_AUDIO_CAPTURE;
79 } else {
80 // This is normal audio device capture.
81 *audio_type = content::MEDIA_DEVICE_AUDIO_CAPTURE;
84 if (options.video_requested) {
85 std::string video_stream_source;
86 bool mandatory = false;
87 if (options.GetFirstVideoConstraintByName(kMediaStreamSource,
88 &video_stream_source,
89 &mandatory)) {
90 DCHECK(mandatory);
91 // This is tab or screen capture.
92 if (video_stream_source == kMediaStreamSourceTab) {
93 *video_type = content::MEDIA_TAB_VIDEO_CAPTURE;
94 } else if (video_stream_source == kMediaStreamSourceScreen) {
95 *video_type = content::MEDIA_DESKTOP_VIDEO_CAPTURE;
96 } else if (video_stream_source == kMediaStreamSourceDesktop) {
97 *video_type = content::MEDIA_DESKTOP_VIDEO_CAPTURE;
99 } else {
100 // This is normal video device capture.
101 *video_type = content::MEDIA_DEVICE_VIDEO_CAPTURE;
106 } // namespace
109 // MediaStreamManager::DeviceRequest represents a request to either enumerate
110 // available devices or open one or more devices.
111 // TODO(perkj): MediaStreamManager still needs refactoring. I propose we create
112 // several subclasses of DeviceRequest and move some of the responsibility of
113 // the MediaStreamManager to the subclasses to get rid of the way too many if
114 // statements in MediaStreamManager.
115 class MediaStreamManager::DeviceRequest {
116 public:
117 DeviceRequest(MediaStreamRequester* requester,
118 int requesting_process_id,
119 int requesting_view_id,
120 int page_request_id,
121 const GURL& security_origin,
122 MediaStreamRequestType request_type,
123 const StreamOptions& options,
124 ResourceContext* resource_context)
125 : requester(requester),
126 requesting_process_id(requesting_process_id),
127 requesting_view_id(requesting_view_id),
128 page_request_id(page_request_id),
129 security_origin(security_origin),
130 request_type(request_type),
131 options(options),
132 resource_context(resource_context),
133 state_(NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_NOT_REQUESTED),
134 audio_type_(MEDIA_NO_SERVICE),
135 video_type_(MEDIA_NO_SERVICE) {
138 ~DeviceRequest() {}
140 void SetAudioType(MediaStreamType audio_type) {
141 DCHECK(IsAudioMediaType(audio_type) || audio_type == MEDIA_NO_SERVICE);
142 audio_type_ = audio_type;
145 MediaStreamType audio_type() const { return audio_type_; }
147 void SetVideoType(MediaStreamType video_type) {
148 DCHECK(IsVideoMediaType(video_type) || video_type == MEDIA_NO_SERVICE);
149 video_type_ = video_type;
152 MediaStreamType video_type() const { return video_type_; }
154 // Creates a MediaStreamRequest object that is used by this request when UI
155 // is asked for permission and device selection.
156 void CreateUIRequest(const std::string& requested_audio_device_id,
157 const std::string& requested_video_device_id) {
158 DCHECK(!ui_request_);
159 ui_request_.reset(new MediaStreamRequest(requesting_process_id,
160 requesting_view_id,
161 page_request_id,
162 security_origin,
163 request_type,
164 requested_audio_device_id,
165 requested_video_device_id,
166 audio_type_,
167 video_type_));
170 // Creates a tab capture specific MediaStreamRequest object that is used by
171 // this request when UI is asked for permission and device selection.
172 void CreateTabCatureUIRequest(int target_render_process_id,
173 int target_render_view_id,
174 const std::string& tab_capture_id) {
175 DCHECK(!ui_request_);
176 ui_request_.reset(new MediaStreamRequest(target_render_process_id,
177 target_render_view_id,
178 page_request_id,
179 security_origin,
180 request_type,
183 audio_type_,
184 video_type_));
185 ui_request_->tab_capture_device_id = tab_capture_id;
188 const MediaStreamRequest* UIRequest() const { return ui_request_.get(); }
190 // Update the request state and notify observers.
191 void SetState(MediaStreamType stream_type, MediaRequestState new_state) {
192 if (stream_type == NUM_MEDIA_TYPES) {
193 for (int i = MEDIA_NO_SERVICE + 1; i < NUM_MEDIA_TYPES; ++i) {
194 const MediaStreamType stream_type = static_cast<MediaStreamType>(i);
195 state_[stream_type] = new_state;
197 } else {
198 state_[stream_type] = new_state;
201 MediaObserver* media_observer =
202 GetContentClient()->browser()->GetMediaObserver();
203 if (!media_observer)
204 return;
206 // If |ui_request_| doesn't exist, it means that the request has not yet
207 // been setup fully and there are no valid observers.
208 if (!ui_request_)
209 return;
211 // If we appended a device_id scheme, we want to remove it when notifying
212 // observers which may be in different modules since this scheme is only
213 // used internally within the content module.
214 std::string device_id =
215 WebContentsCaptureUtil::StripWebContentsDeviceScheme(
216 ui_request_->tab_capture_device_id);
218 media_observer->OnMediaRequestStateChanged(
219 ui_request_->render_process_id, ui_request_->render_view_id,
220 ui_request_->page_request_id,
221 MediaStreamDevice(stream_type, device_id, device_id), new_state);
224 MediaRequestState state(MediaStreamType stream_type) const {
225 return state_[stream_type];
228 MediaStreamRequester* const requester; // Can be NULL.
231 // The render process id that requested this stream to be generated and that
232 // will receive a handle to the MediaStream. This may be different from
233 // MediaStreamRequest::render_process_id which in the tab capture case
234 // specifies the target renderer from which audio and video is captured.
235 const int requesting_process_id;
237 // The render view id that requested this stream to be generated and that
238 // will receive a handle to the MediaStream. This may be different from
239 // MediaStreamRequest::render_view_id which in the tab capture case
240 // specifies the target renderer from which audio and video is captured.
241 const int requesting_view_id;
243 // An ID the render view provided to identify this request.
244 const int page_request_id;
246 const GURL security_origin;
248 const MediaStreamRequestType request_type;
250 const StreamOptions options;
252 ResourceContext* resource_context;
254 StreamDeviceInfoArray devices;
256 // Callback to the requester which audio/video devices have been selected.
257 // It can be null if the requester has no interest to know the result.
258 // Currently it is only used by |DEVICE_ACCESS| type.
259 MediaStreamManager::MediaRequestResponseCallback callback;
261 scoped_ptr<MediaStreamUIProxy> ui_proxy;
263 private:
264 std::vector<MediaRequestState> state_;
265 scoped_ptr<MediaStreamRequest> ui_request_;
266 MediaStreamType audio_type_;
267 MediaStreamType video_type_;
270 MediaStreamManager::EnumerationCache::EnumerationCache()
271 : valid(false) {
274 MediaStreamManager::EnumerationCache::~EnumerationCache() {
277 MediaStreamManager::MediaStreamManager()
278 : audio_manager_(NULL),
279 monitoring_started_(false),
280 io_loop_(NULL),
281 use_fake_ui_(false) {}
283 MediaStreamManager::MediaStreamManager(media::AudioManager* audio_manager)
284 : audio_manager_(audio_manager),
285 monitoring_started_(false),
286 io_loop_(NULL),
287 use_fake_ui_(false) {
288 DCHECK(audio_manager_);
289 memset(active_enumeration_ref_count_, 0,
290 sizeof(active_enumeration_ref_count_));
292 // Some unit tests create the MSM in the IO thread and assumes the
293 // initialization is done synchronously.
294 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
295 InitializeDeviceManagersOnIOThread();
296 } else {
297 BrowserThread::PostTask(
298 BrowserThread::IO, FROM_HERE,
299 base::Bind(&MediaStreamManager::InitializeDeviceManagersOnIOThread,
300 base::Unretained(this)));
304 MediaStreamManager::~MediaStreamManager() {
305 DVLOG(1) << "~MediaStreamManager";
306 DCHECK(requests_.empty());
307 DCHECK(!device_thread_.get());
310 VideoCaptureManager* MediaStreamManager::video_capture_manager() {
311 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
312 DCHECK(video_capture_manager_.get());
313 return video_capture_manager_.get();
316 AudioInputDeviceManager* MediaStreamManager::audio_input_device_manager() {
317 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
318 DCHECK(audio_input_device_manager_.get());
319 return audio_input_device_manager_.get();
322 std::string MediaStreamManager::MakeMediaAccessRequest(
323 int render_process_id,
324 int render_view_id,
325 int page_request_id,
326 const StreamOptions& options,
327 const GURL& security_origin,
328 const MediaRequestResponseCallback& callback) {
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
331 // TODO(perkj): The argument list with NULL parameters to DeviceRequest
332 // suggests that this is the wrong design. Can this be refactored?
333 DeviceRequest* request = new DeviceRequest(NULL,
334 render_process_id,
335 render_view_id,
336 page_request_id,
337 security_origin,
338 MEDIA_DEVICE_ACCESS,
339 options,
340 NULL);
342 const std::string& label = AddRequest(request);
344 request->callback = callback;
345 // Post a task and handle the request asynchronously. The reason is that the
346 // requester won't have a label for the request until this function returns
347 // and thus can not handle a response. Using base::Unretained is safe since
348 // MediaStreamManager is deleted on the UI thread, after the IO thread has
349 // been stopped.
350 BrowserThread::PostTask(
351 BrowserThread::IO, FROM_HERE,
352 base::Bind(&MediaStreamManager::SetupRequest,
353 base::Unretained(this), label));
354 return label;
357 void MediaStreamManager::GenerateStream(MediaStreamRequester* requester,
358 int render_process_id,
359 int render_view_id,
360 ResourceContext* rc,
361 int page_request_id,
362 const StreamOptions& options,
363 const GURL& security_origin) {
364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
365 DVLOG(1) << "GenerateStream()";
366 if (CommandLine::ForCurrentProcess()->HasSwitch(
367 switches::kUseFakeUIForMediaStream)) {
368 UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy>());
371 DeviceRequest* request = new DeviceRequest(requester,
372 render_process_id,
373 render_view_id,
374 page_request_id,
375 security_origin,
376 MEDIA_GENERATE_STREAM,
377 options,
378 rc);
380 const std::string& label = AddRequest(request);
382 // Post a task and handle the request asynchronously. The reason is that the
383 // requester won't have a label for the request until this function returns
384 // and thus can not handle a response. Using base::Unretained is safe since
385 // MediaStreamManager is deleted on the UI thread, after the IO thread has
386 // been stopped.
387 BrowserThread::PostTask(
388 BrowserThread::IO, FROM_HERE,
389 base::Bind(&MediaStreamManager::SetupRequest,
390 base::Unretained(this), label));
393 void MediaStreamManager::CancelRequest(int render_process_id,
394 int render_view_id,
395 int page_request_id) {
396 for (DeviceRequests::const_iterator request_it = requests_.begin();
397 request_it != requests_.end(); ++request_it) {
398 const DeviceRequest* request = request_it->second;
399 if (request->requesting_process_id == render_process_id &&
400 request->requesting_view_id == render_view_id &&
401 request->page_request_id == page_request_id) {
402 CancelRequest(request_it->first);
403 return;
406 NOTREACHED();
409 void MediaStreamManager::CancelRequest(const std::string& label) {
410 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
411 DVLOG(1) << "CancelRequest({label = " << label << "})";
412 DeviceRequest* request = FindRequest(label);
413 if (!request) {
414 // The request does not exist.
415 LOG(ERROR) << "The request with label = " << label << " does not exist.";
416 return;
419 if (request->request_type == MEDIA_ENUMERATE_DEVICES) {
420 // It isn't an ideal use of "CancelRequest" to make it a requirement
421 // for enumeration requests to be deleted via "CancelRequest" _after_
422 // the request has been successfully fulfilled.
423 // See note in FinalizeEnumerateDevices for a recommendation on how
424 // we should refactor this.
425 DeleteRequest(label);
426 return;
429 // This is a request for opening one or more devices.
430 for (StreamDeviceInfoArray::iterator device_it = request->devices.begin();
431 device_it != request->devices.end(); ++device_it) {
432 MediaRequestState state = request->state(device_it->device.type);
433 // If we have not yet requested the device to be opened - just ignore it.
434 if (state != MEDIA_REQUEST_STATE_OPENING &&
435 state != MEDIA_REQUEST_STATE_DONE) {
436 continue;
438 // Stop the opening/opened devices of the requests.
439 CloseDevice(device_it->device.type, device_it->session_id);
442 // Cancel the request if still pending at UI side.
443 request->SetState(NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_CLOSING);
444 DeleteRequest(label);
447 void MediaStreamManager::CancelAllRequests(int render_process_id) {
448 DeviceRequests::iterator request_it = requests_.begin();
449 while (request_it != requests_.end()) {
450 if (request_it->second->requesting_process_id != render_process_id) {
451 ++request_it;
452 continue;
455 std::string label = request_it->first;
456 ++request_it;
457 CancelRequest(label);
461 void MediaStreamManager::StopStreamDevice(int render_process_id,
462 int render_view_id,
463 const std::string& device_id) {
464 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
465 DVLOG(1) << "StopStreamDevice({render_view_id = " << render_view_id << "} "
466 << ", {device_id = " << device_id << "})";
467 // Find the first request for this |render_process_id| and |render_view_id|
468 // of type MEDIA_GENERATE_STREAM that has requested to use |device_id| and
469 // stop it.
470 for (DeviceRequests::iterator request_it = requests_.begin();
471 request_it != requests_.end(); ++request_it) {
472 DeviceRequest* request = request_it->second;
473 if (request->requesting_process_id != render_process_id ||
474 request->requesting_view_id != render_view_id ||
475 request->request_type != MEDIA_GENERATE_STREAM) {
476 continue;
479 StreamDeviceInfoArray& devices = request->devices;
480 for (StreamDeviceInfoArray::iterator device_it = devices.begin();
481 device_it != devices.end(); ++device_it) {
482 if (device_it->device.id == device_id) {
483 StopDevice(device_it->device.type, device_it->session_id);
484 return;
490 void MediaStreamManager::StopDevice(MediaStreamType type, int session_id) {
491 DVLOG(1) << "StopDevice"
492 << "{type = " << type << "}"
493 << "{session_id = " << session_id << "}";
494 DeviceRequests::iterator request_it = requests_.begin();
495 while (request_it != requests_.end()) {
496 DeviceRequest* request = request_it->second;
497 StreamDeviceInfoArray* devices = &request->devices;
498 StreamDeviceInfoArray::iterator device_it = devices->begin();
499 while (device_it != devices->end()) {
500 if (device_it->device.type != type ||
501 device_it->session_id != session_id) {
502 ++device_it;
503 continue;
505 if (request->state(type) == MEDIA_REQUEST_STATE_DONE)
506 CloseDevice(type, session_id);
507 device_it = devices->erase(device_it);
509 // If this request doesn't have any active devices, remove the request.
510 if (devices->empty()) {
511 std::string label = request_it->first;
512 ++request_it;
513 DeleteRequest(label);
514 } else {
515 ++request_it;
520 void MediaStreamManager::CloseDevice(MediaStreamType type, int session_id) {
521 DVLOG(1) << "CloseDevice("
522 << "{type = " << type << "} "
523 << "{session_id = " << session_id << "})";
524 GetDeviceManager(type)->Close(session_id);
526 for (DeviceRequests::iterator request_it = requests_.begin();
527 request_it != requests_.end() ; ++request_it) {
528 StreamDeviceInfoArray* devices = &request_it->second->devices;
529 for (StreamDeviceInfoArray::iterator device_it = devices->begin();
530 device_it != devices->end(); ++device_it) {
531 if (device_it->session_id == session_id &&
532 device_it->device.type == type) {
533 // Notify observers that this device is being closed.
534 // Note that only one device per type can be opened.
535 request_it->second->SetState(type, MEDIA_REQUEST_STATE_CLOSING);
541 std::string MediaStreamManager::EnumerateDevices(
542 MediaStreamRequester* requester,
543 int render_process_id,
544 int render_view_id,
545 ResourceContext* rc,
546 int page_request_id,
547 MediaStreamType type,
548 const GURL& security_origin) {
549 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
550 DCHECK(requester);
551 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
552 type == MEDIA_DEVICE_VIDEO_CAPTURE);
554 DeviceRequest* request = new DeviceRequest(requester,
555 render_process_id,
556 render_view_id,
557 page_request_id,
558 security_origin,
559 MEDIA_ENUMERATE_DEVICES,
560 StreamOptions(),
561 rc);
562 if (IsAudioMediaType(type))
563 request->SetAudioType(type);
564 else if (IsVideoMediaType(type))
565 request->SetVideoType(type);
567 const std::string& label = AddRequest(request);
568 // Post a task and handle the request asynchronously. The reason is that the
569 // requester won't have a label for the request until this function returns
570 // and thus can not handle a response. Using base::Unretained is safe since
571 // MediaStreamManager is deleted on the UI thread, after the IO thread has
572 // been stopped.
573 BrowserThread::PostTask(
574 BrowserThread::IO, FROM_HERE,
575 base::Bind(&MediaStreamManager::DoEnumerateDevices,
576 base::Unretained(this), label));
577 return label;
580 void MediaStreamManager::DoEnumerateDevices(const std::string& label) {
581 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
582 DeviceRequest* request = FindRequest(label);
583 if (!request)
584 return; // This can happen if the request has been canceled.
586 MediaStreamType type;
587 EnumerationCache* cache;
588 if (request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE) {
589 DCHECK_EQ(MEDIA_NO_SERVICE, request->video_type());
590 type = MEDIA_DEVICE_AUDIO_CAPTURE;
591 cache = &audio_enumeration_cache_;
592 } else {
593 DCHECK_EQ(MEDIA_DEVICE_VIDEO_CAPTURE, request->video_type());
594 type = MEDIA_DEVICE_VIDEO_CAPTURE;
595 cache = &video_enumeration_cache_;
598 if (!EnumerationRequired(cache, type)) {
599 // Cached device list of this type exists. Just send it out.
600 request->SetState(type, MEDIA_REQUEST_STATE_REQUESTED);
601 request->devices = cache->devices;
602 FinalizeEnumerateDevices(label, request);
603 } else {
604 StartEnumeration(request);
606 DVLOG(1) << "Enumerate Devices ({label = " << label << "})";
609 void MediaStreamManager::OpenDevice(MediaStreamRequester* requester,
610 int render_process_id,
611 int render_view_id,
612 ResourceContext* rc,
613 int page_request_id,
614 const std::string& device_id,
615 MediaStreamType type,
616 const GURL& security_origin) {
617 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
618 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
619 type == MEDIA_DEVICE_VIDEO_CAPTURE);
620 DVLOG(1) << "OpenDevice ({page_request_id = " << page_request_id << "})";
621 StreamOptions options;
622 if (IsAudioMediaType(type)) {
623 options.audio_requested = true;
624 options.mandatory_audio.push_back(
625 StreamOptions::Constraint(kMediaStreamSourceInfoId, device_id));
626 } else if (IsVideoMediaType(type)) {
627 options.video_requested = true;
628 options.mandatory_video.push_back(
629 StreamOptions::Constraint(kMediaStreamSourceInfoId, device_id));
630 } else {
631 NOTREACHED();
633 DeviceRequest* request = new DeviceRequest(requester,
634 render_process_id,
635 render_view_id,
636 page_request_id,
637 security_origin,
638 MEDIA_OPEN_DEVICE,
639 options,
640 rc);
642 const std::string& label = AddRequest(request);
643 // Post a task and handle the request asynchronously. The reason is that the
644 // requester won't have a label for the request until this function returns
645 // and thus can not handle a response. Using base::Unretained is safe since
646 // MediaStreamManager is deleted on the UI thread, after the IO thread has
647 // been stopped.
648 BrowserThread::PostTask(
649 BrowserThread::IO, FROM_HERE,
650 base::Bind(&MediaStreamManager::SetupRequest,
651 base::Unretained(this), label));
654 void MediaStreamManager::EnsureDeviceMonitorStarted() {
655 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
656 StartMonitoring();
659 void MediaStreamManager::StopRemovedDevices(
660 const StreamDeviceInfoArray& old_devices,
661 const StreamDeviceInfoArray& new_devices) {
662 DVLOG(1) << "StopRemovedDevices("
663 << "{#old_devices = " << old_devices.size() << "} "
664 << "{#new_devices = " << new_devices.size() << "})";
665 for (StreamDeviceInfoArray::const_iterator old_dev_it = old_devices.begin();
666 old_dev_it != old_devices.end(); ++old_dev_it) {
667 bool device_found = false;
668 for (StreamDeviceInfoArray::const_iterator new_dev_it = new_devices.begin();
669 new_dev_it != new_devices.end(); ++new_dev_it) {
670 if (old_dev_it->device.id == new_dev_it->device.id) {
671 device_found = true;
672 break;
676 if (!device_found) {
677 // A device has been removed. We need to check if it is used by a
678 // MediaStream and in that case cleanup and notify the render process.
679 StopRemovedDevice(old_dev_it->device);
684 void MediaStreamManager::StopRemovedDevice(const MediaStreamDevice& device) {
685 std::vector<int> session_ids;
686 for (DeviceRequests::const_iterator it = requests_.begin();
687 it != requests_.end() ; ++it) {
688 const DeviceRequest* request = it->second;
689 for (StreamDeviceInfoArray::const_iterator device_it =
690 request->devices.begin();
691 device_it != request->devices.end(); ++device_it) {
692 std::string source_id = content::GetHMACForMediaDeviceID(
693 request->resource_context,
694 request->security_origin,
695 device.id);
696 if (device_it->device.id == source_id &&
697 device_it->device.type == device.type) {
698 session_ids.push_back(device_it->session_id);
699 if (it->second->requester) {
700 it->second->requester->DeviceStopped(
701 it->second->requesting_view_id,
702 it->first,
703 *device_it);
708 for (std::vector<int>::const_iterator it = session_ids.begin();
709 it != session_ids.end(); ++it) {
710 StopDevice(device.type, *it);
714 void MediaStreamManager::StartMonitoring() {
715 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
716 if (monitoring_started_)
717 return;
719 if (!base::SystemMonitor::Get())
720 return;
722 monitoring_started_ = true;
723 base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
725 // Enumerate both the audio and video devices to cache the device lists
726 // and send them to media observer.
727 ++active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_CAPTURE];
728 audio_input_device_manager_->EnumerateDevices(MEDIA_DEVICE_AUDIO_CAPTURE);
729 ++active_enumeration_ref_count_[MEDIA_DEVICE_VIDEO_CAPTURE];
730 video_capture_manager_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
733 void MediaStreamManager::StopMonitoring() {
734 DCHECK_EQ(base::MessageLoop::current(), io_loop_);
735 if (monitoring_started_) {
736 base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
737 monitoring_started_ = false;
738 ClearEnumerationCache(&audio_enumeration_cache_);
739 ClearEnumerationCache(&video_enumeration_cache_);
743 bool MediaStreamManager::GetRequestedDeviceCaptureId(
744 const DeviceRequest* request,
745 MediaStreamType type,
746 std::string* device_id) const {
747 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
748 type == MEDIA_DEVICE_VIDEO_CAPTURE);
749 const StreamOptions::Constraints* mandatory =
750 (type == MEDIA_DEVICE_AUDIO_CAPTURE) ?
751 &request->options.mandatory_audio : &request->options.mandatory_video;
752 const StreamOptions::Constraints* optional =
753 (type == MEDIA_DEVICE_AUDIO_CAPTURE) ?
754 &request->options.optional_audio : &request->options.optional_video;
756 std::vector<std::string> source_ids;
757 StreamOptions::GetConstraintsByName(*mandatory,
758 kMediaStreamSourceInfoId, &source_ids);
759 if (source_ids.size() > 1) {
760 LOG(ERROR) << "Only one mandatory " << kMediaStreamSourceInfoId
761 << " is supported.";
762 return false;
764 // If a specific device has been requested we need to find the real device
765 // id.
766 if (source_ids.size() == 1 &&
767 !TranslateSourceIdToDeviceId(type,
768 request->resource_context,
769 request->security_origin,
770 source_ids[0], device_id)) {
771 LOG(WARNING) << "Invalid mandatory " << kMediaStreamSourceInfoId
772 << " = " << source_ids[0] << ".";
773 return false;
775 // Check for optional audio sourceIDs.
776 if (device_id->empty()) {
777 StreamOptions::GetConstraintsByName(*optional,
778 kMediaStreamSourceInfoId,
779 &source_ids);
780 // Find the first sourceID that translates to device. Note that only one
781 // device per type can call to GenerateStream is ever opened.
782 for (std::vector<std::string>::const_iterator it = source_ids.begin();
783 it != source_ids.end(); ++it) {
784 if (TranslateSourceIdToDeviceId(type,
785 request->resource_context,
786 request->security_origin,
787 *it,
788 device_id)) {
789 break;
793 return true;
796 void MediaStreamManager::TranslateDeviceIdToSourceId(
797 DeviceRequest* request,
798 MediaStreamDevice* device) {
799 if (request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE ||
800 request->video_type() == MEDIA_DEVICE_VIDEO_CAPTURE) {
801 device->id = content::GetHMACForMediaDeviceID(
802 request->resource_context,
803 request->security_origin,
804 device->id);
808 bool MediaStreamManager::TranslateSourceIdToDeviceId(
809 MediaStreamType stream_type,
810 ResourceContext* rc,
811 const GURL& security_origin,
812 const std::string& source_id,
813 std::string* device_id) const {
814 DCHECK(stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ||
815 stream_type == MEDIA_DEVICE_VIDEO_CAPTURE);
816 // The source_id can be empty if the constraint is set but empty.
817 if (source_id.empty())
818 return false;
820 const EnumerationCache* cache =
821 stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ?
822 &audio_enumeration_cache_ : &video_enumeration_cache_;
824 // If device monitoring hasn't started, the |device_guid| is not valid.
825 if (!cache->valid)
826 return false;
828 for (StreamDeviceInfoArray::const_iterator it = cache->devices.begin();
829 it != cache->devices.end();
830 ++it) {
831 if (content::DoesMediaDeviceIDMatchHMAC(rc, security_origin, source_id,
832 it->device.id)) {
833 *device_id = it->device.id;
834 return true;
837 return false;
840 void MediaStreamManager::ClearEnumerationCache(EnumerationCache* cache) {
841 DCHECK_EQ(base::MessageLoop::current(), io_loop_);
842 cache->valid = false;
845 bool MediaStreamManager::EnumerationRequired(EnumerationCache* cache,
846 MediaStreamType stream_type) {
847 DCHECK_EQ(base::MessageLoop::current(), io_loop_);
848 if (stream_type == MEDIA_NO_SERVICE)
849 return false;
851 DCHECK(stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ||
852 stream_type == MEDIA_DEVICE_VIDEO_CAPTURE);
854 #if defined(OS_ANDROID)
855 // There's no SystemMonitor on Android that notifies us when devices are
856 // added or removed, so we need to populate the cache on every request.
857 // Fortunately, there is an already up-to-date cache in the browser side
858 // audio manager that we can rely on, so the performance impact of
859 // invalidating the cache like this, is minimal.
860 if (stream_type == MEDIA_DEVICE_AUDIO_CAPTURE) {
861 // Make sure the cache is marked as invalid so that FinalizeEnumerateDevices
862 // will be called at the end of the enumeration.
863 ClearEnumerationCache(cache);
865 #endif
866 // If the cache isn't valid, we need to start a full enumeration.
867 return !cache->valid;
870 void MediaStreamManager::StartEnumeration(DeviceRequest* request) {
871 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
873 // Start monitoring the devices when doing the first enumeration.
874 StartMonitoring();
876 // Start enumeration for devices of all requested device types.
877 const MediaStreamType streams[] = { request->audio_type(),
878 request->video_type() };
879 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(streams); ++i) {
880 if (streams[i] == MEDIA_NO_SERVICE)
881 continue;
882 request->SetState(streams[i], MEDIA_REQUEST_STATE_REQUESTED);
883 DCHECK_GE(active_enumeration_ref_count_[streams[i]], 0);
884 if (active_enumeration_ref_count_[streams[i]] == 0) {
885 ++active_enumeration_ref_count_[streams[i]];
886 GetDeviceManager(streams[i])->EnumerateDevices(streams[i]);
891 std::string MediaStreamManager::AddRequest(DeviceRequest* request) {
892 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
894 // Create a label for this request and verify it is unique.
895 std::string unique_label;
896 do {
897 unique_label = RandomLabel();
898 } while (requests_.find(unique_label) != requests_.end());
900 requests_.insert(std::make_pair(unique_label, request));
902 return unique_label;
905 MediaStreamManager::DeviceRequest*
906 MediaStreamManager::FindRequest(const std::string& label) const {
907 DeviceRequests::const_iterator request_it = requests_.find(label);
908 return request_it == requests_.end() ? NULL : request_it->second;
911 void MediaStreamManager::DeleteRequest(const std::string& label) {
912 DeviceRequests::iterator it = requests_.find(label);
913 scoped_ptr<DeviceRequest> request(it->second);
914 requests_.erase(it);
917 void MediaStreamManager::PostRequestToUI(const std::string& label,
918 DeviceRequest* request) {
919 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
920 DCHECK(request->UIRequest());
921 DVLOG(1) << "PostRequestToUI({label= " << label << "})";
923 const MediaStreamType audio_type = request->audio_type();
924 const MediaStreamType video_type = request->video_type();
926 // Post the request to UI and set the state.
927 if (IsAudioMediaType(audio_type))
928 request->SetState(audio_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
929 if (IsVideoMediaType(video_type))
930 request->SetState(video_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
932 if (use_fake_ui_) {
933 if (!fake_ui_)
934 fake_ui_.reset(new FakeMediaStreamUIProxy());
936 MediaStreamDevices devices;
937 if (audio_enumeration_cache_.valid) {
938 for (StreamDeviceInfoArray::const_iterator it =
939 audio_enumeration_cache_.devices.begin();
940 it != audio_enumeration_cache_.devices.end(); ++it) {
941 devices.push_back(it->device);
944 if (video_enumeration_cache_.valid) {
945 for (StreamDeviceInfoArray::const_iterator it =
946 video_enumeration_cache_.devices.begin();
947 it != video_enumeration_cache_.devices.end(); ++it) {
948 devices.push_back(it->device);
952 fake_ui_->SetAvailableDevices(devices);
954 request->ui_proxy = fake_ui_.Pass();
955 } else {
956 request->ui_proxy = MediaStreamUIProxy::Create();
959 request->ui_proxy->RequestAccess(
960 *request->UIRequest(),
961 base::Bind(&MediaStreamManager::HandleAccessRequestResponse,
962 base::Unretained(this), label));
965 void MediaStreamManager::SetupRequest(const std::string& label) {
966 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
967 DeviceRequest* request = FindRequest(label);
968 if (!request) {
969 DVLOG(1) << "SetupRequest label " << label << " doesn't exist!!";
970 return; // This can happen if the request has been canceled.
973 if (!request->security_origin.is_valid()) {
974 LOG(ERROR) << "Invalid security origin. "
975 << request->security_origin;
976 FinalizeRequestFailed(label, request);
977 return;
980 MediaStreamType audio_type = MEDIA_NO_SERVICE;
981 MediaStreamType video_type = MEDIA_NO_SERVICE;
982 ParseStreamType(request->options, &audio_type, &video_type);
983 request->SetAudioType(audio_type);
984 request->SetVideoType(video_type);
986 bool is_web_contents_capture =
987 audio_type == MEDIA_TAB_AUDIO_CAPTURE ||
988 video_type == MEDIA_TAB_VIDEO_CAPTURE;
989 if (is_web_contents_capture && !SetupTabCaptureRequest(request)) {
990 FinalizeRequestFailed(label, request);
991 return;
994 bool is_screen_capture =
995 video_type == MEDIA_DESKTOP_VIDEO_CAPTURE;
996 if (is_screen_capture && !SetupScreenCaptureRequest(request)) {
997 FinalizeRequestFailed(label, request);
998 return;
1001 if (!is_web_contents_capture && !is_screen_capture) {
1002 if (EnumerationRequired(&audio_enumeration_cache_, audio_type) ||
1003 EnumerationRequired(&video_enumeration_cache_, video_type)) {
1004 // Enumerate the devices if there is no valid device lists to be used.
1005 StartEnumeration(request);
1006 return;
1009 if (!SetupDeviceCaptureRequest(request)) {
1010 FinalizeRequestFailed(label, request);
1011 return;
1014 PostRequestToUI(label, request);
1017 bool MediaStreamManager::SetupDeviceCaptureRequest(DeviceRequest* request) {
1018 DCHECK((request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE ||
1019 request->audio_type() == MEDIA_NO_SERVICE) &&
1020 (request->video_type() == MEDIA_DEVICE_VIDEO_CAPTURE ||
1021 request->video_type() == MEDIA_NO_SERVICE));
1022 std::string audio_device_id;
1023 if (request->options.audio_requested &&
1024 !GetRequestedDeviceCaptureId(request, request->audio_type(),
1025 &audio_device_id)) {
1026 return false;
1029 std::string video_device_id;
1030 if (request->options.video_requested &&
1031 !GetRequestedDeviceCaptureId(request, request->video_type(),
1032 &video_device_id)) {
1033 return false;
1035 request->CreateUIRequest(audio_device_id, video_device_id);
1036 DVLOG(3) << "Audio requested " << request->options.audio_requested
1037 << " device id = " << audio_device_id
1038 << "Video requested " << request->options.video_requested
1039 << " device id = " << video_device_id;
1040 return true;
1043 bool MediaStreamManager::SetupTabCaptureRequest(DeviceRequest* request) {
1044 DCHECK(request->audio_type() == MEDIA_TAB_AUDIO_CAPTURE ||
1045 request->video_type() == MEDIA_TAB_VIDEO_CAPTURE);
1047 std::string capture_device_id;
1048 bool mandatory_audio = false;
1049 bool mandatory_video = false;
1050 if (!request->options.GetFirstAudioConstraintByName(kMediaStreamSourceId,
1051 &capture_device_id,
1052 &mandatory_audio) &&
1053 !request->options.GetFirstVideoConstraintByName(kMediaStreamSourceId,
1054 &capture_device_id,
1055 &mandatory_video)) {
1056 return false;
1058 DCHECK(mandatory_audio || mandatory_video);
1060 // Customize options for a WebContents based capture.
1061 int target_render_process_id = 0;
1062 int target_render_view_id = 0;
1064 // TODO(justinlin): Can't plumb audio mirroring using stream type right
1065 // now, so plumbing by device_id. Will revisit once it's refactored.
1066 // http://crbug.com/163100
1067 std::string tab_capture_device_id =
1068 WebContentsCaptureUtil::AppendWebContentsDeviceScheme(capture_device_id);
1070 bool has_valid_device_id = WebContentsCaptureUtil::ExtractTabCaptureTarget(
1071 tab_capture_device_id, &target_render_process_id,
1072 &target_render_view_id);
1073 if (!has_valid_device_id ||
1074 (request->audio_type() != MEDIA_TAB_AUDIO_CAPTURE &&
1075 request->audio_type() != MEDIA_NO_SERVICE) ||
1076 (request->video_type() != MEDIA_TAB_VIDEO_CAPTURE &&
1077 request->video_type() != MEDIA_NO_SERVICE)) {
1078 return false;
1081 request->CreateTabCatureUIRequest(target_render_process_id,
1082 target_render_view_id,
1083 tab_capture_device_id);
1085 DVLOG(3) << "SetupTabCaptureRequest "
1086 << ", {tab_capture_device_id = " << tab_capture_device_id << "}"
1087 << ", {target_render_process_id = " << target_render_process_id
1088 << "}"
1089 << ", {target_render_view_id = " << target_render_view_id << "}";
1090 return true;
1093 bool MediaStreamManager::SetupScreenCaptureRequest(DeviceRequest* request) {
1094 DCHECK(request->audio_type() == MEDIA_LOOPBACK_AUDIO_CAPTURE ||
1095 request->video_type() == MEDIA_DESKTOP_VIDEO_CAPTURE);
1097 // For screen capture we only support two valid combinations:
1098 // (1) screen video capture only, or
1099 // (2) screen video capture with loopback audio capture.
1100 if (request->video_type() != MEDIA_DESKTOP_VIDEO_CAPTURE ||
1101 (request->audio_type() != MEDIA_NO_SERVICE &&
1102 request->audio_type() != MEDIA_LOOPBACK_AUDIO_CAPTURE)) {
1103 LOG(ERROR) << "Invalid screen capture request.";
1104 return false;
1107 std::string video_device_id;
1108 if (request->video_type() == MEDIA_DESKTOP_VIDEO_CAPTURE) {
1109 std::string video_stream_source;
1110 bool mandatory = false;
1111 if (!request->options.GetFirstVideoConstraintByName(
1112 kMediaStreamSource,
1113 &video_stream_source,
1114 &mandatory)) {
1115 LOG(ERROR) << kMediaStreamSource << " not found.";
1116 return false;
1118 DCHECK(mandatory);
1120 if (video_stream_source == kMediaStreamSourceDesktop) {
1121 if (!request->options.GetFirstVideoConstraintByName(
1122 kMediaStreamSourceId,
1123 &video_device_id,
1124 &mandatory)) {
1125 LOG(ERROR) << kMediaStreamSourceId << " not found.";
1126 return false;
1128 DCHECK(mandatory);
1132 request->CreateUIRequest("", video_device_id);
1133 return true;
1136 StreamDeviceInfoArray MediaStreamManager::GetDevicesOpenedByRequest(
1137 const std::string& label) const {
1138 DeviceRequest* request = FindRequest(label);
1139 if (!request)
1140 return StreamDeviceInfoArray();
1141 return request->devices;
1144 bool MediaStreamManager::FindExistingRequestedDeviceInfo(
1145 const DeviceRequest& new_request,
1146 const MediaStreamDevice& new_device_info,
1147 StreamDeviceInfo* existing_device_info,
1148 MediaRequestState* existing_request_state) const {
1149 DCHECK(existing_device_info);
1150 DCHECK(existing_request_state);
1152 std::string source_id = content::GetHMACForMediaDeviceID(
1153 new_request.resource_context,
1154 new_request.security_origin,
1155 new_device_info.id);
1157 for (DeviceRequests::const_iterator it = requests_.begin();
1158 it != requests_.end() ; ++it) {
1159 const DeviceRequest* request = it->second;
1160 if (request->requesting_process_id == new_request.requesting_process_id &&
1161 request->requesting_view_id == new_request.requesting_view_id &&
1162 request->request_type == new_request.request_type) {
1163 for (StreamDeviceInfoArray::const_iterator device_it =
1164 request->devices.begin();
1165 device_it != request->devices.end(); ++device_it) {
1166 if (device_it->device.id == source_id &&
1167 device_it->device.type == new_device_info.type) {
1168 *existing_device_info = *device_it;
1169 *existing_request_state = request->state(device_it->device.type);
1170 return true;
1175 return false;
1178 void MediaStreamManager::FinalizeGenerateStream(const std::string& label,
1179 DeviceRequest* request) {
1180 DVLOG(1) << "FinalizeGenerateStream label " << label;
1181 const StreamDeviceInfoArray& requested_devices = request->devices;
1183 // Partition the array of devices into audio vs video.
1184 StreamDeviceInfoArray audio_devices, video_devices;
1185 for (StreamDeviceInfoArray::const_iterator device_it =
1186 requested_devices.begin();
1187 device_it != requested_devices.end(); ++device_it) {
1188 if (IsAudioMediaType(device_it->device.type)) {
1189 audio_devices.push_back(*device_it);
1190 } else if (IsVideoMediaType(device_it->device.type)) {
1191 video_devices.push_back(*device_it);
1192 } else {
1193 NOTREACHED();
1197 request->requester->StreamGenerated(
1198 request->requesting_view_id,
1199 request->page_request_id,
1200 label, audio_devices, video_devices);
1203 void MediaStreamManager::FinalizeRequestFailed(
1204 const std::string& label,
1205 DeviceRequest* request) {
1206 if (request->requester)
1207 request->requester->StreamGenerationFailed(
1208 request->requesting_view_id,
1209 request->page_request_id);
1211 if (request->request_type == MEDIA_DEVICE_ACCESS &&
1212 !request->callback.is_null()) {
1213 request->callback.Run(MediaStreamDevices(), request->ui_proxy.Pass());
1216 DeleteRequest(label);
1219 void MediaStreamManager::FinalizeOpenDevice(const std::string& label,
1220 DeviceRequest* request) {
1221 const StreamDeviceInfoArray& requested_devices = request->devices;
1222 request->requester->DeviceOpened(request->requesting_view_id,
1223 request->page_request_id,
1224 label, requested_devices.front());
1227 void MediaStreamManager::FinalizeEnumerateDevices(const std::string& label,
1228 DeviceRequest* request) {
1229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1230 DCHECK_EQ(request->request_type, MEDIA_ENUMERATE_DEVICES);
1232 if (request->security_origin.is_valid()) {
1233 for (StreamDeviceInfoArray::iterator it = request->devices.begin();
1234 it != request->devices.end(); ++it) {
1235 TranslateDeviceIdToSourceId(request, &it->device);
1237 } else {
1238 request->devices.clear();
1241 request->requester->DevicesEnumerated(
1242 request->requesting_view_id,
1243 request->page_request_id,
1244 label,
1245 request->devices);
1247 // TODO(tommi):
1248 // Ideally enumeration requests should be deleted once they have been served
1249 // (as any request). However, this implementation mixes requests and
1250 // notifications together so enumeration requests are kept open by some
1251 // implementations (only Pepper?) and enumerations are done again when
1252 // device notifications are fired.
1253 // Implementations that just want to request the device list and be done
1254 // (e.g. DeviceRequestMessageFilter), they must (confusingly) call
1255 // CancelRequest() after the request has been fulfilled. This is not
1256 // obvious, not consistent in this class (see e.g. FinalizeMediaAccessRequest)
1257 // and can lead to subtle bugs (requests not deleted at all deleted too
1258 // early).
1260 // Basically, it is not clear that using requests as an additional layer on
1261 // top of device notifications is necessary or good.
1263 // To add to this, MediaStreamManager currently relies on the external
1264 // implementations of MediaStreamRequester to delete enumeration requests via
1265 // CancelRequest and e.g. DeviceRequestMessageFilter does this. However the
1266 // Pepper implementation does not seem to to this at all (and from what I can
1267 // see, it is the only implementation that uses an enumeration request as a
1268 // notification mechanism).
1270 // We should decouple notifications from enumeration requests and once that
1271 // has been done, remove the requirement to call CancelRequest() to delete
1272 // enumeration requests and uncomment the following line:
1274 // DeleteRequest(label);
1277 void MediaStreamManager::FinalizeMediaAccessRequest(
1278 const std::string& label,
1279 DeviceRequest* request,
1280 const MediaStreamDevices& devices) {
1281 if (!request->callback.is_null())
1282 request->callback.Run(devices, request->ui_proxy.Pass());
1284 // Delete the request since it is done.
1285 DeleteRequest(label);
1288 void MediaStreamManager::InitializeDeviceManagersOnIOThread() {
1289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1290 if (device_thread_)
1291 return;
1293 device_thread_.reset(new base::Thread("MediaStreamDeviceThread"));
1294 #if defined(OS_WIN)
1295 device_thread_->init_com_with_mta(true);
1296 #endif
1297 CHECK(device_thread_->Start());
1299 audio_input_device_manager_ = new AudioInputDeviceManager(audio_manager_);
1300 audio_input_device_manager_->Register(
1301 this, device_thread_->message_loop_proxy().get());
1303 video_capture_manager_ = new VideoCaptureManager();
1304 video_capture_manager_->Register(this,
1305 device_thread_->message_loop_proxy().get());
1307 // We want to be notified of IO message loop destruction to delete the thread
1308 // and the device managers.
1309 io_loop_ = base::MessageLoop::current();
1310 io_loop_->AddDestructionObserver(this);
1312 if (CommandLine::ForCurrentProcess()->HasSwitch(
1313 switches::kUseFakeDeviceForMediaStream)) {
1314 DVLOG(1) << "Using fake device";
1315 UseFakeDevice();
1319 void MediaStreamManager::Opened(MediaStreamType stream_type,
1320 int capture_session_id) {
1321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1322 DVLOG(1) << "Opened({stream_type = " << stream_type << "} "
1323 << "{capture_session_id = " << capture_session_id << "})";
1324 // Find the request(s) containing this device and mark it as used.
1325 // It can be used in several requests since the same device can be
1326 // requested from the same web page.
1327 for (DeviceRequests::iterator request_it = requests_.begin();
1328 request_it != requests_.end(); ++request_it) {
1329 const std::string& label = request_it->first;
1330 DeviceRequest* request = request_it->second;
1331 StreamDeviceInfoArray* devices = &(request->devices);
1332 for (StreamDeviceInfoArray::iterator device_it = devices->begin();
1333 device_it != devices->end(); ++device_it) {
1334 if (device_it->device.type == stream_type &&
1335 device_it->session_id == capture_session_id) {
1336 CHECK(request->state(device_it->device.type) ==
1337 MEDIA_REQUEST_STATE_OPENING);
1338 // We've found a matching request.
1339 request->SetState(device_it->device.type, MEDIA_REQUEST_STATE_DONE);
1341 if (IsAudioMediaType(device_it->device.type)) {
1342 // Store the native audio parameters in the device struct.
1343 // TODO(xians): Handle the tab capture sample rate/channel layout
1344 // in AudioInputDeviceManager::Open().
1345 if (device_it->device.type != content::MEDIA_TAB_AUDIO_CAPTURE) {
1346 const StreamDeviceInfo* info =
1347 audio_input_device_manager_->GetOpenedDeviceInfoById(
1348 device_it->session_id);
1349 device_it->device.input = info->device.input;
1350 device_it->device.matched_output = info->device.matched_output;
1353 if (RequestDone(*request))
1354 HandleRequestDone(label, request);
1355 break;
1361 void MediaStreamManager::HandleRequestDone(const std::string& label,
1362 DeviceRequest* request) {
1363 DCHECK(RequestDone(*request));
1364 DVLOG(1) << "HandleRequestDone("
1365 << ", {label = " << label << "})";
1367 switch (request->request_type) {
1368 case MEDIA_OPEN_DEVICE:
1369 FinalizeOpenDevice(label, request);
1370 break;
1371 case MEDIA_GENERATE_STREAM: {
1372 FinalizeGenerateStream(label, request);
1373 break;
1375 default:
1376 NOTREACHED();
1377 break;
1380 if (request->ui_proxy.get()) {
1381 request->ui_proxy->OnStarted(
1382 base::Bind(&MediaStreamManager::StopMediaStreamFromBrowser,
1383 base::Unretained(this), label));
1387 void MediaStreamManager::Closed(MediaStreamType stream_type,
1388 int capture_session_id) {
1389 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1392 void MediaStreamManager::DevicesEnumerated(
1393 MediaStreamType stream_type, const StreamDeviceInfoArray& devices) {
1394 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1395 DVLOG(1) << "DevicesEnumerated("
1396 << ", {stream_type = " << stream_type << "})";
1398 // Only cache the device list when the device list has been changed.
1399 bool need_update_clients = false;
1400 EnumerationCache* cache =
1401 stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ?
1402 &audio_enumeration_cache_ : &video_enumeration_cache_;
1403 if (!cache->valid ||
1404 devices.size() != cache->devices.size() ||
1405 !std::equal(devices.begin(), devices.end(), cache->devices.begin(),
1406 StreamDeviceInfo::IsEqual)) {
1407 StopRemovedDevices(cache->devices, devices);
1408 cache->devices = devices;
1409 need_update_clients = true;
1411 // The device might not be able to be enumerated when it is not warmed up,
1412 // for example, when the machine just wakes up from sleep. We set the cache
1413 // to be invalid so that the next media request will trigger the
1414 // enumeration again. See issue/317673.
1415 cache->valid = !devices.empty();
1418 if (need_update_clients && monitoring_started_)
1419 NotifyDevicesChanged(stream_type, devices);
1421 // Publish the result for all requests waiting for device list(s).
1422 // Find the requests waiting for this device list, store their labels and
1423 // release the iterator before calling device settings. We might get a call
1424 // back from device_settings that will need to iterate through devices.
1425 std::list<std::string> label_list;
1426 for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end();
1427 ++it) {
1428 if (it->second->state(stream_type) == MEDIA_REQUEST_STATE_REQUESTED &&
1429 (it->second->audio_type() == stream_type ||
1430 it->second->video_type() == stream_type)) {
1431 if (it->second->request_type != MEDIA_ENUMERATE_DEVICES)
1432 it->second->SetState(stream_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
1433 label_list.push_back(it->first);
1437 for (std::list<std::string>::iterator it = label_list.begin();
1438 it != label_list.end(); ++it) {
1439 DeviceRequest* request = FindRequest(*it);
1440 switch (request->request_type) {
1441 case MEDIA_ENUMERATE_DEVICES:
1442 if (need_update_clients && request->requester) {
1443 request->devices = devices;
1444 FinalizeEnumerateDevices(*it, request);
1446 break;
1447 default:
1448 if (request->state(request->audio_type()) ==
1449 MEDIA_REQUEST_STATE_REQUESTED ||
1450 request->state(request->video_type()) ==
1451 MEDIA_REQUEST_STATE_REQUESTED) {
1452 // We are doing enumeration for other type of media, wait until it is
1453 // all done before posting the request to UI because UI needs
1454 // the device lists to handle the request.
1455 break;
1457 if (!SetupDeviceCaptureRequest(request))
1458 FinalizeRequestFailed(*it, request);
1459 else
1460 PostRequestToUI(*it, request);
1461 break;
1464 label_list.clear();
1465 --active_enumeration_ref_count_[stream_type];
1466 DCHECK_GE(active_enumeration_ref_count_[stream_type], 0);
1469 void MediaStreamManager::HandleAccessRequestResponse(
1470 const std::string& label,
1471 const MediaStreamDevices& devices) {
1472 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1473 DVLOG(1) << "HandleAccessRequestResponse("
1474 << ", {label = " << label << "})";
1476 DeviceRequest* request = FindRequest(label);
1477 if (!request) {
1478 // The request has been canceled before the UI returned.
1479 return;
1482 if (request->request_type == MEDIA_DEVICE_ACCESS) {
1483 FinalizeMediaAccessRequest(label, request, devices);
1484 return;
1487 // Handle the case when the request was denied.
1488 if (devices.empty()) {
1489 FinalizeRequestFailed(label, request);
1490 return;
1493 // Process all newly-accepted devices for this request.
1494 bool found_audio = false;
1495 bool found_video = false;
1496 for (MediaStreamDevices::const_iterator device_it = devices.begin();
1497 device_it != devices.end(); ++device_it) {
1498 StreamDeviceInfo device_info;
1499 device_info.device = *device_it;
1501 // TODO(justinlin): Nicer way to do this?
1502 // Re-append the device's id since we lost it when posting request to UI.
1503 if (device_info.device.type == content::MEDIA_TAB_VIDEO_CAPTURE ||
1504 device_info.device.type == content::MEDIA_TAB_AUDIO_CAPTURE) {
1505 device_info.device.id = request->UIRequest()->tab_capture_device_id;
1507 // Initialize the sample_rate and channel_layout here since for audio
1508 // mirroring, we don't go through EnumerateDevices where these are usually
1509 // initialized.
1510 if (device_info.device.type == content::MEDIA_TAB_AUDIO_CAPTURE) {
1511 const media::AudioParameters parameters =
1512 audio_manager_->GetDefaultOutputStreamParameters();
1513 int sample_rate = parameters.sample_rate();
1514 // If we weren't able to get the native sampling rate or the sample_rate
1515 // is outside the valid range for input devices set reasonable defaults.
1516 if (sample_rate <= 0 || sample_rate > 96000)
1517 sample_rate = 44100;
1519 device_info.device.input.sample_rate = sample_rate;
1520 device_info.device.input.channel_layout = media::CHANNEL_LAYOUT_STEREO;
1524 if (device_info.device.type == request->audio_type()) {
1525 found_audio = true;
1526 } else if (device_info.device.type == request->video_type()) {
1527 found_video = true;
1530 // If this is request for a new MediaStream, a device is only opened once
1531 // per render view. This is so that the permission to use a device can be
1532 // revoked by a single call to StopStreamDevice regardless of how many
1533 // MediaStreams it is being used in.
1534 if (request->request_type == MEDIA_GENERATE_STREAM) {
1535 MediaRequestState state;
1536 if (FindExistingRequestedDeviceInfo(*request,
1537 device_info.device,
1538 &device_info,
1539 &state)) {
1540 request->devices.push_back(device_info);
1541 request->SetState(device_info.device.type, state);
1542 DVLOG(1) << "HandleAccessRequestResponse - device already opened "
1543 << ", {label = " << label << "}"
1544 << ", device_id = " << device_it->id << "}";
1545 continue;
1548 device_info.session_id =
1549 GetDeviceManager(device_info.device.type)->Open(device_info);
1550 TranslateDeviceIdToSourceId(request, &device_info.device);
1551 request->devices.push_back(device_info);
1553 request->SetState(device_info.device.type, MEDIA_REQUEST_STATE_OPENING);
1554 DVLOG(1) << "HandleAccessRequestResponse - opening device "
1555 << ", {label = " << label << "}"
1556 << ", {device_id = " << device_info.device.id << "}"
1557 << ", {session_id = " << device_info.session_id << "}";
1560 // Check whether we've received all stream types requested.
1561 if (!found_audio && IsAudioMediaType(request->audio_type())) {
1562 request->SetState(request->audio_type(), MEDIA_REQUEST_STATE_ERROR);
1563 DVLOG(1) << "Set no audio found label " << label;
1566 if (!found_video && IsVideoMediaType(request->video_type()))
1567 request->SetState(request->video_type(), MEDIA_REQUEST_STATE_ERROR);
1569 if (RequestDone(*request))
1570 HandleRequestDone(label, request);
1573 void MediaStreamManager::StopMediaStreamFromBrowser(const std::string& label) {
1574 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1576 DeviceRequest* request = FindRequest(label);
1577 if (!request)
1578 return;
1580 // Notify renderers that the devices in the stream will be stopped.
1581 if (request->requester) {
1582 for (StreamDeviceInfoArray::iterator device_it = request->devices.begin();
1583 device_it != request->devices.end(); ++device_it) {
1584 request->requester->DeviceStopped(request->requesting_view_id,
1585 label,
1586 *device_it);
1590 CancelRequest(label);
1593 void MediaStreamManager::UseFakeDevice() {
1594 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1595 video_capture_manager()->UseFakeDevice();
1596 audio_input_device_manager()->UseFakeDevice();
1599 void MediaStreamManager::UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy> fake_ui) {
1600 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1601 use_fake_ui_ = true;
1602 fake_ui_ = fake_ui.Pass();
1605 void MediaStreamManager::WillDestroyCurrentMessageLoop() {
1606 DVLOG(3) << "MediaStreamManager::WillDestroyCurrentMessageLoop()";
1607 DCHECK_EQ(base::MessageLoop::current(), io_loop_);
1608 DCHECK(requests_.empty());
1609 if (device_thread_) {
1610 StopMonitoring();
1612 video_capture_manager_->Unregister();
1613 audio_input_device_manager_->Unregister();
1614 device_thread_.reset();
1617 audio_input_device_manager_ = NULL;
1618 video_capture_manager_ = NULL;
1621 void MediaStreamManager::NotifyDevicesChanged(
1622 MediaStreamType stream_type,
1623 const StreamDeviceInfoArray& devices) {
1624 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1625 MediaObserver* media_observer =
1626 GetContentClient()->browser()->GetMediaObserver();
1627 if (media_observer == NULL)
1628 return;
1630 // Map the devices to MediaStreamDevices.
1631 MediaStreamDevices new_devices;
1632 for (StreamDeviceInfoArray::const_iterator it = devices.begin();
1633 it != devices.end(); ++it) {
1634 new_devices.push_back(it->device);
1637 if (IsAudioMediaType(stream_type)) {
1638 media_observer->OnAudioCaptureDevicesChanged(new_devices);
1639 } else if (IsVideoMediaType(stream_type)) {
1640 media_observer->OnVideoCaptureDevicesChanged(new_devices);
1641 } else {
1642 NOTREACHED();
1646 bool MediaStreamManager::RequestDone(const DeviceRequest& request) const {
1647 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1649 const bool requested_audio = IsAudioMediaType(request.audio_type());
1650 const bool requested_video = IsVideoMediaType(request.video_type());
1652 const bool audio_done =
1653 !requested_audio ||
1654 request.state(request.audio_type()) == MEDIA_REQUEST_STATE_DONE ||
1655 request.state(request.audio_type()) == MEDIA_REQUEST_STATE_ERROR;
1656 if (!audio_done)
1657 return false;
1659 const bool video_done =
1660 !requested_video ||
1661 request.state(request.video_type()) == MEDIA_REQUEST_STATE_DONE ||
1662 request.state(request.video_type()) == MEDIA_REQUEST_STATE_ERROR;
1663 if (!video_done)
1664 return false;
1666 return true;
1669 MediaStreamProvider* MediaStreamManager::GetDeviceManager(
1670 MediaStreamType stream_type) {
1671 if (IsVideoMediaType(stream_type)) {
1672 return video_capture_manager();
1673 } else if (IsAudioMediaType(stream_type)) {
1674 return audio_input_device_manager();
1676 NOTREACHED();
1677 return NULL;
1680 void MediaStreamManager::OnDevicesChanged(
1681 base::SystemMonitor::DeviceType device_type) {
1682 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1684 // NOTE: This method is only called in response to physical audio/video device
1685 // changes (from the operating system).
1687 MediaStreamType stream_type;
1688 if (device_type == base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE) {
1689 stream_type = MEDIA_DEVICE_AUDIO_CAPTURE;
1690 } else if (device_type == base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE) {
1691 stream_type = MEDIA_DEVICE_VIDEO_CAPTURE;
1692 } else {
1693 return; // Uninteresting device change.
1696 // Always do enumeration even though some enumeration is in progress,
1697 // because those enumeration commands could be sent before these devices
1698 // change.
1699 ++active_enumeration_ref_count_[stream_type];
1700 GetDeviceManager(stream_type)->EnumerateDevices(stream_type);
1703 } // namespace content