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/strings/stringprintf.h"
17 #include "base/threading/thread.h"
18 #include "content/browser/browser_main_loop.h"
19 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
20 #include "content/browser/renderer_host/media/device_request_message_filter.h"
21 #include "content/browser/renderer_host/media/media_stream_requester.h"
22 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
23 #include "content/browser/renderer_host/media/video_capture_manager.h"
24 #include "content/browser/renderer_host/media/web_contents_capture_util.h"
25 #include "content/browser/renderer_host/render_process_host_impl.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/content_browser_client.h"
28 #include "content/public/browser/media_device_id.h"
29 #include "content/public/browser/media_observer.h"
30 #include "content/public/browser/media_request_state.h"
31 #include "content/public/browser/render_process_host.h"
32 #include "content/public/common/content_switches.h"
33 #include "content/public/common/media_stream_request.h"
34 #include "media/audio/audio_manager_base.h"
35 #include "media/audio/audio_parameters.h"
36 #include "media/base/channel_layout.h"
40 #include "base/win/scoped_com_initializer.h"
46 // Creates a random label used to identify requests.
47 std::string RandomLabel() {
48 // An earlier PeerConnection spec,
49 // http://dev.w3.org/2011/webrtc/editor/webrtc.html, specified the
50 // MediaStream::label alphabet as containing 36 characters from
51 // range: U+0021, U+0023 to U+0027, U+002A to U+002B, U+002D to U+002E,
52 // U+0030 to U+0039, U+0041 to U+005A, U+005E to U+007E.
53 // Here we use a safe subset.
54 static const char kAlphabet[] = "0123456789"
55 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
57 std::string label(36, ' ');
58 for (size_t i = 0; i < label.size(); ++i) {
59 int random_char = base::RandGenerator(sizeof(kAlphabet) - 1);
60 label[i] = kAlphabet[random_char];
65 void ParseStreamType(const StreamOptions& options,
66 MediaStreamType* audio_type,
67 MediaStreamType* video_type) {
68 *audio_type = MEDIA_NO_SERVICE;
69 *video_type = MEDIA_NO_SERVICE;
70 if (options.audio_requested) {
71 std::string audio_stream_source;
72 bool mandatory = false;
73 if (options.GetFirstAudioConstraintByName(kMediaStreamSource,
77 // This is tab or screen capture.
78 if (audio_stream_source == kMediaStreamSourceTab) {
79 *audio_type = content::MEDIA_TAB_AUDIO_CAPTURE;
80 } else if (audio_stream_source == kMediaStreamSourceSystem) {
81 *audio_type = content::MEDIA_LOOPBACK_AUDIO_CAPTURE;
84 // This is normal audio device capture.
85 *audio_type = content::MEDIA_DEVICE_AUDIO_CAPTURE;
88 if (options.video_requested) {
89 std::string video_stream_source;
90 bool mandatory = false;
91 if (options.GetFirstVideoConstraintByName(kMediaStreamSource,
95 // This is tab or screen capture.
96 if (video_stream_source == kMediaStreamSourceTab) {
97 *video_type = content::MEDIA_TAB_VIDEO_CAPTURE;
98 } else if (video_stream_source == kMediaStreamSourceScreen) {
99 *video_type = content::MEDIA_DESKTOP_VIDEO_CAPTURE;
100 } else if (video_stream_source == kMediaStreamSourceDesktop) {
101 *video_type = content::MEDIA_DESKTOP_VIDEO_CAPTURE;
104 // This is normal video device capture.
105 *video_type = content::MEDIA_DEVICE_VIDEO_CAPTURE;
110 // Private helper method for SendMessageToNativeLog() that obtains the global
111 // MediaStreamManager instance on the UI thread before sending |message| to the
112 // webrtcLoggingPrivate API.
113 void DoAddLogMessage(const std::string& message) {
114 // Must be on the UI thread to access BrowserMainLoop.
115 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
116 // May be null in tests.
117 // TODO(vrk): Handle this more elegantly by having native log messages become
118 // no-ops until MediaStreamManager is aware that a renderer process has
119 // started logging. crbug.com/333894
120 if (content::BrowserMainLoop::GetInstance()) {
121 BrowserThread::PostTask(
124 base::Bind(&MediaStreamManager::AddLogMessageOnIOThread,
125 base::Unretained(content::BrowserMainLoop::GetInstance()
126 ->media_stream_manager()),
131 // Private helper method to generate a string for the log message that lists the
132 // human readable names of |devices|.
133 std::string GetLogMessageString(MediaStreamType stream_type,
134 const StreamDeviceInfoArray& devices) {
135 std::string output_string =
136 base::StringPrintf("Getting devices for stream type %d:\n", stream_type);
137 if (devices.empty()) {
138 output_string += "No devices found.";
140 for (StreamDeviceInfoArray::const_iterator it = devices.begin();
141 it != devices.end(); ++it) {
142 output_string += " " + it->device.name + "\n";
145 return output_string;
148 // Needed for MediaStreamManager::GenerateStream below.
149 std::string ReturnEmptySalt() {
150 return std::string();
156 // MediaStreamManager::DeviceRequest represents a request to either enumerate
157 // available devices or open one or more devices.
158 // TODO(perkj): MediaStreamManager still needs refactoring. I propose we create
159 // several subclasses of DeviceRequest and move some of the responsibility of
160 // the MediaStreamManager to the subclasses to get rid of the way too many if
161 // statements in MediaStreamManager.
162 class MediaStreamManager::DeviceRequest {
164 DeviceRequest(MediaStreamRequester* requester,
165 int requesting_process_id,
166 int requesting_view_id,
168 const GURL& security_origin,
169 MediaStreamRequestType request_type,
170 const StreamOptions& options,
171 const ResourceContext::SaltCallback& salt_callback)
172 : requester(requester),
173 requesting_process_id(requesting_process_id),
174 requesting_view_id(requesting_view_id),
175 page_request_id(page_request_id),
176 security_origin(security_origin),
177 request_type(request_type),
179 salt_callback(salt_callback),
180 state_(NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_NOT_REQUESTED),
181 audio_type_(MEDIA_NO_SERVICE),
182 video_type_(MEDIA_NO_SERVICE) {
187 void SetAudioType(MediaStreamType audio_type) {
188 DCHECK(IsAudioMediaType(audio_type) || audio_type == MEDIA_NO_SERVICE);
189 audio_type_ = audio_type;
192 MediaStreamType audio_type() const { return audio_type_; }
194 void SetVideoType(MediaStreamType video_type) {
195 DCHECK(IsVideoMediaType(video_type) || video_type == MEDIA_NO_SERVICE);
196 video_type_ = video_type;
199 MediaStreamType video_type() const { return video_type_; }
201 // Creates a MediaStreamRequest object that is used by this request when UI
202 // is asked for permission and device selection.
203 void CreateUIRequest(const std::string& requested_audio_device_id,
204 const std::string& requested_video_device_id) {
205 DCHECK(!ui_request_);
206 ui_request_.reset(new MediaStreamRequest(requesting_process_id,
211 requested_audio_device_id,
212 requested_video_device_id,
217 // Creates a tab capture specific MediaStreamRequest object that is used by
218 // this request when UI is asked for permission and device selection.
219 void CreateTabCatureUIRequest(int target_render_process_id,
220 int target_render_view_id,
221 const std::string& tab_capture_id) {
222 DCHECK(!ui_request_);
223 ui_request_.reset(new MediaStreamRequest(target_render_process_id,
224 target_render_view_id,
232 ui_request_->tab_capture_device_id = tab_capture_id;
235 const MediaStreamRequest* UIRequest() const { return ui_request_.get(); }
237 // Update the request state and notify observers.
238 void SetState(MediaStreamType stream_type, MediaRequestState new_state) {
239 if (stream_type == NUM_MEDIA_TYPES) {
240 for (int i = MEDIA_NO_SERVICE + 1; i < NUM_MEDIA_TYPES; ++i) {
241 const MediaStreamType stream_type = static_cast<MediaStreamType>(i);
242 state_[stream_type] = new_state;
245 state_[stream_type] = new_state;
248 MediaObserver* media_observer =
249 GetContentClient()->browser()->GetMediaObserver();
253 // If |ui_request_| doesn't exist, it means that the request has not yet
254 // been setup fully and there are no valid observers.
258 // If we appended a device_id scheme, we want to remove it when notifying
259 // observers which may be in different modules since this scheme is only
260 // used internally within the content module.
261 std::string device_id =
262 WebContentsCaptureUtil::StripWebContentsDeviceScheme(
263 ui_request_->tab_capture_device_id);
265 media_observer->OnMediaRequestStateChanged(
266 ui_request_->render_process_id, ui_request_->render_view_id,
267 ui_request_->page_request_id, ui_request_->security_origin,
268 MediaStreamDevice(stream_type, device_id, device_id), new_state);
271 MediaRequestState state(MediaStreamType stream_type) const {
272 return state_[stream_type];
275 MediaStreamRequester* const requester; // Can be NULL.
278 // The render process id that requested this stream to be generated and that
279 // will receive a handle to the MediaStream. This may be different from
280 // MediaStreamRequest::render_process_id which in the tab capture case
281 // specifies the target renderer from which audio and video is captured.
282 const int requesting_process_id;
284 // The render view id that requested this stream to be generated and that
285 // will receive a handle to the MediaStream. This may be different from
286 // MediaStreamRequest::render_view_id which in the tab capture case
287 // specifies the target renderer from which audio and video is captured.
288 const int requesting_view_id;
290 // An ID the render view provided to identify this request.
291 const int page_request_id;
293 const GURL security_origin;
295 const MediaStreamRequestType request_type;
297 const StreamOptions options;
299 ResourceContext::SaltCallback salt_callback;
301 StreamDeviceInfoArray devices;
303 // Callback to the requester which audio/video devices have been selected.
304 // It can be null if the requester has no interest to know the result.
305 // Currently it is only used by |DEVICE_ACCESS| type.
306 MediaStreamManager::MediaRequestResponseCallback callback;
308 scoped_ptr<MediaStreamUIProxy> ui_proxy;
311 std::vector<MediaRequestState> state_;
312 scoped_ptr<MediaStreamRequest> ui_request_;
313 MediaStreamType audio_type_;
314 MediaStreamType video_type_;
317 MediaStreamManager::EnumerationCache::EnumerationCache()
321 MediaStreamManager::EnumerationCache::~EnumerationCache() {
324 MediaStreamManager::MediaStreamManager()
325 : audio_manager_(NULL),
326 monitoring_started_(false),
328 use_fake_ui_(false) {}
330 MediaStreamManager::MediaStreamManager(media::AudioManager* audio_manager)
331 : audio_manager_(audio_manager),
332 monitoring_started_(false),
334 use_fake_ui_(false) {
335 DCHECK(audio_manager_);
336 memset(active_enumeration_ref_count_, 0,
337 sizeof(active_enumeration_ref_count_));
339 // Some unit tests create the MSM in the IO thread and assumes the
340 // initialization is done synchronously.
341 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
342 InitializeDeviceManagersOnIOThread();
344 BrowserThread::PostTask(
345 BrowserThread::IO, FROM_HERE,
346 base::Bind(&MediaStreamManager::InitializeDeviceManagersOnIOThread,
347 base::Unretained(this)));
351 MediaStreamManager::~MediaStreamManager() {
352 DVLOG(1) << "~MediaStreamManager";
353 DCHECK(requests_.empty());
354 DCHECK(!device_task_runner_);
357 VideoCaptureManager* MediaStreamManager::video_capture_manager() {
358 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
359 DCHECK(video_capture_manager_.get());
360 return video_capture_manager_.get();
363 AudioInputDeviceManager* MediaStreamManager::audio_input_device_manager() {
364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
365 DCHECK(audio_input_device_manager_.get());
366 return audio_input_device_manager_.get();
369 std::string MediaStreamManager::MakeMediaAccessRequest(
370 int render_process_id,
373 const StreamOptions& options,
374 const GURL& security_origin,
375 const MediaRequestResponseCallback& callback) {
376 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
378 // TODO(perkj): The argument list with NULL parameters to DeviceRequest
379 // suggests that this is the wrong design. Can this be refactored?
380 DeviceRequest* request = new DeviceRequest(NULL,
387 base::Bind(&ReturnEmptySalt));
389 const std::string& label = AddRequest(request);
391 request->callback = callback;
392 // Post a task and handle the request asynchronously. The reason is that the
393 // requester won't have a label for the request until this function returns
394 // and thus can not handle a response. Using base::Unretained is safe since
395 // MediaStreamManager is deleted on the UI thread, after the IO thread has
397 BrowserThread::PostTask(
398 BrowserThread::IO, FROM_HERE,
399 base::Bind(&MediaStreamManager::SetupRequest,
400 base::Unretained(this), label));
404 void MediaStreamManager::GenerateStream(MediaStreamRequester* requester,
405 int render_process_id,
407 const ResourceContext::SaltCallback& sc,
409 const StreamOptions& options,
410 const GURL& security_origin) {
411 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
412 DVLOG(1) << "GenerateStream()";
413 if (CommandLine::ForCurrentProcess()->HasSwitch(
414 switches::kUseFakeUIForMediaStream)) {
415 UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy>());
418 DeviceRequest* request = new DeviceRequest(requester,
423 MEDIA_GENERATE_STREAM,
427 const std::string& label = AddRequest(request);
429 // Post a task and handle the request asynchronously. The reason is that the
430 // requester won't have a label for the request until this function returns
431 // and thus can not handle a response. Using base::Unretained is safe since
432 // MediaStreamManager is deleted on the UI thread, after the IO thread has
434 BrowserThread::PostTask(
435 BrowserThread::IO, FROM_HERE,
436 base::Bind(&MediaStreamManager::SetupRequest,
437 base::Unretained(this), label));
440 void MediaStreamManager::CancelRequest(int render_process_id,
442 int page_request_id) {
443 for (DeviceRequests::const_iterator request_it = requests_.begin();
444 request_it != requests_.end(); ++request_it) {
445 const DeviceRequest* request = request_it->second;
446 if (request->requesting_process_id == render_process_id &&
447 request->requesting_view_id == render_view_id &&
448 request->page_request_id == page_request_id) {
449 CancelRequest(request_it->first);
456 void MediaStreamManager::CancelRequest(const std::string& label) {
457 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
458 DVLOG(1) << "CancelRequest({label = " << label << "})";
459 DeviceRequest* request = FindRequest(label);
461 // The request does not exist.
462 LOG(ERROR) << "The request with label = " << label << " does not exist.";
466 if (request->request_type == MEDIA_ENUMERATE_DEVICES) {
467 // It isn't an ideal use of "CancelRequest" to make it a requirement
468 // for enumeration requests to be deleted via "CancelRequest" _after_
469 // the request has been successfully fulfilled.
470 // See note in FinalizeEnumerateDevices for a recommendation on how
471 // we should refactor this.
472 DeleteRequest(label);
476 // This is a request for opening one or more devices.
477 for (StreamDeviceInfoArray::iterator device_it = request->devices.begin();
478 device_it != request->devices.end(); ++device_it) {
479 MediaRequestState state = request->state(device_it->device.type);
480 // If we have not yet requested the device to be opened - just ignore it.
481 if (state != MEDIA_REQUEST_STATE_OPENING &&
482 state != MEDIA_REQUEST_STATE_DONE) {
485 // Stop the opening/opened devices of the requests.
486 CloseDevice(device_it->device.type, device_it->session_id);
489 // Cancel the request if still pending at UI side.
490 request->SetState(NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_CLOSING);
491 DeleteRequest(label);
494 void MediaStreamManager::CancelAllRequests(int render_process_id) {
495 DeviceRequests::iterator request_it = requests_.begin();
496 while (request_it != requests_.end()) {
497 if (request_it->second->requesting_process_id != render_process_id) {
502 std::string label = request_it->first;
504 CancelRequest(label);
508 void MediaStreamManager::StopStreamDevice(int render_process_id,
510 const std::string& device_id) {
511 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
512 DVLOG(1) << "StopStreamDevice({render_view_id = " << render_view_id << "} "
513 << ", {device_id = " << device_id << "})";
514 // Find the first request for this |render_process_id| and |render_view_id|
515 // of type MEDIA_GENERATE_STREAM that has requested to use |device_id| and
517 for (DeviceRequests::iterator request_it = requests_.begin();
518 request_it != requests_.end(); ++request_it) {
519 DeviceRequest* request = request_it->second;
520 if (request->requesting_process_id != render_process_id ||
521 request->requesting_view_id != render_view_id ||
522 request->request_type != MEDIA_GENERATE_STREAM) {
526 StreamDeviceInfoArray& devices = request->devices;
527 for (StreamDeviceInfoArray::iterator device_it = devices.begin();
528 device_it != devices.end(); ++device_it) {
529 if (device_it->device.id == device_id) {
530 StopDevice(device_it->device.type, device_it->session_id);
537 void MediaStreamManager::StopDevice(MediaStreamType type, int session_id) {
538 DVLOG(1) << "StopDevice"
539 << "{type = " << type << "}"
540 << "{session_id = " << session_id << "}";
541 DeviceRequests::iterator request_it = requests_.begin();
542 while (request_it != requests_.end()) {
543 DeviceRequest* request = request_it->second;
544 StreamDeviceInfoArray* devices = &request->devices;
545 if (devices->empty()) {
546 // There is no device in use yet by this request.
550 StreamDeviceInfoArray::iterator device_it = devices->begin();
551 while (device_it != devices->end()) {
552 if (device_it->device.type != type ||
553 device_it->session_id != session_id) {
558 if (request->state(type) == MEDIA_REQUEST_STATE_DONE)
559 CloseDevice(type, session_id);
560 device_it = devices->erase(device_it);
563 // If this request doesn't have any active devices after a device
564 // has been stopped above, remove the request. Note that the request is
565 // only deleted if a device as been removed from |devices|.
566 if (devices->empty()) {
567 std::string label = request_it->first;
569 DeleteRequest(label);
576 void MediaStreamManager::CloseDevice(MediaStreamType type, int session_id) {
577 DVLOG(1) << "CloseDevice("
578 << "{type = " << type << "} "
579 << "{session_id = " << session_id << "})";
580 GetDeviceManager(type)->Close(session_id);
582 for (DeviceRequests::iterator request_it = requests_.begin();
583 request_it != requests_.end() ; ++request_it) {
584 StreamDeviceInfoArray* devices = &request_it->second->devices;
585 for (StreamDeviceInfoArray::iterator device_it = devices->begin();
586 device_it != devices->end(); ++device_it) {
587 if (device_it->session_id == session_id &&
588 device_it->device.type == type) {
589 // Notify observers that this device is being closed.
590 // Note that only one device per type can be opened.
591 request_it->second->SetState(type, MEDIA_REQUEST_STATE_CLOSING);
597 std::string MediaStreamManager::EnumerateDevices(
598 MediaStreamRequester* requester,
599 int render_process_id,
601 const ResourceContext::SaltCallback& sc,
603 MediaStreamType type,
604 const GURL& security_origin) {
605 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
607 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
608 type == MEDIA_DEVICE_VIDEO_CAPTURE);
610 DeviceRequest* request = new DeviceRequest(requester,
615 MEDIA_ENUMERATE_DEVICES,
618 if (IsAudioMediaType(type))
619 request->SetAudioType(type);
620 else if (IsVideoMediaType(type))
621 request->SetVideoType(type);
623 const std::string& label = AddRequest(request);
624 // Post a task and handle the request asynchronously. The reason is that the
625 // requester won't have a label for the request until this function returns
626 // and thus can not handle a response. Using base::Unretained is safe since
627 // MediaStreamManager is deleted on the UI thread, after the IO thread has
629 BrowserThread::PostTask(
630 BrowserThread::IO, FROM_HERE,
631 base::Bind(&MediaStreamManager::DoEnumerateDevices,
632 base::Unretained(this), label));
636 void MediaStreamManager::DoEnumerateDevices(const std::string& label) {
637 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
638 DeviceRequest* request = FindRequest(label);
640 return; // This can happen if the request has been canceled.
642 MediaStreamType type;
643 EnumerationCache* cache;
644 if (request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE) {
645 DCHECK_EQ(MEDIA_NO_SERVICE, request->video_type());
646 type = MEDIA_DEVICE_AUDIO_CAPTURE;
647 cache = &audio_enumeration_cache_;
649 DCHECK_EQ(MEDIA_DEVICE_VIDEO_CAPTURE, request->video_type());
650 type = MEDIA_DEVICE_VIDEO_CAPTURE;
651 cache = &video_enumeration_cache_;
654 if (!EnumerationRequired(cache, type)) {
655 // Cached device list of this type exists. Just send it out.
656 request->SetState(type, MEDIA_REQUEST_STATE_REQUESTED);
657 request->devices = cache->devices;
658 FinalizeEnumerateDevices(label, request);
660 StartEnumeration(request);
662 DVLOG(1) << "Enumerate Devices ({label = " << label << "})";
665 void MediaStreamManager::OpenDevice(MediaStreamRequester* requester,
666 int render_process_id,
668 const ResourceContext::SaltCallback& sc,
670 const std::string& device_id,
671 MediaStreamType type,
672 const GURL& security_origin) {
673 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
674 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
675 type == MEDIA_DEVICE_VIDEO_CAPTURE);
676 DVLOG(1) << "OpenDevice ({page_request_id = " << page_request_id << "})";
677 StreamOptions options;
678 if (IsAudioMediaType(type)) {
679 options.audio_requested = true;
680 options.mandatory_audio.push_back(
681 StreamOptions::Constraint(kMediaStreamSourceInfoId, device_id));
682 } else if (IsVideoMediaType(type)) {
683 options.video_requested = true;
684 options.mandatory_video.push_back(
685 StreamOptions::Constraint(kMediaStreamSourceInfoId, device_id));
689 DeviceRequest* request = new DeviceRequest(requester,
698 const std::string& label = AddRequest(request);
699 // Post a task and handle the request asynchronously. The reason is that the
700 // requester won't have a label for the request until this function returns
701 // and thus can not handle a response. Using base::Unretained is safe since
702 // MediaStreamManager is deleted on the UI thread, after the IO thread has
704 BrowserThread::PostTask(
705 BrowserThread::IO, FROM_HERE,
706 base::Bind(&MediaStreamManager::SetupRequest,
707 base::Unretained(this), label));
710 void MediaStreamManager::EnsureDeviceMonitorStarted() {
711 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
715 void MediaStreamManager::StopRemovedDevices(
716 const StreamDeviceInfoArray& old_devices,
717 const StreamDeviceInfoArray& new_devices) {
718 DVLOG(1) << "StopRemovedDevices("
719 << "{#old_devices = " << old_devices.size() << "} "
720 << "{#new_devices = " << new_devices.size() << "})";
721 for (StreamDeviceInfoArray::const_iterator old_dev_it = old_devices.begin();
722 old_dev_it != old_devices.end(); ++old_dev_it) {
723 bool device_found = false;
724 StreamDeviceInfoArray::const_iterator new_dev_it = new_devices.begin();
725 for (; new_dev_it != new_devices.end(); ++new_dev_it) {
726 if (old_dev_it->device.id == new_dev_it->device.id) {
733 // A device has been removed. We need to check if it is used by a
734 // MediaStream and in that case cleanup and notify the render process.
735 StopRemovedDevice(old_dev_it->device);
740 void MediaStreamManager::StopRemovedDevice(const MediaStreamDevice& device) {
741 std::vector<int> session_ids;
742 for (DeviceRequests::const_iterator it = requests_.begin();
743 it != requests_.end() ; ++it) {
744 const DeviceRequest* request = it->second;
745 for (StreamDeviceInfoArray::const_iterator device_it =
746 request->devices.begin();
747 device_it != request->devices.end(); ++device_it) {
748 std::string source_id = content::GetHMACForMediaDeviceID(
749 request->salt_callback,
750 request->security_origin,
752 if (device_it->device.id == source_id &&
753 device_it->device.type == device.type) {
754 session_ids.push_back(device_it->session_id);
755 if (it->second->requester) {
756 it->second->requester->DeviceStopped(
757 it->second->requesting_view_id,
764 for (std::vector<int>::const_iterator it = session_ids.begin();
765 it != session_ids.end(); ++it) {
766 StopDevice(device.type, *it);
770 void MediaStreamManager::StartMonitoring() {
771 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
772 if (monitoring_started_)
775 if (!base::SystemMonitor::Get())
778 monitoring_started_ = true;
779 base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
781 // Enumerate both the audio and video devices to cache the device lists
782 // and send them to media observer.
783 ++active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_CAPTURE];
784 audio_input_device_manager_->EnumerateDevices(MEDIA_DEVICE_AUDIO_CAPTURE);
785 ++active_enumeration_ref_count_[MEDIA_DEVICE_VIDEO_CAPTURE];
786 video_capture_manager_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
789 void MediaStreamManager::StopMonitoring() {
790 DCHECK_EQ(base::MessageLoop::current(), io_loop_);
791 if (monitoring_started_) {
792 base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
793 monitoring_started_ = false;
794 ClearEnumerationCache(&audio_enumeration_cache_);
795 ClearEnumerationCache(&video_enumeration_cache_);
799 bool MediaStreamManager::GetRequestedDeviceCaptureId(
800 const DeviceRequest* request,
801 MediaStreamType type,
802 std::string* device_id) const {
803 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
804 type == MEDIA_DEVICE_VIDEO_CAPTURE);
805 const StreamOptions::Constraints* mandatory =
806 (type == MEDIA_DEVICE_AUDIO_CAPTURE) ?
807 &request->options.mandatory_audio : &request->options.mandatory_video;
808 const StreamOptions::Constraints* optional =
809 (type == MEDIA_DEVICE_AUDIO_CAPTURE) ?
810 &request->options.optional_audio : &request->options.optional_video;
812 std::vector<std::string> source_ids;
813 StreamOptions::GetConstraintsByName(*mandatory,
814 kMediaStreamSourceInfoId, &source_ids);
815 if (source_ids.size() > 1) {
816 LOG(ERROR) << "Only one mandatory " << kMediaStreamSourceInfoId
820 // If a specific device has been requested we need to find the real device
822 if (source_ids.size() == 1 &&
823 !TranslateSourceIdToDeviceId(type,
824 request->salt_callback,
825 request->security_origin,
826 source_ids[0], device_id)) {
827 LOG(WARNING) << "Invalid mandatory " << kMediaStreamSourceInfoId
828 << " = " << source_ids[0] << ".";
831 // Check for optional audio sourceIDs.
832 if (device_id->empty()) {
833 StreamOptions::GetConstraintsByName(*optional,
834 kMediaStreamSourceInfoId,
836 // Find the first sourceID that translates to device. Note that only one
837 // device per type can call to GenerateStream is ever opened.
838 for (std::vector<std::string>::const_iterator it = source_ids.begin();
839 it != source_ids.end(); ++it) {
840 if (TranslateSourceIdToDeviceId(type,
841 request->salt_callback,
842 request->security_origin,
852 void MediaStreamManager::TranslateDeviceIdToSourceId(
853 DeviceRequest* request,
854 MediaStreamDevice* device) {
855 if (request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE ||
856 request->video_type() == MEDIA_DEVICE_VIDEO_CAPTURE) {
857 device->id = content::GetHMACForMediaDeviceID(
858 request->salt_callback,
859 request->security_origin,
864 bool MediaStreamManager::TranslateSourceIdToDeviceId(
865 MediaStreamType stream_type,
866 const ResourceContext::SaltCallback& sc,
867 const GURL& security_origin,
868 const std::string& source_id,
869 std::string* device_id) const {
870 DCHECK(stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ||
871 stream_type == MEDIA_DEVICE_VIDEO_CAPTURE);
872 // The source_id can be empty if the constraint is set but empty.
873 if (source_id.empty())
876 const EnumerationCache* cache =
877 stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ?
878 &audio_enumeration_cache_ : &video_enumeration_cache_;
880 // If device monitoring hasn't started, the |device_guid| is not valid.
884 for (StreamDeviceInfoArray::const_iterator it = cache->devices.begin();
885 it != cache->devices.end();
887 if (content::DoesMediaDeviceIDMatchHMAC(sc, security_origin, source_id,
889 *device_id = it->device.id;
896 void MediaStreamManager::ClearEnumerationCache(EnumerationCache* cache) {
897 DCHECK_EQ(base::MessageLoop::current(), io_loop_);
898 cache->valid = false;
901 bool MediaStreamManager::EnumerationRequired(EnumerationCache* cache,
902 MediaStreamType stream_type) {
903 DCHECK_EQ(base::MessageLoop::current(), io_loop_);
904 if (stream_type == MEDIA_NO_SERVICE)
907 DCHECK(stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ||
908 stream_type == MEDIA_DEVICE_VIDEO_CAPTURE);
910 #if defined(OS_ANDROID)
911 // There's no SystemMonitor on Android that notifies us when devices are
912 // added or removed, so we need to populate the cache on every request.
913 // Fortunately, there is an already up-to-date cache in the browser side
914 // audio manager that we can rely on, so the performance impact of
915 // invalidating the cache like this, is minimal.
916 if (stream_type == MEDIA_DEVICE_AUDIO_CAPTURE) {
917 // Make sure the cache is marked as invalid so that FinalizeEnumerateDevices
918 // will be called at the end of the enumeration.
919 ClearEnumerationCache(cache);
922 // If the cache isn't valid, we need to start a full enumeration.
923 return !cache->valid;
926 void MediaStreamManager::StartEnumeration(DeviceRequest* request) {
927 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
929 // Start monitoring the devices when doing the first enumeration.
932 // Start enumeration for devices of all requested device types.
933 const MediaStreamType streams[] = { request->audio_type(),
934 request->video_type() };
935 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(streams); ++i) {
936 if (streams[i] == MEDIA_NO_SERVICE)
938 request->SetState(streams[i], MEDIA_REQUEST_STATE_REQUESTED);
939 DCHECK_GE(active_enumeration_ref_count_[streams[i]], 0);
940 if (active_enumeration_ref_count_[streams[i]] == 0) {
941 ++active_enumeration_ref_count_[streams[i]];
942 GetDeviceManager(streams[i])->EnumerateDevices(streams[i]);
947 std::string MediaStreamManager::AddRequest(DeviceRequest* request) {
948 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
950 // Create a label for this request and verify it is unique.
951 std::string unique_label;
953 unique_label = RandomLabel();
954 } while (requests_.find(unique_label) != requests_.end());
956 requests_.insert(std::make_pair(unique_label, request));
961 MediaStreamManager::DeviceRequest*
962 MediaStreamManager::FindRequest(const std::string& label) const {
963 DeviceRequests::const_iterator request_it = requests_.find(label);
964 return request_it == requests_.end() ? NULL : request_it->second;
967 void MediaStreamManager::DeleteRequest(const std::string& label) {
968 DVLOG(1) << "DeleteRequest({label= " << label << "})";
969 DeviceRequests::iterator it = requests_.find(label);
970 scoped_ptr<DeviceRequest> request(it->second);
974 void MediaStreamManager::PostRequestToUI(const std::string& label,
975 DeviceRequest* request) {
976 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
977 DCHECK(request->UIRequest());
978 DVLOG(1) << "PostRequestToUI({label= " << label << "})";
980 const MediaStreamType audio_type = request->audio_type();
981 const MediaStreamType video_type = request->video_type();
983 // Post the request to UI and set the state.
984 if (IsAudioMediaType(audio_type))
985 request->SetState(audio_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
986 if (IsVideoMediaType(video_type))
987 request->SetState(video_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
991 fake_ui_.reset(new FakeMediaStreamUIProxy());
993 MediaStreamDevices devices;
994 if (audio_enumeration_cache_.valid) {
995 for (StreamDeviceInfoArray::const_iterator it =
996 audio_enumeration_cache_.devices.begin();
997 it != audio_enumeration_cache_.devices.end(); ++it) {
998 devices.push_back(it->device);
1001 if (video_enumeration_cache_.valid) {
1002 for (StreamDeviceInfoArray::const_iterator it =
1003 video_enumeration_cache_.devices.begin();
1004 it != video_enumeration_cache_.devices.end(); ++it) {
1005 devices.push_back(it->device);
1009 fake_ui_->SetAvailableDevices(devices);
1011 request->ui_proxy = fake_ui_.Pass();
1013 request->ui_proxy = MediaStreamUIProxy::Create();
1016 request->ui_proxy->RequestAccess(
1017 *request->UIRequest(),
1018 base::Bind(&MediaStreamManager::HandleAccessRequestResponse,
1019 base::Unretained(this), label));
1022 void MediaStreamManager::SetupRequest(const std::string& label) {
1023 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1024 DeviceRequest* request = FindRequest(label);
1026 DVLOG(1) << "SetupRequest label " << label << " doesn't exist!!";
1027 return; // This can happen if the request has been canceled.
1030 if (!request->security_origin.is_valid()) {
1031 LOG(ERROR) << "Invalid security origin. "
1032 << request->security_origin;
1033 FinalizeRequestFailed(label, request);
1037 MediaStreamType audio_type = MEDIA_NO_SERVICE;
1038 MediaStreamType video_type = MEDIA_NO_SERVICE;
1039 ParseStreamType(request->options, &audio_type, &video_type);
1040 request->SetAudioType(audio_type);
1041 request->SetVideoType(video_type);
1043 bool is_web_contents_capture =
1044 audio_type == MEDIA_TAB_AUDIO_CAPTURE ||
1045 video_type == MEDIA_TAB_VIDEO_CAPTURE;
1046 if (is_web_contents_capture && !SetupTabCaptureRequest(request)) {
1047 FinalizeRequestFailed(label, request);
1051 bool is_screen_capture =
1052 video_type == MEDIA_DESKTOP_VIDEO_CAPTURE;
1053 if (is_screen_capture && !SetupScreenCaptureRequest(request)) {
1054 FinalizeRequestFailed(label, request);
1058 if (!is_web_contents_capture && !is_screen_capture) {
1059 if (EnumerationRequired(&audio_enumeration_cache_, audio_type) ||
1060 EnumerationRequired(&video_enumeration_cache_, video_type)) {
1061 // Enumerate the devices if there is no valid device lists to be used.
1062 StartEnumeration(request);
1065 // Cache is valid, so log the cached devices for MediaStream requests.
1066 if (request->request_type == MEDIA_GENERATE_STREAM) {
1067 std::string log_message("Using cached devices for request.\n");
1068 if (audio_type != MEDIA_NO_SERVICE) {
1070 GetLogMessageString(audio_type, audio_enumeration_cache_.devices);
1072 if (video_type != MEDIA_NO_SERVICE) {
1074 GetLogMessageString(video_type, video_enumeration_cache_.devices);
1076 SendMessageToNativeLog(log_message);
1080 if (!SetupDeviceCaptureRequest(request)) {
1081 FinalizeRequestFailed(label, request);
1085 PostRequestToUI(label, request);
1088 bool MediaStreamManager::SetupDeviceCaptureRequest(DeviceRequest* request) {
1089 DCHECK((request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE ||
1090 request->audio_type() == MEDIA_NO_SERVICE) &&
1091 (request->video_type() == MEDIA_DEVICE_VIDEO_CAPTURE ||
1092 request->video_type() == MEDIA_NO_SERVICE));
1093 std::string audio_device_id;
1094 if (request->options.audio_requested &&
1095 !GetRequestedDeviceCaptureId(request, request->audio_type(),
1096 &audio_device_id)) {
1100 std::string video_device_id;
1101 if (request->options.video_requested &&
1102 !GetRequestedDeviceCaptureId(request, request->video_type(),
1103 &video_device_id)) {
1106 request->CreateUIRequest(audio_device_id, video_device_id);
1107 DVLOG(3) << "Audio requested " << request->options.audio_requested
1108 << " device id = " << audio_device_id
1109 << "Video requested " << request->options.video_requested
1110 << " device id = " << video_device_id;
1114 bool MediaStreamManager::SetupTabCaptureRequest(DeviceRequest* request) {
1115 DCHECK(request->audio_type() == MEDIA_TAB_AUDIO_CAPTURE ||
1116 request->video_type() == MEDIA_TAB_VIDEO_CAPTURE);
1118 std::string capture_device_id;
1119 bool mandatory_audio = false;
1120 bool mandatory_video = false;
1121 if (!request->options.GetFirstAudioConstraintByName(kMediaStreamSourceId,
1123 &mandatory_audio) &&
1124 !request->options.GetFirstVideoConstraintByName(kMediaStreamSourceId,
1126 &mandatory_video)) {
1129 DCHECK(mandatory_audio || mandatory_video);
1131 // Customize options for a WebContents based capture.
1132 int target_render_process_id = 0;
1133 int target_render_view_id = 0;
1135 // TODO(justinlin): Can't plumb audio mirroring using stream type right
1136 // now, so plumbing by device_id. Will revisit once it's refactored.
1137 // http://crbug.com/163100
1138 std::string tab_capture_device_id =
1139 WebContentsCaptureUtil::AppendWebContentsDeviceScheme(capture_device_id);
1141 bool has_valid_device_id = WebContentsCaptureUtil::ExtractTabCaptureTarget(
1142 tab_capture_device_id, &target_render_process_id,
1143 &target_render_view_id);
1144 if (!has_valid_device_id ||
1145 (request->audio_type() != MEDIA_TAB_AUDIO_CAPTURE &&
1146 request->audio_type() != MEDIA_NO_SERVICE) ||
1147 (request->video_type() != MEDIA_TAB_VIDEO_CAPTURE &&
1148 request->video_type() != MEDIA_NO_SERVICE)) {
1152 request->CreateTabCatureUIRequest(target_render_process_id,
1153 target_render_view_id,
1154 tab_capture_device_id);
1156 DVLOG(3) << "SetupTabCaptureRequest "
1157 << ", {tab_capture_device_id = " << tab_capture_device_id << "}"
1158 << ", {target_render_process_id = " << target_render_process_id
1160 << ", {target_render_view_id = " << target_render_view_id << "}";
1164 bool MediaStreamManager::SetupScreenCaptureRequest(DeviceRequest* request) {
1165 DCHECK(request->audio_type() == MEDIA_LOOPBACK_AUDIO_CAPTURE ||
1166 request->video_type() == MEDIA_DESKTOP_VIDEO_CAPTURE);
1168 // For screen capture we only support two valid combinations:
1169 // (1) screen video capture only, or
1170 // (2) screen video capture with loopback audio capture.
1171 if (request->video_type() != MEDIA_DESKTOP_VIDEO_CAPTURE ||
1172 (request->audio_type() != MEDIA_NO_SERVICE &&
1173 request->audio_type() != MEDIA_LOOPBACK_AUDIO_CAPTURE)) {
1174 LOG(ERROR) << "Invalid screen capture request.";
1178 std::string video_device_id;
1179 if (request->video_type() == MEDIA_DESKTOP_VIDEO_CAPTURE) {
1180 std::string video_stream_source;
1181 bool mandatory = false;
1182 if (!request->options.GetFirstVideoConstraintByName(
1184 &video_stream_source,
1186 LOG(ERROR) << kMediaStreamSource << " not found.";
1191 if (video_stream_source == kMediaStreamSourceDesktop) {
1192 if (!request->options.GetFirstVideoConstraintByName(
1193 kMediaStreamSourceId,
1196 LOG(ERROR) << kMediaStreamSourceId << " not found.";
1203 request->CreateUIRequest("", video_device_id);
1207 StreamDeviceInfoArray MediaStreamManager::GetDevicesOpenedByRequest(
1208 const std::string& label) const {
1209 DeviceRequest* request = FindRequest(label);
1211 return StreamDeviceInfoArray();
1212 return request->devices;
1215 bool MediaStreamManager::FindExistingRequestedDeviceInfo(
1216 const DeviceRequest& new_request,
1217 const MediaStreamDevice& new_device_info,
1218 StreamDeviceInfo* existing_device_info,
1219 MediaRequestState* existing_request_state) const {
1220 DCHECK(existing_device_info);
1221 DCHECK(existing_request_state);
1223 std::string source_id = content::GetHMACForMediaDeviceID(
1224 new_request.salt_callback,
1225 new_request.security_origin,
1226 new_device_info.id);
1228 for (DeviceRequests::const_iterator it = requests_.begin();
1229 it != requests_.end() ; ++it) {
1230 const DeviceRequest* request = it->second;
1231 if (request->requesting_process_id == new_request.requesting_process_id &&
1232 request->requesting_view_id == new_request.requesting_view_id &&
1233 request->request_type == new_request.request_type) {
1234 for (StreamDeviceInfoArray::const_iterator device_it =
1235 request->devices.begin();
1236 device_it != request->devices.end(); ++device_it) {
1237 if (device_it->device.id == source_id &&
1238 device_it->device.type == new_device_info.type) {
1239 *existing_device_info = *device_it;
1240 *existing_request_state = request->state(device_it->device.type);
1249 void MediaStreamManager::FinalizeGenerateStream(const std::string& label,
1250 DeviceRequest* request) {
1251 DVLOG(1) << "FinalizeGenerateStream label " << label;
1252 const StreamDeviceInfoArray& requested_devices = request->devices;
1254 // Partition the array of devices into audio vs video.
1255 StreamDeviceInfoArray audio_devices, video_devices;
1256 for (StreamDeviceInfoArray::const_iterator device_it =
1257 requested_devices.begin();
1258 device_it != requested_devices.end(); ++device_it) {
1259 if (IsAudioMediaType(device_it->device.type)) {
1260 audio_devices.push_back(*device_it);
1261 } else if (IsVideoMediaType(device_it->device.type)) {
1262 video_devices.push_back(*device_it);
1268 request->requester->StreamGenerated(
1269 request->requesting_view_id,
1270 request->page_request_id,
1271 label, audio_devices, video_devices);
1274 void MediaStreamManager::FinalizeRequestFailed(
1275 const std::string& label,
1276 DeviceRequest* request) {
1277 if (request->requester)
1278 request->requester->StreamGenerationFailed(
1279 request->requesting_view_id,
1280 request->page_request_id);
1282 if (request->request_type == MEDIA_DEVICE_ACCESS &&
1283 !request->callback.is_null()) {
1284 request->callback.Run(MediaStreamDevices(), request->ui_proxy.Pass());
1287 DeleteRequest(label);
1290 void MediaStreamManager::FinalizeOpenDevice(const std::string& label,
1291 DeviceRequest* request) {
1292 const StreamDeviceInfoArray& requested_devices = request->devices;
1293 request->requester->DeviceOpened(request->requesting_view_id,
1294 request->page_request_id,
1295 label, requested_devices.front());
1298 void MediaStreamManager::FinalizeEnumerateDevices(const std::string& label,
1299 DeviceRequest* request) {
1300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1301 DCHECK_EQ(request->request_type, MEDIA_ENUMERATE_DEVICES);
1303 if (request->security_origin.is_valid()) {
1304 for (StreamDeviceInfoArray::iterator it = request->devices.begin();
1305 it != request->devices.end(); ++it) {
1306 TranslateDeviceIdToSourceId(request, &it->device);
1309 request->devices.clear();
1312 request->requester->DevicesEnumerated(
1313 request->requesting_view_id,
1314 request->page_request_id,
1319 // Ideally enumeration requests should be deleted once they have been served
1320 // (as any request). However, this implementation mixes requests and
1321 // notifications together so enumeration requests are kept open by some
1322 // implementations (only Pepper?) and enumerations are done again when
1323 // device notifications are fired.
1324 // Implementations that just want to request the device list and be done
1325 // (e.g. DeviceRequestMessageFilter), they must (confusingly) call
1326 // CancelRequest() after the request has been fulfilled. This is not
1327 // obvious, not consistent in this class (see e.g. FinalizeMediaAccessRequest)
1328 // and can lead to subtle bugs (requests not deleted at all deleted too
1331 // Basically, it is not clear that using requests as an additional layer on
1332 // top of device notifications is necessary or good.
1334 // To add to this, MediaStreamManager currently relies on the external
1335 // implementations of MediaStreamRequester to delete enumeration requests via
1336 // CancelRequest and e.g. DeviceRequestMessageFilter does this. However the
1337 // Pepper implementation does not seem to to this at all (and from what I can
1338 // see, it is the only implementation that uses an enumeration request as a
1339 // notification mechanism).
1341 // We should decouple notifications from enumeration requests and once that
1342 // has been done, remove the requirement to call CancelRequest() to delete
1343 // enumeration requests and uncomment the following line:
1345 // DeleteRequest(label);
1348 void MediaStreamManager::FinalizeMediaAccessRequest(
1349 const std::string& label,
1350 DeviceRequest* request,
1351 const MediaStreamDevices& devices) {
1352 if (!request->callback.is_null())
1353 request->callback.Run(devices, request->ui_proxy.Pass());
1355 // Delete the request since it is done.
1356 DeleteRequest(label);
1359 void MediaStreamManager::InitializeDeviceManagersOnIOThread() {
1360 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1361 if (device_task_runner_)
1364 device_task_runner_ = audio_manager_->GetWorkerTaskRunner();
1366 audio_input_device_manager_ = new AudioInputDeviceManager(audio_manager_);
1367 audio_input_device_manager_->Register(this, device_task_runner_);
1369 video_capture_manager_ = new VideoCaptureManager();
1370 video_capture_manager_->Register(this, device_task_runner_);
1372 // We want to be notified of IO message loop destruction to delete the thread
1373 // and the device managers.
1374 io_loop_ = base::MessageLoop::current();
1375 io_loop_->AddDestructionObserver(this);
1377 if (CommandLine::ForCurrentProcess()->HasSwitch(
1378 switches::kUseFakeDeviceForMediaStream)) {
1379 DVLOG(1) << "Using fake device";
1384 void MediaStreamManager::Opened(MediaStreamType stream_type,
1385 int capture_session_id) {
1386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1387 DVLOG(1) << "Opened({stream_type = " << stream_type << "} "
1388 << "{capture_session_id = " << capture_session_id << "})";
1389 // Find the request(s) containing this device and mark it as used.
1390 // It can be used in several requests since the same device can be
1391 // requested from the same web page.
1392 for (DeviceRequests::iterator request_it = requests_.begin();
1393 request_it != requests_.end(); ++request_it) {
1394 const std::string& label = request_it->first;
1395 DeviceRequest* request = request_it->second;
1396 StreamDeviceInfoArray* devices = &(request->devices);
1397 for (StreamDeviceInfoArray::iterator device_it = devices->begin();
1398 device_it != devices->end(); ++device_it) {
1399 if (device_it->device.type == stream_type &&
1400 device_it->session_id == capture_session_id) {
1401 CHECK(request->state(device_it->device.type) ==
1402 MEDIA_REQUEST_STATE_OPENING);
1403 // We've found a matching request.
1404 request->SetState(device_it->device.type, MEDIA_REQUEST_STATE_DONE);
1406 if (IsAudioMediaType(device_it->device.type)) {
1407 // Store the native audio parameters in the device struct.
1408 // TODO(xians): Handle the tab capture sample rate/channel layout
1409 // in AudioInputDeviceManager::Open().
1410 if (device_it->device.type != content::MEDIA_TAB_AUDIO_CAPTURE) {
1411 const StreamDeviceInfo* info =
1412 audio_input_device_manager_->GetOpenedDeviceInfoById(
1413 device_it->session_id);
1414 device_it->device.input = info->device.input;
1415 device_it->device.matched_output = info->device.matched_output;
1418 if (RequestDone(*request))
1419 HandleRequestDone(label, request);
1426 void MediaStreamManager::HandleRequestDone(const std::string& label,
1427 DeviceRequest* request) {
1428 DCHECK(RequestDone(*request));
1429 DVLOG(1) << "HandleRequestDone("
1430 << ", {label = " << label << "})";
1432 switch (request->request_type) {
1433 case MEDIA_OPEN_DEVICE:
1434 FinalizeOpenDevice(label, request);
1436 case MEDIA_GENERATE_STREAM: {
1437 FinalizeGenerateStream(label, request);
1445 if (request->ui_proxy.get()) {
1446 request->ui_proxy->OnStarted(
1447 base::Bind(&MediaStreamManager::StopMediaStreamFromBrowser,
1448 base::Unretained(this), label));
1452 void MediaStreamManager::Closed(MediaStreamType stream_type,
1453 int capture_session_id) {
1454 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1457 void MediaStreamManager::DevicesEnumerated(
1458 MediaStreamType stream_type, const StreamDeviceInfoArray& devices) {
1459 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1460 DVLOG(1) << "DevicesEnumerated("
1461 << "{stream_type = " << stream_type << "})" << std::endl;
1463 std::string log_message = "New device enumeration result:\n" +
1464 GetLogMessageString(stream_type, devices);
1465 SendMessageToNativeLog(log_message);
1467 // Only cache the device list when the device list has been changed.
1468 bool need_update_clients = false;
1469 EnumerationCache* cache =
1470 stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ?
1471 &audio_enumeration_cache_ : &video_enumeration_cache_;
1472 if (!cache->valid ||
1473 devices.size() != cache->devices.size() ||
1474 !std::equal(devices.begin(), devices.end(), cache->devices.begin(),
1475 StreamDeviceInfo::IsEqual)) {
1476 StopRemovedDevices(cache->devices, devices);
1477 cache->devices = devices;
1478 need_update_clients = true;
1480 // The device might not be able to be enumerated when it is not warmed up,
1481 // for example, when the machine just wakes up from sleep. We set the cache
1482 // to be invalid so that the next media request will trigger the
1483 // enumeration again. See issue/317673.
1484 cache->valid = !devices.empty();
1487 if (need_update_clients && monitoring_started_)
1488 NotifyDevicesChanged(stream_type, devices);
1490 // Publish the result for all requests waiting for device list(s).
1491 // Find the requests waiting for this device list, store their labels and
1492 // release the iterator before calling device settings. We might get a call
1493 // back from device_settings that will need to iterate through devices.
1494 std::list<std::string> label_list;
1495 for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end();
1497 if (it->second->state(stream_type) == MEDIA_REQUEST_STATE_REQUESTED &&
1498 (it->second->audio_type() == stream_type ||
1499 it->second->video_type() == stream_type)) {
1500 if (it->second->request_type != MEDIA_ENUMERATE_DEVICES)
1501 it->second->SetState(stream_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
1502 label_list.push_back(it->first);
1506 for (std::list<std::string>::iterator it = label_list.begin();
1507 it != label_list.end(); ++it) {
1508 DeviceRequest* request = FindRequest(*it);
1509 switch (request->request_type) {
1510 case MEDIA_ENUMERATE_DEVICES:
1511 if (need_update_clients && request->requester) {
1512 request->devices = devices;
1513 FinalizeEnumerateDevices(*it, request);
1517 if (request->state(request->audio_type()) ==
1518 MEDIA_REQUEST_STATE_REQUESTED ||
1519 request->state(request->video_type()) ==
1520 MEDIA_REQUEST_STATE_REQUESTED) {
1521 // We are doing enumeration for other type of media, wait until it is
1522 // all done before posting the request to UI because UI needs
1523 // the device lists to handle the request.
1526 if (!SetupDeviceCaptureRequest(request))
1527 FinalizeRequestFailed(*it, request);
1529 PostRequestToUI(*it, request);
1534 --active_enumeration_ref_count_[stream_type];
1535 DCHECK_GE(active_enumeration_ref_count_[stream_type], 0);
1539 void MediaStreamManager::SendMessageToNativeLog(const std::string& message) {
1540 BrowserThread::PostTask(
1541 BrowserThread::UI, FROM_HERE,
1542 base::Bind(DoAddLogMessage, message));
1545 void MediaStreamManager::AddLogMessageOnIOThread(const std::string& message) {
1546 // Get render process ids on the IO thread.
1547 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1549 // Grab all unique process ids that request a MediaStream or have a
1550 // MediaStream running.
1551 std::set<int> requesting_process_ids;
1552 for (DeviceRequests::const_iterator it = requests_.begin();
1553 it != requests_.end(); ++it) {
1554 DeviceRequest* request = it->second;
1555 if (request->request_type == MEDIA_GENERATE_STREAM)
1556 requesting_process_ids.insert(request->requesting_process_id);
1559 // MediaStreamManager is a singleton in BrowserMainLoop, which owns the UI
1560 // thread. MediaStreamManager has the same lifetime as the UI thread, so it is
1561 // safe to use base::Unretained.
1562 BrowserThread::PostTask(
1565 base::Bind(&MediaStreamManager::AddLogMessageOnUIThread,
1566 base::Unretained(this),
1567 requesting_process_ids,
1571 void MediaStreamManager::AddLogMessageOnUIThread(
1572 const std::set<int>& requesting_process_ids,
1573 const std::string& message) {
1574 #if defined(ENABLE_WEBRTC)
1575 // Must be on the UI thread to access RenderProcessHost from process ID.
1576 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1578 for (std::set<int>::const_iterator it = requesting_process_ids.begin();
1579 it != requesting_process_ids.end(); ++it) {
1580 // Log the message to all renderers that are requesting a MediaStream or
1581 // have a MediaStream running.
1582 content::RenderProcessHostImpl* render_process_host_impl =
1583 static_cast<content::RenderProcessHostImpl*>(
1584 content::RenderProcessHost::FromID(*it));
1585 if (render_process_host_impl)
1586 render_process_host_impl->WebRtcLogMessage(message);
1591 void MediaStreamManager::HandleAccessRequestResponse(
1592 const std::string& label,
1593 const MediaStreamDevices& devices) {
1594 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1595 DVLOG(1) << "HandleAccessRequestResponse("
1596 << ", {label = " << label << "})";
1598 DeviceRequest* request = FindRequest(label);
1600 // The request has been canceled before the UI returned.
1604 if (request->request_type == MEDIA_DEVICE_ACCESS) {
1605 FinalizeMediaAccessRequest(label, request, devices);
1609 // Handle the case when the request was denied.
1610 if (devices.empty()) {
1611 FinalizeRequestFailed(label, request);
1615 // Process all newly-accepted devices for this request.
1616 bool found_audio = false;
1617 bool found_video = false;
1618 for (MediaStreamDevices::const_iterator device_it = devices.begin();
1619 device_it != devices.end(); ++device_it) {
1620 StreamDeviceInfo device_info;
1621 device_info.device = *device_it;
1623 // TODO(justinlin): Nicer way to do this?
1624 // Re-append the device's id since we lost it when posting request to UI.
1625 if (device_info.device.type == content::MEDIA_TAB_VIDEO_CAPTURE ||
1626 device_info.device.type == content::MEDIA_TAB_AUDIO_CAPTURE) {
1627 device_info.device.id = request->UIRequest()->tab_capture_device_id;
1629 // Initialize the sample_rate and channel_layout here since for audio
1630 // mirroring, we don't go through EnumerateDevices where these are usually
1632 if (device_info.device.type == content::MEDIA_TAB_AUDIO_CAPTURE) {
1633 const media::AudioParameters parameters =
1634 audio_manager_->GetDefaultOutputStreamParameters();
1635 int sample_rate = parameters.sample_rate();
1636 // If we weren't able to get the native sampling rate or the sample_rate
1637 // is outside the valid range for input devices set reasonable defaults.
1638 if (sample_rate <= 0 || sample_rate > 96000)
1639 sample_rate = 44100;
1641 device_info.device.input.sample_rate = sample_rate;
1642 device_info.device.input.channel_layout = media::CHANNEL_LAYOUT_STEREO;
1646 if (device_info.device.type == request->audio_type()) {
1648 } else if (device_info.device.type == request->video_type()) {
1652 // If this is request for a new MediaStream, a device is only opened once
1653 // per render view. This is so that the permission to use a device can be
1654 // revoked by a single call to StopStreamDevice regardless of how many
1655 // MediaStreams it is being used in.
1656 if (request->request_type == MEDIA_GENERATE_STREAM) {
1657 MediaRequestState state;
1658 if (FindExistingRequestedDeviceInfo(*request,
1662 request->devices.push_back(device_info);
1663 request->SetState(device_info.device.type, state);
1664 DVLOG(1) << "HandleAccessRequestResponse - device already opened "
1665 << ", {label = " << label << "}"
1666 << ", device_id = " << device_it->id << "}";
1670 device_info.session_id =
1671 GetDeviceManager(device_info.device.type)->Open(device_info);
1672 TranslateDeviceIdToSourceId(request, &device_info.device);
1673 request->devices.push_back(device_info);
1675 request->SetState(device_info.device.type, MEDIA_REQUEST_STATE_OPENING);
1676 DVLOG(1) << "HandleAccessRequestResponse - opening device "
1677 << ", {label = " << label << "}"
1678 << ", {device_id = " << device_info.device.id << "}"
1679 << ", {session_id = " << device_info.session_id << "}";
1682 // Check whether we've received all stream types requested.
1683 if (!found_audio && IsAudioMediaType(request->audio_type())) {
1684 request->SetState(request->audio_type(), MEDIA_REQUEST_STATE_ERROR);
1685 DVLOG(1) << "Set no audio found label " << label;
1688 if (!found_video && IsVideoMediaType(request->video_type()))
1689 request->SetState(request->video_type(), MEDIA_REQUEST_STATE_ERROR);
1691 if (RequestDone(*request))
1692 HandleRequestDone(label, request);
1695 void MediaStreamManager::StopMediaStreamFromBrowser(const std::string& label) {
1696 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1698 DeviceRequest* request = FindRequest(label);
1702 // Notify renderers that the devices in the stream will be stopped.
1703 if (request->requester) {
1704 for (StreamDeviceInfoArray::iterator device_it = request->devices.begin();
1705 device_it != request->devices.end(); ++device_it) {
1706 request->requester->DeviceStopped(request->requesting_view_id,
1712 CancelRequest(label);
1715 void MediaStreamManager::UseFakeDevice() {
1716 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1717 video_capture_manager()->UseFakeDevice();
1718 audio_input_device_manager()->UseFakeDevice();
1721 void MediaStreamManager::UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy> fake_ui) {
1722 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1723 use_fake_ui_ = true;
1724 fake_ui_ = fake_ui.Pass();
1727 void MediaStreamManager::WillDestroyCurrentMessageLoop() {
1728 DVLOG(3) << "MediaStreamManager::WillDestroyCurrentMessageLoop()";
1729 DCHECK_EQ(base::MessageLoop::current(), io_loop_);
1730 DCHECK(requests_.empty());
1731 if (device_task_runner_) {
1734 video_capture_manager_->Unregister();
1735 audio_input_device_manager_->Unregister();
1736 device_task_runner_ = NULL;
1739 audio_input_device_manager_ = NULL;
1740 video_capture_manager_ = NULL;
1743 void MediaStreamManager::NotifyDevicesChanged(
1744 MediaStreamType stream_type,
1745 const StreamDeviceInfoArray& devices) {
1746 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1747 MediaObserver* media_observer =
1748 GetContentClient()->browser()->GetMediaObserver();
1749 if (media_observer == NULL)
1752 // Map the devices to MediaStreamDevices.
1753 MediaStreamDevices new_devices;
1754 for (StreamDeviceInfoArray::const_iterator it = devices.begin();
1755 it != devices.end(); ++it) {
1756 new_devices.push_back(it->device);
1759 if (IsAudioMediaType(stream_type)) {
1760 media_observer->OnAudioCaptureDevicesChanged(new_devices);
1761 } else if (IsVideoMediaType(stream_type)) {
1762 media_observer->OnVideoCaptureDevicesChanged(new_devices);
1768 bool MediaStreamManager::RequestDone(const DeviceRequest& request) const {
1769 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1771 const bool requested_audio = IsAudioMediaType(request.audio_type());
1772 const bool requested_video = IsVideoMediaType(request.video_type());
1774 const bool audio_done =
1776 request.state(request.audio_type()) == MEDIA_REQUEST_STATE_DONE ||
1777 request.state(request.audio_type()) == MEDIA_REQUEST_STATE_ERROR;
1781 const bool video_done =
1783 request.state(request.video_type()) == MEDIA_REQUEST_STATE_DONE ||
1784 request.state(request.video_type()) == MEDIA_REQUEST_STATE_ERROR;
1791 MediaStreamProvider* MediaStreamManager::GetDeviceManager(
1792 MediaStreamType stream_type) {
1793 if (IsVideoMediaType(stream_type)) {
1794 return video_capture_manager();
1795 } else if (IsAudioMediaType(stream_type)) {
1796 return audio_input_device_manager();
1802 void MediaStreamManager::OnDevicesChanged(
1803 base::SystemMonitor::DeviceType device_type) {
1804 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1806 // NOTE: This method is only called in response to physical audio/video device
1807 // changes (from the operating system).
1809 MediaStreamType stream_type;
1810 if (device_type == base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE) {
1811 stream_type = MEDIA_DEVICE_AUDIO_CAPTURE;
1812 } else if (device_type == base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE) {
1813 stream_type = MEDIA_DEVICE_VIDEO_CAPTURE;
1815 return; // Uninteresting device change.
1818 // Always do enumeration even though some enumeration is in progress,
1819 // because those enumeration commands could be sent before these devices
1821 ++active_enumeration_ref_count_[stream_type];
1822 GetDeviceManager(stream_type)->EnumerateDevices(stream_type);
1825 } // namespace content