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/command_line.h"
11 #include "base/compiler_specific.h"
12 #include "base/logging.h"
13 #include "base/rand_util.h"
14 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
15 #include "content/browser/renderer_host/media/media_stream_requester.h"
16 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
17 #include "content/browser/renderer_host/media/video_capture_manager.h"
18 #include "content/browser/renderer_host/media/web_contents_capture_util.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/content_browser_client.h"
21 #include "content/public/browser/media_observer.h"
22 #include "content/public/browser/media_request_state.h"
23 #include "content/public/common/content_switches.h"
24 #include "content/public/common/media_stream_request.h"
25 #include "googleurl/src/gurl.h"
26 #include "media/audio/audio_manager_base.h"
27 #include "media/audio/audio_parameters.h"
28 #include "media/base/channel_layout.h"
31 #include "base/win/scoped_com_initializer.h"
36 // Creates a random label used to identify requests.
37 static std::string
RandomLabel() {
38 // An earlier PeerConnection spec,
39 // http://dev.w3.org/2011/webrtc/editor/webrtc.html, specified the
40 // MediaStream::label alphabet as containing 36 characters from
41 // range: U+0021, U+0023 to U+0027, U+002A to U+002B, U+002D to U+002E,
42 // U+0030 to U+0039, U+0041 to U+005A, U+005E to U+007E.
43 // Here we use a safe subset.
44 static const char kAlphabet
[] = "0123456789"
45 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
47 std::string
label(36, ' ');
48 for (size_t i
= 0; i
< label
.size(); ++i
) {
49 int random_char
= base::RandGenerator(sizeof(kAlphabet
) - 1);
50 label
[i
] = kAlphabet
[random_char
];
55 // Helper to verify if a media stream type is part of options or not.
56 static bool Requested(const MediaStreamRequest
& request
,
57 MediaStreamType stream_type
) {
58 return (request
.audio_type
== stream_type
||
59 request
.video_type
== stream_type
);
62 // TODO(xians): Merge DeviceRequest with MediaStreamRequest.
63 class MediaStreamManager::DeviceRequest
{
65 DeviceRequest(MediaStreamRequester
* requester
,
66 const MediaStreamRequest
& request
)
67 : requester(requester
),
69 state_(NUM_MEDIA_TYPES
, MEDIA_REQUEST_STATE_NOT_REQUESTED
) {
74 // Update the request state and notify observers.
75 void SetState(MediaStreamType stream_type
, MediaRequestState new_state
) {
76 state_
[stream_type
] = new_state
;
78 if (request
.video_type
!= MEDIA_TAB_VIDEO_CAPTURE
&&
79 request
.audio_type
!= MEDIA_TAB_AUDIO_CAPTURE
) {
83 MediaObserver
* media_observer
=
84 GetContentClient()->browser()->GetMediaObserver();
85 if (media_observer
== NULL
)
88 // If we appended a device_id scheme, we want to remove it when notifying
89 // observers which may be in different modules since this scheme is only
90 // used internally within the content module.
91 std::string device_id
=
92 WebContentsCaptureUtil::StripWebContentsDeviceScheme(
93 request
.requested_device_id
);
95 media_observer
->OnMediaRequestStateChanged(
96 request
.render_process_id
, request
.render_view_id
,
97 MediaStreamDevice(stream_type
, device_id
, device_id
), new_state
);
100 MediaRequestState
state(MediaStreamType stream_type
) const {
101 return state_
[stream_type
];
104 MediaStreamRequester
* const requester
; // Can be NULL.
105 MediaStreamRequest request
;
107 StreamDeviceInfoArray devices
;
109 // Callback to the requester which audio/video devices have been selected.
110 // It can be null if the requester has no interest to know the result.
111 // Currently it is only used by |DEVICE_ACCESS| type.
112 MediaStreamManager::MediaRequestResponseCallback callback
;
114 scoped_ptr
<MediaStreamUIProxy
> ui_proxy
;
117 std::vector
<MediaRequestState
> state_
;
120 MediaStreamManager::EnumerationCache::EnumerationCache()
124 MediaStreamManager::EnumerationCache::~EnumerationCache() {
127 MediaStreamManager::MediaStreamManager(media::AudioManager
* audio_manager
)
128 : audio_manager_(audio_manager
),
129 monitoring_started_(false),
131 screen_capture_active_(false),
132 use_fake_ui_(false) {
133 DCHECK(audio_manager_
);
134 memset(active_enumeration_ref_count_
, 0,
135 sizeof(active_enumeration_ref_count_
));
137 // Some unit tests create the MSM in the IO thread and assumes the
138 // initialization is done synchronously.
139 if (BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
140 InitializeDeviceManagersOnIOThread();
142 BrowserThread::PostTask(
143 BrowserThread::IO
, FROM_HERE
,
144 base::Bind(&MediaStreamManager::InitializeDeviceManagersOnIOThread
,
145 base::Unretained(this)));
149 MediaStreamManager::~MediaStreamManager() {
150 DCHECK(requests_
.empty());
151 DCHECK(!device_loop_
);
155 VideoCaptureManager
* MediaStreamManager::video_capture_manager() {
156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
157 DCHECK(video_capture_manager_
.get());
158 return video_capture_manager_
.get();
161 AudioInputDeviceManager
* MediaStreamManager::audio_input_device_manager() {
162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
163 DCHECK(audio_input_device_manager_
.get());
164 return audio_input_device_manager_
.get();
167 std::string
MediaStreamManager::MakeMediaAccessRequest(
168 int render_process_id
,
170 const StreamOptions
& options
,
171 const GURL
& security_origin
,
172 const MediaRequestResponseCallback
& callback
) {
173 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
174 // Create a new request based on options.
175 MediaStreamRequest
stream_request(
176 render_process_id
, render_view_id
, security_origin
,
177 MEDIA_DEVICE_ACCESS
, std::string(),
178 options
.audio_type
, options
.video_type
);
179 DeviceRequest
* request
= new DeviceRequest(NULL
, stream_request
);
180 const std::string
& label
= AddRequest(request
);
182 request
->callback
= callback
;
184 HandleRequest(label
);
189 std::string
MediaStreamManager::GenerateStream(
190 MediaStreamRequester
* requester
,
191 int render_process_id
,
193 const StreamOptions
& options
,
194 const GURL
& security_origin
) {
195 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
196 if (CommandLine::ForCurrentProcess()->HasSwitch(
197 switches::kUseFakeDeviceForMediaStream
)) {
200 if (CommandLine::ForCurrentProcess()->HasSwitch(
201 switches::kUseFakeUIForMediaStream
)) {
202 UseFakeUI(scoped_ptr
<FakeMediaStreamUIProxy
>());
205 int target_render_process_id
= render_process_id
;
206 int target_render_view_id
= render_view_id
;
207 std::string requested_device_id
;
209 // Customize options for a WebContents based capture.
210 if (options
.audio_type
== MEDIA_TAB_AUDIO_CAPTURE
||
211 options
.video_type
== MEDIA_TAB_VIDEO_CAPTURE
) {
212 // TODO(justinlin): Can't plumb audio mirroring using stream type right
213 // now, so plumbing by device_id. Will revisit once it's refactored.
214 // http://crbug.com/163100
215 requested_device_id
=
216 WebContentsCaptureUtil::AppendWebContentsDeviceScheme(
217 !options
.video_device_id
.empty() ?
218 options
.video_device_id
: options
.audio_device_id
);
220 bool has_valid_device_id
= WebContentsCaptureUtil::ExtractTabCaptureTarget(
221 requested_device_id
, &target_render_process_id
, &target_render_view_id
);
222 if (!has_valid_device_id
||
223 (options
.audio_type
!= MEDIA_TAB_AUDIO_CAPTURE
&&
224 options
.audio_type
!= MEDIA_NO_SERVICE
) ||
225 (options
.video_type
!= MEDIA_TAB_VIDEO_CAPTURE
&&
226 options
.video_type
!= MEDIA_NO_SERVICE
)) {
227 LOG(ERROR
) << "Invalid request.";
228 return std::string();
232 if (options
.video_type
== MEDIA_SCREEN_VIDEO_CAPTURE
) {
233 if (options
.audio_type
!= MEDIA_NO_SERVICE
) {
234 // TODO(sergeyu): Surface error message to the calling JS code.
235 LOG(ERROR
) << "Audio is not supported for screen capture streams.";
236 return std::string();
239 if (screen_capture_active_
) {
240 // TODO(sergeyu): Implement support for more than one concurrent screen
242 LOG(ERROR
) << "Another screen capture stream is active.";
243 return std::string();
246 screen_capture_active_
= true;
249 // Create a new request based on options.
250 MediaStreamRequest
stream_request(
251 target_render_process_id
, target_render_view_id
, security_origin
,
252 MEDIA_GENERATE_STREAM
, requested_device_id
,
253 options
.audio_type
, options
.video_type
);
254 DeviceRequest
* request
= new DeviceRequest(requester
, stream_request
);
255 const std::string
& label
= AddRequest(request
);
256 HandleRequest(label
);
260 void MediaStreamManager::CancelRequest(const std::string
& label
) {
261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
263 DeviceRequests::iterator it
= requests_
.find(label
);
264 if (it
!= requests_
.end()) {
265 if (!RequestDone(*it
->second
)) {
266 // TODO(xians): update the |state| to STATE_DONE to trigger a state
267 // changed notification to UI before deleting the request?
268 scoped_ptr
<DeviceRequest
> request(it
->second
);
270 for (int i
= MEDIA_NO_SERVICE
+ 1; i
< NUM_MEDIA_TYPES
; ++i
) {
271 const MediaStreamType stream_type
= static_cast<MediaStreamType
>(i
);
272 MediaStreamProvider
* device_manager
= GetDeviceManager(stream_type
);
275 if (request
->state(stream_type
) != MEDIA_REQUEST_STATE_OPENING
) {
278 for (StreamDeviceInfoArray::const_iterator device_it
=
279 request
->devices
.begin();
280 device_it
!= request
->devices
.end(); ++device_it
) {
281 if (device_it
->device
.type
== stream_type
) {
282 device_manager
->Close(device_it
->session_id
);
287 StopGeneratedStream(label
);
292 void MediaStreamManager::StopGeneratedStream(const std::string
& label
) {
293 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
295 // Find the request and close all open devices for the request.
296 DeviceRequests::iterator it
= requests_
.find(label
);
297 if (it
!= requests_
.end()) {
298 if (it
->second
->request
.request_type
== MEDIA_ENUMERATE_DEVICES
) {
299 StopEnumerateDevices(label
);
303 scoped_ptr
<DeviceRequest
> request(it
->second
);
305 for (StreamDeviceInfoArray::const_iterator device_it
=
306 request
->devices
.begin();
307 device_it
!= request
->devices
.end(); ++device_it
) {
308 GetDeviceManager(device_it
->device
.type
)->Close(device_it
->session_id
);
310 if (request
->request
.request_type
== MEDIA_GENERATE_STREAM
&&
311 RequestDone(*request
)) {
312 // Notify observers that this device is being closed.
313 for (int i
= MEDIA_NO_SERVICE
+ 1; i
!= NUM_MEDIA_TYPES
; ++i
) {
314 if (request
->state(static_cast<MediaStreamType
>(i
)) !=
315 MEDIA_REQUEST_STATE_NOT_REQUESTED
) {
316 request
->SetState(static_cast<MediaStreamType
>(i
),
317 MEDIA_REQUEST_STATE_CLOSING
);
324 std::string
MediaStreamManager::EnumerateDevices(
325 MediaStreamRequester
* requester
,
326 int render_process_id
,
328 MediaStreamType type
,
329 const GURL
& security_origin
) {
330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
331 DCHECK(type
== MEDIA_DEVICE_AUDIO_CAPTURE
||
332 type
== MEDIA_DEVICE_VIDEO_CAPTURE
);
334 // When the requester is NULL, the request is made by the UI to ensure MSM
335 // starts monitoring devices.
337 if (!monitoring_started_
)
340 return std::string();
343 // Create a new request.
344 StreamOptions options
;
345 EnumerationCache
* cache
= NULL
;
346 if (type
== MEDIA_DEVICE_AUDIO_CAPTURE
) {
347 options
.audio_type
= type
;
348 cache
= &audio_enumeration_cache_
;
349 } else if (type
== MEDIA_DEVICE_VIDEO_CAPTURE
) {
350 options
.video_type
= type
;
351 cache
= &video_enumeration_cache_
;
354 return std::string();
357 MediaStreamRequest
stream_request(
358 render_process_id
, render_view_id
, security_origin
,
359 MEDIA_ENUMERATE_DEVICES
, std::string(),
360 options
.audio_type
, options
.video_type
);
361 DeviceRequest
* request
= new DeviceRequest(requester
, stream_request
);
362 const std::string
& label
= AddRequest(request
);
365 // Cached device list of this type exists. Just send it out.
366 request
->SetState(type
, MEDIA_REQUEST_STATE_REQUESTED
);
368 // Need to post a task since the requester won't have label till
369 // this function returns.
370 BrowserThread::PostTask(
371 BrowserThread::IO
, FROM_HERE
,
372 base::Bind(&MediaStreamManager::SendCachedDeviceList
,
373 base::Unretained(this), cache
, label
));
375 StartEnumeration(request
);
381 void MediaStreamManager::StopEnumerateDevices(const std::string
& label
) {
382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
384 DeviceRequests::iterator it
= requests_
.find(label
);
385 if (it
!= requests_
.end()) {
386 DCHECK_EQ(it
->second
->request
.request_type
, MEDIA_ENUMERATE_DEVICES
);
387 // Delete the DeviceRequest.
388 scoped_ptr
<DeviceRequest
> request(it
->second
);
393 std::string
MediaStreamManager::OpenDevice(
394 MediaStreamRequester
* requester
,
395 int render_process_id
,
397 const std::string
& device_id
,
398 MediaStreamType type
,
399 const GURL
& security_origin
) {
400 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
401 DCHECK(type
== MEDIA_DEVICE_AUDIO_CAPTURE
||
402 type
== MEDIA_DEVICE_VIDEO_CAPTURE
);
404 // Create a new request.
405 StreamOptions options
;
406 if (IsAudioMediaType(type
)) {
407 options
.audio_type
= type
;
408 } else if (IsVideoMediaType(type
)) {
409 options
.video_type
= type
;
412 return std::string();
415 MediaStreamRequest
stream_request(
416 render_process_id
, render_view_id
, security_origin
,
417 MEDIA_OPEN_DEVICE
, device_id
,
418 options
.audio_type
, options
.video_type
);
419 DeviceRequest
* request
= new DeviceRequest(requester
, stream_request
);
420 const std::string
& label
= AddRequest(request
);
421 StartEnumeration(request
);
426 void MediaStreamManager::SendCachedDeviceList(
427 EnumerationCache
* cache
,
428 const std::string
& label
) {
429 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
431 DeviceRequests::iterator it
= requests_
.find(label
);
432 if (it
!= requests_
.end()) {
433 it
->second
->requester
->DevicesEnumerated(label
, cache
->devices
);
438 void MediaStreamManager::StartMonitoring() {
439 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
440 if (!base::SystemMonitor::Get())
443 if (!monitoring_started_
) {
444 monitoring_started_
= true;
445 base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
447 // Enumerate both the audio and video devices to cache the device lists
448 // and send them to media observer.
449 ++active_enumeration_ref_count_
[MEDIA_DEVICE_AUDIO_CAPTURE
];
450 audio_input_device_manager_
->EnumerateDevices(MEDIA_DEVICE_AUDIO_CAPTURE
);
451 ++active_enumeration_ref_count_
[MEDIA_DEVICE_VIDEO_CAPTURE
];
452 video_capture_manager_
->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE
);
456 void MediaStreamManager::StopMonitoring() {
457 DCHECK_EQ(base::MessageLoop::current(), io_loop_
);
458 if (monitoring_started_
) {
459 base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
460 monitoring_started_
= false;
461 ClearEnumerationCache(&audio_enumeration_cache_
);
462 ClearEnumerationCache(&video_enumeration_cache_
);
466 void MediaStreamManager::ClearEnumerationCache(EnumerationCache
* cache
) {
467 DCHECK_EQ(base::MessageLoop::current(), io_loop_
);
468 cache
->valid
= false;
471 void MediaStreamManager::StartEnumeration(DeviceRequest
* request
) {
472 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
474 // Start monitoring the devices when doing the first enumeration.
475 if (!monitoring_started_
&& base::SystemMonitor::Get()) {
479 // Start enumeration for devices of all requested device types.
480 for (int i
= MEDIA_NO_SERVICE
+ 1; i
< NUM_MEDIA_TYPES
; ++i
) {
481 const MediaStreamType stream_type
= static_cast<MediaStreamType
>(i
);
482 if (Requested(request
->request
, stream_type
)) {
483 request
->SetState(stream_type
, MEDIA_REQUEST_STATE_REQUESTED
);
484 DCHECK_GE(active_enumeration_ref_count_
[stream_type
], 0);
485 if (active_enumeration_ref_count_
[stream_type
] == 0) {
486 ++active_enumeration_ref_count_
[stream_type
];
487 GetDeviceManager(stream_type
)->EnumerateDevices(stream_type
);
493 std::string
MediaStreamManager::AddRequest(DeviceRequest
* request
) {
494 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
496 // Create a label for this request and verify it is unique.
497 std::string unique_label
;
499 unique_label
= RandomLabel();
500 } while (requests_
.find(unique_label
) != requests_
.end());
502 requests_
.insert(std::make_pair(unique_label
, request
));
507 void MediaStreamManager::RemoveRequest(DeviceRequests::iterator it
) {
508 if (it
->second
->request
.video_type
== MEDIA_SCREEN_VIDEO_CAPTURE
) {
509 DCHECK(screen_capture_active_
);
510 screen_capture_active_
= false;
516 void MediaStreamManager::PostRequestToUI(const std::string
& label
) {
517 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
518 DeviceRequest
* request
= requests_
[label
];
522 fake_ui_
.reset(new FakeMediaStreamUIProxy());
524 MediaStreamDevices devices
;
525 if (audio_enumeration_cache_
.valid
) {
526 for (StreamDeviceInfoArray::const_iterator it
=
527 audio_enumeration_cache_
.devices
.begin();
528 it
!= audio_enumeration_cache_
.devices
.end(); ++it
) {
529 devices
.push_back(it
->device
);
532 if (video_enumeration_cache_
.valid
) {
533 for (StreamDeviceInfoArray::const_iterator it
=
534 video_enumeration_cache_
.devices
.begin();
535 it
!= video_enumeration_cache_
.devices
.end(); ++it
) {
536 devices
.push_back(it
->device
);
540 fake_ui_
->SetAvailableDevices(devices
);
542 request
->ui_proxy
= fake_ui_
.Pass();
544 request
->ui_proxy
= MediaStreamUIProxy::Create();
547 request
->ui_proxy
->RequestAccess(
549 base::Bind(&MediaStreamManager::HandleAccessRequestResponse
,
550 base::Unretained(this), label
));
553 void MediaStreamManager::HandleRequest(const std::string
& label
) {
554 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
555 DeviceRequest
* request
= requests_
[label
];
557 const MediaStreamType audio_type
= request
->request
.audio_type
;
558 const MediaStreamType video_type
= request
->request
.video_type
;
560 bool is_web_contents_capture
=
561 audio_type
== MEDIA_TAB_AUDIO_CAPTURE
||
562 video_type
== MEDIA_TAB_VIDEO_CAPTURE
;
564 bool is_screen_capure
=
565 video_type
== MEDIA_SCREEN_VIDEO_CAPTURE
;
567 if (!is_web_contents_capture
&&
569 ((IsAudioMediaType(audio_type
) && !audio_enumeration_cache_
.valid
) ||
570 (IsVideoMediaType(video_type
) && !video_enumeration_cache_
.valid
))) {
571 // Enumerate the devices if there is no valid device lists to be used.
572 StartEnumeration(request
);
576 // No need to do new device enumerations, post the request to UI
578 if (IsAudioMediaType(audio_type
))
579 request
->SetState(audio_type
, MEDIA_REQUEST_STATE_PENDING_APPROVAL
);
580 if (IsVideoMediaType(video_type
))
581 request
->SetState(video_type
, MEDIA_REQUEST_STATE_PENDING_APPROVAL
);
583 PostRequestToUI(label
);
586 void MediaStreamManager::InitializeDeviceManagersOnIOThread() {
587 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
590 device_loop_
= audio_manager_
->GetMessageLoop();
592 audio_input_device_manager_
= new AudioInputDeviceManager(audio_manager_
);
593 audio_input_device_manager_
->Register(this, device_loop_
);
595 video_capture_manager_
= new VideoCaptureManager();
596 video_capture_manager_
->Register(this, device_loop_
);
598 // We want to be notified of IO message loop destruction to delete the thread
599 // and the device managers.
600 io_loop_
= base::MessageLoop::current();
601 io_loop_
->AddDestructionObserver(this);
604 void MediaStreamManager::Opened(MediaStreamType stream_type
,
605 int capture_session_id
) {
606 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
608 // Find the request containing this device and mark it as used.
609 DeviceRequest
* request
= NULL
;
610 StreamDeviceInfoArray
* devices
= NULL
;
612 for (DeviceRequests::iterator request_it
= requests_
.begin();
613 request_it
!= requests_
.end() && request
== NULL
; ++request_it
) {
614 devices
= &(request_it
->second
->devices
);
615 for (StreamDeviceInfoArray::iterator device_it
= devices
->begin();
616 device_it
!= devices
->end(); ++device_it
) {
617 if (device_it
->device
.type
== stream_type
&&
618 device_it
->session_id
== capture_session_id
) {
619 // We've found the request.
620 device_it
->in_use
= true;
621 label
= request_it
->first
;
622 request
= request_it
->second
;
627 if (request
== NULL
) {
628 // The request doesn't exist.
632 DCHECK_NE(request
->state(stream_type
), MEDIA_REQUEST_STATE_REQUESTED
);
634 // Check if all devices for this stream type are opened. Update the state if
636 for (StreamDeviceInfoArray::iterator device_it
= devices
->begin();
637 device_it
!= devices
->end(); ++device_it
) {
638 if (device_it
->device
.type
!= stream_type
) {
641 if (device_it
->in_use
== false) {
642 // Wait for more devices to be opened before we're done.
647 request
->SetState(stream_type
, MEDIA_REQUEST_STATE_DONE
);
649 if (!RequestDone(*request
)) {
650 // This stream_type is done, but not the other type.
654 switch (request
->request
.request_type
) {
655 case MEDIA_OPEN_DEVICE
:
656 request
->requester
->DeviceOpened(label
, devices
->front());
658 case MEDIA_GENERATE_STREAM
: {
659 // Partition the array of devices into audio vs video.
660 StreamDeviceInfoArray audio_devices
, video_devices
;
661 for (StreamDeviceInfoArray::iterator device_it
= devices
->begin();
662 device_it
!= devices
->end(); ++device_it
) {
663 if (IsAudioMediaType(device_it
->device
.type
)) {
664 // Store the native audio parameters in the device struct.
665 // TODO(xians): Handle the tab capture sample rate/channel layout
666 // in AudioInputDeviceManager::Open().
667 if (device_it
->device
.type
!= content::MEDIA_TAB_AUDIO_CAPTURE
) {
668 const StreamDeviceInfo
* info
=
669 audio_input_device_manager_
->GetOpenedDeviceInfoById(
670 device_it
->session_id
);
671 DCHECK_EQ(info
->device
.id
, device_it
->device
.id
);
672 device_it
->device
.sample_rate
= info
->device
.sample_rate
;
673 device_it
->device
.channel_layout
= info
->device
.channel_layout
;
675 audio_devices
.push_back(*device_it
);
676 } else if (IsVideoMediaType(device_it
->device
.type
)) {
677 video_devices
.push_back(*device_it
);
683 request
->requester
->StreamGenerated(label
, audio_devices
, video_devices
);
684 request
->ui_proxy
->OnStarted(
685 base::Bind(&MediaStreamManager::StopStreamFromUI
,
686 base::Unretained(this), label
));
695 void MediaStreamManager::Closed(MediaStreamType stream_type
,
696 int capture_session_id
) {
697 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
700 void MediaStreamManager::DevicesEnumerated(
701 MediaStreamType stream_type
, const StreamDeviceInfoArray
& devices
) {
702 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
704 // Only cache the device list when the device list has been changed.
705 bool need_update_clients
= false;
706 EnumerationCache
* cache
=
707 stream_type
== MEDIA_DEVICE_AUDIO_CAPTURE
?
708 &audio_enumeration_cache_
: &video_enumeration_cache_
;
710 devices
.size() != cache
->devices
.size() ||
711 !std::equal(devices
.begin(), devices
.end(), cache
->devices
.begin(),
712 StreamDeviceInfo::IsEqual
)) {
714 cache
->devices
= devices
;
715 need_update_clients
= true;
718 if (need_update_clients
&& monitoring_started_
)
719 NotifyDevicesChanged(stream_type
, devices
);
721 // Publish the result for all requests waiting for device list(s).
722 // Find the requests waiting for this device list, store their labels and
723 // release the iterator before calling device settings. We might get a call
724 // back from device_settings that will need to iterate through devices.
725 std::list
<std::string
> label_list
;
726 for (DeviceRequests::iterator it
= requests_
.begin(); it
!= requests_
.end();
728 if (it
->second
->state(stream_type
) == MEDIA_REQUEST_STATE_REQUESTED
&&
729 Requested(it
->second
->request
, stream_type
)) {
730 if (it
->second
->request
.request_type
!= MEDIA_ENUMERATE_DEVICES
)
731 it
->second
->SetState(stream_type
, MEDIA_REQUEST_STATE_PENDING_APPROVAL
);
732 label_list
.push_back(it
->first
);
735 for (std::list
<std::string
>::iterator it
= label_list
.begin();
736 it
!= label_list
.end(); ++it
) {
737 DeviceRequest
* request
= requests_
[*it
];
738 switch (request
->request
.request_type
) {
739 case MEDIA_ENUMERATE_DEVICES
:
740 if (need_update_clients
&& request
->requester
)
741 request
->requester
->DevicesEnumerated(*it
, devices
);
744 if (request
->state(request
->request
.audio_type
) ==
745 MEDIA_REQUEST_STATE_REQUESTED
||
746 request
->state(request
->request
.video_type
) ==
747 MEDIA_REQUEST_STATE_REQUESTED
) {
748 // We are doing enumeration for other type of media, wait until it is
749 // all done before posting the request to UI because UI needs
750 // the device lists to handle the request.
754 // Post the request to UI for permission approval.
755 PostRequestToUI(*it
);
760 --active_enumeration_ref_count_
[stream_type
];
761 DCHECK_GE(active_enumeration_ref_count_
[stream_type
], 0);
764 void MediaStreamManager::Error(MediaStreamType stream_type
,
765 int capture_session_id
,
766 MediaStreamProviderError error
) {
767 // Find the device for the error call.
768 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
770 for (DeviceRequests::iterator it
= requests_
.begin(); it
!= requests_
.end();
772 StreamDeviceInfoArray
& devices
= it
->second
->devices
;
774 // TODO(miu): BUG. It's possible for the audio (or video) device array in
775 // the "requester" to become out-of-sync with the order of devices we have
776 // here. See http://crbug.com/147650
777 int audio_device_idx
= -1;
778 int video_device_idx
= -1;
779 for (StreamDeviceInfoArray::iterator device_it
= devices
.begin();
780 device_it
!= devices
.end(); ++device_it
) {
781 if (IsAudioMediaType(device_it
->device
.type
)) {
783 } else if (IsVideoMediaType(device_it
->device
.type
)) {
789 if (device_it
->device
.type
!= stream_type
||
790 device_it
->session_id
!= capture_session_id
) {
793 // We've found the failing device. Find the error case:
794 // An error should only be reported to the MediaStreamManager if
795 // the request has not been fulfilled yet.
796 DCHECK(it
->second
->state(stream_type
) != MEDIA_REQUEST_STATE_DONE
);
797 if (it
->second
->state(stream_type
) != MEDIA_REQUEST_STATE_DONE
) {
798 // Request is not done, devices are not opened in this case.
799 if (devices
.size() <= 1) {
800 scoped_ptr
<DeviceRequest
> request(it
->second
);
801 // 1. Device not opened and no other devices for this request ->
802 // signal stream error and remove the request.
803 if (request
->requester
)
804 request
->requester
->StreamGenerationFailed(it
->first
);
808 // 2. Not opened but other devices exists for this request -> remove
809 // device from list, but don't signal an error.
810 devices
.erase(device_it
); // NOTE: This invalidates device_it!
818 void MediaStreamManager::HandleAccessRequestResponse(
819 const std::string
& label
,
820 const MediaStreamDevices
& devices
) {
821 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
823 DeviceRequests::iterator request_it
= requests_
.find(label
);
824 if (request_it
== requests_
.end()) {
828 // Handle the case when the request was denied.
829 if (devices
.empty()) {
830 // Notify the users about the request result.
831 scoped_ptr
<DeviceRequest
> request(request_it
->second
);
832 if (request
->requester
)
833 request
->requester
->StreamGenerationFailed(label
);
835 if (request
->request
.request_type
== MEDIA_DEVICE_ACCESS
&&
836 !request
->callback
.is_null()) {
837 request
->callback
.Run(MediaStreamDevices(), request
->ui_proxy
.Pass());
840 RemoveRequest(request_it
);
844 if (request_it
->second
->request
.request_type
== MEDIA_DEVICE_ACCESS
) {
845 scoped_ptr
<DeviceRequest
> request(request_it
->second
);
846 if (!request
->callback
.is_null())
847 request
->callback
.Run(devices
, request
->ui_proxy
.Pass());
849 // Delete the request since it is done.
850 RemoveRequest(request_it
);
854 // Process all newly-accepted devices for this request.
855 DeviceRequest
* request
= request_it
->second
;
856 bool found_audio
= false;
857 bool found_video
= false;
858 for (MediaStreamDevices::const_iterator device_it
= devices
.begin();
859 device_it
!= devices
.end(); ++device_it
) {
860 StreamDeviceInfo device_info
;
861 device_info
.device
= *device_it
;
863 // TODO(justinlin): Nicer way to do this?
864 // Re-append the device's id since we lost it when posting request to UI.
865 if (device_info
.device
.type
== content::MEDIA_TAB_VIDEO_CAPTURE
||
866 device_info
.device
.type
== content::MEDIA_TAB_AUDIO_CAPTURE
) {
867 device_info
.device
.id
= request
->request
.requested_device_id
;
869 // Initialize the sample_rate and channel_layout here since for audio
870 // mirroring, we don't go through EnumerateDevices where these are usually
872 if (device_info
.device
.type
== content::MEDIA_TAB_AUDIO_CAPTURE
) {
873 const media::AudioParameters parameters
=
874 audio_manager_
->GetDefaultOutputStreamParameters();
875 int sample_rate
= parameters
.sample_rate();
876 // If we weren't able to get the native sampling rate or the sample_rate
877 // is outside the valid range for input devices set reasonable defaults.
878 if (sample_rate
<= 0 || sample_rate
> 96000)
881 device_info
.device
.sample_rate
= sample_rate
;
882 device_info
.device
.channel_layout
= media::CHANNEL_LAYOUT_STEREO
;
886 // Set in_use to false to be able to track if this device has been
887 // opened. in_use might be true if the device type can be used in more
889 device_info
.in_use
= false;
891 device_info
.session_id
=
892 GetDeviceManager(device_info
.device
.type
)->Open(device_info
);
893 request
->SetState(device_info
.device
.type
, MEDIA_REQUEST_STATE_OPENING
);
894 request
->devices
.push_back(device_info
);
896 if (device_info
.device
.type
== request
->request
.audio_type
) {
898 } else if (device_info
.device
.type
== request
->request
.video_type
) {
903 // Check whether we've received all stream types requested.
904 if (!found_audio
&& IsAudioMediaType(request
->request
.audio_type
))
905 request
->SetState(request
->request
.audio_type
, MEDIA_REQUEST_STATE_ERROR
);
907 if (!found_video
&& IsVideoMediaType(request
->request
.video_type
))
908 request
->SetState(request
->request
.video_type
, MEDIA_REQUEST_STATE_ERROR
);
911 void MediaStreamManager::StopStreamFromUI(const std::string
& label
) {
912 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
914 DeviceRequests::iterator it
= requests_
.find(label
);
915 if (it
== requests_
.end())
918 // Notify renderers that the stream has been stopped.
919 if (it
->second
->requester
)
920 it
->second
->requester
->StreamGenerationFailed(label
);
922 StopGeneratedStream(label
);
925 void MediaStreamManager::UseFakeDevice() {
926 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
927 video_capture_manager()->UseFakeDevice();
928 audio_input_device_manager()->UseFakeDevice();
931 void MediaStreamManager::UseFakeUI(scoped_ptr
<FakeMediaStreamUIProxy
> fake_ui
) {
932 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
934 fake_ui_
= fake_ui
.Pass();
937 void MediaStreamManager::WillDestroyCurrentMessageLoop() {
938 DCHECK_EQ(base::MessageLoop::current(), io_loop_
);
939 DCHECK(requests_
.empty());
943 video_capture_manager_
->Unregister();
944 audio_input_device_manager_
->Unregister();
948 audio_input_device_manager_
= NULL
;
949 video_capture_manager_
= NULL
;
953 void MediaStreamManager::NotifyDevicesChanged(
954 MediaStreamType stream_type
,
955 const StreamDeviceInfoArray
& devices
) {
956 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
957 MediaObserver
* media_observer
=
958 GetContentClient()->browser()->GetMediaObserver();
959 if (media_observer
== NULL
)
962 // Map the devices to MediaStreamDevices.
963 MediaStreamDevices new_devices
;
964 for (StreamDeviceInfoArray::const_iterator it
= devices
.begin();
965 it
!= devices
.end(); ++it
) {
966 new_devices
.push_back(it
->device
);
969 if (IsAudioMediaType(stream_type
)) {
970 media_observer
->OnAudioCaptureDevicesChanged(new_devices
);
971 } else if (IsVideoMediaType(stream_type
)) {
972 media_observer
->OnVideoCaptureDevicesChanged(new_devices
);
978 bool MediaStreamManager::RequestDone(const DeviceRequest
& request
) const {
979 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
981 const bool requested_audio
= IsAudioMediaType(request
.request
.audio_type
);
982 const bool requested_video
= IsVideoMediaType(request
.request
.video_type
);
984 const bool audio_done
=
986 request
.state(request
.request
.audio_type
) ==
987 MEDIA_REQUEST_STATE_DONE
||
988 request
.state(request
.request
.audio_type
) ==
989 MEDIA_REQUEST_STATE_ERROR
;
993 const bool video_done
=
995 request
.state(request
.request
.video_type
) ==
996 MEDIA_REQUEST_STATE_DONE
||
997 request
.state(request
.request
.video_type
) ==
998 MEDIA_REQUEST_STATE_ERROR
;
1002 for (StreamDeviceInfoArray::const_iterator it
= request
.devices
.begin();
1003 it
!= request
.devices
.end(); ++it
) {
1004 if (it
->in_use
== false)
1011 MediaStreamProvider
* MediaStreamManager::GetDeviceManager(
1012 MediaStreamType stream_type
) {
1013 if (IsVideoMediaType(stream_type
)) {
1014 return video_capture_manager();
1015 } else if (IsAudioMediaType(stream_type
)) {
1016 return audio_input_device_manager();
1022 void MediaStreamManager::OnDevicesChanged(
1023 base::SystemMonitor::DeviceType device_type
) {
1024 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
1026 // NOTE: This method is only called in response to physical audio/video device
1027 // changes (from the operating system).
1029 MediaStreamType stream_type
;
1030 if (device_type
== base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE
) {
1031 stream_type
= MEDIA_DEVICE_AUDIO_CAPTURE
;
1032 } else if (device_type
== base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE
) {
1033 stream_type
= MEDIA_DEVICE_VIDEO_CAPTURE
;
1035 return; // Uninteresting device change.
1038 // Always do enumeration even though some enumeration is in progress,
1039 // because those enumeration commands could be sent before these devices
1041 ++active_enumeration_ref_count_
[stream_type
];
1042 GetDeviceManager(stream_type
)->EnumerateDevices(stream_type
);
1045 } // namespace content