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/bind_helpers.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/bind_to_current_loop.h" 24#include "media/base/scoped_histogram_timer.h" 25#include "media/video/capture/video_capture_device.h" 26#include "media/video/capture/video_capture_device_factory.h" 27 28#if defined(ENABLE_SCREEN_CAPTURE) 29#include "content/browser/media/capture/desktop_capture_device.h" 30#if defined(USE_AURA) 31#include "content/browser/media/capture/desktop_capture_device_aura.h" 32#endif 33#endif 34 35namespace { 36 37// Compares two VideoCaptureFormat by checking smallest frame_size area, then 38// by _largest_ frame_rate. Used to order a VideoCaptureFormats vector so that 39// the first entry for a given resolution has the largest frame rate, as needed 40// by the ConsolidateCaptureFormats() method. 41bool IsCaptureFormatSmaller(const media::VideoCaptureFormat& format1, 42 const media::VideoCaptureFormat& format2) { 43 if (format1.frame_size.GetArea() == format2.frame_size.GetArea()) 44 return format1.frame_rate > format2.frame_rate; 45 return format1.frame_size.GetArea() < format2.frame_size.GetArea(); 46} 47 48bool IsCaptureFormatSizeEqual(const media::VideoCaptureFormat& format1, 49 const media::VideoCaptureFormat& format2) { 50 return format1.frame_size.GetArea() == format2.frame_size.GetArea(); 51} 52 53// This function receives a list of capture formats, removes duplicated 54// resolutions while keeping the highest frame rate for each, and forcing I420 55// pixel format. 56void ConsolidateCaptureFormats(media::VideoCaptureFormats* formats) { 57 if (formats->empty()) 58 return; 59 std::sort(formats->begin(), formats->end(), IsCaptureFormatSmaller); 60 // Due to the ordering imposed, the largest frame_rate is kept while removing 61 // duplicated resolutions. 62 media::VideoCaptureFormats::iterator last = 63 std::unique(formats->begin(), formats->end(), IsCaptureFormatSizeEqual); 64 formats->erase(last, formats->end()); 65 // Mark all formats as I420, since this is what the renderer side will get 66 // anyhow: the actual pixel format is decided at the device level. 67 for (media::VideoCaptureFormats::iterator it = formats->begin(); 68 it != formats->end(); ++it) { 69 it->pixel_format = media::PIXEL_FORMAT_I420; 70 } 71} 72 73// The maximum number of buffers in the capture pipeline. See 74// VideoCaptureController ctor comments for more details. 75const int kMaxNumberOfBuffers = 3; 76const int kMaxNumberOfBuffersForTabCapture = 5; 77 78// Used for logging capture events. 79// Elements in this enum should not be deleted or rearranged; the only 80// permitted operation is to add new elements before NUM_VIDEO_CAPTURE_EVENT. 81enum VideoCaptureEvent { 82 VIDEO_CAPTURE_EVENT_START_CAPTURE = 0, 83 VIDEO_CAPTURE_EVENT_STOP_CAPTURE_NORMAL = 1, 84 VIDEO_CAPTURE_EVENT_STOP_CAPTURE_DUE_TO_ERROR = 2, 85 NUM_VIDEO_CAPTURE_EVENT 86}; 87 88void LogVideoCaptureEvent(VideoCaptureEvent event) { 89 UMA_HISTOGRAM_ENUMERATION("Media.VideoCaptureManager.Event", 90 event, 91 NUM_VIDEO_CAPTURE_EVENT); 92} 93 94} // namespace 95 96namespace content { 97 98VideoCaptureManager::DeviceEntry::DeviceEntry( 99 MediaStreamType stream_type, 100 const std::string& id, 101 scoped_ptr<VideoCaptureController> controller) 102 : stream_type(stream_type), 103 id(id), 104 video_capture_controller(controller.Pass()) {} 105 106VideoCaptureManager::DeviceEntry::~DeviceEntry() {} 107 108VideoCaptureManager::DeviceInfo::DeviceInfo() {} 109 110VideoCaptureManager::DeviceInfo::DeviceInfo( 111 const media::VideoCaptureDevice::Name& name, 112 const media::VideoCaptureFormats& supported_formats) 113 : name(name), 114 supported_formats(supported_formats) {} 115 116VideoCaptureManager::DeviceInfo::~DeviceInfo() {} 117 118VideoCaptureManager::VideoCaptureManager( 119 scoped_ptr<media::VideoCaptureDeviceFactory> factory) 120 : listener_(NULL), 121 new_capture_session_id_(1), 122 video_capture_device_factory_(factory.Pass()) { 123} 124 125VideoCaptureManager::~VideoCaptureManager() { 126 DCHECK(devices_.empty()); 127} 128 129void VideoCaptureManager::Register( 130 MediaStreamProviderListener* listener, 131 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) { 132 DCHECK_CURRENTLY_ON(BrowserThread::IO); 133 DCHECK(!listener_); 134 DCHECK(!device_task_runner_.get()); 135 listener_ = listener; 136 device_task_runner_ = device_task_runner; 137} 138 139void VideoCaptureManager::Unregister() { 140 DCHECK(listener_); 141 listener_ = NULL; 142} 143 144void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) { 145 DCHECK_CURRENTLY_ON(BrowserThread::IO); 146 DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type; 147 DCHECK(listener_); 148 DCHECK_EQ(stream_type, MEDIA_DEVICE_VIDEO_CAPTURE); 149 150 // Bind a callback to ConsolidateDevicesInfoOnDeviceThread() with an argument 151 // for another callback to OnDevicesInfoEnumerated() to be run in the current 152 // loop, i.e. IO loop. Pass a timer for UMA histogram collection. 153 base::Callback<void(scoped_ptr<media::VideoCaptureDevice::Names>)> 154 devices_enumerated_callback = 155 base::Bind(&VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread, 156 this, 157 media::BindToCurrentLoop(base::Bind( 158 &VideoCaptureManager::OnDevicesInfoEnumerated, 159 this, 160 stream_type, 161 base::Owned(new base::ElapsedTimer()))), 162 stream_type, 163 devices_info_cache_); 164 // OK to use base::Unretained() since we own the VCDFactory and |this| is 165 // bound in |devices_enumerated_callback|. 166 device_task_runner_->PostTask(FROM_HERE, 167 base::Bind(&media::VideoCaptureDeviceFactory::EnumerateDeviceNames, 168 base::Unretained(video_capture_device_factory_.get()), 169 devices_enumerated_callback)); 170} 171 172int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) { 173 DCHECK_CURRENTLY_ON(BrowserThread::IO); 174 DCHECK(listener_); 175 176 // Generate a new id for the session being opened. 177 const media::VideoCaptureSessionId capture_session_id = 178 new_capture_session_id_++; 179 180 DCHECK(sessions_.find(capture_session_id) == sessions_.end()); 181 DVLOG(1) << "VideoCaptureManager::Open, id " << capture_session_id; 182 183 // We just save the stream info for processing later. 184 sessions_[capture_session_id] = device_info.device; 185 186 // Notify our listener asynchronously; this ensures that we return 187 // |capture_session_id| to the caller of this function before using that same 188 // id in a listener event. 189 base::MessageLoop::current()->PostTask(FROM_HERE, 190 base::Bind(&VideoCaptureManager::OnOpened, this, 191 device_info.device.type, capture_session_id)); 192 return capture_session_id; 193} 194 195void VideoCaptureManager::Close(int capture_session_id) { 196 DCHECK_CURRENTLY_ON(BrowserThread::IO); 197 DCHECK(listener_); 198 DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id; 199 200 SessionMap::iterator session_it = sessions_.find(capture_session_id); 201 if (session_it == sessions_.end()) { 202 NOTREACHED(); 203 return; 204 } 205 206 DeviceEntry* const existing_device = GetDeviceEntryForMediaStreamDevice( 207 session_it->second); 208 if (existing_device) { 209 // Remove any client that is still using the session. This is safe to call 210 // even if there are no clients using the session. 211 existing_device->video_capture_controller->StopSession(capture_session_id); 212 213 // StopSession() may have removed the last client, so we might need to 214 // close the device. 215 DestroyDeviceEntryIfNoClients(existing_device); 216 } 217 218 // Notify listeners asynchronously, and forget the session. 219 base::MessageLoop::current()->PostTask(FROM_HERE, 220 base::Bind(&VideoCaptureManager::OnClosed, this, session_it->second.type, 221 capture_session_id)); 222 sessions_.erase(session_it); 223} 224 225void VideoCaptureManager::DoStartDeviceOnDeviceThread( 226 media::VideoCaptureSessionId session_id, 227 DeviceEntry* entry, 228 const media::VideoCaptureParams& params, 229 scoped_ptr<media::VideoCaptureDevice::Client> device_client) { 230 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime"); 231 DCHECK(IsOnDeviceThread()); 232 233 scoped_ptr<media::VideoCaptureDevice> video_capture_device; 234 switch (entry->stream_type) { 235 case MEDIA_DEVICE_VIDEO_CAPTURE: { 236 // We look up the device id from the renderer in our local enumeration 237 // since the renderer does not have all the information that might be 238 // held in the browser-side VideoCaptureDevice::Name structure. 239 DeviceInfo* found = FindDeviceInfoById(entry->id, devices_info_cache_); 240 if (found) { 241 video_capture_device = 242 video_capture_device_factory_->Create(found->name); 243 } 244 break; 245 } 246 case MEDIA_TAB_VIDEO_CAPTURE: { 247 video_capture_device.reset( 248 WebContentsVideoCaptureDevice::Create(entry->id)); 249 break; 250 } 251 case MEDIA_DESKTOP_VIDEO_CAPTURE: { 252#if defined(ENABLE_SCREEN_CAPTURE) 253 DesktopMediaID id = DesktopMediaID::Parse(entry->id); 254#if defined(USE_AURA) 255 if (id.type == DesktopMediaID::TYPE_AURA_WINDOW) { 256 video_capture_device.reset(DesktopCaptureDeviceAura::Create(id)); 257 } else 258#endif 259 if (id.type != DesktopMediaID::TYPE_NONE && 260 id.type != DesktopMediaID::TYPE_AURA_WINDOW) { 261 video_capture_device = DesktopCaptureDevice::Create(id); 262 if (notification_window_ids_.find(session_id) != 263 notification_window_ids_.end()) { 264 static_cast<DesktopCaptureDevice*>(video_capture_device.get()) 265 ->SetNotificationWindowId(notification_window_ids_[session_id]); 266 VLOG(2) << "Screen capture notification window passed for session " 267 << session_id; 268 } 269 } 270#endif // defined(ENABLE_SCREEN_CAPTURE) 271 break; 272 } 273 default: { 274 NOTIMPLEMENTED(); 275 break; 276 } 277 } 278 279 if (!video_capture_device) { 280 device_client->OnError("Could not create capture device"); 281 return; 282 } 283 284 video_capture_device->AllocateAndStart(params, device_client.Pass()); 285 entry->video_capture_device = video_capture_device.Pass(); 286} 287 288void VideoCaptureManager::StartCaptureForClient( 289 media::VideoCaptureSessionId session_id, 290 const media::VideoCaptureParams& params, 291 base::ProcessHandle client_render_process, 292 VideoCaptureControllerID client_id, 293 VideoCaptureControllerEventHandler* client_handler, 294 const DoneCB& done_cb) { 295 DCHECK_CURRENTLY_ON(BrowserThread::IO); 296 DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, " 297 << params.requested_format.frame_size.ToString() << ", " 298 << params.requested_format.frame_rate << ", #" << session_id << ")"; 299 300 DeviceEntry* entry = GetOrCreateDeviceEntry(session_id); 301 if (!entry) { 302 done_cb.Run(base::WeakPtr<VideoCaptureController>()); 303 return; 304 } 305 306 DCHECK(entry->video_capture_controller); 307 308 LogVideoCaptureEvent(VIDEO_CAPTURE_EVENT_START_CAPTURE); 309 310 // First client starts the device. 311 if (entry->video_capture_controller->GetActiveClientCount() == 0) { 312 DVLOG(1) << "VideoCaptureManager starting device (type = " 313 << entry->stream_type << ", id = " << entry->id << ")"; 314 315 device_task_runner_->PostTask( 316 FROM_HERE, 317 base::Bind( 318 &VideoCaptureManager::DoStartDeviceOnDeviceThread, 319 this, 320 session_id, 321 entry, 322 params, 323 base::Passed(entry->video_capture_controller->NewDeviceClient()))); 324 } 325 // Run the callback first, as AddClient() may trigger OnFrameInfo(). 326 done_cb.Run(entry->video_capture_controller->GetWeakPtr()); 327 entry->video_capture_controller->AddClient( 328 client_id, client_handler, client_render_process, session_id, params); 329} 330 331void VideoCaptureManager::StopCaptureForClient( 332 VideoCaptureController* controller, 333 VideoCaptureControllerID client_id, 334 VideoCaptureControllerEventHandler* client_handler, 335 bool aborted_due_to_error) { 336 DCHECK_CURRENTLY_ON(BrowserThread::IO); 337 DCHECK(controller); 338 DCHECK(client_handler); 339 340 LogVideoCaptureEvent(aborted_due_to_error ? 341 VIDEO_CAPTURE_EVENT_STOP_CAPTURE_DUE_TO_ERROR : 342 VIDEO_CAPTURE_EVENT_STOP_CAPTURE_NORMAL); 343 344 DeviceEntry* entry = GetDeviceEntryForController(controller); 345 if (!entry) { 346 NOTREACHED(); 347 return; 348 } 349 if (aborted_due_to_error) { 350 SessionMap::iterator it; 351 for (it = sessions_.begin(); it != sessions_.end(); ++it) { 352 if (it->second.type == entry->stream_type && 353 it->second.id == entry->id) { 354 listener_->Aborted(it->second.type, it->first); 355 break; 356 } 357 } 358 } 359 360 // Detach client from controller. 361 media::VideoCaptureSessionId session_id = 362 controller->RemoveClient(client_id, client_handler); 363 DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = " 364 << session_id; 365 366 // If controller has no more clients, delete controller and device. 367 DestroyDeviceEntryIfNoClients(entry); 368} 369 370void VideoCaptureManager::PauseCaptureForClient( 371 VideoCaptureController* controller, 372 VideoCaptureControllerID client_id, 373 VideoCaptureControllerEventHandler* client_handler) { 374 DCHECK_CURRENTLY_ON(BrowserThread::IO); 375 DCHECK(controller); 376 DCHECK(client_handler); 377 DeviceEntry* entry = GetDeviceEntryForController(controller); 378 if (!entry) { 379 NOTREACHED(); 380 return; 381 } 382 383 // We only pause the MEDIA_DEVICE_VIDEO_CAPTURE entry to release camera to 384 // system. 385 if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE) 386 return; 387 388 controller->PauseOrResumeClient(client_id, client_handler, true); 389 if (controller->GetActiveClientCount() != 0) 390 return; 391 392 // There is no more client, release the camera. 393 device_task_runner_->PostTask( 394 FROM_HERE, 395 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, 396 base::Unretained(entry))); 397} 398 399void VideoCaptureManager::ResumeCaptureForClient( 400 media::VideoCaptureSessionId session_id, 401 const media::VideoCaptureParams& params, 402 VideoCaptureController* controller, 403 VideoCaptureControllerID client_id, 404 VideoCaptureControllerEventHandler* client_handler) { 405 DCHECK_CURRENTLY_ON(BrowserThread::IO); 406 DCHECK(controller); 407 DCHECK(client_handler); 408 409 DeviceEntry* entry = GetDeviceEntryForController(controller); 410 if (!entry) { 411 NOTREACHED(); 412 return; 413 } 414 415 // We only pause/resume the MEDIA_DEVICE_VIDEO_CAPTURE entry. 416 if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE) 417 return; 418 419 controller->PauseOrResumeClient(client_id, client_handler, false); 420 if (controller->GetActiveClientCount() != 1) 421 return; 422 423 // This is first active client, allocate the camera. 424 device_task_runner_->PostTask( 425 FROM_HERE, 426 base::Bind( 427 &VideoCaptureManager::DoStartDeviceOnDeviceThread, 428 this, 429 session_id, 430 entry, 431 params, 432 base::Passed(entry->video_capture_controller->NewDeviceClient()))); 433} 434 435bool VideoCaptureManager::GetDeviceSupportedFormats( 436 media::VideoCaptureSessionId capture_session_id, 437 media::VideoCaptureFormats* supported_formats) { 438 DCHECK_CURRENTLY_ON(BrowserThread::IO); 439 DCHECK(supported_formats->empty()); 440 441 SessionMap::iterator it = sessions_.find(capture_session_id); 442 if (it == sessions_.end()) 443 return false; 444 DVLOG(1) << "GetDeviceSupportedFormats for device: " << it->second.name; 445 446 // Return all available formats of the device, regardless its started state. 447 DeviceInfo* existing_device = 448 FindDeviceInfoById(it->second.id, devices_info_cache_); 449 if (existing_device) 450 *supported_formats = existing_device->supported_formats; 451 return true; 452} 453 454bool VideoCaptureManager::GetDeviceFormatsInUse( 455 media::VideoCaptureSessionId capture_session_id, 456 media::VideoCaptureFormats* formats_in_use) { 457 DCHECK_CURRENTLY_ON(BrowserThread::IO); 458 DCHECK(formats_in_use->empty()); 459 460 SessionMap::iterator it = sessions_.find(capture_session_id); 461 if (it == sessions_.end()) 462 return false; 463 DVLOG(1) << "GetDeviceFormatsInUse for device: " << it->second.name; 464 465 // Return the currently in-use format(s) of the device, if it's started. 466 DeviceEntry* device_in_use = 467 GetDeviceEntryForMediaStreamDevice(it->second); 468 if (device_in_use) { 469 // Currently only one format-in-use is supported at the VCC level. 470 formats_in_use->push_back( 471 device_in_use->video_capture_controller->GetVideoCaptureFormat()); 472 } 473 return true; 474} 475 476void VideoCaptureManager::SetDesktopCaptureWindowId( 477 media::VideoCaptureSessionId session_id, 478 gfx::NativeViewId window_id) { 479 DCHECK_CURRENTLY_ON(BrowserThread::IO); 480 VLOG(2) << "SetDesktopCaptureWindowId called for session " << session_id; 481 482 SessionMap::iterator session_it = sessions_.find(session_id); 483 if (session_it == sessions_.end()) { 484 VLOG(2) << "Session not found, will save the notification window."; 485 device_task_runner_->PostTask( 486 FROM_HERE, 487 base::Bind( 488 &VideoCaptureManager::SaveDesktopCaptureWindowIdOnDeviceThread, 489 this, 490 session_id, 491 window_id)); 492 return; 493 } 494 495 DeviceEntry* const existing_device = 496 GetDeviceEntryForMediaStreamDevice(session_it->second); 497 if (!existing_device) { 498 VLOG(2) << "Failed to find an existing device."; 499 return; 500 } 501 502 DCHECK_EQ(MEDIA_DESKTOP_VIDEO_CAPTURE, existing_device->stream_type); 503 DesktopMediaID id = DesktopMediaID::Parse(existing_device->id); 504 if (id.type == DesktopMediaID::TYPE_NONE || 505 id.type == DesktopMediaID::TYPE_AURA_WINDOW) { 506 VLOG(2) << "Video capture device type mismatch."; 507 return; 508 } 509 510 device_task_runner_->PostTask( 511 FROM_HERE, 512 base::Bind(&VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread, 513 this, 514 existing_device, 515 window_id)); 516} 517 518void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) { 519 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime"); 520 DCHECK(IsOnDeviceThread()); 521 if (entry->video_capture_device) { 522 entry->video_capture_device->StopAndDeAllocate(); 523 } 524 entry->video_capture_device.reset(); 525} 526 527void VideoCaptureManager::OnOpened( 528 MediaStreamType stream_type, 529 media::VideoCaptureSessionId capture_session_id) { 530 DCHECK_CURRENTLY_ON(BrowserThread::IO); 531 if (!listener_) { 532 // Listener has been removed. 533 return; 534 } 535 listener_->Opened(stream_type, capture_session_id); 536} 537 538void VideoCaptureManager::OnClosed( 539 MediaStreamType stream_type, 540 media::VideoCaptureSessionId capture_session_id) { 541 DCHECK_CURRENTLY_ON(BrowserThread::IO); 542 if (!listener_) { 543 // Listener has been removed. 544 return; 545 } 546 listener_->Closed(stream_type, capture_session_id); 547} 548 549void VideoCaptureManager::OnDevicesInfoEnumerated( 550 MediaStreamType stream_type, 551 base::ElapsedTimer* timer, 552 const DeviceInfos& new_devices_info_cache) { 553 DCHECK_CURRENTLY_ON(BrowserThread::IO); 554 UMA_HISTOGRAM_TIMES( 555 "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime", 556 timer->Elapsed()); 557 if (!listener_) { 558 // Listener has been removed. 559 return; 560 } 561 devices_info_cache_ = new_devices_info_cache; 562 563 // Walk the |devices_info_cache_| and transform from VCD::Name to 564 // StreamDeviceInfo for return purposes. 565 StreamDeviceInfoArray devices; 566 for (DeviceInfos::const_iterator it = devices_info_cache_.begin(); 567 it != devices_info_cache_.end(); ++it) { 568 devices.push_back(StreamDeviceInfo( 569 stream_type, it->name.GetNameAndModel(), it->name.id())); 570 } 571 listener_->DevicesEnumerated(stream_type, devices); 572} 573 574bool VideoCaptureManager::IsOnDeviceThread() const { 575 return device_task_runner_->BelongsToCurrentThread(); 576} 577 578void VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread( 579 base::Callback<void(const DeviceInfos&)> on_devices_enumerated_callback, 580 MediaStreamType stream_type, 581 const DeviceInfos& old_device_info_cache, 582 scoped_ptr<media::VideoCaptureDevice::Names> names_snapshot) { 583 DCHECK(IsOnDeviceThread()); 584 // Construct |new_devices_info_cache| with the cached devices that are still 585 // present in the system, and remove their names from |names_snapshot|, so we 586 // keep there the truly new devices. 587 DeviceInfos new_devices_info_cache; 588 for (DeviceInfos::const_iterator it_device_info = 589 old_device_info_cache.begin(); 590 it_device_info != old_device_info_cache.end(); ++it_device_info) { 591 for (media::VideoCaptureDevice::Names::iterator it = 592 names_snapshot->begin(); 593 it != names_snapshot->end(); ++it) { 594 if (it_device_info->name.id() == it->id()) { 595 new_devices_info_cache.push_back(*it_device_info); 596 names_snapshot->erase(it); 597 break; 598 } 599 } 600 } 601 602 // Get the supported capture formats for the new devices in |names_snapshot|. 603 for (media::VideoCaptureDevice::Names::const_iterator it = 604 names_snapshot->begin(); 605 it != names_snapshot->end(); ++it) { 606 media::VideoCaptureFormats supported_formats; 607 DeviceInfo device_info(*it, media::VideoCaptureFormats()); 608 video_capture_device_factory_->GetDeviceSupportedFormats( 609 *it, &(device_info.supported_formats)); 610 ConsolidateCaptureFormats(&device_info.supported_formats); 611 new_devices_info_cache.push_back(device_info); 612 } 613 614 on_devices_enumerated_callback.Run(new_devices_info_cache); 615} 616 617VideoCaptureManager::DeviceEntry* 618VideoCaptureManager::GetDeviceEntryForMediaStreamDevice( 619 const MediaStreamDevice& device_info) { 620 DCHECK_CURRENTLY_ON(BrowserThread::IO); 621 622 for (DeviceEntries::iterator it = devices_.begin(); 623 it != devices_.end(); ++it) { 624 DeviceEntry* device = *it; 625 if (device_info.type == device->stream_type && 626 device_info.id == device->id) { 627 return device; 628 } 629 } 630 return NULL; 631} 632 633VideoCaptureManager::DeviceEntry* 634VideoCaptureManager::GetDeviceEntryForController( 635 const VideoCaptureController* controller) const { 636 // Look up |controller| in |devices_|. 637 for (DeviceEntries::const_iterator it = devices_.begin(); 638 it != devices_.end(); ++it) { 639 if ((*it)->video_capture_controller.get() == controller) { 640 return *it; 641 } 642 } 643 return NULL; 644} 645 646void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) { 647 DCHECK_CURRENTLY_ON(BrowserThread::IO); 648 // Removal of the last client stops the device. 649 if (entry->video_capture_controller->GetClientCount() == 0) { 650 DVLOG(1) << "VideoCaptureManager stopping device (type = " 651 << entry->stream_type << ", id = " << entry->id << ")"; 652 653 // The DeviceEntry is removed from |devices_| immediately. The controller is 654 // deleted immediately, and the device is freed asynchronously. After this 655 // point, subsequent requests to open this same device ID will create a new 656 // DeviceEntry, VideoCaptureController, and VideoCaptureDevice. 657 devices_.erase(entry); 658 entry->video_capture_controller.reset(); 659 device_task_runner_->PostTask( 660 FROM_HERE, 661 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, 662 base::Owned(entry))); 663 } 664} 665 666VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry( 667 media::VideoCaptureSessionId capture_session_id) { 668 DCHECK_CURRENTLY_ON(BrowserThread::IO); 669 670 SessionMap::iterator session_it = sessions_.find(capture_session_id); 671 if (session_it == sessions_.end()) { 672 return NULL; 673 } 674 const MediaStreamDevice& device_info = session_it->second; 675 676 // Check if another session has already opened this device. If so, just 677 // use that opened device. 678 DeviceEntry* const existing_device = 679 GetDeviceEntryForMediaStreamDevice(device_info); 680 if (existing_device) { 681 DCHECK_EQ(device_info.type, existing_device->stream_type); 682 return existing_device; 683 } 684 685 const int max_buffers = device_info.type == MEDIA_TAB_VIDEO_CAPTURE ? 686 kMaxNumberOfBuffersForTabCapture : kMaxNumberOfBuffers; 687 scoped_ptr<VideoCaptureController> video_capture_controller( 688 new VideoCaptureController(max_buffers)); 689 DeviceEntry* new_device = new DeviceEntry(device_info.type, 690 device_info.id, 691 video_capture_controller.Pass()); 692 devices_.insert(new_device); 693 return new_device; 694} 695 696VideoCaptureManager::DeviceInfo* VideoCaptureManager::FindDeviceInfoById( 697 const std::string& id, 698 DeviceInfos& device_vector) { 699 for (DeviceInfos::iterator it = device_vector.begin(); 700 it != device_vector.end(); ++it) { 701 if (it->name.id() == id) 702 return &(*it); 703 } 704 return NULL; 705} 706 707void VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread( 708 DeviceEntry* entry, 709 gfx::NativeViewId window_id) { 710 DCHECK(IsOnDeviceThread()); 711 DCHECK(entry->stream_type == MEDIA_DESKTOP_VIDEO_CAPTURE); 712#if defined(ENABLE_SCREEN_CAPTURE) 713 DesktopCaptureDevice* device = 714 static_cast<DesktopCaptureDevice*>(entry->video_capture_device.get()); 715 device->SetNotificationWindowId(window_id); 716 VLOG(2) << "Screen capture notification window passed on device thread."; 717#endif 718} 719 720void VideoCaptureManager::SaveDesktopCaptureWindowIdOnDeviceThread( 721 media::VideoCaptureSessionId session_id, 722 gfx::NativeViewId window_id) { 723 DCHECK(IsOnDeviceThread()); 724 DCHECK(notification_window_ids_.find(session_id) == 725 notification_window_ids_.end()); 726 notification_window_ids_[session_id] = window_id; 727 VLOG(2) << "Screen capture notification window saved for session " 728 << session_id << " on device thread."; 729} 730 731} // namespace content 732