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