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