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