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"
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"
36 #include "base/win/scoped_com_initializer.h"
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
];
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
,
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
;
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
,
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
;
100 // This is normal video device capture.
101 *video_type
= content::MEDIA_DEVICE_VIDEO_CAPTURE
;
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
{
117 DeviceRequest(MediaStreamRequester
* requester
,
118 int requesting_process_id
,
119 int requesting_view_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
),
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
) {
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
,
164 requested_audio_device_id
,
165 requested_video_device_id
,
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
,
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
;
198 state_
[stream_type
] = new_state
;
201 MediaObserver
* media_observer
=
202 GetContentClient()->browser()->GetMediaObserver();
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.
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
;
264 std::vector
<MediaRequestState
> state_
;
265 scoped_ptr
<MediaStreamRequest
> ui_request_
;
266 MediaStreamType audio_type_
;
267 MediaStreamType video_type_
;
270 MediaStreamManager::EnumerationCache::EnumerationCache()
274 MediaStreamManager::EnumerationCache::~EnumerationCache() {
277 MediaStreamManager::MediaStreamManager()
278 : audio_manager_(NULL
),
279 monitoring_started_(false),
281 use_fake_ui_(false) {}
283 MediaStreamManager::MediaStreamManager(media::AudioManager
* audio_manager
)
284 : audio_manager_(audio_manager
),
285 monitoring_started_(false),
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();
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
,
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
,
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
350 BrowserThread::PostTask(
351 BrowserThread::IO
, FROM_HERE
,
352 base::Bind(&MediaStreamManager::SetupRequest
,
353 base::Unretained(this), label
));
357 void MediaStreamManager::GenerateStream(MediaStreamRequester
* requester
,
358 int render_process_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
,
376 MEDIA_GENERATE_STREAM
,
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
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
,
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
);
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
);
414 // The request does not exist.
415 LOG(ERROR
) << "The request with label = " << label
<< " does not exist.";
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
);
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
) {
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
) {
455 std::string label
= request_it
->first
;
457 CancelRequest(label
);
461 void MediaStreamManager::StopStreamDevice(int render_process_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
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
) {
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
);
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
) {
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
;
513 DeleteRequest(label
);
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
,
547 MediaStreamType type
,
548 const GURL
& security_origin
) {
549 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
551 DCHECK(type
== MEDIA_DEVICE_AUDIO_CAPTURE
||
552 type
== MEDIA_DEVICE_VIDEO_CAPTURE
);
554 DeviceRequest
* request
= new DeviceRequest(requester
,
559 MEDIA_ENUMERATE_DEVICES
,
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
573 BrowserThread::PostTask(
574 BrowserThread::IO
, FROM_HERE
,
575 base::Bind(&MediaStreamManager::DoEnumerateDevices
,
576 base::Unretained(this), label
));
580 void MediaStreamManager::DoEnumerateDevices(const std::string
& label
) {
581 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
582 DeviceRequest
* request
= FindRequest(label
);
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_
;
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
);
604 StartEnumeration(request
);
606 DVLOG(1) << "Enumerate Devices ({label = " << label
<< "})";
609 void MediaStreamManager::OpenDevice(MediaStreamRequester
* requester
,
610 int render_process_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
));
633 DeviceRequest
* request
= new DeviceRequest(requester
,
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
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
));
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
) {
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
,
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
,
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_
)
719 if (!base::SystemMonitor::Get())
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
764 // If a specific device has been requested we need to find the real device
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] << ".";
775 // Check for optional audio sourceIDs.
776 if (device_id
->empty()) {
777 StreamOptions::GetConstraintsByName(*optional
,
778 kMediaStreamSourceInfoId
,
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
,
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
,
808 bool MediaStreamManager::TranslateSourceIdToDeviceId(
809 MediaStreamType stream_type
,
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())
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.
828 for (StreamDeviceInfoArray::const_iterator it
= cache
->devices
.begin();
829 it
!= cache
->devices
.end();
831 if (content::DoesMediaDeviceIDMatchHMAC(rc
, security_origin
, source_id
,
833 *device_id
= it
->device
.id
;
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
)
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
);
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.
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
)
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
;
897 unique_label
= RandomLabel();
898 } while (requests_
.find(unique_label
) != requests_
.end());
900 requests_
.insert(std::make_pair(unique_label
, request
));
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
);
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
);
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();
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
);
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
);
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
);
994 bool is_screen_capture
=
995 video_type
== MEDIA_DESKTOP_VIDEO_CAPTURE
;
996 if (is_screen_capture
&& !SetupScreenCaptureRequest(request
)) {
997 FinalizeRequestFailed(label
, request
);
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
);
1009 if (!SetupDeviceCaptureRequest(request
)) {
1010 FinalizeRequestFailed(label
, request
);
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
)) {
1029 std::string video_device_id
;
1030 if (request
->options
.video_requested
&&
1031 !GetRequestedDeviceCaptureId(request
, request
->video_type(),
1032 &video_device_id
)) {
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
;
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
,
1052 &mandatory_audio
) &&
1053 !request
->options
.GetFirstVideoConstraintByName(kMediaStreamSourceId
,
1055 &mandatory_video
)) {
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
)) {
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
1089 << ", {target_render_view_id = " << target_render_view_id
<< "}";
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.";
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(
1113 &video_stream_source
,
1115 LOG(ERROR
) << kMediaStreamSource
<< " not found.";
1120 if (video_stream_source
== kMediaStreamSourceDesktop
) {
1121 if (!request
->options
.GetFirstVideoConstraintByName(
1122 kMediaStreamSourceId
,
1125 LOG(ERROR
) << kMediaStreamSourceId
<< " not found.";
1132 request
->CreateUIRequest("", video_device_id
);
1136 StreamDeviceInfoArray
MediaStreamManager::GetDevicesOpenedByRequest(
1137 const std::string
& label
) const {
1138 DeviceRequest
* request
= FindRequest(label
);
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
);
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
);
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
);
1238 request
->devices
.clear();
1241 request
->requester
->DevicesEnumerated(
1242 request
->requesting_view_id
,
1243 request
->page_request_id
,
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
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
));
1293 device_thread_
.reset(new base::Thread("MediaStreamDeviceThread"));
1295 device_thread_
->init_com_with_mta(true);
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";
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
);
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
);
1371 case MEDIA_GENERATE_STREAM
: {
1372 FinalizeGenerateStream(label
, request
);
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();
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
);
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.
1457 if (!SetupDeviceCaptureRequest(request
))
1458 FinalizeRequestFailed(*it
, request
);
1460 PostRequestToUI(*it
, request
);
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
);
1478 // The request has been canceled before the UI returned.
1482 if (request
->request_type
== MEDIA_DEVICE_ACCESS
) {
1483 FinalizeMediaAccessRequest(label
, request
, devices
);
1487 // Handle the case when the request was denied.
1488 if (devices
.empty()) {
1489 FinalizeRequestFailed(label
, request
);
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
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()) {
1526 } else if (device_info
.device
.type
== request
->video_type()) {
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
,
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
<< "}";
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
);
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
,
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_
) {
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
)
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
);
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
=
1654 request
.state(request
.audio_type()) == MEDIA_REQUEST_STATE_DONE
||
1655 request
.state(request
.audio_type()) == MEDIA_REQUEST_STATE_ERROR
;
1659 const bool video_done
=
1661 request
.state(request
.video_type()) == MEDIA_REQUEST_STATE_DONE
||
1662 request
.state(request
.video_type()) == MEDIA_REQUEST_STATE_ERROR
;
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();
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
;
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
1699 ++active_enumeration_ref_count_
[stream_type
];
1700 GetDeviceManager(stream_type
)->EnumerateDevices(stream_type
);
1703 } // namespace content