Change to let media_stream_devices_controller.cc decide if a gUM request fail if...
[chromium-blink-merge.git] / content / browser / renderer_host / media / media_stream_manager.cc
blobe4b8c0f590c121ccec56a5e9ccac5c4f962cb90e
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/renderer_host/media/media_stream_manager.h"
7 #include <list>
8 #include <vector>
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/compiler_specific.h"
13 #include "base/logging.h"
14 #include "base/rand_util.h"
15 #include "base/run_loop.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/threading/thread.h"
18 #include "content/browser/browser_main_loop.h"
19 #include "content/browser/media/capture/web_contents_capture_util.h"
20 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
21 #include "content/browser/renderer_host/media/device_request_message_filter.h"
22 #include "content/browser/renderer_host/media/media_capture_devices_impl.h"
23 #include "content/browser/renderer_host/media/media_stream_requester.h"
24 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
25 #include "content/browser/renderer_host/media/video_capture_manager.h"
26 #include "content/browser/renderer_host/render_process_host_impl.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/content_browser_client.h"
29 #include "content/public/browser/media_device_id.h"
30 #include "content/public/browser/media_observer.h"
31 #include "content/public/browser/media_request_state.h"
32 #include "content/public/browser/render_process_host.h"
33 #include "content/public/common/content_switches.h"
34 #include "content/public/common/media_stream_request.h"
35 #include "media/audio/audio_manager_base.h"
36 #include "media/audio/audio_parameters.h"
37 #include "media/base/channel_layout.h"
38 #include "url/gurl.h"
40 #if defined(OS_WIN)
41 #include "base/win/scoped_com_initializer.h"
42 #endif
44 namespace content {
46 // Forward declaration of DeviceMonitorMac and its only useable method.
47 class DeviceMonitorMac {
48 public:
49 void StartMonitoring();
52 namespace {
53 // Creates a random label used to identify requests.
54 std::string RandomLabel() {
55 // An earlier PeerConnection spec,
56 // http://dev.w3.org/2011/webrtc/editor/webrtc.html, specified the
57 // MediaStream::label alphabet as containing 36 characters from
58 // range: U+0021, U+0023 to U+0027, U+002A to U+002B, U+002D to U+002E,
59 // U+0030 to U+0039, U+0041 to U+005A, U+005E to U+007E.
60 // Here we use a safe subset.
61 static const char kAlphabet[] = "0123456789"
62 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
64 std::string label(36, ' ');
65 for (size_t i = 0; i < label.size(); ++i) {
66 int random_char = base::RandGenerator(sizeof(kAlphabet) - 1);
67 label[i] = kAlphabet[random_char];
69 return label;
72 void ParseStreamType(const StreamOptions& options,
73 MediaStreamType* audio_type,
74 MediaStreamType* video_type) {
75 *audio_type = MEDIA_NO_SERVICE;
76 *video_type = MEDIA_NO_SERVICE;
77 if (options.audio_requested) {
78 std::string audio_stream_source;
79 bool mandatory = false;
80 if (options.GetFirstAudioConstraintByName(kMediaStreamSource,
81 &audio_stream_source,
82 &mandatory)) {
83 DCHECK(mandatory);
84 // This is tab or screen capture.
85 if (audio_stream_source == kMediaStreamSourceTab) {
86 *audio_type = content::MEDIA_TAB_AUDIO_CAPTURE;
87 } else if (audio_stream_source == kMediaStreamSourceSystem) {
88 *audio_type = content::MEDIA_LOOPBACK_AUDIO_CAPTURE;
90 } else {
91 // This is normal audio device capture.
92 *audio_type = MEDIA_DEVICE_AUDIO_CAPTURE;
95 if (options.video_requested) {
96 std::string video_stream_source;
97 bool mandatory = false;
98 if (options.GetFirstVideoConstraintByName(kMediaStreamSource,
99 &video_stream_source,
100 &mandatory)) {
101 DCHECK(mandatory);
102 // This is tab or screen capture.
103 if (video_stream_source == kMediaStreamSourceTab) {
104 *video_type = content::MEDIA_TAB_VIDEO_CAPTURE;
105 } else if (video_stream_source == kMediaStreamSourceScreen) {
106 *video_type = content::MEDIA_DESKTOP_VIDEO_CAPTURE;
107 } else if (video_stream_source == kMediaStreamSourceDesktop) {
108 *video_type = content::MEDIA_DESKTOP_VIDEO_CAPTURE;
110 } else {
111 // This is normal video device capture.
112 *video_type = MEDIA_DEVICE_VIDEO_CAPTURE;
117 // Private helper method for SendMessageToNativeLog() that obtains the global
118 // MediaStreamManager instance on the UI thread before sending |message| to the
119 // webrtcLoggingPrivate API.
120 void DoAddLogMessage(const std::string& message) {
121 // Must be on the UI thread to access BrowserMainLoop.
122 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
123 // May be null in tests.
124 // TODO(vrk): Handle this more elegantly by having native log messages become
125 // no-ops until MediaStreamManager is aware that a renderer process has
126 // started logging. crbug.com/333894
127 if (content::BrowserMainLoop::GetInstance()) {
128 BrowserThread::PostTask(
129 BrowserThread::IO,
130 FROM_HERE,
131 base::Bind(&MediaStreamManager::AddLogMessageOnIOThread,
132 base::Unretained(content::BrowserMainLoop::GetInstance()
133 ->media_stream_manager()),
134 message));
138 // Private helper method to generate a string for the log message that lists the
139 // human readable names of |devices|.
140 std::string GetLogMessageString(MediaStreamType stream_type,
141 const StreamDeviceInfoArray& devices) {
142 std::string output_string =
143 base::StringPrintf("Getting devices for stream type %d:\n", stream_type);
144 if (devices.empty()) {
145 output_string += "No devices found.";
146 } else {
147 for (StreamDeviceInfoArray::const_iterator it = devices.begin();
148 it != devices.end(); ++it) {
149 output_string += " " + it->device.name + "\n";
152 return output_string;
155 // Needed for MediaStreamManager::GenerateStream below.
156 std::string ReturnEmptySalt() {
157 return std::string();
160 } // namespace
163 // MediaStreamManager::DeviceRequest represents a request to either enumerate
164 // available devices or open one or more devices.
165 // TODO(perkj): MediaStreamManager still needs refactoring. I propose we create
166 // several subclasses of DeviceRequest and move some of the responsibility of
167 // the MediaStreamManager to the subclasses to get rid of the way too many if
168 // statements in MediaStreamManager.
169 class MediaStreamManager::DeviceRequest {
170 public:
171 DeviceRequest(MediaStreamRequester* requester,
172 int requesting_process_id,
173 int requesting_view_id,
174 int page_request_id,
175 const GURL& security_origin,
176 MediaStreamRequestType request_type,
177 const StreamOptions& options,
178 const ResourceContext::SaltCallback& salt_callback)
179 : requester(requester),
180 requesting_process_id(requesting_process_id),
181 requesting_view_id(requesting_view_id),
182 page_request_id(page_request_id),
183 security_origin(security_origin),
184 request_type(request_type),
185 options(options),
186 salt_callback(salt_callback),
187 state_(NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_NOT_REQUESTED),
188 audio_type_(MEDIA_NO_SERVICE),
189 video_type_(MEDIA_NO_SERVICE) {
192 ~DeviceRequest() {}
194 void SetAudioType(MediaStreamType audio_type) {
195 DCHECK(IsAudioMediaType(audio_type) || audio_type == MEDIA_NO_SERVICE);
196 audio_type_ = audio_type;
199 MediaStreamType audio_type() const { return audio_type_; }
201 void SetVideoType(MediaStreamType video_type) {
202 DCHECK(IsVideoMediaType(video_type) || video_type == MEDIA_NO_SERVICE);
203 video_type_ = video_type;
206 MediaStreamType video_type() const { return video_type_; }
208 // Creates a MediaStreamRequest object that is used by this request when UI
209 // is asked for permission and device selection.
210 void CreateUIRequest(const std::string& requested_audio_device_id,
211 const std::string& requested_video_device_id) {
212 DCHECK(!ui_request_);
213 ui_request_.reset(new MediaStreamRequest(requesting_process_id,
214 requesting_view_id,
215 page_request_id,
216 security_origin,
217 request_type,
218 requested_audio_device_id,
219 requested_video_device_id,
220 audio_type_,
221 video_type_));
224 // Creates a tab capture specific MediaStreamRequest object that is used by
225 // this request when UI is asked for permission and device selection.
226 void CreateTabCatureUIRequest(int target_render_process_id,
227 int target_render_view_id,
228 const std::string& tab_capture_id) {
229 DCHECK(!ui_request_);
230 ui_request_.reset(new MediaStreamRequest(target_render_process_id,
231 target_render_view_id,
232 page_request_id,
233 security_origin,
234 request_type,
237 audio_type_,
238 video_type_));
239 ui_request_->tab_capture_device_id = tab_capture_id;
242 const MediaStreamRequest* UIRequest() const { return ui_request_.get(); }
244 // Update the request state and notify observers.
245 void SetState(MediaStreamType stream_type, MediaRequestState new_state) {
246 if (stream_type == NUM_MEDIA_TYPES) {
247 for (int i = MEDIA_NO_SERVICE + 1; i < NUM_MEDIA_TYPES; ++i) {
248 const MediaStreamType stream_type = static_cast<MediaStreamType>(i);
249 state_[stream_type] = new_state;
251 } else {
252 state_[stream_type] = new_state;
255 MediaObserver* media_observer =
256 GetContentClient()->browser()->GetMediaObserver();
257 if (!media_observer)
258 return;
260 // If |ui_request_| doesn't exist, it means that the request has not yet
261 // been setup fully and there are no valid observers.
262 if (!ui_request_)
263 return;
265 // If we appended a device_id scheme, we want to remove it when notifying
266 // observers which may be in different modules since this scheme is only
267 // used internally within the content module.
268 std::string device_id =
269 WebContentsCaptureUtil::StripWebContentsDeviceScheme(
270 ui_request_->tab_capture_device_id);
272 media_observer->OnMediaRequestStateChanged(
273 ui_request_->render_process_id, ui_request_->render_view_id,
274 ui_request_->page_request_id, ui_request_->security_origin,
275 MediaStreamDevice(stream_type, device_id, device_id), new_state);
278 MediaRequestState state(MediaStreamType stream_type) const {
279 return state_[stream_type];
282 MediaStreamRequester* const requester; // Can be NULL.
285 // The render process id that requested this stream to be generated and that
286 // will receive a handle to the MediaStream. This may be different from
287 // MediaStreamRequest::render_process_id which in the tab capture case
288 // specifies the target renderer from which audio and video is captured.
289 const int requesting_process_id;
291 // The render view id that requested this stream to be generated and that
292 // will receive a handle to the MediaStream. This may be different from
293 // MediaStreamRequest::render_view_id which in the tab capture case
294 // specifies the target renderer from which audio and video is captured.
295 const int requesting_view_id;
297 // An ID the render view provided to identify this request.
298 const int page_request_id;
300 const GURL security_origin;
302 const MediaStreamRequestType request_type;
304 const StreamOptions options;
306 ResourceContext::SaltCallback salt_callback;
308 StreamDeviceInfoArray devices;
310 // Callback to the requester which audio/video devices have been selected.
311 // It can be null if the requester has no interest to know the result.
312 // Currently it is only used by |DEVICE_ACCESS| type.
313 MediaStreamManager::MediaRequestResponseCallback callback;
315 scoped_ptr<MediaStreamUIProxy> ui_proxy;
317 private:
318 std::vector<MediaRequestState> state_;
319 scoped_ptr<MediaStreamRequest> ui_request_;
320 MediaStreamType audio_type_;
321 MediaStreamType video_type_;
324 MediaStreamManager::EnumerationCache::EnumerationCache()
325 : valid(false) {
328 MediaStreamManager::EnumerationCache::~EnumerationCache() {
331 MediaStreamManager::MediaStreamManager()
332 : audio_manager_(NULL),
333 monitoring_started_(false),
334 io_loop_(NULL),
335 use_fake_ui_(false) {}
337 MediaStreamManager::MediaStreamManager(media::AudioManager* audio_manager)
338 : audio_manager_(audio_manager),
339 monitoring_started_(false),
340 io_loop_(NULL),
341 use_fake_ui_(false) {
342 DCHECK(audio_manager_);
343 memset(active_enumeration_ref_count_, 0,
344 sizeof(active_enumeration_ref_count_));
346 // Some unit tests create the MSM in the IO thread and assumes the
347 // initialization is done synchronously.
348 if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
349 InitializeDeviceManagersOnIOThread();
350 } else {
351 BrowserThread::PostTask(
352 BrowserThread::IO, FROM_HERE,
353 base::Bind(&MediaStreamManager::InitializeDeviceManagersOnIOThread,
354 base::Unretained(this)));
358 MediaStreamManager::~MediaStreamManager() {
359 DVLOG(1) << "~MediaStreamManager";
360 DCHECK(requests_.empty());
361 DCHECK(!device_task_runner_);
364 VideoCaptureManager* MediaStreamManager::video_capture_manager() {
365 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
366 DCHECK(video_capture_manager_.get());
367 return video_capture_manager_.get();
370 AudioInputDeviceManager* MediaStreamManager::audio_input_device_manager() {
371 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
372 DCHECK(audio_input_device_manager_.get());
373 return audio_input_device_manager_.get();
376 std::string MediaStreamManager::MakeMediaAccessRequest(
377 int render_process_id,
378 int render_view_id,
379 int page_request_id,
380 const StreamOptions& options,
381 const GURL& security_origin,
382 const MediaRequestResponseCallback& callback) {
383 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
385 // TODO(perkj): The argument list with NULL parameters to DeviceRequest
386 // suggests that this is the wrong design. Can this be refactored?
387 DeviceRequest* request = new DeviceRequest(NULL,
388 render_process_id,
389 render_view_id,
390 page_request_id,
391 security_origin,
392 MEDIA_DEVICE_ACCESS,
393 options,
394 base::Bind(&ReturnEmptySalt));
396 const std::string& label = AddRequest(request);
398 request->callback = callback;
399 // Post a task and handle the request asynchronously. The reason is that the
400 // requester won't have a label for the request until this function returns
401 // and thus can not handle a response. Using base::Unretained is safe since
402 // MediaStreamManager is deleted on the UI thread, after the IO thread has
403 // been stopped.
404 BrowserThread::PostTask(
405 BrowserThread::IO, FROM_HERE,
406 base::Bind(&MediaStreamManager::SetupRequest,
407 base::Unretained(this), label));
408 return label;
411 void MediaStreamManager::GenerateStream(MediaStreamRequester* requester,
412 int render_process_id,
413 int render_view_id,
414 const ResourceContext::SaltCallback& sc,
415 int page_request_id,
416 const StreamOptions& options,
417 const GURL& security_origin) {
418 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
419 DVLOG(1) << "GenerateStream()";
420 if (CommandLine::ForCurrentProcess()->HasSwitch(
421 switches::kUseFakeUIForMediaStream)) {
422 UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy>());
425 DeviceRequest* request = new DeviceRequest(requester,
426 render_process_id,
427 render_view_id,
428 page_request_id,
429 security_origin,
430 MEDIA_GENERATE_STREAM,
431 options,
432 sc);
434 const std::string& label = AddRequest(request);
436 // Post a task and handle the request asynchronously. The reason is that the
437 // requester won't have a label for the request until this function returns
438 // and thus can not handle a response. Using base::Unretained is safe since
439 // MediaStreamManager is deleted on the UI thread, after the IO thread has
440 // been stopped.
441 BrowserThread::PostTask(
442 BrowserThread::IO, FROM_HERE,
443 base::Bind(&MediaStreamManager::SetupRequest,
444 base::Unretained(this), label));
447 void MediaStreamManager::CancelRequest(int render_process_id,
448 int render_view_id,
449 int page_request_id) {
450 for (DeviceRequests::const_iterator request_it = requests_.begin();
451 request_it != requests_.end(); ++request_it) {
452 const DeviceRequest* request = request_it->second;
453 if (request->requesting_process_id == render_process_id &&
454 request->requesting_view_id == render_view_id &&
455 request->page_request_id == page_request_id) {
456 CancelRequest(request_it->first);
457 return;
460 NOTREACHED();
463 void MediaStreamManager::CancelRequest(const std::string& label) {
464 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
465 DVLOG(1) << "CancelRequest({label = " << label << "})";
466 DeviceRequest* request = FindRequest(label);
467 if (!request) {
468 // The request does not exist.
469 LOG(ERROR) << "The request with label = " << label << " does not exist.";
470 return;
473 if (request->request_type == MEDIA_ENUMERATE_DEVICES) {
474 // It isn't an ideal use of "CancelRequest" to make it a requirement
475 // for enumeration requests to be deleted via "CancelRequest" _after_
476 // the request has been successfully fulfilled.
477 // See note in FinalizeEnumerateDevices for a recommendation on how
478 // we should refactor this.
479 DeleteRequest(label);
480 return;
483 // This is a request for opening one or more devices.
484 for (StreamDeviceInfoArray::iterator device_it = request->devices.begin();
485 device_it != request->devices.end(); ++device_it) {
486 MediaRequestState state = request->state(device_it->device.type);
487 // If we have not yet requested the device to be opened - just ignore it.
488 if (state != MEDIA_REQUEST_STATE_OPENING &&
489 state != MEDIA_REQUEST_STATE_DONE) {
490 continue;
492 // Stop the opening/opened devices of the requests.
493 CloseDevice(device_it->device.type, device_it->session_id);
496 // Cancel the request if still pending at UI side.
497 request->SetState(NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_CLOSING);
498 DeleteRequest(label);
501 void MediaStreamManager::CancelAllRequests(int render_process_id) {
502 DeviceRequests::iterator request_it = requests_.begin();
503 while (request_it != requests_.end()) {
504 if (request_it->second->requesting_process_id != render_process_id) {
505 ++request_it;
506 continue;
509 std::string label = request_it->first;
510 ++request_it;
511 CancelRequest(label);
515 void MediaStreamManager::StopStreamDevice(int render_process_id,
516 int render_view_id,
517 const std::string& device_id) {
518 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
519 DVLOG(1) << "StopStreamDevice({render_view_id = " << render_view_id << "} "
520 << ", {device_id = " << device_id << "})";
521 // Find the first request for this |render_process_id| and |render_view_id|
522 // of type MEDIA_GENERATE_STREAM that has requested to use |device_id| and
523 // stop it.
524 for (DeviceRequests::iterator request_it = requests_.begin();
525 request_it != requests_.end(); ++request_it) {
526 DeviceRequest* request = request_it->second;
527 if (request->requesting_process_id != render_process_id ||
528 request->requesting_view_id != render_view_id ||
529 request->request_type != MEDIA_GENERATE_STREAM) {
530 continue;
533 StreamDeviceInfoArray& devices = request->devices;
534 for (StreamDeviceInfoArray::iterator device_it = devices.begin();
535 device_it != devices.end(); ++device_it) {
536 if (device_it->device.id == device_id) {
537 StopDevice(device_it->device.type, device_it->session_id);
538 return;
544 void MediaStreamManager::StopDevice(MediaStreamType type, int session_id) {
545 DVLOG(1) << "StopDevice"
546 << "{type = " << type << "}"
547 << "{session_id = " << session_id << "}";
548 DeviceRequests::iterator request_it = requests_.begin();
549 while (request_it != requests_.end()) {
550 DeviceRequest* request = request_it->second;
551 StreamDeviceInfoArray* devices = &request->devices;
552 if (devices->empty()) {
553 // There is no device in use yet by this request.
554 ++request_it;
555 continue;
557 StreamDeviceInfoArray::iterator device_it = devices->begin();
558 while (device_it != devices->end()) {
559 if (device_it->device.type != type ||
560 device_it->session_id != session_id) {
561 ++device_it;
562 continue;
565 if (request->state(type) == MEDIA_REQUEST_STATE_DONE)
566 CloseDevice(type, session_id);
567 device_it = devices->erase(device_it);
570 // If this request doesn't have any active devices after a device
571 // has been stopped above, remove the request. Note that the request is
572 // only deleted if a device as been removed from |devices|.
573 if (devices->empty()) {
574 std::string label = request_it->first;
575 ++request_it;
576 DeleteRequest(label);
577 } else {
578 ++request_it;
583 void MediaStreamManager::CloseDevice(MediaStreamType type, int session_id) {
584 DVLOG(1) << "CloseDevice("
585 << "{type = " << type << "} "
586 << "{session_id = " << session_id << "})";
587 GetDeviceManager(type)->Close(session_id);
589 for (DeviceRequests::iterator request_it = requests_.begin();
590 request_it != requests_.end() ; ++request_it) {
591 StreamDeviceInfoArray* devices = &request_it->second->devices;
592 for (StreamDeviceInfoArray::iterator device_it = devices->begin();
593 device_it != devices->end(); ++device_it) {
594 if (device_it->session_id == session_id &&
595 device_it->device.type == type) {
596 // Notify observers that this device is being closed.
597 // Note that only one device per type can be opened.
598 request_it->second->SetState(type, MEDIA_REQUEST_STATE_CLOSING);
604 std::string MediaStreamManager::EnumerateDevices(
605 MediaStreamRequester* requester,
606 int render_process_id,
607 int render_view_id,
608 const ResourceContext::SaltCallback& sc,
609 int page_request_id,
610 MediaStreamType type,
611 const GURL& security_origin) {
612 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
613 DCHECK(requester);
614 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
615 type == MEDIA_DEVICE_VIDEO_CAPTURE);
617 DeviceRequest* request = new DeviceRequest(requester,
618 render_process_id,
619 render_view_id,
620 page_request_id,
621 security_origin,
622 MEDIA_ENUMERATE_DEVICES,
623 StreamOptions(),
624 sc);
625 if (IsAudioMediaType(type))
626 request->SetAudioType(type);
627 else if (IsVideoMediaType(type))
628 request->SetVideoType(type);
630 const std::string& label = AddRequest(request);
631 // Post a task and handle the request asynchronously. The reason is that the
632 // requester won't have a label for the request until this function returns
633 // and thus can not handle a response. Using base::Unretained is safe since
634 // MediaStreamManager is deleted on the UI thread, after the IO thread has
635 // been stopped.
636 BrowserThread::PostTask(
637 BrowserThread::IO, FROM_HERE,
638 base::Bind(&MediaStreamManager::DoEnumerateDevices,
639 base::Unretained(this), label));
640 return label;
643 void MediaStreamManager::DoEnumerateDevices(const std::string& label) {
644 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
645 DeviceRequest* request = FindRequest(label);
646 if (!request)
647 return; // This can happen if the request has been canceled.
649 MediaStreamType type;
650 EnumerationCache* cache;
651 if (request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE) {
652 DCHECK_EQ(MEDIA_NO_SERVICE, request->video_type());
653 type = MEDIA_DEVICE_AUDIO_CAPTURE;
654 cache = &audio_enumeration_cache_;
655 } else {
656 DCHECK_EQ(MEDIA_DEVICE_VIDEO_CAPTURE, request->video_type());
657 type = MEDIA_DEVICE_VIDEO_CAPTURE;
658 cache = &video_enumeration_cache_;
661 if (!EnumerationRequired(cache, type)) {
662 // Cached device list of this type exists. Just send it out.
663 request->SetState(type, MEDIA_REQUEST_STATE_REQUESTED);
664 request->devices = cache->devices;
665 FinalizeEnumerateDevices(label, request);
666 } else {
667 StartEnumeration(request);
669 DVLOG(1) << "Enumerate Devices ({label = " << label << "})";
672 void MediaStreamManager::OpenDevice(MediaStreamRequester* requester,
673 int render_process_id,
674 int render_view_id,
675 const ResourceContext::SaltCallback& sc,
676 int page_request_id,
677 const std::string& device_id,
678 MediaStreamType type,
679 const GURL& security_origin) {
680 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
681 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
682 type == MEDIA_DEVICE_VIDEO_CAPTURE);
683 DVLOG(1) << "OpenDevice ({page_request_id = " << page_request_id << "})";
684 StreamOptions options;
685 if (IsAudioMediaType(type)) {
686 options.audio_requested = true;
687 options.mandatory_audio.push_back(
688 StreamOptions::Constraint(kMediaStreamSourceInfoId, device_id));
689 } else if (IsVideoMediaType(type)) {
690 options.video_requested = true;
691 options.mandatory_video.push_back(
692 StreamOptions::Constraint(kMediaStreamSourceInfoId, device_id));
693 } else {
694 NOTREACHED();
696 DeviceRequest* request = new DeviceRequest(requester,
697 render_process_id,
698 render_view_id,
699 page_request_id,
700 security_origin,
701 MEDIA_OPEN_DEVICE,
702 options,
703 sc);
705 const std::string& label = AddRequest(request);
706 // Post a task and handle the request asynchronously. The reason is that the
707 // requester won't have a label for the request until this function returns
708 // and thus can not handle a response. Using base::Unretained is safe since
709 // MediaStreamManager is deleted on the UI thread, after the IO thread has
710 // been stopped.
711 BrowserThread::PostTask(
712 BrowserThread::IO, FROM_HERE,
713 base::Bind(&MediaStreamManager::SetupRequest,
714 base::Unretained(this), label));
717 void MediaStreamManager::EnsureDeviceMonitorStarted() {
718 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
719 StartMonitoring();
722 void MediaStreamManager::StopRemovedDevices(
723 const StreamDeviceInfoArray& old_devices,
724 const StreamDeviceInfoArray& new_devices) {
725 DVLOG(1) << "StopRemovedDevices("
726 << "{#old_devices = " << old_devices.size() << "} "
727 << "{#new_devices = " << new_devices.size() << "})";
728 for (StreamDeviceInfoArray::const_iterator old_dev_it = old_devices.begin();
729 old_dev_it != old_devices.end(); ++old_dev_it) {
730 bool device_found = false;
731 StreamDeviceInfoArray::const_iterator new_dev_it = new_devices.begin();
732 for (; new_dev_it != new_devices.end(); ++new_dev_it) {
733 if (old_dev_it->device.id == new_dev_it->device.id) {
734 device_found = true;
735 break;
739 if (!device_found) {
740 // A device has been removed. We need to check if it is used by a
741 // MediaStream and in that case cleanup and notify the render process.
742 StopRemovedDevice(old_dev_it->device);
747 void MediaStreamManager::StopRemovedDevice(const MediaStreamDevice& device) {
748 std::vector<int> session_ids;
749 for (DeviceRequests::const_iterator it = requests_.begin();
750 it != requests_.end() ; ++it) {
751 const DeviceRequest* request = it->second;
752 for (StreamDeviceInfoArray::const_iterator device_it =
753 request->devices.begin();
754 device_it != request->devices.end(); ++device_it) {
755 std::string source_id = content::GetHMACForMediaDeviceID(
756 request->salt_callback,
757 request->security_origin,
758 device.id);
759 if (device_it->device.id == source_id &&
760 device_it->device.type == device.type) {
761 session_ids.push_back(device_it->session_id);
762 if (it->second->requester) {
763 it->second->requester->DeviceStopped(
764 it->second->requesting_view_id,
765 it->first,
766 *device_it);
771 for (std::vector<int>::const_iterator it = session_ids.begin();
772 it != session_ids.end(); ++it) {
773 StopDevice(device.type, *it);
777 void MediaStreamManager::StartMonitoring() {
778 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
779 if (monitoring_started_)
780 return;
782 if (!base::SystemMonitor::Get())
783 return;
785 monitoring_started_ = true;
786 base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
788 // Enumerate both the audio and video devices to cache the device lists
789 // and send them to media observer.
790 ++active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_CAPTURE];
791 audio_input_device_manager_->EnumerateDevices(MEDIA_DEVICE_AUDIO_CAPTURE);
792 ++active_enumeration_ref_count_[MEDIA_DEVICE_VIDEO_CAPTURE];
793 video_capture_manager_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
795 #if defined(OS_MACOSX)
796 BrowserThread::PostTask(
797 BrowserThread::UI, FROM_HERE,
798 base::Bind(&MediaStreamManager::StartMonitoringOnUIThread,
799 base::Unretained(this)));
800 #endif
803 #if defined(OS_MACOSX)
804 void MediaStreamManager::StartMonitoringOnUIThread() {
805 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
806 BrowserMainLoop* browser_main_loop = content::BrowserMainLoop::GetInstance();
807 if (browser_main_loop)
808 browser_main_loop->device_monitor_mac()->StartMonitoring();
810 #endif
812 void MediaStreamManager::StopMonitoring() {
813 DCHECK_EQ(base::MessageLoop::current(), io_loop_);
814 if (monitoring_started_) {
815 base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
816 monitoring_started_ = false;
817 ClearEnumerationCache(&audio_enumeration_cache_);
818 ClearEnumerationCache(&video_enumeration_cache_);
822 bool MediaStreamManager::GetRequestedDeviceCaptureId(
823 const DeviceRequest* request,
824 MediaStreamType type,
825 std::string* device_id) const {
826 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
827 type == MEDIA_DEVICE_VIDEO_CAPTURE);
828 const StreamOptions::Constraints* mandatory =
829 (type == MEDIA_DEVICE_AUDIO_CAPTURE) ?
830 &request->options.mandatory_audio : &request->options.mandatory_video;
831 const StreamOptions::Constraints* optional =
832 (type == MEDIA_DEVICE_AUDIO_CAPTURE) ?
833 &request->options.optional_audio : &request->options.optional_video;
835 std::vector<std::string> source_ids;
836 StreamOptions::GetConstraintsByName(*mandatory,
837 kMediaStreamSourceInfoId, &source_ids);
838 if (source_ids.size() > 1) {
839 LOG(ERROR) << "Only one mandatory " << kMediaStreamSourceInfoId
840 << " is supported.";
841 return false;
843 // If a specific device has been requested we need to find the real device
844 // id.
845 if (source_ids.size() == 1 &&
846 !TranslateSourceIdToDeviceId(type,
847 request->salt_callback,
848 request->security_origin,
849 source_ids[0], device_id)) {
850 LOG(WARNING) << "Invalid mandatory " << kMediaStreamSourceInfoId
851 << " = " << source_ids[0] << ".";
852 return false;
854 // Check for optional audio sourceIDs.
855 if (device_id->empty()) {
856 StreamOptions::GetConstraintsByName(*optional,
857 kMediaStreamSourceInfoId,
858 &source_ids);
859 // Find the first sourceID that translates to device. Note that only one
860 // device per type can call to GenerateStream is ever opened.
861 for (std::vector<std::string>::const_iterator it = source_ids.begin();
862 it != source_ids.end(); ++it) {
863 if (TranslateSourceIdToDeviceId(type,
864 request->salt_callback,
865 request->security_origin,
866 *it,
867 device_id)) {
868 break;
872 return true;
875 void MediaStreamManager::TranslateDeviceIdToSourceId(
876 DeviceRequest* request,
877 MediaStreamDevice* device) {
878 if (request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE ||
879 request->video_type() == MEDIA_DEVICE_VIDEO_CAPTURE) {
880 device->id = content::GetHMACForMediaDeviceID(
881 request->salt_callback,
882 request->security_origin,
883 device->id);
887 bool MediaStreamManager::TranslateSourceIdToDeviceId(
888 MediaStreamType stream_type,
889 const ResourceContext::SaltCallback& sc,
890 const GURL& security_origin,
891 const std::string& source_id,
892 std::string* device_id) const {
893 DCHECK(stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ||
894 stream_type == MEDIA_DEVICE_VIDEO_CAPTURE);
895 // The source_id can be empty if the constraint is set but empty.
896 if (source_id.empty())
897 return false;
899 const EnumerationCache* cache =
900 stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ?
901 &audio_enumeration_cache_ : &video_enumeration_cache_;
903 // If device monitoring hasn't started, the |device_guid| is not valid.
904 if (!cache->valid)
905 return false;
907 for (StreamDeviceInfoArray::const_iterator it = cache->devices.begin();
908 it != cache->devices.end();
909 ++it) {
910 if (content::DoesMediaDeviceIDMatchHMAC(sc, security_origin, source_id,
911 it->device.id)) {
912 *device_id = it->device.id;
913 return true;
916 return false;
919 void MediaStreamManager::ClearEnumerationCache(EnumerationCache* cache) {
920 DCHECK_EQ(base::MessageLoop::current(), io_loop_);
921 cache->valid = false;
924 bool MediaStreamManager::EnumerationRequired(EnumerationCache* cache,
925 MediaStreamType stream_type) {
926 DCHECK_EQ(base::MessageLoop::current(), io_loop_);
927 if (stream_type == MEDIA_NO_SERVICE)
928 return false;
930 DCHECK(stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ||
931 stream_type == MEDIA_DEVICE_VIDEO_CAPTURE);
933 #if defined(OS_ANDROID)
934 // There's no SystemMonitor on Android that notifies us when devices are
935 // added or removed, so we need to populate the cache on every request.
936 // Fortunately, there is an already up-to-date cache in the browser side
937 // audio manager that we can rely on, so the performance impact of
938 // invalidating the cache like this, is minimal.
939 if (stream_type == MEDIA_DEVICE_AUDIO_CAPTURE) {
940 // Make sure the cache is marked as invalid so that FinalizeEnumerateDevices
941 // will be called at the end of the enumeration.
942 ClearEnumerationCache(cache);
944 #endif
945 // If the cache isn't valid, we need to start a full enumeration.
946 return !cache->valid;
949 void MediaStreamManager::StartEnumeration(DeviceRequest* request) {
950 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
952 // Start monitoring the devices when doing the first enumeration.
953 StartMonitoring();
955 // Start enumeration for devices of all requested device types.
956 const MediaStreamType streams[] = { request->audio_type(),
957 request->video_type() };
958 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(streams); ++i) {
959 if (streams[i] == MEDIA_NO_SERVICE)
960 continue;
961 request->SetState(streams[i], MEDIA_REQUEST_STATE_REQUESTED);
962 DCHECK_GE(active_enumeration_ref_count_[streams[i]], 0);
963 if (active_enumeration_ref_count_[streams[i]] == 0) {
964 ++active_enumeration_ref_count_[streams[i]];
965 GetDeviceManager(streams[i])->EnumerateDevices(streams[i]);
970 std::string MediaStreamManager::AddRequest(DeviceRequest* request) {
971 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
973 // Create a label for this request and verify it is unique.
974 std::string unique_label;
975 do {
976 unique_label = RandomLabel();
977 } while (requests_.find(unique_label) != requests_.end());
979 requests_.insert(std::make_pair(unique_label, request));
981 return unique_label;
984 MediaStreamManager::DeviceRequest*
985 MediaStreamManager::FindRequest(const std::string& label) const {
986 DeviceRequests::const_iterator request_it = requests_.find(label);
987 return request_it == requests_.end() ? NULL : request_it->second;
990 void MediaStreamManager::DeleteRequest(const std::string& label) {
991 DVLOG(1) << "DeleteRequest({label= " << label << "})";
992 DeviceRequests::iterator it = requests_.find(label);
993 scoped_ptr<DeviceRequest> request(it->second);
994 requests_.erase(it);
997 void MediaStreamManager::PostRequestToUI(const std::string& label,
998 DeviceRequest* request) {
999 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1000 DCHECK(request->UIRequest());
1001 DVLOG(1) << "PostRequestToUI({label= " << label << "})";
1003 const MediaStreamType audio_type = request->audio_type();
1004 const MediaStreamType video_type = request->video_type();
1006 // Post the request to UI and set the state.
1007 if (IsAudioMediaType(audio_type))
1008 request->SetState(audio_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
1009 if (IsVideoMediaType(video_type))
1010 request->SetState(video_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
1012 if (use_fake_ui_) {
1013 if (!fake_ui_)
1014 fake_ui_.reset(new FakeMediaStreamUIProxy());
1016 MediaStreamDevices devices;
1017 if (audio_enumeration_cache_.valid) {
1018 for (StreamDeviceInfoArray::const_iterator it =
1019 audio_enumeration_cache_.devices.begin();
1020 it != audio_enumeration_cache_.devices.end(); ++it) {
1021 devices.push_back(it->device);
1024 if (video_enumeration_cache_.valid) {
1025 for (StreamDeviceInfoArray::const_iterator it =
1026 video_enumeration_cache_.devices.begin();
1027 it != video_enumeration_cache_.devices.end(); ++it) {
1028 devices.push_back(it->device);
1032 fake_ui_->SetAvailableDevices(devices);
1034 request->ui_proxy = fake_ui_.Pass();
1035 } else {
1036 request->ui_proxy = MediaStreamUIProxy::Create();
1039 request->ui_proxy->RequestAccess(
1040 *request->UIRequest(),
1041 base::Bind(&MediaStreamManager::HandleAccessRequestResponse,
1042 base::Unretained(this), label));
1045 void MediaStreamManager::SetupRequest(const std::string& label) {
1046 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1047 DeviceRequest* request = FindRequest(label);
1048 if (!request) {
1049 DVLOG(1) << "SetupRequest label " << label << " doesn't exist!!";
1050 return; // This can happen if the request has been canceled.
1053 if (!request->security_origin.is_valid()) {
1054 LOG(ERROR) << "Invalid security origin. "
1055 << request->security_origin;
1056 FinalizeRequestFailed(label,
1057 request,
1058 MEDIA_DEVICE_INVALID_SECURITY_ORIGIN);
1059 return;
1062 MediaStreamType audio_type = MEDIA_NO_SERVICE;
1063 MediaStreamType video_type = MEDIA_NO_SERVICE;
1064 ParseStreamType(request->options, &audio_type, &video_type);
1065 request->SetAudioType(audio_type);
1066 request->SetVideoType(video_type);
1068 bool is_web_contents_capture =
1069 audio_type == MEDIA_TAB_AUDIO_CAPTURE ||
1070 video_type == MEDIA_TAB_VIDEO_CAPTURE;
1071 if (is_web_contents_capture && !SetupTabCaptureRequest(request)) {
1072 FinalizeRequestFailed(label,
1073 request,
1074 MEDIA_DEVICE_TAB_CAPTURE_FAILURE);
1075 return;
1078 bool is_screen_capture =
1079 video_type == MEDIA_DESKTOP_VIDEO_CAPTURE;
1080 if (is_screen_capture && !SetupScreenCaptureRequest(request)) {
1081 FinalizeRequestFailed(label,
1082 request,
1083 MEDIA_DEVICE_SCREEN_CAPTURE_FAILURE);
1084 return;
1087 if (!is_web_contents_capture && !is_screen_capture) {
1088 if (EnumerationRequired(&audio_enumeration_cache_, audio_type) ||
1089 EnumerationRequired(&video_enumeration_cache_, video_type)) {
1090 // Enumerate the devices if there is no valid device lists to be used.
1091 StartEnumeration(request);
1092 return;
1093 } else {
1094 // Cache is valid, so log the cached devices for MediaStream requests.
1095 if (request->request_type == MEDIA_GENERATE_STREAM) {
1096 std::string log_message("Using cached devices for request.\n");
1097 if (audio_type != MEDIA_NO_SERVICE) {
1098 log_message +=
1099 GetLogMessageString(audio_type, audio_enumeration_cache_.devices);
1101 if (video_type != MEDIA_NO_SERVICE) {
1102 log_message +=
1103 GetLogMessageString(video_type, video_enumeration_cache_.devices);
1105 SendMessageToNativeLog(log_message);
1109 if (!SetupDeviceCaptureRequest(request)) {
1110 FinalizeRequestFailed(label, request, MEDIA_DEVICE_NO_HARDWARE);
1111 return;
1114 PostRequestToUI(label, request);
1117 bool MediaStreamManager::SetupDeviceCaptureRequest(DeviceRequest* request) {
1118 DCHECK((request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE ||
1119 request->audio_type() == MEDIA_NO_SERVICE) &&
1120 (request->video_type() == MEDIA_DEVICE_VIDEO_CAPTURE ||
1121 request->video_type() == MEDIA_NO_SERVICE));
1122 std::string audio_device_id;
1123 if (request->options.audio_requested &&
1124 !GetRequestedDeviceCaptureId(request, request->audio_type(),
1125 &audio_device_id)) {
1126 return false;
1129 std::string video_device_id;
1130 if (request->options.video_requested &&
1131 !GetRequestedDeviceCaptureId(request, request->video_type(),
1132 &video_device_id)) {
1133 return false;
1135 request->CreateUIRequest(audio_device_id, video_device_id);
1136 DVLOG(3) << "Audio requested " << request->options.audio_requested
1137 << " device id = " << audio_device_id
1138 << "Video requested " << request->options.video_requested
1139 << " device id = " << video_device_id;
1140 return true;
1143 bool MediaStreamManager::SetupTabCaptureRequest(DeviceRequest* request) {
1144 DCHECK(request->audio_type() == MEDIA_TAB_AUDIO_CAPTURE ||
1145 request->video_type() == MEDIA_TAB_VIDEO_CAPTURE);
1147 std::string capture_device_id;
1148 bool mandatory_audio = false;
1149 bool mandatory_video = false;
1150 if (!request->options.GetFirstAudioConstraintByName(kMediaStreamSourceId,
1151 &capture_device_id,
1152 &mandatory_audio) &&
1153 !request->options.GetFirstVideoConstraintByName(kMediaStreamSourceId,
1154 &capture_device_id,
1155 &mandatory_video)) {
1156 return false;
1158 DCHECK(mandatory_audio || mandatory_video);
1160 // Customize options for a WebContents based capture.
1161 int target_render_process_id = 0;
1162 int target_render_view_id = 0;
1164 // TODO(justinlin): Can't plumb audio mirroring using stream type right
1165 // now, so plumbing by device_id. Will revisit once it's refactored.
1166 // http://crbug.com/163100
1167 std::string tab_capture_device_id =
1168 WebContentsCaptureUtil::AppendWebContentsDeviceScheme(capture_device_id);
1170 bool has_valid_device_id = WebContentsCaptureUtil::ExtractTabCaptureTarget(
1171 tab_capture_device_id, &target_render_process_id,
1172 &target_render_view_id);
1173 if (!has_valid_device_id ||
1174 (request->audio_type() != MEDIA_TAB_AUDIO_CAPTURE &&
1175 request->audio_type() != MEDIA_NO_SERVICE) ||
1176 (request->video_type() != MEDIA_TAB_VIDEO_CAPTURE &&
1177 request->video_type() != MEDIA_NO_SERVICE)) {
1178 return false;
1181 request->CreateTabCatureUIRequest(target_render_process_id,
1182 target_render_view_id,
1183 tab_capture_device_id);
1185 DVLOG(3) << "SetupTabCaptureRequest "
1186 << ", {tab_capture_device_id = " << tab_capture_device_id << "}"
1187 << ", {target_render_process_id = " << target_render_process_id
1188 << "}"
1189 << ", {target_render_view_id = " << target_render_view_id << "}";
1190 return true;
1193 bool MediaStreamManager::SetupScreenCaptureRequest(DeviceRequest* request) {
1194 DCHECK(request->audio_type() == MEDIA_LOOPBACK_AUDIO_CAPTURE ||
1195 request->video_type() == MEDIA_DESKTOP_VIDEO_CAPTURE);
1197 // For screen capture we only support two valid combinations:
1198 // (1) screen video capture only, or
1199 // (2) screen video capture with loopback audio capture.
1200 if (request->video_type() != MEDIA_DESKTOP_VIDEO_CAPTURE ||
1201 (request->audio_type() != MEDIA_NO_SERVICE &&
1202 request->audio_type() != MEDIA_LOOPBACK_AUDIO_CAPTURE)) {
1203 LOG(ERROR) << "Invalid screen capture request.";
1204 return false;
1207 std::string video_device_id;
1208 if (request->video_type() == MEDIA_DESKTOP_VIDEO_CAPTURE) {
1209 std::string video_stream_source;
1210 bool mandatory = false;
1211 if (!request->options.GetFirstVideoConstraintByName(
1212 kMediaStreamSource,
1213 &video_stream_source,
1214 &mandatory)) {
1215 LOG(ERROR) << kMediaStreamSource << " not found.";
1216 return false;
1218 DCHECK(mandatory);
1220 if (video_stream_source == kMediaStreamSourceDesktop) {
1221 if (!request->options.GetFirstVideoConstraintByName(
1222 kMediaStreamSourceId,
1223 &video_device_id,
1224 &mandatory)) {
1225 LOG(ERROR) << kMediaStreamSourceId << " not found.";
1226 return false;
1228 DCHECK(mandatory);
1232 request->CreateUIRequest("", video_device_id);
1233 return true;
1236 StreamDeviceInfoArray MediaStreamManager::GetDevicesOpenedByRequest(
1237 const std::string& label) const {
1238 DeviceRequest* request = FindRequest(label);
1239 if (!request)
1240 return StreamDeviceInfoArray();
1241 return request->devices;
1244 bool MediaStreamManager::FindExistingRequestedDeviceInfo(
1245 const DeviceRequest& new_request,
1246 const MediaStreamDevice& new_device_info,
1247 StreamDeviceInfo* existing_device_info,
1248 MediaRequestState* existing_request_state) const {
1249 DCHECK(existing_device_info);
1250 DCHECK(existing_request_state);
1252 std::string source_id = content::GetHMACForMediaDeviceID(
1253 new_request.salt_callback,
1254 new_request.security_origin,
1255 new_device_info.id);
1257 for (DeviceRequests::const_iterator it = requests_.begin();
1258 it != requests_.end() ; ++it) {
1259 const DeviceRequest* request = it->second;
1260 if (request->requesting_process_id == new_request.requesting_process_id &&
1261 request->requesting_view_id == new_request.requesting_view_id &&
1262 request->request_type == new_request.request_type) {
1263 for (StreamDeviceInfoArray::const_iterator device_it =
1264 request->devices.begin();
1265 device_it != request->devices.end(); ++device_it) {
1266 if (device_it->device.id == source_id &&
1267 device_it->device.type == new_device_info.type) {
1268 *existing_device_info = *device_it;
1269 *existing_request_state = request->state(device_it->device.type);
1270 return true;
1275 return false;
1278 void MediaStreamManager::FinalizeGenerateStream(const std::string& label,
1279 DeviceRequest* request) {
1280 DVLOG(1) << "FinalizeGenerateStream label " << label;
1281 const StreamDeviceInfoArray& requested_devices = request->devices;
1283 // Partition the array of devices into audio vs video.
1284 StreamDeviceInfoArray audio_devices, video_devices;
1285 for (StreamDeviceInfoArray::const_iterator device_it =
1286 requested_devices.begin();
1287 device_it != requested_devices.end(); ++device_it) {
1288 if (IsAudioMediaType(device_it->device.type)) {
1289 audio_devices.push_back(*device_it);
1290 } else if (IsVideoMediaType(device_it->device.type)) {
1291 video_devices.push_back(*device_it);
1292 } else {
1293 NOTREACHED();
1297 request->requester->StreamGenerated(
1298 request->requesting_view_id,
1299 request->page_request_id,
1300 label, audio_devices, video_devices);
1303 void MediaStreamManager::FinalizeRequestFailed(
1304 const std::string& label,
1305 DeviceRequest* request,
1306 content::MediaStreamRequestResult result) {
1307 if (request->requester)
1308 request->requester->StreamGenerationFailed(
1309 request->requesting_view_id,
1310 request->page_request_id,
1311 result);
1313 if (request->request_type == MEDIA_DEVICE_ACCESS &&
1314 !request->callback.is_null()) {
1315 request->callback.Run(MediaStreamDevices(), request->ui_proxy.Pass());
1318 DeleteRequest(label);
1321 void MediaStreamManager::FinalizeOpenDevice(const std::string& label,
1322 DeviceRequest* request) {
1323 const StreamDeviceInfoArray& requested_devices = request->devices;
1324 request->requester->DeviceOpened(request->requesting_view_id,
1325 request->page_request_id,
1326 label, requested_devices.front());
1329 void MediaStreamManager::FinalizeEnumerateDevices(const std::string& label,
1330 DeviceRequest* request) {
1331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1332 DCHECK_EQ(request->request_type, MEDIA_ENUMERATE_DEVICES);
1334 if (request->security_origin.is_valid()) {
1335 for (StreamDeviceInfoArray::iterator it = request->devices.begin();
1336 it != request->devices.end(); ++it) {
1337 TranslateDeviceIdToSourceId(request, &it->device);
1339 } else {
1340 request->devices.clear();
1343 request->requester->DevicesEnumerated(
1344 request->requesting_view_id,
1345 request->page_request_id,
1346 label,
1347 request->devices);
1349 // TODO(tommi):
1350 // Ideally enumeration requests should be deleted once they have been served
1351 // (as any request). However, this implementation mixes requests and
1352 // notifications together so enumeration requests are kept open by some
1353 // implementations (only Pepper?) and enumerations are done again when
1354 // device notifications are fired.
1355 // Implementations that just want to request the device list and be done
1356 // (e.g. DeviceRequestMessageFilter), they must (confusingly) call
1357 // CancelRequest() after the request has been fulfilled. This is not
1358 // obvious, not consistent in this class (see e.g. FinalizeMediaAccessRequest)
1359 // and can lead to subtle bugs (requests not deleted at all deleted too
1360 // early).
1362 // Basically, it is not clear that using requests as an additional layer on
1363 // top of device notifications is necessary or good.
1365 // To add to this, MediaStreamManager currently relies on the external
1366 // implementations of MediaStreamRequester to delete enumeration requests via
1367 // CancelRequest and e.g. DeviceRequestMessageFilter does this. However the
1368 // Pepper implementation does not seem to to this at all (and from what I can
1369 // see, it is the only implementation that uses an enumeration request as a
1370 // notification mechanism).
1372 // We should decouple notifications from enumeration requests and once that
1373 // has been done, remove the requirement to call CancelRequest() to delete
1374 // enumeration requests and uncomment the following line:
1376 // DeleteRequest(label);
1379 void MediaStreamManager::FinalizeMediaAccessRequest(
1380 const std::string& label,
1381 DeviceRequest* request,
1382 const MediaStreamDevices& devices) {
1383 if (!request->callback.is_null())
1384 request->callback.Run(devices, request->ui_proxy.Pass());
1386 // Delete the request since it is done.
1387 DeleteRequest(label);
1390 void MediaStreamManager::InitializeDeviceManagersOnIOThread() {
1391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1392 if (device_task_runner_)
1393 return;
1395 device_task_runner_ = audio_manager_->GetWorkerTaskRunner();
1397 audio_input_device_manager_ = new AudioInputDeviceManager(audio_manager_);
1398 audio_input_device_manager_->Register(this, device_task_runner_);
1400 video_capture_manager_ = new VideoCaptureManager();
1401 video_capture_manager_->Register(this, device_task_runner_);
1403 // We want to be notified of IO message loop destruction to delete the thread
1404 // and the device managers.
1405 io_loop_ = base::MessageLoop::current();
1406 io_loop_->AddDestructionObserver(this);
1408 if (CommandLine::ForCurrentProcess()->HasSwitch(
1409 switches::kUseFakeDeviceForMediaStream)) {
1410 DVLOG(1) << "Using fake device";
1411 UseFakeDevice();
1415 void MediaStreamManager::Opened(MediaStreamType stream_type,
1416 int capture_session_id) {
1417 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1418 DVLOG(1) << "Opened({stream_type = " << stream_type << "} "
1419 << "{capture_session_id = " << capture_session_id << "})";
1420 // Find the request(s) containing this device and mark it as used.
1421 // It can be used in several requests since the same device can be
1422 // requested from the same web page.
1423 for (DeviceRequests::iterator request_it = requests_.begin();
1424 request_it != requests_.end(); ++request_it) {
1425 const std::string& label = request_it->first;
1426 DeviceRequest* request = request_it->second;
1427 StreamDeviceInfoArray* devices = &(request->devices);
1428 for (StreamDeviceInfoArray::iterator device_it = devices->begin();
1429 device_it != devices->end(); ++device_it) {
1430 if (device_it->device.type == stream_type &&
1431 device_it->session_id == capture_session_id) {
1432 CHECK(request->state(device_it->device.type) ==
1433 MEDIA_REQUEST_STATE_OPENING);
1434 // We've found a matching request.
1435 request->SetState(device_it->device.type, MEDIA_REQUEST_STATE_DONE);
1437 if (IsAudioMediaType(device_it->device.type)) {
1438 // Store the native audio parameters in the device struct.
1439 // TODO(xians): Handle the tab capture sample rate/channel layout
1440 // in AudioInputDeviceManager::Open().
1441 if (device_it->device.type != content::MEDIA_TAB_AUDIO_CAPTURE) {
1442 const StreamDeviceInfo* info =
1443 audio_input_device_manager_->GetOpenedDeviceInfoById(
1444 device_it->session_id);
1445 device_it->device.input = info->device.input;
1446 device_it->device.matched_output = info->device.matched_output;
1449 if (RequestDone(*request))
1450 HandleRequestDone(label, request);
1451 break;
1457 void MediaStreamManager::HandleRequestDone(const std::string& label,
1458 DeviceRequest* request) {
1459 DCHECK(RequestDone(*request));
1460 DVLOG(1) << "HandleRequestDone("
1461 << ", {label = " << label << "})";
1463 switch (request->request_type) {
1464 case MEDIA_OPEN_DEVICE:
1465 FinalizeOpenDevice(label, request);
1466 break;
1467 case MEDIA_GENERATE_STREAM: {
1468 FinalizeGenerateStream(label, request);
1469 break;
1471 default:
1472 NOTREACHED();
1473 break;
1476 if (request->ui_proxy.get()) {
1477 request->ui_proxy->OnStarted(
1478 base::Bind(&MediaStreamManager::StopMediaStreamFromBrowser,
1479 base::Unretained(this), label));
1483 void MediaStreamManager::Closed(MediaStreamType stream_type,
1484 int capture_session_id) {
1485 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1488 void MediaStreamManager::DevicesEnumerated(
1489 MediaStreamType stream_type, const StreamDeviceInfoArray& devices) {
1490 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1491 DVLOG(1) << "DevicesEnumerated("
1492 << "{stream_type = " << stream_type << "})" << std::endl;
1494 std::string log_message = "New device enumeration result:\n" +
1495 GetLogMessageString(stream_type, devices);
1496 SendMessageToNativeLog(log_message);
1498 // Only cache the device list when the device list has been changed.
1499 bool need_update_clients = false;
1500 EnumerationCache* cache =
1501 stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ?
1502 &audio_enumeration_cache_ : &video_enumeration_cache_;
1503 if (!cache->valid ||
1504 devices.size() != cache->devices.size() ||
1505 !std::equal(devices.begin(), devices.end(), cache->devices.begin(),
1506 StreamDeviceInfo::IsEqual)) {
1507 StopRemovedDevices(cache->devices, devices);
1508 cache->devices = devices;
1509 need_update_clients = true;
1511 // The device might not be able to be enumerated when it is not warmed up,
1512 // for example, when the machine just wakes up from sleep. We set the cache
1513 // to be invalid so that the next media request will trigger the
1514 // enumeration again. See issue/317673.
1515 cache->valid = !devices.empty();
1518 if (need_update_clients && monitoring_started_)
1519 NotifyDevicesChanged(stream_type, devices);
1521 // Publish the result for all requests waiting for device list(s).
1522 // Find the requests waiting for this device list, store their labels and
1523 // release the iterator before calling device settings. We might get a call
1524 // back from device_settings that will need to iterate through devices.
1525 std::list<std::string> label_list;
1526 for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end();
1527 ++it) {
1528 if (it->second->state(stream_type) == MEDIA_REQUEST_STATE_REQUESTED &&
1529 (it->second->audio_type() == stream_type ||
1530 it->second->video_type() == stream_type)) {
1531 if (it->second->request_type != MEDIA_ENUMERATE_DEVICES)
1532 it->second->SetState(stream_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
1533 label_list.push_back(it->first);
1537 for (std::list<std::string>::iterator it = label_list.begin();
1538 it != label_list.end(); ++it) {
1539 DeviceRequest* request = FindRequest(*it);
1540 switch (request->request_type) {
1541 case MEDIA_ENUMERATE_DEVICES:
1542 if (need_update_clients && request->requester) {
1543 request->devices = devices;
1544 FinalizeEnumerateDevices(*it, request);
1546 break;
1547 default:
1548 if (request->state(request->audio_type()) ==
1549 MEDIA_REQUEST_STATE_REQUESTED ||
1550 request->state(request->video_type()) ==
1551 MEDIA_REQUEST_STATE_REQUESTED) {
1552 // We are doing enumeration for other type of media, wait until it is
1553 // all done before posting the request to UI because UI needs
1554 // the device lists to handle the request.
1555 break;
1557 if (!SetupDeviceCaptureRequest(request)) {
1558 FinalizeRequestFailed(*it,
1559 request,
1560 MEDIA_DEVICE_NO_HARDWARE);
1561 } else {
1562 PostRequestToUI(*it, request);
1564 break;
1567 label_list.clear();
1568 --active_enumeration_ref_count_[stream_type];
1569 DCHECK_GE(active_enumeration_ref_count_[stream_type], 0);
1572 // static
1573 void MediaStreamManager::SendMessageToNativeLog(const std::string& message) {
1574 BrowserThread::PostTask(
1575 BrowserThread::UI, FROM_HERE,
1576 base::Bind(DoAddLogMessage, message));
1579 void MediaStreamManager::AddLogMessageOnIOThread(const std::string& message) {
1580 // Get render process ids on the IO thread.
1581 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1583 // Grab all unique process ids that request a MediaStream or have a
1584 // MediaStream running.
1585 std::set<int> requesting_process_ids;
1586 for (DeviceRequests::const_iterator it = requests_.begin();
1587 it != requests_.end(); ++it) {
1588 DeviceRequest* request = it->second;
1589 if (request->request_type == MEDIA_GENERATE_STREAM)
1590 requesting_process_ids.insert(request->requesting_process_id);
1593 // MediaStreamManager is a singleton in BrowserMainLoop, which owns the UI
1594 // thread. MediaStreamManager has the same lifetime as the UI thread, so it is
1595 // safe to use base::Unretained.
1596 BrowserThread::PostTask(
1597 BrowserThread::UI,
1598 FROM_HERE,
1599 base::Bind(&MediaStreamManager::AddLogMessageOnUIThread,
1600 base::Unretained(this),
1601 requesting_process_ids,
1602 message));
1605 void MediaStreamManager::AddLogMessageOnUIThread(
1606 const std::set<int>& requesting_process_ids,
1607 const std::string& message) {
1608 #if defined(ENABLE_WEBRTC)
1609 // Must be on the UI thread to access RenderProcessHost from process ID.
1610 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1612 for (std::set<int>::const_iterator it = requesting_process_ids.begin();
1613 it != requesting_process_ids.end(); ++it) {
1614 // Log the message to all renderers that are requesting a MediaStream or
1615 // have a MediaStream running.
1616 content::RenderProcessHostImpl* render_process_host_impl =
1617 static_cast<content::RenderProcessHostImpl*>(
1618 content::RenderProcessHost::FromID(*it));
1619 if (render_process_host_impl)
1620 render_process_host_impl->WebRtcLogMessage(message);
1622 #endif
1625 void MediaStreamManager::HandleAccessRequestResponse(
1626 const std::string& label,
1627 const MediaStreamDevices& devices,
1628 content::MediaStreamRequestResult result) {
1629 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1630 DVLOG(1) << "HandleAccessRequestResponse("
1631 << ", {label = " << label << "})";
1633 DeviceRequest* request = FindRequest(label);
1634 if (!request) {
1635 // The request has been canceled before the UI returned.
1636 return;
1639 if (request->request_type == MEDIA_DEVICE_ACCESS) {
1640 FinalizeMediaAccessRequest(label, request, devices);
1641 return;
1644 // Handle the case when the request was denied.
1645 if (result != MEDIA_DEVICE_OK) {
1646 FinalizeRequestFailed(label, request, result);
1647 return;
1650 // Process all newly-accepted devices for this request.
1651 bool found_audio = false;
1652 bool found_video = false;
1653 for (MediaStreamDevices::const_iterator device_it = devices.begin();
1654 device_it != devices.end(); ++device_it) {
1655 StreamDeviceInfo device_info;
1656 device_info.device = *device_it;
1658 // TODO(justinlin): Nicer way to do this?
1659 // Re-append the device's id since we lost it when posting request to UI.
1660 if (device_info.device.type == content::MEDIA_TAB_VIDEO_CAPTURE ||
1661 device_info.device.type == content::MEDIA_TAB_AUDIO_CAPTURE) {
1662 device_info.device.id = request->UIRequest()->tab_capture_device_id;
1664 // Initialize the sample_rate and channel_layout here since for audio
1665 // mirroring, we don't go through EnumerateDevices where these are usually
1666 // initialized.
1667 if (device_info.device.type == content::MEDIA_TAB_AUDIO_CAPTURE) {
1668 const media::AudioParameters parameters =
1669 audio_manager_->GetDefaultOutputStreamParameters();
1670 int sample_rate = parameters.sample_rate();
1671 // If we weren't able to get the native sampling rate or the sample_rate
1672 // is outside the valid range for input devices set reasonable defaults.
1673 if (sample_rate <= 0 || sample_rate > 96000)
1674 sample_rate = 44100;
1676 device_info.device.input.sample_rate = sample_rate;
1677 device_info.device.input.channel_layout = media::CHANNEL_LAYOUT_STEREO;
1681 if (device_info.device.type == request->audio_type()) {
1682 found_audio = true;
1683 } else if (device_info.device.type == request->video_type()) {
1684 found_video = true;
1687 // If this is request for a new MediaStream, a device is only opened once
1688 // per render view. This is so that the permission to use a device can be
1689 // revoked by a single call to StopStreamDevice regardless of how many
1690 // MediaStreams it is being used in.
1691 if (request->request_type == MEDIA_GENERATE_STREAM) {
1692 MediaRequestState state;
1693 if (FindExistingRequestedDeviceInfo(*request,
1694 device_info.device,
1695 &device_info,
1696 &state)) {
1697 request->devices.push_back(device_info);
1698 request->SetState(device_info.device.type, state);
1699 DVLOG(1) << "HandleAccessRequestResponse - device already opened "
1700 << ", {label = " << label << "}"
1701 << ", device_id = " << device_it->id << "}";
1702 continue;
1705 device_info.session_id =
1706 GetDeviceManager(device_info.device.type)->Open(device_info);
1707 TranslateDeviceIdToSourceId(request, &device_info.device);
1708 request->devices.push_back(device_info);
1710 request->SetState(device_info.device.type, MEDIA_REQUEST_STATE_OPENING);
1711 DVLOG(1) << "HandleAccessRequestResponse - opening device "
1712 << ", {label = " << label << "}"
1713 << ", {device_id = " << device_info.device.id << "}"
1714 << ", {session_id = " << device_info.session_id << "}";
1717 // Check whether we've received all stream types requested.
1718 if (!found_audio && IsAudioMediaType(request->audio_type())) {
1719 request->SetState(request->audio_type(), MEDIA_REQUEST_STATE_ERROR);
1720 DVLOG(1) << "Set no audio found label " << label;
1723 if (!found_video && IsVideoMediaType(request->video_type()))
1724 request->SetState(request->video_type(), MEDIA_REQUEST_STATE_ERROR);
1726 if (RequestDone(*request))
1727 HandleRequestDone(label, request);
1730 void MediaStreamManager::StopMediaStreamFromBrowser(const std::string& label) {
1731 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1733 DeviceRequest* request = FindRequest(label);
1734 if (!request)
1735 return;
1737 // Notify renderers that the devices in the stream will be stopped.
1738 if (request->requester) {
1739 for (StreamDeviceInfoArray::iterator device_it = request->devices.begin();
1740 device_it != request->devices.end(); ++device_it) {
1741 request->requester->DeviceStopped(request->requesting_view_id,
1742 label,
1743 *device_it);
1747 CancelRequest(label);
1750 void MediaStreamManager::UseFakeDevice() {
1751 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1752 video_capture_manager()->UseFakeDevice();
1753 audio_input_device_manager()->UseFakeDevice();
1756 void MediaStreamManager::UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy> fake_ui) {
1757 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1758 use_fake_ui_ = true;
1759 fake_ui_ = fake_ui.Pass();
1762 void MediaStreamManager::WillDestroyCurrentMessageLoop() {
1763 DVLOG(3) << "MediaStreamManager::WillDestroyCurrentMessageLoop()";
1764 DCHECK_EQ(base::MessageLoop::current(), io_loop_);
1765 DCHECK(requests_.empty());
1766 if (device_task_runner_) {
1767 StopMonitoring();
1769 video_capture_manager_->Unregister();
1770 audio_input_device_manager_->Unregister();
1771 device_task_runner_ = NULL;
1774 audio_input_device_manager_ = NULL;
1775 video_capture_manager_ = NULL;
1778 void MediaStreamManager::NotifyDevicesChanged(
1779 MediaStreamType stream_type,
1780 const StreamDeviceInfoArray& devices) {
1781 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1782 MediaObserver* media_observer =
1783 GetContentClient()->browser()->GetMediaObserver();
1784 if (media_observer == NULL)
1785 return;
1787 // Map the devices to MediaStreamDevices.
1788 MediaStreamDevices new_devices;
1789 for (StreamDeviceInfoArray::const_iterator it = devices.begin();
1790 it != devices.end(); ++it) {
1791 new_devices.push_back(it->device);
1794 if (IsAudioMediaType(stream_type)) {
1795 MediaCaptureDevicesImpl::GetInstance()->OnAudioCaptureDevicesChanged(
1796 new_devices);
1797 media_observer->OnAudioCaptureDevicesChanged();
1798 } else if (IsVideoMediaType(stream_type)) {
1799 MediaCaptureDevicesImpl::GetInstance()->OnVideoCaptureDevicesChanged(
1800 new_devices);
1801 media_observer->OnVideoCaptureDevicesChanged();
1802 } else {
1803 NOTREACHED();
1807 bool MediaStreamManager::RequestDone(const DeviceRequest& request) const {
1808 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1810 const bool requested_audio = IsAudioMediaType(request.audio_type());
1811 const bool requested_video = IsVideoMediaType(request.video_type());
1813 const bool audio_done =
1814 !requested_audio ||
1815 request.state(request.audio_type()) == MEDIA_REQUEST_STATE_DONE ||
1816 request.state(request.audio_type()) == MEDIA_REQUEST_STATE_ERROR;
1817 if (!audio_done)
1818 return false;
1820 const bool video_done =
1821 !requested_video ||
1822 request.state(request.video_type()) == MEDIA_REQUEST_STATE_DONE ||
1823 request.state(request.video_type()) == MEDIA_REQUEST_STATE_ERROR;
1824 if (!video_done)
1825 return false;
1827 return true;
1830 MediaStreamProvider* MediaStreamManager::GetDeviceManager(
1831 MediaStreamType stream_type) {
1832 if (IsVideoMediaType(stream_type)) {
1833 return video_capture_manager();
1834 } else if (IsAudioMediaType(stream_type)) {
1835 return audio_input_device_manager();
1837 NOTREACHED();
1838 return NULL;
1841 void MediaStreamManager::OnDevicesChanged(
1842 base::SystemMonitor::DeviceType device_type) {
1843 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1845 // NOTE: This method is only called in response to physical audio/video device
1846 // changes (from the operating system).
1848 MediaStreamType stream_type;
1849 if (device_type == base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE) {
1850 stream_type = MEDIA_DEVICE_AUDIO_CAPTURE;
1851 } else if (device_type == base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE) {
1852 stream_type = MEDIA_DEVICE_VIDEO_CAPTURE;
1853 } else {
1854 return; // Uninteresting device change.
1857 // Always do enumeration even though some enumeration is in progress,
1858 // because those enumeration commands could be sent before these devices
1859 // change.
1860 ++active_enumeration_ref_count_[stream_type];
1861 GetDeviceManager(stream_type)->EnumerateDevices(stream_type);
1864 } // namespace content