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