video_capture_manager.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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/message_loop/message_loop.h" 13#include "base/stl_util.h" 14#include "base/task_runner_util.h" 15#include "base/threading/sequenced_worker_pool.h" 16#include "content/browser/media/capture/web_contents_video_capture_device.h" 17#include "content/browser/renderer_host/media/video_capture_controller.h" 18#include "content/browser/renderer_host/media/video_capture_controller_event_handler.h" 19#include "content/public/browser/browser_thread.h" 20#include "content/public/browser/desktop_media_id.h" 21#include "content/public/common/content_switches.h" 22#include "content/public/common/media_stream_request.h" 23#include "media/base/media_switches.h" 24#include "media/base/scoped_histogram_timer.h" 25#include "media/video/capture/fake_video_capture_device.h" 26#include "media/video/capture/file_video_capture_device.h" 27#include "media/video/capture/video_capture_device.h" 28 29#if defined(ENABLE_SCREEN_CAPTURE) 30#include "content/browser/media/capture/desktop_capture_device.h" 31#if defined(USE_AURA) 32#include "content/browser/media/capture/desktop_capture_device_aura.h" 33#endif 34#endif 35 36namespace content { 37 38VideoCaptureManager::DeviceEntry::DeviceEntry( 39 MediaStreamType stream_type, 40 const std::string& id, 41 scoped_ptr<VideoCaptureController> controller) 42 : stream_type(stream_type), 43 id(id), 44 video_capture_controller(controller.Pass()) {} 45 46VideoCaptureManager::DeviceEntry::~DeviceEntry() {} 47 48VideoCaptureManager::DeviceInfo::DeviceInfo() {} 49 50VideoCaptureManager::DeviceInfo::DeviceInfo( 51 const media::VideoCaptureDevice::Name& name, 52 const media::VideoCaptureFormats& supported_formats) 53 : name(name), 54 supported_formats(supported_formats) {} 55 56VideoCaptureManager::DeviceInfo::~DeviceInfo() {} 57 58VideoCaptureManager::VideoCaptureManager() 59 : listener_(NULL), 60 new_capture_session_id_(1), 61 artificial_device_source_for_testing_(DISABLED) { 62} 63 64VideoCaptureManager::~VideoCaptureManager() { 65 DCHECK(devices_.empty()); 66} 67 68void VideoCaptureManager::Register( 69 MediaStreamProviderListener* listener, 70 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) { 71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 72 DCHECK(!listener_); 73 DCHECK(!device_task_runner_.get()); 74 listener_ = listener; 75 device_task_runner_ = device_task_runner; 76} 77 78void VideoCaptureManager::Unregister() { 79 DCHECK(listener_); 80 listener_ = NULL; 81} 82 83void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) { 84 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 85 DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type; 86 DCHECK(listener_); 87 base::PostTaskAndReplyWithResult( 88 device_task_runner_, FROM_HERE, 89 base::Bind(&VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread, 90 this, stream_type, devices_info_cache_), 91 base::Bind(&VideoCaptureManager::OnDevicesInfoEnumerated, this, 92 stream_type)); 93} 94 95int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) { 96 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 97 DCHECK(listener_); 98 99 // Generate a new id for the session being opened. 100 const media::VideoCaptureSessionId capture_session_id = 101 new_capture_session_id_++; 102 103 DCHECK(sessions_.find(capture_session_id) == sessions_.end()); 104 DVLOG(1) << "VideoCaptureManager::Open, id " << capture_session_id; 105 106 // We just save the stream info for processing later. 107 sessions_[capture_session_id] = device_info.device; 108 109 // Notify our listener asynchronously; this ensures that we return 110 // |capture_session_id| to the caller of this function before using that same 111 // id in a listener event. 112 base::MessageLoop::current()->PostTask(FROM_HERE, 113 base::Bind(&VideoCaptureManager::OnOpened, this, 114 device_info.device.type, capture_session_id)); 115 return capture_session_id; 116} 117 118void VideoCaptureManager::Close(int capture_session_id) { 119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 120 DCHECK(listener_); 121 DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id; 122 123 std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator 124 session_it = sessions_.find(capture_session_id); 125 if (session_it == sessions_.end()) { 126 NOTREACHED(); 127 return; 128 } 129 130 DeviceEntry* const existing_device = GetDeviceEntryForMediaStreamDevice( 131 session_it->second); 132 if (existing_device) { 133 // Remove any client that is still using the session. This is safe to call 134 // even if there are no clients using the session. 135 existing_device->video_capture_controller->StopSession(capture_session_id); 136 137 // StopSession() may have removed the last client, so we might need to 138 // close the device. 139 DestroyDeviceEntryIfNoClients(existing_device); 140 } 141 142 // Notify listeners asynchronously, and forget the session. 143 base::MessageLoop::current()->PostTask(FROM_HERE, 144 base::Bind(&VideoCaptureManager::OnClosed, this, session_it->second.type, 145 capture_session_id)); 146 sessions_.erase(session_it); 147} 148 149void VideoCaptureManager::UseFakeDevice() { 150 if (CommandLine::ForCurrentProcess()->HasSwitch( 151 switches::kUseFileForFakeVideoCapture)) { 152 artificial_device_source_for_testing_ = Y4M_FILE; 153 } else { 154 artificial_device_source_for_testing_ = TEST_PATTERN; 155 } 156} 157 158void VideoCaptureManager::DoStartDeviceOnDeviceThread( 159 DeviceEntry* entry, 160 const media::VideoCaptureParams& params, 161 scoped_ptr<media::VideoCaptureDevice::Client> device_client) { 162 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime"); 163 DCHECK(IsOnDeviceThread()); 164 165 scoped_ptr<media::VideoCaptureDevice> video_capture_device; 166 switch (entry->stream_type) { 167 case MEDIA_DEVICE_VIDEO_CAPTURE: { 168 // We look up the device id from the renderer in our local enumeration 169 // since the renderer does not have all the information that might be 170 // held in the browser-side VideoCaptureDevice::Name structure. 171 DeviceInfo* found = FindDeviceInfoById(entry->id, devices_info_cache_); 172 if (found) { 173 switch (artificial_device_source_for_testing_) { 174 case DISABLED: 175 video_capture_device.reset( 176 media::VideoCaptureDevice::Create(found->name)); 177 break; 178 case TEST_PATTERN: 179 video_capture_device.reset( 180 media::FakeVideoCaptureDevice::Create(found->name)); 181 break; 182 case Y4M_FILE: 183 video_capture_device.reset( 184 media::FileVideoCaptureDevice::Create(found->name)); 185 break; 186 } 187 } 188 break; 189 } 190 case MEDIA_TAB_VIDEO_CAPTURE: { 191 video_capture_device.reset( 192 WebContentsVideoCaptureDevice::Create(entry->id)); 193 break; 194 } 195 case MEDIA_DESKTOP_VIDEO_CAPTURE: { 196#if defined(ENABLE_SCREEN_CAPTURE) 197 DesktopMediaID id = DesktopMediaID::Parse(entry->id); 198#if defined(USE_AURA) 199 if (id.type == DesktopMediaID::TYPE_AURA_WINDOW) { 200 video_capture_device.reset(DesktopCaptureDeviceAura::Create(id)); 201 } else 202#endif 203 if (id.type != DesktopMediaID::TYPE_NONE && 204 id.type != DesktopMediaID::TYPE_AURA_WINDOW) { 205 video_capture_device = DesktopCaptureDevice::Create(id); 206 } 207#endif // defined(ENABLE_SCREEN_CAPTURE) 208 break; 209 } 210 default: { 211 NOTIMPLEMENTED(); 212 break; 213 } 214 } 215 216 if (!video_capture_device) { 217 device_client->OnError("Could not create capture device"); 218 return; 219 } 220 221 video_capture_device->AllocateAndStart(params, device_client.Pass()); 222 entry->video_capture_device = video_capture_device.Pass(); 223} 224 225void VideoCaptureManager::StartCaptureForClient( 226 media::VideoCaptureSessionId session_id, 227 const media::VideoCaptureParams& params, 228 base::ProcessHandle client_render_process, 229 VideoCaptureControllerID client_id, 230 VideoCaptureControllerEventHandler* client_handler, 231 const DoneCB& done_cb) { 232 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 233 DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, " 234 << params.requested_format.frame_size.ToString() << ", " 235 << params.requested_format.frame_rate << ", #" << session_id << ")"; 236 237 DeviceEntry* entry = GetOrCreateDeviceEntry(session_id); 238 if (!entry) { 239 done_cb.Run(base::WeakPtr<VideoCaptureController>()); 240 return; 241 } 242 243 DCHECK(entry->video_capture_controller); 244 245 // First client starts the device. 246 if (entry->video_capture_controller->GetClientCount() == 0) { 247 DVLOG(1) << "VideoCaptureManager starting device (type = " 248 << entry->stream_type << ", id = " << entry->id << ")"; 249 250 device_task_runner_->PostTask( 251 FROM_HERE, 252 base::Bind( 253 &VideoCaptureManager::DoStartDeviceOnDeviceThread, 254 this, 255 entry, 256 params, 257 base::Passed(entry->video_capture_controller->NewDeviceClient()))); 258 } 259 // Run the callback first, as AddClient() may trigger OnFrameInfo(). 260 done_cb.Run(entry->video_capture_controller->GetWeakPtr()); 261 entry->video_capture_controller->AddClient( 262 client_id, client_handler, client_render_process, session_id, params); 263} 264 265void VideoCaptureManager::StopCaptureForClient( 266 VideoCaptureController* controller, 267 VideoCaptureControllerID client_id, 268 VideoCaptureControllerEventHandler* client_handler) { 269 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 270 DCHECK(controller); 271 DCHECK(client_handler); 272 273 DeviceEntry* entry = GetDeviceEntryForController(controller); 274 if (!entry) { 275 NOTREACHED(); 276 return; 277 } 278 279 // Detach client from controller. 280 media::VideoCaptureSessionId session_id = 281 controller->RemoveClient(client_id, client_handler); 282 DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = " 283 << session_id; 284 285 // If controller has no more clients, delete controller and device. 286 DestroyDeviceEntryIfNoClients(entry); 287} 288 289bool VideoCaptureManager::GetDeviceSupportedFormats( 290 media::VideoCaptureSessionId capture_session_id, 291 media::VideoCaptureFormats* supported_formats) { 292 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 293 DCHECK(supported_formats->empty()); 294 295 std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator it = 296 sessions_.find(capture_session_id); 297 if (it == sessions_.end()) 298 return false; 299 DVLOG(1) << "GetDeviceSupportedFormats for device: " << it->second.name; 300 301 // Return all available formats of the device, regardless its started state. 302 DeviceInfo* existing_device = 303 FindDeviceInfoById(it->second.id, devices_info_cache_); 304 if (existing_device) 305 *supported_formats = existing_device->supported_formats; 306 return true; 307} 308 309bool VideoCaptureManager::GetDeviceFormatsInUse( 310 media::VideoCaptureSessionId capture_session_id, 311 media::VideoCaptureFormats* formats_in_use) { 312 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 313 DCHECK(formats_in_use->empty()); 314 315 std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator it = 316 sessions_.find(capture_session_id); 317 if (it == sessions_.end()) 318 return false; 319 DVLOG(1) << "GetDeviceFormatsInUse for device: " << it->second.name; 320 321 // Return the currently in-use format(s) of the device, if it's started. 322 DeviceEntry* device_in_use = 323 GetDeviceEntryForMediaStreamDevice(it->second); 324 if (device_in_use) { 325 // Currently only one format-in-use is supported at the VCC level. 326 formats_in_use->push_back( 327 device_in_use->video_capture_controller->GetVideoCaptureFormat()); 328 } 329 return true; 330} 331 332void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) { 333 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime"); 334 DCHECK(IsOnDeviceThread()); 335 if (entry->video_capture_device) { 336 entry->video_capture_device->StopAndDeAllocate(); 337 } 338 entry->video_capture_device.reset(); 339} 340 341void VideoCaptureManager::OnOpened( 342 MediaStreamType stream_type, 343 media::VideoCaptureSessionId capture_session_id) { 344 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 345 if (!listener_) { 346 // Listener has been removed. 347 return; 348 } 349 listener_->Opened(stream_type, capture_session_id); 350} 351 352void VideoCaptureManager::OnClosed( 353 MediaStreamType stream_type, 354 media::VideoCaptureSessionId capture_session_id) { 355 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 356 if (!listener_) { 357 // Listener has been removed. 358 return; 359 } 360 listener_->Closed(stream_type, capture_session_id); 361} 362 363void VideoCaptureManager::OnDevicesInfoEnumerated( 364 MediaStreamType stream_type, 365 const DeviceInfos& new_devices_info_cache) { 366 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 367 368 if (!listener_) { 369 // Listener has been removed. 370 return; 371 } 372 devices_info_cache_ = new_devices_info_cache; 373 374 // Walk the |devices_info_cache_| and transform from VCD::Name to 375 // StreamDeviceInfo for return purposes. 376 StreamDeviceInfoArray devices; 377 for (DeviceInfos::const_iterator it = devices_info_cache_.begin(); 378 it != devices_info_cache_.end(); ++it) { 379 devices.push_back(StreamDeviceInfo( 380 stream_type, it->name.GetNameAndModel(), it->name.id())); 381 } 382 listener_->DevicesEnumerated(stream_type, devices); 383} 384 385bool VideoCaptureManager::IsOnDeviceThread() const { 386 return device_task_runner_->BelongsToCurrentThread(); 387} 388 389VideoCaptureManager::DeviceInfos 390VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread( 391 MediaStreamType stream_type, 392 const DeviceInfos& old_device_info_cache) { 393 SCOPED_UMA_HISTOGRAM_TIMER( 394 "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime"); 395 DCHECK(IsOnDeviceThread()); 396 media::VideoCaptureDevice::Names names_snapshot; 397 switch (stream_type) { 398 case MEDIA_DEVICE_VIDEO_CAPTURE: 399 // Cache the latest enumeration of video capture devices. 400 // We'll refer to this list again in OnOpen to avoid having to 401 // enumerate the devices again. 402 switch (artificial_device_source_for_testing_) { 403 case DISABLED: 404 media::VideoCaptureDevice::GetDeviceNames(&names_snapshot); 405 break; 406 case TEST_PATTERN: 407 media::FakeVideoCaptureDevice::GetDeviceNames(&names_snapshot); 408 break; 409 case Y4M_FILE: 410 media::FileVideoCaptureDevice::GetDeviceNames(&names_snapshot); 411 break; 412 } 413 break; 414 415 case MEDIA_DESKTOP_VIDEO_CAPTURE: 416 // Do nothing. 417 break; 418 419 default: 420 NOTREACHED(); 421 break; 422 } 423 424 // Construct |new_devices_info_cache| with the cached devices that are still 425 // present in the system, and remove their names from |names_snapshot|, so we 426 // keep there the truly new devices. 427 DeviceInfos new_devices_info_cache; 428 for (DeviceInfos::const_iterator it_device_info = 429 old_device_info_cache.begin(); 430 it_device_info != old_device_info_cache.end(); ++it_device_info) { 431 for (media::VideoCaptureDevice::Names::iterator it = 432 names_snapshot.begin(); 433 it != names_snapshot.end(); ++it) { 434 if (it_device_info->name.id() == it->id()) { 435 new_devices_info_cache.push_back(*it_device_info); 436 names_snapshot.erase(it); 437 break; 438 } 439 } 440 } 441 442 // Get the supported capture formats for the new devices in |names_snapshot|. 443 for (media::VideoCaptureDevice::Names::const_iterator it = 444 names_snapshot.begin(); 445 it != names_snapshot.end(); ++it) { 446 media::VideoCaptureFormats supported_formats; 447 DeviceInfo device_info(*it, media::VideoCaptureFormats()); 448 switch (artificial_device_source_for_testing_) { 449 case DISABLED: 450 media::VideoCaptureDevice::GetDeviceSupportedFormats( 451 *it, &(device_info.supported_formats)); 452 break; 453 case TEST_PATTERN: 454 media::FakeVideoCaptureDevice::GetDeviceSupportedFormats( 455 *it, &(device_info.supported_formats)); 456 break; 457 case Y4M_FILE: 458 media::FileVideoCaptureDevice::GetDeviceSupportedFormats( 459 *it, &(device_info.supported_formats)); 460 break; 461 } 462 new_devices_info_cache.push_back(device_info); 463 } 464 return new_devices_info_cache; 465} 466 467VideoCaptureManager::DeviceEntry* 468VideoCaptureManager::GetDeviceEntryForMediaStreamDevice( 469 const MediaStreamDevice& device_info) { 470 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 471 472 for (DeviceEntries::iterator it = devices_.begin(); 473 it != devices_.end(); ++it) { 474 DeviceEntry* device = *it; 475 if (device_info.type == device->stream_type && 476 device_info.id == device->id) { 477 return device; 478 } 479 } 480 return NULL; 481} 482 483VideoCaptureManager::DeviceEntry* 484VideoCaptureManager::GetDeviceEntryForController( 485 const VideoCaptureController* controller) { 486 // Look up |controller| in |devices_|. 487 for (DeviceEntries::iterator it = devices_.begin(); 488 it != devices_.end(); ++it) { 489 if ((*it)->video_capture_controller.get() == controller) { 490 return *it; 491 } 492 } 493 return NULL; 494} 495 496void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) { 497 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 498 // Removal of the last client stops the device. 499 if (entry->video_capture_controller->GetClientCount() == 0) { 500 DVLOG(1) << "VideoCaptureManager stopping device (type = " 501 << entry->stream_type << ", id = " << entry->id << ")"; 502 503 // The DeviceEntry is removed from |devices_| immediately. The controller is 504 // deleted immediately, and the device is freed asynchronously. After this 505 // point, subsequent requests to open this same device ID will create a new 506 // DeviceEntry, VideoCaptureController, and VideoCaptureDevice. 507 devices_.erase(entry); 508 entry->video_capture_controller.reset(); 509 device_task_runner_->PostTask( 510 FROM_HERE, 511 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, 512 base::Owned(entry))); 513 } 514} 515 516VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry( 517 media::VideoCaptureSessionId capture_session_id) { 518 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 519 520 std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator 521 session_it = sessions_.find(capture_session_id); 522 if (session_it == sessions_.end()) { 523 return NULL; 524 } 525 const MediaStreamDevice& device_info = session_it->second; 526 527 // Check if another session has already opened this device. If so, just 528 // use that opened device. 529 DeviceEntry* const existing_device = 530 GetDeviceEntryForMediaStreamDevice(device_info); 531 if (existing_device) { 532 DCHECK_EQ(device_info.type, existing_device->stream_type); 533 return existing_device; 534 } 535 536 scoped_ptr<VideoCaptureController> video_capture_controller( 537 new VideoCaptureController()); 538 DeviceEntry* new_device = new DeviceEntry(device_info.type, 539 device_info.id, 540 video_capture_controller.Pass()); 541 devices_.insert(new_device); 542 return new_device; 543} 544 545VideoCaptureManager::DeviceInfo* VideoCaptureManager::FindDeviceInfoById( 546 const std::string& id, 547 DeviceInfos& device_vector) { 548 for (DeviceInfos::iterator it = device_vector.begin(); 549 it != device_vector.end(); ++it) { 550 if (it->name.id() == id) 551 return &(*it); 552 } 553 return NULL; 554} 555 556} // namespace content 557