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/video_capture_manager.h" 6 7 #include <set> 8 9 #include "base/bind.h" 10 #include "base/command_line.h" 11 #include "base/logging.h" 12 #include "base/stl_util.h" 13 #include "base/threading/sequenced_worker_pool.h" 14 #include "content/browser/renderer_host/media/video_capture_controller.h" 15 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h" 16 #include "content/browser/renderer_host/media/web_contents_video_capture_device.h" 17 #include "content/public/browser/browser_thread.h" 18 #include "content/public/common/content_switches.h" 19 #include "content/public/common/desktop_media_id.h" 20 #include "content/public/common/media_stream_request.h" 21 #include "media/base/scoped_histogram_timer.h" 22 #include "media/video/capture/fake_video_capture_device.h" 23 #include "media/video/capture/video_capture_device.h" 24 25 #if defined(ENABLE_SCREEN_CAPTURE) 26 #include "content/browser/renderer_host/media/desktop_capture_device.h" 27 #endif 28 29 namespace content { 30 31 // Starting id for the first capture session. 32 // VideoCaptureManager::kStartOpenSessionId is used as default id without 33 // explicitly calling open device. 34 enum { kFirstSessionId = VideoCaptureManager::kStartOpenSessionId + 1 }; 35 36 struct VideoCaptureManager::Controller { 37 Controller( 38 VideoCaptureController* vc_controller, 39 VideoCaptureControllerEventHandler* handler) 40 : controller(vc_controller), 41 ready_to_delete(false) { 42 handlers.push_front(handler); 43 } 44 ~Controller() {} 45 46 scoped_refptr<VideoCaptureController> controller; 47 bool ready_to_delete; 48 Handlers handlers; 49 }; 50 51 VideoCaptureManager::VideoCaptureManager() 52 : listener_(NULL), 53 new_capture_session_id_(kFirstSessionId), 54 use_fake_device_(false) { 55 } 56 57 VideoCaptureManager::~VideoCaptureManager() { 58 DCHECK(devices_.empty()); 59 DCHECK(controllers_.empty()); 60 } 61 62 void VideoCaptureManager::Register(MediaStreamProviderListener* listener, 63 base::MessageLoopProxy* device_thread_loop) { 64 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 65 DCHECK(!listener_); 66 DCHECK(!device_loop_.get()); 67 listener_ = listener; 68 device_loop_ = device_thread_loop; 69 } 70 71 void VideoCaptureManager::Unregister() { 72 DCHECK(listener_); 73 listener_ = NULL; 74 } 75 76 void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) { 77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 78 DCHECK(listener_); 79 device_loop_->PostTask( 80 FROM_HERE, 81 base::Bind(&VideoCaptureManager::OnEnumerateDevices, this, stream_type)); 82 } 83 84 int VideoCaptureManager::Open(const StreamDeviceInfo& device) { 85 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 86 DCHECK(listener_); 87 88 // Generate a new id for this device. 89 int video_capture_session_id = new_capture_session_id_++; 90 91 device_loop_->PostTask( 92 FROM_HERE, 93 base::Bind(&VideoCaptureManager::OnOpen, this, video_capture_session_id, 94 device)); 95 96 return video_capture_session_id; 97 } 98 99 void VideoCaptureManager::Close(int capture_session_id) { 100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 101 DCHECK(listener_); 102 DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id; 103 device_loop_->PostTask( 104 FROM_HERE, 105 base::Bind(&VideoCaptureManager::OnClose, this, capture_session_id)); 106 } 107 108 void VideoCaptureManager::Start( 109 const media::VideoCaptureParams& capture_params, 110 media::VideoCaptureDevice::EventHandler* video_capture_receiver) { 111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 112 device_loop_->PostTask( 113 FROM_HERE, 114 base::Bind(&VideoCaptureManager::OnStart, this, capture_params, 115 video_capture_receiver)); 116 } 117 118 void VideoCaptureManager::Stop( 119 const media::VideoCaptureSessionId& capture_session_id, 120 base::Closure stopped_cb) { 121 DVLOG(1) << "VideoCaptureManager::Stop, id " << capture_session_id; 122 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 123 device_loop_->PostTask( 124 FROM_HERE, 125 base::Bind(&VideoCaptureManager::OnStop, this, capture_session_id, 126 stopped_cb)); 127 } 128 129 void VideoCaptureManager::UseFakeDevice() { 130 use_fake_device_ = true; 131 } 132 133 void VideoCaptureManager::OnEnumerateDevices(MediaStreamType stream_type) { 134 SCOPED_UMA_HISTOGRAM_TIMER( 135 "Media.VideoCaptureManager.OnEnumerateDevicesTime"); 136 DCHECK(IsOnDeviceThread()); 137 138 media::VideoCaptureDevice::Names device_names; 139 GetAvailableDevices(stream_type, &device_names); 140 141 scoped_ptr<StreamDeviceInfoArray> devices(new StreamDeviceInfoArray()); 142 for (media::VideoCaptureDevice::Names::iterator it = 143 device_names.begin(); it != device_names.end(); ++it) { 144 bool opened = DeviceOpened(*it); 145 devices->push_back(StreamDeviceInfo( 146 stream_type, it->GetNameAndModel(), it->id(), opened)); 147 } 148 149 PostOnDevicesEnumerated(stream_type, devices.Pass()); 150 } 151 152 void VideoCaptureManager::OnOpen(int capture_session_id, 153 const StreamDeviceInfo& device) { 154 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnOpenTime"); 155 DCHECK(IsOnDeviceThread()); 156 DCHECK(devices_.find(capture_session_id) == devices_.end()); 157 DVLOG(1) << "VideoCaptureManager::OnOpen, id " << capture_session_id; 158 159 // Check if another session has already opened this device. If so, just 160 // use that opened device. 161 media::VideoCaptureDevice* opened_video_capture_device = 162 GetOpenedDevice(device); 163 if (opened_video_capture_device) { 164 DeviceEntry& new_entry = devices_[capture_session_id]; 165 new_entry.stream_type = device.device.type; 166 new_entry.capture_device = opened_video_capture_device; 167 PostOnOpened(device.device.type, capture_session_id); 168 return; 169 } 170 171 scoped_ptr<media::VideoCaptureDevice> video_capture_device; 172 173 // Open the device. 174 switch (device.device.type) { 175 case MEDIA_DEVICE_VIDEO_CAPTURE: { 176 // We look up the device id from the renderer in our local enumeration 177 // since the renderer does not have all the information that might be 178 // held in the browser-side VideoCaptureDevice::Name structure. 179 media::VideoCaptureDevice::Name* found = 180 video_capture_devices_.FindById(device.device.id); 181 if (found) { 182 video_capture_device.reset(use_fake_device_ ? 183 media::FakeVideoCaptureDevice::Create(*found) : 184 media::VideoCaptureDevice::Create(*found)); 185 } 186 break; 187 } 188 case MEDIA_TAB_VIDEO_CAPTURE: { 189 video_capture_device.reset( 190 WebContentsVideoCaptureDevice::Create(device.device.id)); 191 break; 192 } 193 case MEDIA_DESKTOP_VIDEO_CAPTURE: { 194 #if defined(ENABLE_SCREEN_CAPTURE) 195 DesktopMediaID id = DesktopMediaID::Parse(device.device.id); 196 if (id.type != DesktopMediaID::TYPE_NONE) { 197 video_capture_device = DesktopCaptureDevice::Create(id); 198 } 199 #endif // defined(ENABLE_SCREEN_CAPTURE) 200 break; 201 } 202 default: { 203 NOTIMPLEMENTED(); 204 break; 205 } 206 } 207 208 if (!video_capture_device) { 209 PostOnError(capture_session_id, kDeviceNotAvailable); 210 return; 211 } 212 213 DeviceEntry& new_entry = devices_[capture_session_id]; 214 new_entry.stream_type = device.device.type; 215 new_entry.capture_device = video_capture_device.release(); 216 PostOnOpened(device.device.type, capture_session_id); 217 } 218 219 void VideoCaptureManager::OnClose(int capture_session_id) { 220 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnCloseTime"); 221 DCHECK(IsOnDeviceThread()); 222 DVLOG(1) << "VideoCaptureManager::OnClose, id " << capture_session_id; 223 224 VideoCaptureDevices::iterator device_it = devices_.find(capture_session_id); 225 if (device_it == devices_.end()) { 226 return; 227 } 228 const DeviceEntry removed_entry = device_it->second; 229 devices_.erase(device_it); 230 231 Controllers::iterator cit = controllers_.find(removed_entry.capture_device); 232 if (cit != controllers_.end()) { 233 BrowserThread::PostTask( 234 BrowserThread::IO, FROM_HERE, 235 base::Bind(&VideoCaptureController::StopSession, 236 cit->second->controller, capture_session_id)); 237 } 238 239 if (!DeviceInUse(removed_entry.capture_device)) { 240 // No other users of this device, deallocate (if not done already) and 241 // delete the device. No need to take care of the controller, that is done 242 // by |OnStop|. 243 removed_entry.capture_device->DeAllocate(); 244 Controllers::iterator cit = controllers_.find(removed_entry.capture_device); 245 if (cit != controllers_.end()) { 246 delete cit->second; 247 controllers_.erase(cit); 248 } 249 delete removed_entry.capture_device; 250 } 251 252 PostOnClosed(removed_entry.stream_type, capture_session_id); 253 } 254 255 void VideoCaptureManager::OnStart( 256 const media::VideoCaptureParams capture_params, 257 media::VideoCaptureDevice::EventHandler* video_capture_receiver) { 258 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnStartTime"); 259 DCHECK(IsOnDeviceThread()); 260 DCHECK(video_capture_receiver != NULL); 261 DVLOG(1) << "VideoCaptureManager::OnStart, (" << capture_params.width 262 << ", " << capture_params.height 263 << ", " << capture_params.frame_per_second 264 << ", " << capture_params.session_id 265 << ")"; 266 267 media::VideoCaptureDevice* video_capture_device = 268 GetDeviceInternal(capture_params.session_id); 269 if (!video_capture_device) { 270 // Invalid session id. 271 video_capture_receiver->OnError(); 272 return; 273 } 274 // TODO(mcasas): Variable resolution video capture devices, are not yet 275 // fully supported, see crbug.com/261410, second part, and crbug.com/266082 . 276 if (capture_params.frame_size_type != 277 media::ConstantResolutionVideoCaptureDevice) { 278 LOG(DFATAL) << "Only constant Video Capture resolution device supported."; 279 video_capture_receiver->OnError(); 280 return; 281 } 282 Controllers::iterator cit = controllers_.find(video_capture_device); 283 if (cit != controllers_.end()) { 284 cit->second->ready_to_delete = false; 285 } 286 287 // Possible errors are signaled to video_capture_receiver by 288 // video_capture_device. video_capture_receiver to perform actions. 289 media::VideoCaptureCapability params_as_capability_copy; 290 params_as_capability_copy.width = capture_params.width; 291 params_as_capability_copy.height = capture_params.height; 292 params_as_capability_copy.frame_rate = capture_params.frame_per_second; 293 params_as_capability_copy.session_id = capture_params.session_id; 294 params_as_capability_copy.frame_size_type = capture_params.frame_size_type; 295 video_capture_device->Allocate(params_as_capability_copy, 296 video_capture_receiver); 297 video_capture_device->Start(); 298 } 299 300 void VideoCaptureManager::OnStop( 301 const media::VideoCaptureSessionId capture_session_id, 302 base::Closure stopped_cb) { 303 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnStopTime"); 304 DCHECK(IsOnDeviceThread()); 305 DVLOG(1) << "VideoCaptureManager::OnStop, id " << capture_session_id; 306 307 VideoCaptureDevices::iterator it = devices_.find(capture_session_id); 308 if (it != devices_.end()) { 309 media::VideoCaptureDevice* video_capture_device = it->second.capture_device; 310 // Possible errors are signaled to video_capture_receiver by 311 // video_capture_device. video_capture_receiver to perform actions. 312 video_capture_device->Stop(); 313 video_capture_device->DeAllocate(); 314 Controllers::iterator cit = controllers_.find(video_capture_device); 315 if (cit != controllers_.end()) { 316 cit->second->ready_to_delete = true; 317 if (cit->second->handlers.empty()) { 318 delete cit->second; 319 controllers_.erase(cit); 320 } 321 } 322 } 323 324 if (!stopped_cb.is_null()) 325 stopped_cb.Run(); 326 327 if (capture_session_id == kStartOpenSessionId) { 328 // This device was opened from Start(), not Open(). Close it! 329 OnClose(capture_session_id); 330 } 331 } 332 333 void VideoCaptureManager::OnOpened(MediaStreamType stream_type, 334 int capture_session_id) { 335 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 336 if (!listener_) { 337 // Listener has been removed. 338 return; 339 } 340 listener_->Opened(stream_type, capture_session_id); 341 } 342 343 void VideoCaptureManager::OnClosed(MediaStreamType stream_type, 344 int capture_session_id) { 345 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 346 if (!listener_) { 347 // Listener has been removed. 348 return; 349 } 350 listener_->Closed(stream_type, capture_session_id); 351 } 352 353 void VideoCaptureManager::OnDevicesEnumerated( 354 MediaStreamType stream_type, 355 scoped_ptr<StreamDeviceInfoArray> devices) { 356 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 357 if (!listener_) { 358 // Listener has been removed. 359 return; 360 } 361 listener_->DevicesEnumerated(stream_type, *devices); 362 } 363 364 void VideoCaptureManager::OnError(MediaStreamType stream_type, 365 int capture_session_id, 366 MediaStreamProviderError error) { 367 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 368 if (!listener_) { 369 // Listener has been removed. 370 return; 371 } 372 listener_->Error(stream_type, capture_session_id, error); 373 } 374 375 void VideoCaptureManager::PostOnOpened( 376 MediaStreamType stream_type, int capture_session_id) { 377 DCHECK(IsOnDeviceThread()); 378 BrowserThread::PostTask(BrowserThread::IO, 379 FROM_HERE, 380 base::Bind(&VideoCaptureManager::OnOpened, this, 381 stream_type, capture_session_id)); 382 } 383 384 void VideoCaptureManager::PostOnClosed( 385 MediaStreamType stream_type, int capture_session_id) { 386 DCHECK(IsOnDeviceThread()); 387 BrowserThread::PostTask(BrowserThread::IO, 388 FROM_HERE, 389 base::Bind(&VideoCaptureManager::OnClosed, this, 390 stream_type, capture_session_id)); 391 } 392 393 void VideoCaptureManager::PostOnDevicesEnumerated( 394 MediaStreamType stream_type, 395 scoped_ptr<StreamDeviceInfoArray> devices) { 396 DCHECK(IsOnDeviceThread()); 397 BrowserThread::PostTask( 398 BrowserThread::IO, FROM_HERE, 399 base::Bind(&VideoCaptureManager::OnDevicesEnumerated, 400 this, stream_type, base::Passed(&devices))); 401 } 402 403 void VideoCaptureManager::PostOnError(int capture_session_id, 404 MediaStreamProviderError error) { 405 DCHECK(IsOnDeviceThread()); 406 MediaStreamType stream_type = MEDIA_DEVICE_VIDEO_CAPTURE; 407 VideoCaptureDevices::const_iterator it = devices_.find(capture_session_id); 408 if (it != devices_.end()) 409 stream_type = it->second.stream_type; 410 BrowserThread::PostTask(BrowserThread::IO, 411 FROM_HERE, 412 base::Bind(&VideoCaptureManager::OnError, this, 413 stream_type, capture_session_id, error)); 414 } 415 416 bool VideoCaptureManager::IsOnDeviceThread() const { 417 return device_loop_->BelongsToCurrentThread(); 418 } 419 420 void VideoCaptureManager::GetAvailableDevices( 421 MediaStreamType stream_type, 422 media::VideoCaptureDevice::Names* device_names) { 423 DCHECK(IsOnDeviceThread()); 424 425 switch (stream_type) { 426 case MEDIA_DEVICE_VIDEO_CAPTURE: 427 // Cache the latest enumeration of video capture devices. 428 // We'll refer to this list again in OnOpen to avoid having to 429 // enumerate the devices again. 430 video_capture_devices_.clear(); 431 if (!use_fake_device_) { 432 media::VideoCaptureDevice::GetDeviceNames(&video_capture_devices_); 433 } else { 434 media::FakeVideoCaptureDevice::GetDeviceNames(&video_capture_devices_); 435 } 436 *device_names = video_capture_devices_; 437 break; 438 439 case MEDIA_DESKTOP_VIDEO_CAPTURE: 440 device_names->clear(); 441 break; 442 443 default: 444 NOTREACHED(); 445 break; 446 } 447 } 448 449 bool VideoCaptureManager::DeviceOpened( 450 const media::VideoCaptureDevice::Name& device_name) { 451 DCHECK(IsOnDeviceThread()); 452 453 for (VideoCaptureDevices::iterator it = devices_.begin(); 454 it != devices_.end(); ++it) { 455 if (device_name.id() == it->second.capture_device->device_name().id()) { 456 // We've found the device! 457 return true; 458 } 459 } 460 return false; 461 } 462 463 media::VideoCaptureDevice* VideoCaptureManager::GetOpenedDevice( 464 const StreamDeviceInfo& device_info) { 465 DCHECK(IsOnDeviceThread()); 466 467 for (VideoCaptureDevices::iterator it = devices_.begin(); 468 it != devices_.end(); it++) { 469 if (device_info.device.id == 470 it->second.capture_device->device_name().id()) { 471 return it->second.capture_device; 472 } 473 } 474 return NULL; 475 } 476 477 bool VideoCaptureManager::DeviceInUse( 478 const media::VideoCaptureDevice* video_capture_device) { 479 DCHECK(IsOnDeviceThread()); 480 481 for (VideoCaptureDevices::iterator it = devices_.begin(); 482 it != devices_.end(); ++it) { 483 if (video_capture_device == it->second.capture_device) { 484 // We've found the device! 485 return true; 486 } 487 } 488 return false; 489 } 490 491 void VideoCaptureManager::AddController( 492 const media::VideoCaptureParams& capture_params, 493 VideoCaptureControllerEventHandler* handler, 494 base::Callback<void(VideoCaptureController*)> added_cb) { 495 DCHECK(handler); 496 device_loop_->PostTask( 497 FROM_HERE, 498 base::Bind(&VideoCaptureManager::DoAddControllerOnDeviceThread, 499 this, capture_params, handler, added_cb)); 500 } 501 502 void VideoCaptureManager::DoAddControllerOnDeviceThread( 503 const media::VideoCaptureParams capture_params, 504 VideoCaptureControllerEventHandler* handler, 505 base::Callback<void(VideoCaptureController*)> added_cb) { 506 DCHECK(IsOnDeviceThread()); 507 508 media::VideoCaptureDevice* video_capture_device = 509 GetDeviceInternal(capture_params.session_id); 510 scoped_refptr<VideoCaptureController> controller; 511 if (video_capture_device) { 512 Controllers::iterator cit = controllers_.find(video_capture_device); 513 if (cit == controllers_.end()) { 514 controller = new VideoCaptureController(this); 515 controllers_[video_capture_device] = 516 new Controller(controller.get(), handler); 517 } else { 518 controllers_[video_capture_device]->handlers.push_front(handler); 519 controller = controllers_[video_capture_device]->controller; 520 } 521 } 522 added_cb.Run(controller.get()); 523 } 524 525 void VideoCaptureManager::RemoveController( 526 VideoCaptureController* controller, 527 VideoCaptureControllerEventHandler* handler) { 528 DCHECK(handler); 529 device_loop_->PostTask( 530 FROM_HERE, 531 base::Bind(&VideoCaptureManager::DoRemoveControllerOnDeviceThread, this, 532 make_scoped_refptr(controller), handler)); 533 } 534 535 void VideoCaptureManager::DoRemoveControllerOnDeviceThread( 536 VideoCaptureController* controller, 537 VideoCaptureControllerEventHandler* handler) { 538 DCHECK(IsOnDeviceThread()); 539 540 for (Controllers::iterator cit = controllers_.begin(); 541 cit != controllers_.end(); ++cit) { 542 if (controller == cit->second->controller.get()) { 543 Handlers& handlers = cit->second->handlers; 544 for (Handlers::iterator hit = handlers.begin(); 545 hit != handlers.end(); ++hit) { 546 if ((*hit) == handler) { 547 handlers.erase(hit); 548 break; 549 } 550 } 551 if (handlers.empty() && cit->second->ready_to_delete) { 552 delete cit->second; 553 controllers_.erase(cit); 554 } 555 return; 556 } 557 } 558 } 559 560 media::VideoCaptureDevice* VideoCaptureManager::GetDeviceInternal( 561 int capture_session_id) { 562 DCHECK(IsOnDeviceThread()); 563 VideoCaptureDevices::iterator dit = devices_.find(capture_session_id); 564 if (dit != devices_.end()) { 565 return dit->second.capture_device; 566 } 567 568 // Solution for not using MediaStreamManager. 569 // This session id won't be returned by Open(). 570 if (capture_session_id == kStartOpenSessionId) { 571 media::VideoCaptureDevice::Names device_names; 572 GetAvailableDevices(MEDIA_DEVICE_VIDEO_CAPTURE, &device_names); 573 if (device_names.empty()) { 574 // No devices available. 575 return NULL; 576 } 577 StreamDeviceInfo device(MEDIA_DEVICE_VIDEO_CAPTURE, 578 device_names.front().GetNameAndModel(), 579 device_names.front().id(), 580 false); 581 582 // Call OnOpen to open using the first device in the list. 583 OnOpen(capture_session_id, device); 584 585 VideoCaptureDevices::iterator dit = devices_.find(capture_session_id); 586 if (dit != devices_.end()) { 587 return dit->second.capture_device; 588 } 589 } 590 return NULL; 591 } 592 593 } // namespace content 594