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/threading/thread.h"
16 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
17 #include "content/browser/renderer_host/media/device_request_message_filter.h"
18 #include "content/browser/renderer_host/media/media_stream_requester.h"
19 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
20 #include "content/browser/renderer_host/media/video_capture_manager.h"
21 #include "content/browser/renderer_host/media/web_contents_capture_util.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/content_browser_client.h"
24 #include "content/public/browser/media_device_id.h"
25 #include "content/public/browser/media_observer.h"
26 #include "content/public/browser/media_request_state.h"
27 #include "content/public/common/content_switches.h"
28 #include "content/public/common/media_stream_request.h"
29 #include "media/audio/audio_manager_base.h"
30 #include "media/audio/audio_parameters.h"
31 #include "media/base/channel_layout.h"
35 #include "base/win/scoped_com_initializer.h"
40 // Creates a random label used to identify requests.
41 static std::string RandomLabel() {
42 // An earlier PeerConnection spec,
43 // http://dev.w3.org/2011/webrtc/editor/webrtc.html, specified the
44 // MediaStream::label alphabet as containing 36 characters from
45 // range: U+0021, U+0023 to U+0027, U+002A to U+002B, U+002D to U+002E,
46 // U+0030 to U+0039, U+0041 to U+005A, U+005E to U+007E.
47 // Here we use a safe subset.
48 static const char kAlphabet[] = "0123456789"
49 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
51 std::string label(36, ' ');
52 for (size_t i = 0; i < label.size(); ++i) {
53 int random_char = base::RandGenerator(sizeof(kAlphabet) - 1);
54 label[i] = kAlphabet[random_char];
59 // Helper to verify if a media stream type is part of options or not.
60 static bool Requested(const MediaStreamRequest& request,
61 MediaStreamType stream_type) {
62 return (request.audio_type == stream_type ||
63 request.video_type == stream_type);
66 // TODO(xians): Merge DeviceRequest with MediaStreamRequest.
67 class MediaStreamManager::DeviceRequest {
69 DeviceRequest(MediaStreamRequester* requester,
70 const MediaStreamRequest& request,
71 int requesting_process_id,
72 int requesting_view_id)
73 : requester(requester),
75 requesting_process_id(requesting_process_id),
76 requesting_view_id(requesting_view_id),
77 state_(NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_NOT_REQUESTED) {
82 // Update the request state and notify observers.
83 void SetState(MediaStreamType stream_type, MediaRequestState new_state) {
84 if (stream_type == NUM_MEDIA_TYPES) {
85 for (int i = MEDIA_NO_SERVICE + 1; i < NUM_MEDIA_TYPES; ++i) {
86 const MediaStreamType stream_type = static_cast<MediaStreamType>(i);
87 state_[stream_type] = new_state;
90 state_[stream_type] = new_state;
93 if (request.video_type != MEDIA_TAB_VIDEO_CAPTURE &&
94 request.audio_type != MEDIA_TAB_AUDIO_CAPTURE &&
95 new_state != MEDIA_REQUEST_STATE_CLOSING) {
99 MediaObserver* media_observer =
100 GetContentClient()->browser()->GetMediaObserver();
101 if (media_observer == NULL)
104 // If we appended a device_id scheme, we want to remove it when notifying
105 // observers which may be in different modules since this scheme is only
106 // used internally within the content module.
107 std::string device_id =
108 WebContentsCaptureUtil::StripWebContentsDeviceScheme(
109 request.tab_capture_device_id);
111 media_observer->OnMediaRequestStateChanged(
112 request.render_process_id, request.render_view_id,
113 request.page_request_id,
114 MediaStreamDevice(stream_type, device_id, device_id), new_state);
117 MediaRequestState state(MediaStreamType stream_type) const {
118 return state_[stream_type];
121 MediaStreamRequester* const requester; // Can be NULL.
122 MediaStreamRequest request;
124 // The render process id that requested this stream to be generated and that
125 // will receive a handle to the MediaStream. This may be different from
126 // MediaStreamRequest::render_process_id which in the tab capture case
127 // specifies the target renderer from which audio and video is captured.
128 const int requesting_process_id;
130 // The render view id that requested this stream to be generated and that
131 // will receive a handle to the MediaStream. This may be different from
132 // MediaStreamRequest::render_view_id which in the tab capture case
133 // specifies the target renderer from which audio and video is captured.
134 const int requesting_view_id;
136 StreamDeviceInfoArray devices;
138 // Callback to the requester which audio/video devices have been selected.
139 // It can be null if the requester has no interest to know the result.
140 // Currently it is only used by |DEVICE_ACCESS| type.
141 MediaStreamManager::MediaRequestResponseCallback callback;
143 scoped_ptr<MediaStreamUIProxy> ui_proxy;
146 std::vector<MediaRequestState> state_;
149 MediaStreamManager::EnumerationCache::EnumerationCache()
153 MediaStreamManager::EnumerationCache::~EnumerationCache() {
156 MediaStreamManager::MediaStreamManager()
157 : audio_manager_(NULL),
158 monitoring_started_(false),
160 use_fake_ui_(false) {}
162 MediaStreamManager::MediaStreamManager(media::AudioManager* audio_manager)
163 : audio_manager_(audio_manager),
164 monitoring_started_(false),
166 use_fake_ui_(false) {
167 DCHECK(audio_manager_);
168 memset(active_enumeration_ref_count_, 0,
169 sizeof(active_enumeration_ref_count_));
171 // Some unit tests create the MSM in the IO thread and assumes the
172 // initialization is done synchronously.
173 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
174 InitializeDeviceManagersOnIOThread();
176 BrowserThread::PostTask(
177 BrowserThread::IO, FROM_HERE,
178 base::Bind(&MediaStreamManager::InitializeDeviceManagersOnIOThread,
179 base::Unretained(this)));
183 MediaStreamManager::~MediaStreamManager() {
184 DCHECK(requests_.empty());
185 DCHECK(!device_thread_.get());
188 VideoCaptureManager* MediaStreamManager::video_capture_manager() {
189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
190 DCHECK(video_capture_manager_.get());
191 return video_capture_manager_.get();
194 AudioInputDeviceManager* MediaStreamManager::audio_input_device_manager() {
195 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
196 DCHECK(audio_input_device_manager_.get());
197 return audio_input_device_manager_.get();
200 std::string MediaStreamManager::MakeMediaAccessRequest(
201 int render_process_id,
204 const StreamOptions& options,
205 const GURL& security_origin,
206 const MediaRequestResponseCallback& callback) {
207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
208 // Create a new request based on options.
209 MediaStreamRequest stream_request(
210 render_process_id, render_view_id, page_request_id, std::string(),
211 security_origin, MEDIA_DEVICE_ACCESS, std::string(), std::string(),
212 options.audio_type, options.video_type);
213 DeviceRequest* request = new DeviceRequest(NULL, stream_request,
214 render_process_id, render_view_id);
215 const std::string& label = AddRequest(request);
217 request->callback = callback;
219 HandleRequest(label);
224 std::string MediaStreamManager::GenerateStream(
225 MediaStreamRequester* requester,
226 int render_process_id,
229 const StreamOptions& options,
230 const GURL& security_origin) {
231 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
232 if (CommandLine::ForCurrentProcess()->HasSwitch(
233 switches::kUseFakeDeviceForMediaStream)) {
236 if (CommandLine::ForCurrentProcess()->HasSwitch(
237 switches::kUseFakeUIForMediaStream)) {
238 UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy>());
241 int target_render_process_id = render_process_id;
242 int target_render_view_id = render_view_id;
243 std::string tab_capture_device_id;
245 // Customize options for a WebContents based capture.
246 if (options.audio_type == MEDIA_TAB_AUDIO_CAPTURE ||
247 options.video_type == MEDIA_TAB_VIDEO_CAPTURE) {
248 // TODO(justinlin): Can't plumb audio mirroring using stream type right
249 // now, so plumbing by device_id. Will revisit once it's refactored.
250 // http://crbug.com/163100
251 tab_capture_device_id =
252 WebContentsCaptureUtil::AppendWebContentsDeviceScheme(
253 !options.video_device_id.empty() ?
254 options.video_device_id : options.audio_device_id);
256 bool has_valid_device_id = WebContentsCaptureUtil::ExtractTabCaptureTarget(
257 tab_capture_device_id, &target_render_process_id,
258 &target_render_view_id);
259 if (!has_valid_device_id ||
260 (options.audio_type != MEDIA_TAB_AUDIO_CAPTURE &&
261 options.audio_type != MEDIA_NO_SERVICE) ||
262 (options.video_type != MEDIA_TAB_VIDEO_CAPTURE &&
263 options.video_type != MEDIA_NO_SERVICE)) {
264 LOG(ERROR) << "Invalid request.";
265 return std::string();
269 std::string translated_audio_device_id;
270 std::string translated_video_device_id;
271 if (options.audio_type == MEDIA_DEVICE_AUDIO_CAPTURE) {
272 bool found_match = TranslateGUIDToRawId(
273 MEDIA_DEVICE_AUDIO_CAPTURE, security_origin, options.audio_device_id,
274 &translated_audio_device_id);
275 DCHECK(found_match || translated_audio_device_id.empty());
278 if (options.video_type == MEDIA_DEVICE_VIDEO_CAPTURE) {
279 bool found_match = TranslateGUIDToRawId(
280 MEDIA_DEVICE_VIDEO_CAPTURE, security_origin, options.video_device_id,
281 &translated_video_device_id);
282 DCHECK(found_match || translated_video_device_id.empty());
285 if (options.video_type == MEDIA_DESKTOP_VIDEO_CAPTURE ||
286 options.audio_type == MEDIA_LOOPBACK_AUDIO_CAPTURE) {
287 // For screen capture we only support two valid combinations:
288 // (1) screen video capture only, or
289 // (2) screen video capture with loopback audio capture.
290 if (options.video_type != MEDIA_DESKTOP_VIDEO_CAPTURE ||
291 (options.audio_type != MEDIA_NO_SERVICE &&
292 options.audio_type != MEDIA_LOOPBACK_AUDIO_CAPTURE)) {
293 // TODO(sergeyu): Surface error message to the calling JS code.
294 LOG(ERROR) << "Invalid screen capture request.";
295 return std::string();
297 translated_video_device_id = options.video_device_id;
300 // Create a new request based on options.
301 MediaStreamRequest stream_request(
302 target_render_process_id, target_render_view_id, page_request_id,
303 tab_capture_device_id, security_origin, MEDIA_GENERATE_STREAM,
304 translated_audio_device_id, translated_video_device_id,
305 options.audio_type, options.video_type);
306 DeviceRequest* request = new DeviceRequest(requester, stream_request,
309 const std::string& label = AddRequest(request);
310 HandleRequest(label);
314 void MediaStreamManager::CancelRequest(const std::string& label) {
315 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
317 DeviceRequests::iterator request_it = requests_.find(label);
318 if (request_it == requests_.end()) {
322 scoped_ptr<DeviceRequest> request(request_it->second);
323 RemoveRequest(request_it);
325 if (request->request.request_type == MEDIA_ENUMERATE_DEVICES) {
329 // This is a request for opening one or more devices.
330 for (StreamDeviceInfoArray::iterator device_it = request->devices.begin();
331 device_it != request->devices.end(); ++device_it) {
332 // If we have not yet requested the device to be opened - just ignore it.
333 if (request->state(device_it->device.type) != MEDIA_REQUEST_STATE_OPENING
335 request->state(device_it->device.type) != MEDIA_REQUEST_STATE_DONE) {
338 // Stop the opening/opened devices of the requests.
339 StopDevice(*device_it);
342 // Cancel the request if still pending at UI side.
343 request->SetState(NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_CLOSING);
346 void MediaStreamManager::CancelAllRequests(int render_process_id) {
347 DeviceRequests::iterator request_it = requests_.begin();
348 while (request_it != requests_.end()) {
349 if (request_it->second->requesting_process_id != render_process_id) {
354 std::string label = request_it->first;
356 CancelRequest(label);
360 void MediaStreamManager::StopStreamDevice(int render_process_id,
362 const std::string& device_id) {
363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
364 DVLOG(1) << "StopStreamDevice({render_view_id = " << render_view_id << "} "
365 << ", {device_id = " << device_id << "})";
367 // Find all requests for this |render_process_id| and |render_view_id| of type
368 // MEDIA_GENERATE_STREAM that has requested to use |device_id|.
369 DeviceRequests::iterator request_it = requests_.begin();
370 while (request_it != requests_.end()) {
371 DeviceRequest* request = request_it->second;
372 const MediaStreamRequest& ms_request = request->request;
373 if (request->requesting_process_id != render_process_id ||
374 request->requesting_view_id != render_view_id ||
375 ms_request.request_type != MEDIA_GENERATE_STREAM) {
380 StreamDeviceInfoArray* devices = &request->devices;
381 StreamDeviceInfoArray::iterator device_it = devices->begin();
382 while (device_it != devices->end()) {
383 MediaStreamType device_type = device_it->device.type;
384 if (device_it->device.id == device_id) {
385 if (request->state(device_type) == MEDIA_REQUEST_STATE_DONE) {
386 StopDevice(*device_it);
388 device_it = devices->erase(device_it);
394 // If this request doesn't have any active devices, remove the request.
395 if (devices->empty()) {
396 DeviceRequests::iterator del_itor(request_it);
398 scoped_ptr<DeviceRequest> request(del_itor->second);
399 RemoveRequest(del_itor);
406 void MediaStreamManager::StopDevice(const StreamDeviceInfo& device_info) {
407 DVLOG(1) << "StopDevice("
408 << "{device_info.session_id = " << device_info.session_id << "} "
409 << "{device_id = " << device_info.device.id << "})";
410 GetDeviceManager(device_info.device.type)->Close(device_info.session_id);
412 for (DeviceRequests::iterator request_it = requests_.begin();
413 request_it != requests_.end() ; ++request_it) {
414 StreamDeviceInfoArray* devices = &request_it->second->devices;
415 for (StreamDeviceInfoArray::iterator device_it = devices->begin();
416 device_it != devices->end(); ++device_it) {
417 if (device_it->session_id == device_info.session_id &&
418 device_it->device.type == device_info.device.type) {
419 // Notify observers that this device is being closed.
420 // Note that only one device per type can be opened.
421 request_it->second->SetState(device_it->device.type,
422 MEDIA_REQUEST_STATE_CLOSING);
428 std::string MediaStreamManager::EnumerateDevices(
429 MediaStreamRequester* requester,
430 int render_process_id,
433 MediaStreamType type,
434 const GURL& security_origin) {
435 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
436 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
437 type == MEDIA_DEVICE_VIDEO_CAPTURE);
439 // When the requester is NULL, the request is made by the UI to ensure MSM
440 // starts monitoring devices.
442 if (!monitoring_started_)
445 return std::string();
448 // Create a new request.
449 StreamOptions options;
450 EnumerationCache* cache = NULL;
451 if (type == MEDIA_DEVICE_AUDIO_CAPTURE) {
452 options.audio_type = type;
453 cache = &audio_enumeration_cache_;
454 } else if (type == MEDIA_DEVICE_VIDEO_CAPTURE) {
455 options.video_type = type;
456 cache = &video_enumeration_cache_;
459 return std::string();
462 MediaStreamRequest stream_request(
463 render_process_id, render_view_id, page_request_id, std::string(),
464 security_origin, MEDIA_ENUMERATE_DEVICES, std::string(), std::string(),
465 options.audio_type, options.video_type);
466 DeviceRequest* request = new DeviceRequest(requester, stream_request,
469 const std::string& label = AddRequest(request);
472 // Cached device list of this type exists. Just send it out.
473 request->SetState(type, MEDIA_REQUEST_STATE_REQUESTED);
475 // Need to post a task since the requester won't have label till
476 // this function returns.
477 BrowserThread::PostTask(
478 BrowserThread::IO, FROM_HERE,
479 base::Bind(&MediaStreamManager::SendCachedDeviceList,
480 base::Unretained(this), cache, label));
482 StartEnumeration(request);
488 std::string MediaStreamManager::OpenDevice(
489 MediaStreamRequester* requester,
490 int render_process_id,
493 const std::string& device_id,
494 MediaStreamType type,
495 const GURL& security_origin) {
496 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
497 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
498 type == MEDIA_DEVICE_VIDEO_CAPTURE);
500 // Create a new request.
501 StreamOptions options;
502 if (IsAudioMediaType(type)) {
503 options.audio_type = type;
504 options.audio_device_id = device_id;
505 } else if (IsVideoMediaType(type)) {
506 options.video_type = type;
507 options.video_device_id = device_id;
510 return std::string();
513 MediaStreamRequest stream_request(
514 render_process_id, render_view_id, page_request_id, std::string(),
515 security_origin, MEDIA_OPEN_DEVICE, options.audio_device_id,
516 options.video_device_id, options.audio_type, options.video_type);
517 DeviceRequest* request = new DeviceRequest(requester, stream_request,
520 const std::string& label = AddRequest(request);
521 StartEnumeration(request);
526 void MediaStreamManager::SendCachedDeviceList(
527 EnumerationCache* cache,
528 const std::string& label) {
529 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
531 DeviceRequests::iterator it = requests_.find(label);
532 if (it != requests_.end()) {
533 it->second->requester->DevicesEnumerated(label, cache->devices);
538 void MediaStreamManager::StopRemovedDevices(
539 const StreamDeviceInfoArray& old_devices,
540 const StreamDeviceInfoArray& new_devices) {
541 DVLOG(1) << "StopRemovedDevices("
542 << "{#old_devices = " << old_devices.size() << "} "
543 << "{#new_devices = " << new_devices.size() << "})";
544 for (StreamDeviceInfoArray::const_iterator old_dev_it = old_devices.begin();
545 old_dev_it != old_devices.end(); ++old_dev_it) {
546 bool device_found = false;
547 for (StreamDeviceInfoArray::const_iterator new_dev_it = new_devices.begin();
548 new_dev_it != new_devices.end(); ++new_dev_it) {
549 if (old_dev_it->device.id == new_dev_it->device.id) {
556 // A device has been removed. We need to check if it is used by a
557 // MediaStream and in that case cleanup and notify the render process.
560 FindFirstMediaStreamRequestWithDevice(old_dev_it->device);
563 // TODO(perkj): We would like to stop all tracks that use the removed
564 // device, not the MediaStream. But at the moment, there is no way of
565 // doing that from the browser process. crbug/315585
566 StopMediaStreamFromBrowser(label);
572 void MediaStreamManager::StartMonitoring() {
573 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
574 if (!base::SystemMonitor::Get())
577 if (!monitoring_started_) {
578 monitoring_started_ = true;
579 base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
581 // Enumerate both the audio and video devices to cache the device lists
582 // and send them to media observer.
583 ++active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_CAPTURE];
584 audio_input_device_manager_->EnumerateDevices(MEDIA_DEVICE_AUDIO_CAPTURE);
585 ++active_enumeration_ref_count_[MEDIA_DEVICE_VIDEO_CAPTURE];
586 video_capture_manager_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
590 void MediaStreamManager::StopMonitoring() {
591 DCHECK_EQ(base::MessageLoop::current(), io_loop_);
592 if (monitoring_started_) {
593 base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
594 monitoring_started_ = false;
595 ClearEnumerationCache(&audio_enumeration_cache_);
596 ClearEnumerationCache(&video_enumeration_cache_);
600 bool MediaStreamManager::TranslateGUIDToRawId(MediaStreamType stream_type,
601 const GURL& security_origin,
602 const std::string& device_guid,
603 std::string* raw_device_id) {
604 DCHECK(stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ||
605 stream_type == MEDIA_DEVICE_VIDEO_CAPTURE);
606 if (device_guid.empty())
609 EnumerationCache* cache =
610 stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ?
611 &audio_enumeration_cache_ : &video_enumeration_cache_;
613 // If device monitoring hasn't started, the |device_guid| is not valid.
617 for (StreamDeviceInfoArray::const_iterator it = cache->devices.begin();
618 it != cache->devices.end();
620 if (content::DoesMediaDeviceIDMatchHMAC(
621 security_origin, device_guid, it->device.id)) {
622 *raw_device_id = it->device.id;
629 void MediaStreamManager::ClearEnumerationCache(EnumerationCache* cache) {
630 DCHECK_EQ(base::MessageLoop::current(), io_loop_);
631 cache->valid = false;
634 void MediaStreamManager::StartEnumeration(DeviceRequest* request) {
635 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
637 // Start monitoring the devices when doing the first enumeration.
638 if (!monitoring_started_ && base::SystemMonitor::Get()) {
642 // Start enumeration for devices of all requested device types.
643 for (int i = MEDIA_NO_SERVICE + 1; i < NUM_MEDIA_TYPES; ++i) {
644 const MediaStreamType stream_type = static_cast<MediaStreamType>(i);
645 if (Requested(request->request, stream_type)) {
646 request->SetState(stream_type, MEDIA_REQUEST_STATE_REQUESTED);
647 DCHECK_GE(active_enumeration_ref_count_[stream_type], 0);
648 if (active_enumeration_ref_count_[stream_type] == 0) {
649 ++active_enumeration_ref_count_[stream_type];
650 GetDeviceManager(stream_type)->EnumerateDevices(stream_type);
656 std::string MediaStreamManager::AddRequest(DeviceRequest* request) {
657 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
659 // Create a label for this request and verify it is unique.
660 std::string unique_label;
662 unique_label = RandomLabel();
663 } while (requests_.find(unique_label) != requests_.end());
665 requests_.insert(std::make_pair(unique_label, request));
670 void MediaStreamManager::RemoveRequest(DeviceRequests::iterator it) {
674 void MediaStreamManager::PostRequestToUI(const std::string& label) {
675 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
676 DeviceRequest* request = requests_[label];
680 fake_ui_.reset(new FakeMediaStreamUIProxy());
682 MediaStreamDevices devices;
683 if (audio_enumeration_cache_.valid) {
684 for (StreamDeviceInfoArray::const_iterator it =
685 audio_enumeration_cache_.devices.begin();
686 it != audio_enumeration_cache_.devices.end(); ++it) {
687 devices.push_back(it->device);
690 if (video_enumeration_cache_.valid) {
691 for (StreamDeviceInfoArray::const_iterator it =
692 video_enumeration_cache_.devices.begin();
693 it != video_enumeration_cache_.devices.end(); ++it) {
694 devices.push_back(it->device);
698 fake_ui_->SetAvailableDevices(devices);
700 request->ui_proxy = fake_ui_.Pass();
702 request->ui_proxy = MediaStreamUIProxy::Create();
705 request->ui_proxy->RequestAccess(
707 base::Bind(&MediaStreamManager::HandleAccessRequestResponse,
708 base::Unretained(this), label));
711 void MediaStreamManager::HandleRequest(const std::string& label) {
712 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
713 DeviceRequest* request = requests_[label];
715 const MediaStreamType audio_type = request->request.audio_type;
716 const MediaStreamType video_type = request->request.video_type;
718 bool is_web_contents_capture =
719 audio_type == MEDIA_TAB_AUDIO_CAPTURE ||
720 video_type == MEDIA_TAB_VIDEO_CAPTURE;
722 bool is_screen_capture =
723 video_type == MEDIA_DESKTOP_VIDEO_CAPTURE;
725 if (!is_web_contents_capture &&
726 !is_screen_capture &&
727 ((IsAudioMediaType(audio_type) && !audio_enumeration_cache_.valid) ||
728 (IsVideoMediaType(video_type) && !video_enumeration_cache_.valid))) {
729 // Enumerate the devices if there is no valid device lists to be used.
730 StartEnumeration(request);
734 // No need to do new device enumerations, post the request to UI
736 if (IsAudioMediaType(audio_type))
737 request->SetState(audio_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
738 if (IsVideoMediaType(video_type))
739 request->SetState(video_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
741 PostRequestToUI(label);
744 bool MediaStreamManager::FindExistingRequestedDeviceInfo(
745 int render_process_id,
747 MediaStreamRequestType type,
748 const std::string& device_id,
749 StreamDeviceInfo* device_info,
750 MediaRequestState* request_state) const {
752 DCHECK(request_state);
753 for (DeviceRequests::const_iterator it = requests_.begin();
754 it != requests_.end() ; ++it) {
755 const DeviceRequest* request = it->second;
756 if (request->requesting_process_id ==render_process_id &&
757 request->requesting_view_id == render_view_id &&
758 request->request.request_type == type) {
759 for (StreamDeviceInfoArray::const_iterator device_it =
760 request->devices.begin();
761 device_it != request->devices.end(); ++device_it) {
762 if (device_it->device.id == device_id) {
763 *device_info = *device_it;
764 *request_state = request->state(device_it->device.type);
773 std::string MediaStreamManager::FindFirstMediaStreamRequestWithDevice(
774 const MediaStreamDevice& device) const {
775 for (DeviceRequests::const_iterator it = requests_.begin();
776 it != requests_.end() ; ++it) {
777 const DeviceRequest* request = it->second;
778 if (request->request.request_type != MEDIA_GENERATE_STREAM)
780 for (StreamDeviceInfoArray::const_iterator device_it =
781 request->devices.begin();
782 device_it != request->devices.end(); ++device_it) {
783 if (device_it->device.id == device.id &&
784 device_it->device.type == device.type) {
789 return std::string();
792 void MediaStreamManager::InitializeDeviceManagersOnIOThread() {
793 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
797 device_thread_.reset(new base::Thread("MediaStreamDeviceThread"));
799 device_thread_->init_com_with_mta(true);
801 CHECK(device_thread_->Start());
803 audio_input_device_manager_ = new AudioInputDeviceManager(audio_manager_);
804 audio_input_device_manager_->Register(
805 this, device_thread_->message_loop_proxy().get());
807 video_capture_manager_ = new VideoCaptureManager();
808 video_capture_manager_->Register(this,
809 device_thread_->message_loop_proxy().get());
811 // We want to be notified of IO message loop destruction to delete the thread
812 // and the device managers.
813 io_loop_ = base::MessageLoop::current();
814 io_loop_->AddDestructionObserver(this);
817 void MediaStreamManager::Opened(MediaStreamType stream_type,
818 int capture_session_id) {
819 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
820 DVLOG(1) << "Opened({stream_type = " << stream_type << "} "
821 << "{capture_session_id = " << capture_session_id << "})";
822 // Find the request(s) containing this device and mark it as used.
823 // It can be used in several requests since the same device can be
824 // requested from the same web page.
825 for (DeviceRequests::iterator request_it = requests_.begin();
826 request_it != requests_.end(); ++request_it) {
827 const std::string& label = request_it->first;
828 DeviceRequest* request = request_it->second;
829 StreamDeviceInfoArray* devices = &(request->devices);
830 for (StreamDeviceInfoArray::iterator device_it = devices->begin();
831 device_it != devices->end(); ++device_it) {
832 if (device_it->device.type == stream_type &&
833 device_it->session_id == capture_session_id &&
834 request->state(device_it->device.type) != MEDIA_REQUEST_STATE_DONE) {
835 // We've found a matching request.
836 request->SetState(device_it->device.type, MEDIA_REQUEST_STATE_DONE);
838 if (IsAudioMediaType(device_it->device.type)) {
839 // Store the native audio parameters in the device struct.
840 // TODO(xians): Handle the tab capture sample rate/channel layout
841 // in AudioInputDeviceManager::Open().
842 if (device_it->device.type != content::MEDIA_TAB_AUDIO_CAPTURE) {
843 const StreamDeviceInfo* info =
844 audio_input_device_manager_->GetOpenedDeviceInfoById(
845 device_it->session_id);
846 DCHECK_EQ(info->device.id, device_it->device.id);
847 device_it->device.input = info->device.input;
848 device_it->device.matched_output = info->device.matched_output;
851 if (RequestDone(*request))
852 HandleRequestDone(label, request);
859 void MediaStreamManager::HandleRequestDone(const std::string& label,
860 DeviceRequest* request) {
861 DCHECK(RequestDone(*request));
862 DVLOG(1) << "HandleRequestDone("
863 << ", {label = " << label << "})";
865 const StreamDeviceInfoArray& requested_devices = request->devices;
866 switch (request->request.request_type) {
867 case MEDIA_OPEN_DEVICE:
868 request->requester->DeviceOpened(label, requested_devices.front());
870 case MEDIA_GENERATE_STREAM: {
871 // Partition the array of devices into audio vs video.
872 StreamDeviceInfoArray audio_devices, video_devices;
873 for (StreamDeviceInfoArray::const_iterator device_it =
874 requested_devices.begin();
875 device_it != requested_devices.end(); ++device_it) {
876 if (IsAudioMediaType(device_it->device.type)) {
877 audio_devices.push_back(*device_it);
878 } else if (IsVideoMediaType(device_it->device.type)) {
879 video_devices.push_back(*device_it);
885 request->requester->StreamGenerated(label, audio_devices, video_devices);
893 if (request->ui_proxy.get()) {
894 request->ui_proxy->OnStarted(
895 base::Bind(&MediaStreamManager::StopMediaStreamFromBrowser,
896 base::Unretained(this), label));
900 void MediaStreamManager::Closed(MediaStreamType stream_type,
901 int capture_session_id) {
902 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
905 void MediaStreamManager::DevicesEnumerated(
906 MediaStreamType stream_type, const StreamDeviceInfoArray& devices) {
907 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
908 DVLOG(1) << "DevicesEnumerated("
909 << ", {stream_type = " << stream_type << "})";
911 // Only cache the device list when the device list has been changed.
912 bool need_update_clients = false;
913 EnumerationCache* cache =
914 stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ?
915 &audio_enumeration_cache_ : &video_enumeration_cache_;
917 devices.size() != cache->devices.size() ||
918 !std::equal(devices.begin(), devices.end(), cache->devices.begin(),
919 StreamDeviceInfo::IsEqual)) {
920 StopRemovedDevices(cache->devices, devices);
922 cache->devices = devices;
923 need_update_clients = true;
926 if (need_update_clients && monitoring_started_)
927 NotifyDevicesChanged(stream_type, devices);
929 // Publish the result for all requests waiting for device list(s).
930 // Find the requests waiting for this device list, store their labels and
931 // release the iterator before calling device settings. We might get a call
932 // back from device_settings that will need to iterate through devices.
933 std::list<std::string> label_list;
934 for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end();
936 if (it->second->state(stream_type) == MEDIA_REQUEST_STATE_REQUESTED &&
937 Requested(it->second->request, stream_type)) {
938 if (it->second->request.request_type != MEDIA_ENUMERATE_DEVICES)
939 it->second->SetState(stream_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
940 label_list.push_back(it->first);
943 for (std::list<std::string>::iterator it = label_list.begin();
944 it != label_list.end(); ++it) {
945 DeviceRequest* request = requests_[*it];
946 switch (request->request.request_type) {
947 case MEDIA_ENUMERATE_DEVICES:
948 if (need_update_clients && request->requester)
949 request->requester->DevicesEnumerated(*it, devices);
952 if (request->state(request->request.audio_type) ==
953 MEDIA_REQUEST_STATE_REQUESTED ||
954 request->state(request->request.video_type) ==
955 MEDIA_REQUEST_STATE_REQUESTED) {
956 // We are doing enumeration for other type of media, wait until it is
957 // all done before posting the request to UI because UI needs
958 // the device lists to handle the request.
962 // Post the request to UI for permission approval.
963 PostRequestToUI(*it);
968 --active_enumeration_ref_count_[stream_type];
969 DCHECK_GE(active_enumeration_ref_count_[stream_type], 0);
972 void MediaStreamManager::Error(MediaStreamType stream_type,
973 int capture_session_id,
974 MediaStreamProviderError error) {
975 // Find the device for the error call.
976 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
978 << "{stream_type = " << stream_type << "} ,"
979 << "{capture_session_id = " << capture_session_id << "})";
982 for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end();
984 StreamDeviceInfoArray& devices = it->second->devices;
986 // TODO(miu): BUG. It's possible for the audio (or video) device array in
987 // the "requester" to become out-of-sync with the order of devices we have
988 // here. See http://crbug.com/147650
989 int audio_device_idx = -1;
990 int video_device_idx = -1;
991 for (StreamDeviceInfoArray::iterator device_it = devices.begin();
992 device_it != devices.end(); ++device_it) {
993 if (IsAudioMediaType(device_it->device.type)) {
995 } else if (IsVideoMediaType(device_it->device.type)) {
1001 if (device_it->device.type != stream_type ||
1002 device_it->session_id != capture_session_id) {
1005 // We've found the failing device. Find the error case:
1006 // An error should only be reported to the MediaStreamManager if
1007 // the request has not been fulfilled yet.
1008 DCHECK(it->second->state(stream_type) != MEDIA_REQUEST_STATE_DONE);
1009 if (it->second->state(stream_type) != MEDIA_REQUEST_STATE_DONE) {
1010 // Request is not done, devices are not opened in this case.
1011 if (devices.size() <= 1) {
1012 scoped_ptr<DeviceRequest> request(it->second);
1013 // 1. Device not opened and no other devices for this request ->
1014 // signal stream error and remove the request.
1015 if (request->requester)
1016 request->requester->StreamGenerationFailed(it->first);
1020 // 2. Not opened but other devices exists for this request -> remove
1021 // device from list, but don't signal an error.
1022 devices.erase(device_it); // NOTE: This invalidates device_it!
1023 it->second->SetState(stream_type, MEDIA_REQUEST_STATE_ERROR);
1024 DVLOG(1) << "Error("
1025 << ", {capture_session_id = " << capture_session_id << "})";
1028 if (RequestDone(*it->second))
1029 HandleRequestDone(it->first, it->second);
1035 void MediaStreamManager::HandleAccessRequestResponse(
1036 const std::string& label,
1037 const MediaStreamDevices& devices) {
1038 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1039 DVLOG(1) << "HandleAccessRequestResponse("
1040 << ", {label = " << label << "})";
1042 DeviceRequests::iterator request_it = requests_.find(label);
1043 if (request_it == requests_.end()) {
1047 // Handle the case when the request was denied.
1048 if (devices.empty()) {
1049 // Notify the users about the request result.
1050 scoped_ptr<DeviceRequest> request(request_it->second);
1051 if (request->requester)
1052 request->requester->StreamGenerationFailed(label);
1054 if (request->request.request_type == MEDIA_DEVICE_ACCESS &&
1055 !request->callback.is_null()) {
1056 request->callback.Run(MediaStreamDevices(), request->ui_proxy.Pass());
1059 RemoveRequest(request_it);
1063 if (request_it->second->request.request_type == MEDIA_DEVICE_ACCESS) {
1064 scoped_ptr<DeviceRequest> request(request_it->second);
1065 if (!request->callback.is_null())
1066 request->callback.Run(devices, request->ui_proxy.Pass());
1068 // Delete the request since it is done.
1069 RemoveRequest(request_it);
1073 // Process all newly-accepted devices for this request.
1074 DeviceRequest* request = request_it->second;
1075 bool found_audio = false;
1076 bool found_video = false;
1077 for (MediaStreamDevices::const_iterator device_it = devices.begin();
1078 device_it != devices.end(); ++device_it) {
1079 StreamDeviceInfo device_info;
1080 device_info.device = *device_it;
1082 // TODO(justinlin): Nicer way to do this?
1083 // Re-append the device's id since we lost it when posting request to UI.
1084 if (device_info.device.type == content::MEDIA_TAB_VIDEO_CAPTURE ||
1085 device_info.device.type == content::MEDIA_TAB_AUDIO_CAPTURE) {
1086 device_info.device.id = request->request.tab_capture_device_id;
1088 // Initialize the sample_rate and channel_layout here since for audio
1089 // mirroring, we don't go through EnumerateDevices where these are usually
1091 if (device_info.device.type == content::MEDIA_TAB_AUDIO_CAPTURE) {
1092 const media::AudioParameters parameters =
1093 audio_manager_->GetDefaultOutputStreamParameters();
1094 int sample_rate = parameters.sample_rate();
1095 // If we weren't able to get the native sampling rate or the sample_rate
1096 // is outside the valid range for input devices set reasonable defaults.
1097 if (sample_rate <= 0 || sample_rate > 96000)
1098 sample_rate = 44100;
1100 device_info.device.input.sample_rate = sample_rate;
1101 device_info.device.input.channel_layout = media::CHANNEL_LAYOUT_STEREO;
1105 if (device_info.device.type == request->request.audio_type) {
1107 } else if (device_info.device.type == request->request.video_type) {
1111 // If this is request for a new MediaStream, a device is only opened once
1112 // per render view. This is so that the permission to use a device can be
1113 // revoked by a single call to StopStreamDevice regardless of how many
1114 // MediaStreams it is being used in.
1116 if (request->request.request_type == MEDIA_GENERATE_STREAM) {
1117 MediaRequestState state;
1118 if (FindExistingRequestedDeviceInfo(request->requesting_process_id,
1119 request->requesting_view_id,
1120 request->request.request_type,
1124 request->devices.push_back(device_info);
1125 request->SetState(device_info.device.type, state);
1126 DVLOG(1) << "HandleAccessRequestResponse - device already opened "
1127 << ", {label = " << label << "}"
1128 << ", device_id = " << device_it->id << "}";
1132 device_info.session_id =
1133 GetDeviceManager(device_info.device.type)->Open(device_info);
1134 request->devices.push_back(device_info);
1135 request->SetState(device_info.device.type, MEDIA_REQUEST_STATE_OPENING);
1136 DVLOG(1) << "HandleAccessRequestResponse - opening device "
1137 << ", {label = " << label << "}"
1138 << ", {device_id = " << device_it->id << "}"
1139 << ", {session_id = " << device_info.session_id << "}";
1142 // Check whether we've received all stream types requested.
1143 if (!found_audio && IsAudioMediaType(request->request.audio_type)) {
1144 request->SetState(request->request.audio_type, MEDIA_REQUEST_STATE_ERROR);
1145 DVLOG(1) << "Set no audio found label " << label;
1148 if (!found_video && IsVideoMediaType(request->request.video_type))
1149 request->SetState(request->request.video_type, MEDIA_REQUEST_STATE_ERROR);
1151 if (RequestDone(*request))
1152 HandleRequestDone(label, request);
1155 void MediaStreamManager::StopMediaStreamFromBrowser(const std::string& label) {
1156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1158 DeviceRequests::iterator it = requests_.find(label);
1159 if (it == requests_.end())
1162 // Notify renderers that the stream has been stopped.
1163 if (it->second->requester)
1164 it->second->requester->StopGeneratedStream(
1165 it->second->request.render_view_id,
1168 CancelRequest(label);
1171 void MediaStreamManager::UseFakeDevice() {
1172 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1173 video_capture_manager()->UseFakeDevice();
1174 audio_input_device_manager()->UseFakeDevice();
1177 void MediaStreamManager::UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy> fake_ui) {
1178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1179 use_fake_ui_ = true;
1180 fake_ui_ = fake_ui.Pass();
1183 void MediaStreamManager::WillDestroyCurrentMessageLoop() {
1184 DCHECK_EQ(base::MessageLoop::current(), io_loop_);
1185 DCHECK(requests_.empty());
1186 if (device_thread_) {
1189 video_capture_manager_->Unregister();
1190 audio_input_device_manager_->Unregister();
1191 device_thread_.reset();
1194 audio_input_device_manager_ = NULL;
1195 video_capture_manager_ = NULL;
1198 void MediaStreamManager::NotifyDevicesChanged(
1199 MediaStreamType stream_type,
1200 const StreamDeviceInfoArray& devices) {
1201 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1202 MediaObserver* media_observer =
1203 GetContentClient()->browser()->GetMediaObserver();
1204 if (media_observer == NULL)
1207 // Map the devices to MediaStreamDevices.
1208 MediaStreamDevices new_devices;
1209 for (StreamDeviceInfoArray::const_iterator it = devices.begin();
1210 it != devices.end(); ++it) {
1211 new_devices.push_back(it->device);
1214 if (IsAudioMediaType(stream_type)) {
1215 media_observer->OnAudioCaptureDevicesChanged(new_devices);
1216 } else if (IsVideoMediaType(stream_type)) {
1217 media_observer->OnVideoCaptureDevicesChanged(new_devices);
1223 bool MediaStreamManager::RequestDone(const DeviceRequest& request) const {
1224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1226 const bool requested_audio = IsAudioMediaType(request.request.audio_type);
1227 const bool requested_video = IsVideoMediaType(request.request.video_type);
1229 const bool audio_done =
1231 request.state(request.request.audio_type) ==
1232 MEDIA_REQUEST_STATE_DONE ||
1233 request.state(request.request.audio_type) ==
1234 MEDIA_REQUEST_STATE_ERROR;
1238 const bool video_done =
1240 request.state(request.request.video_type) ==
1241 MEDIA_REQUEST_STATE_DONE ||
1242 request.state(request.request.video_type) ==
1243 MEDIA_REQUEST_STATE_ERROR;
1250 MediaStreamProvider* MediaStreamManager::GetDeviceManager(
1251 MediaStreamType stream_type) {
1252 if (IsVideoMediaType(stream_type)) {
1253 return video_capture_manager();
1254 } else if (IsAudioMediaType(stream_type)) {
1255 return audio_input_device_manager();
1261 void MediaStreamManager::OnDevicesChanged(
1262 base::SystemMonitor::DeviceType device_type) {
1263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1265 // NOTE: This method is only called in response to physical audio/video device
1266 // changes (from the operating system).
1268 MediaStreamType stream_type;
1269 if (device_type == base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE) {
1270 stream_type = MEDIA_DEVICE_AUDIO_CAPTURE;
1271 } else if (device_type == base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE) {
1272 stream_type = MEDIA_DEVICE_VIDEO_CAPTURE;
1274 return; // Uninteresting device change.
1277 // Always do enumeration even though some enumeration is in progress,
1278 // because those enumeration commands could be sent before these devices
1280 ++active_enumeration_ref_count_[stream_type];
1281 GetDeviceManager(stream_type)->EnumerateDevices(stream_type);
1284 } // namespace content