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} // namespace 74 75namespace content { 76 77VideoCaptureManager::DeviceEntry::DeviceEntry( 78 MediaStreamType stream_type, 79 const std::string& id, 80 scoped_ptr<VideoCaptureController> controller) 81 : stream_type(stream_type), 82 id(id), 83 video_capture_controller(controller.Pass()) {} 84 85VideoCaptureManager::DeviceEntry::~DeviceEntry() {} 86 87VideoCaptureManager::DeviceInfo::DeviceInfo() {} 88 89VideoCaptureManager::DeviceInfo::DeviceInfo( 90 const media::VideoCaptureDevice::Name& name, 91 const media::VideoCaptureFormats& supported_formats) 92 : name(name), 93 supported_formats(supported_formats) {} 94 95VideoCaptureManager::DeviceInfo::~DeviceInfo() {} 96 97VideoCaptureManager::VideoCaptureManager( 98 scoped_ptr<media::VideoCaptureDeviceFactory> factory) 99 : listener_(NULL), 100 new_capture_session_id_(1), 101 video_capture_device_factory_(factory.Pass()) { 102} 103 104VideoCaptureManager::~VideoCaptureManager() { 105 DCHECK(devices_.empty()); 106} 107 108void VideoCaptureManager::Register( 109 MediaStreamProviderListener* listener, 110 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) { 111 DCHECK_CURRENTLY_ON(BrowserThread::IO); 112 DCHECK(!listener_); 113 DCHECK(!device_task_runner_.get()); 114 listener_ = listener; 115 device_task_runner_ = device_task_runner; 116} 117 118void VideoCaptureManager::Unregister() { 119 DCHECK(listener_); 120 listener_ = NULL; 121} 122 123void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) { 124 DCHECK_CURRENTLY_ON(BrowserThread::IO); 125 DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type; 126 DCHECK(listener_); 127 DCHECK_EQ(stream_type, MEDIA_DEVICE_VIDEO_CAPTURE); 128 129 // Bind a callback to ConsolidateDevicesInfoOnDeviceThread() with an argument 130 // for another callback to OnDevicesInfoEnumerated() to be run in the current 131 // loop, i.e. IO loop. Pass a timer for UMA histogram collection. 132 base::Callback<void(scoped_ptr<media::VideoCaptureDevice::Names>)> 133 devices_enumerated_callback = 134 base::Bind(&VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread, 135 this, 136 media::BindToCurrentLoop(base::Bind( 137 &VideoCaptureManager::OnDevicesInfoEnumerated, 138 this, 139 stream_type, 140 base::Owned(new base::ElapsedTimer()))), 141 stream_type, 142 devices_info_cache_); 143 // OK to use base::Unretained() since we own the VCDFactory and |this| is 144 // bound in |devices_enumerated_callback|. 145 device_task_runner_->PostTask(FROM_HERE, 146 base::Bind(&media::VideoCaptureDeviceFactory::EnumerateDeviceNames, 147 base::Unretained(video_capture_device_factory_.get()), 148 devices_enumerated_callback)); 149} 150 151int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) { 152 DCHECK_CURRENTLY_ON(BrowserThread::IO); 153 DCHECK(listener_); 154 155 // Generate a new id for the session being opened. 156 const media::VideoCaptureSessionId capture_session_id = 157 new_capture_session_id_++; 158 159 DCHECK(sessions_.find(capture_session_id) == sessions_.end()); 160 DVLOG(1) << "VideoCaptureManager::Open, id " << capture_session_id; 161 162 // We just save the stream info for processing later. 163 sessions_[capture_session_id] = device_info.device; 164 165 // Notify our listener asynchronously; this ensures that we return 166 // |capture_session_id| to the caller of this function before using that same 167 // id in a listener event. 168 base::MessageLoop::current()->PostTask(FROM_HERE, 169 base::Bind(&VideoCaptureManager::OnOpened, this, 170 device_info.device.type, capture_session_id)); 171 return capture_session_id; 172} 173 174void VideoCaptureManager::Close(int capture_session_id) { 175 DCHECK_CURRENTLY_ON(BrowserThread::IO); 176 DCHECK(listener_); 177 DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id; 178 179 SessionMap::iterator session_it = sessions_.find(capture_session_id); 180 if (session_it == sessions_.end()) { 181 NOTREACHED(); 182 return; 183 } 184 185 DeviceEntry* const existing_device = GetDeviceEntryForMediaStreamDevice( 186 session_it->second); 187 if (existing_device) { 188 // Remove any client that is still using the session. This is safe to call 189 // even if there are no clients using the session. 190 existing_device->video_capture_controller->StopSession(capture_session_id); 191 192 // StopSession() may have removed the last client, so we might need to 193 // close the device. 194 DestroyDeviceEntryIfNoClients(existing_device); 195 } 196 197 // Notify listeners asynchronously, and forget the session. 198 base::MessageLoop::current()->PostTask(FROM_HERE, 199 base::Bind(&VideoCaptureManager::OnClosed, this, session_it->second.type, 200 capture_session_id)); 201 sessions_.erase(session_it); 202} 203 204void VideoCaptureManager::DoStartDeviceOnDeviceThread( 205 media::VideoCaptureSessionId session_id, 206 DeviceEntry* entry, 207 const media::VideoCaptureParams& params, 208 scoped_ptr<media::VideoCaptureDevice::Client> device_client) { 209 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime"); 210 DCHECK(IsOnDeviceThread()); 211 212 scoped_ptr<media::VideoCaptureDevice> video_capture_device; 213 switch (entry->stream_type) { 214 case MEDIA_DEVICE_VIDEO_CAPTURE: { 215 // We look up the device id from the renderer in our local enumeration 216 // since the renderer does not have all the information that might be 217 // held in the browser-side VideoCaptureDevice::Name structure. 218 DeviceInfo* found = FindDeviceInfoById(entry->id, devices_info_cache_); 219 if (found) { 220 video_capture_device = 221 video_capture_device_factory_->Create(found->name); 222 } 223 break; 224 } 225 case MEDIA_TAB_VIDEO_CAPTURE: { 226 video_capture_device.reset( 227 WebContentsVideoCaptureDevice::Create(entry->id)); 228 break; 229 } 230 case MEDIA_DESKTOP_VIDEO_CAPTURE: { 231#if defined(ENABLE_SCREEN_CAPTURE) 232 DesktopMediaID id = DesktopMediaID::Parse(entry->id); 233#if defined(USE_AURA) 234 if (id.type == DesktopMediaID::TYPE_AURA_WINDOW) { 235 video_capture_device.reset(DesktopCaptureDeviceAura::Create(id)); 236 } else 237#endif 238 if (id.type != DesktopMediaID::TYPE_NONE && 239 id.type != DesktopMediaID::TYPE_AURA_WINDOW) { 240 video_capture_device = DesktopCaptureDevice::Create(id); 241 if (notification_window_ids_.find(session_id) != 242 notification_window_ids_.end()) { 243 static_cast<DesktopCaptureDevice*>(video_capture_device.get()) 244 ->SetNotificationWindowId(notification_window_ids_[session_id]); 245 } 246 } 247#endif // defined(ENABLE_SCREEN_CAPTURE) 248 break; 249 } 250 default: { 251 NOTIMPLEMENTED(); 252 break; 253 } 254 } 255 256 if (!video_capture_device) { 257 device_client->OnError("Could not create capture device"); 258 return; 259 } 260 261 video_capture_device->AllocateAndStart(params, device_client.Pass()); 262 entry->video_capture_device = video_capture_device.Pass(); 263} 264 265void VideoCaptureManager::StartCaptureForClient( 266 media::VideoCaptureSessionId session_id, 267 const media::VideoCaptureParams& params, 268 base::ProcessHandle client_render_process, 269 VideoCaptureControllerID client_id, 270 VideoCaptureControllerEventHandler* client_handler, 271 const DoneCB& done_cb) { 272 DCHECK_CURRENTLY_ON(BrowserThread::IO); 273 DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, " 274 << params.requested_format.frame_size.ToString() << ", " 275 << params.requested_format.frame_rate << ", #" << session_id << ")"; 276 277 DeviceEntry* entry = GetOrCreateDeviceEntry(session_id); 278 if (!entry) { 279 done_cb.Run(base::WeakPtr<VideoCaptureController>()); 280 return; 281 } 282 283 DCHECK(entry->video_capture_controller); 284 285 // First client starts the device. 286 if (entry->video_capture_controller->GetClientCount() == 0) { 287 DVLOG(1) << "VideoCaptureManager starting device (type = " 288 << entry->stream_type << ", id = " << entry->id << ")"; 289 290 device_task_runner_->PostTask( 291 FROM_HERE, 292 base::Bind( 293 &VideoCaptureManager::DoStartDeviceOnDeviceThread, 294 this, 295 session_id, 296 entry, 297 params, 298 base::Passed(entry->video_capture_controller->NewDeviceClient()))); 299 } 300 // Run the callback first, as AddClient() may trigger OnFrameInfo(). 301 done_cb.Run(entry->video_capture_controller->GetWeakPtr()); 302 entry->video_capture_controller->AddClient( 303 client_id, client_handler, client_render_process, session_id, params); 304} 305 306void VideoCaptureManager::StopCaptureForClient( 307 VideoCaptureController* controller, 308 VideoCaptureControllerID client_id, 309 VideoCaptureControllerEventHandler* client_handler, 310 bool aborted_due_to_error) { 311 DCHECK_CURRENTLY_ON(BrowserThread::IO); 312 DCHECK(controller); 313 DCHECK(client_handler); 314 315 DeviceEntry* entry = GetDeviceEntryForController(controller); 316 if (!entry) { 317 NOTREACHED(); 318 return; 319 } 320 if (aborted_due_to_error) { 321 SessionMap::iterator it; 322 for (it = sessions_.begin(); it != sessions_.end(); ++it) { 323 if (it->second.type == entry->stream_type && 324 it->second.id == entry->id) { 325 listener_->Aborted(it->second.type, it->first); 326 break; 327 } 328 } 329 } 330 331 // Detach client from controller. 332 media::VideoCaptureSessionId session_id = 333 controller->RemoveClient(client_id, client_handler); 334 DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = " 335 << session_id; 336 337 // If controller has no more clients, delete controller and device. 338 DestroyDeviceEntryIfNoClients(entry); 339} 340 341bool VideoCaptureManager::GetDeviceSupportedFormats( 342 media::VideoCaptureSessionId capture_session_id, 343 media::VideoCaptureFormats* supported_formats) { 344 DCHECK_CURRENTLY_ON(BrowserThread::IO); 345 DCHECK(supported_formats->empty()); 346 347 SessionMap::iterator it = sessions_.find(capture_session_id); 348 if (it == sessions_.end()) 349 return false; 350 DVLOG(1) << "GetDeviceSupportedFormats for device: " << it->second.name; 351 352 // Return all available formats of the device, regardless its started state. 353 DeviceInfo* existing_device = 354 FindDeviceInfoById(it->second.id, devices_info_cache_); 355 if (existing_device) 356 *supported_formats = existing_device->supported_formats; 357 return true; 358} 359 360bool VideoCaptureManager::GetDeviceFormatsInUse( 361 media::VideoCaptureSessionId capture_session_id, 362 media::VideoCaptureFormats* formats_in_use) { 363 DCHECK_CURRENTLY_ON(BrowserThread::IO); 364 DCHECK(formats_in_use->empty()); 365 366 SessionMap::iterator it = sessions_.find(capture_session_id); 367 if (it == sessions_.end()) 368 return false; 369 DVLOG(1) << "GetDeviceFormatsInUse for device: " << it->second.name; 370 371 // Return the currently in-use format(s) of the device, if it's started. 372 DeviceEntry* device_in_use = 373 GetDeviceEntryForMediaStreamDevice(it->second); 374 if (device_in_use) { 375 // Currently only one format-in-use is supported at the VCC level. 376 formats_in_use->push_back( 377 device_in_use->video_capture_controller->GetVideoCaptureFormat()); 378 } 379 return true; 380} 381 382void VideoCaptureManager::SetDesktopCaptureWindowId( 383 media::VideoCaptureSessionId session_id, 384 gfx::NativeViewId window_id) { 385 DCHECK_CURRENTLY_ON(BrowserThread::IO); 386 SessionMap::iterator session_it = sessions_.find(session_id); 387 if (session_it == sessions_.end()) { 388 device_task_runner_->PostTask( 389 FROM_HERE, 390 base::Bind( 391 &VideoCaptureManager::SaveDesktopCaptureWindowIdOnDeviceThread, 392 this, 393 session_id, 394 window_id)); 395 return; 396 } 397 398 DeviceEntry* const existing_device = 399 GetDeviceEntryForMediaStreamDevice(session_it->second); 400 if (!existing_device) 401 return; 402 403 DCHECK_EQ(MEDIA_DESKTOP_VIDEO_CAPTURE, existing_device->stream_type); 404 DesktopMediaID id = DesktopMediaID::Parse(existing_device->id); 405 if (id.type == DesktopMediaID::TYPE_NONE || 406 id.type == DesktopMediaID::TYPE_AURA_WINDOW) { 407 return; 408 } 409 410 device_task_runner_->PostTask( 411 FROM_HERE, 412 base::Bind(&VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread, 413 this, 414 existing_device, 415 window_id)); 416} 417 418void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) { 419 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime"); 420 DCHECK(IsOnDeviceThread()); 421 if (entry->video_capture_device) { 422 entry->video_capture_device->StopAndDeAllocate(); 423 } 424 entry->video_capture_device.reset(); 425} 426 427void VideoCaptureManager::OnOpened( 428 MediaStreamType stream_type, 429 media::VideoCaptureSessionId capture_session_id) { 430 DCHECK_CURRENTLY_ON(BrowserThread::IO); 431 if (!listener_) { 432 // Listener has been removed. 433 return; 434 } 435 listener_->Opened(stream_type, capture_session_id); 436} 437 438void VideoCaptureManager::OnClosed( 439 MediaStreamType stream_type, 440 media::VideoCaptureSessionId capture_session_id) { 441 DCHECK_CURRENTLY_ON(BrowserThread::IO); 442 if (!listener_) { 443 // Listener has been removed. 444 return; 445 } 446 listener_->Closed(stream_type, capture_session_id); 447} 448 449void VideoCaptureManager::OnDevicesInfoEnumerated( 450 MediaStreamType stream_type, 451 base::ElapsedTimer* timer, 452 const DeviceInfos& new_devices_info_cache) { 453 DCHECK_CURRENTLY_ON(BrowserThread::IO); 454 UMA_HISTOGRAM_TIMES( 455 "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime", 456 timer->Elapsed()); 457 if (!listener_) { 458 // Listener has been removed. 459 return; 460 } 461 devices_info_cache_ = new_devices_info_cache; 462 463 // Walk the |devices_info_cache_| and transform from VCD::Name to 464 // StreamDeviceInfo for return purposes. 465 StreamDeviceInfoArray devices; 466 for (DeviceInfos::const_iterator it = devices_info_cache_.begin(); 467 it != devices_info_cache_.end(); ++it) { 468 devices.push_back(StreamDeviceInfo( 469 stream_type, it->name.GetNameAndModel(), it->name.id())); 470 } 471 listener_->DevicesEnumerated(stream_type, devices); 472} 473 474bool VideoCaptureManager::IsOnDeviceThread() const { 475 return device_task_runner_->BelongsToCurrentThread(); 476} 477 478void VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread( 479 base::Callback<void(const DeviceInfos&)> on_devices_enumerated_callback, 480 MediaStreamType stream_type, 481 const DeviceInfos& old_device_info_cache, 482 scoped_ptr<media::VideoCaptureDevice::Names> names_snapshot) { 483 DCHECK(IsOnDeviceThread()); 484 // Construct |new_devices_info_cache| with the cached devices that are still 485 // present in the system, and remove their names from |names_snapshot|, so we 486 // keep there the truly new devices. 487 DeviceInfos new_devices_info_cache; 488 for (DeviceInfos::const_iterator it_device_info = 489 old_device_info_cache.begin(); 490 it_device_info != old_device_info_cache.end(); ++it_device_info) { 491 for (media::VideoCaptureDevice::Names::iterator it = 492 names_snapshot->begin(); 493 it != names_snapshot->end(); ++it) { 494 if (it_device_info->name.id() == it->id()) { 495 new_devices_info_cache.push_back(*it_device_info); 496 names_snapshot->erase(it); 497 break; 498 } 499 } 500 } 501 502 // Get the supported capture formats for the new devices in |names_snapshot|. 503 for (media::VideoCaptureDevice::Names::const_iterator it = 504 names_snapshot->begin(); 505 it != names_snapshot->end(); ++it) { 506 media::VideoCaptureFormats supported_formats; 507 DeviceInfo device_info(*it, media::VideoCaptureFormats()); 508 video_capture_device_factory_->GetDeviceSupportedFormats( 509 *it, &(device_info.supported_formats)); 510 ConsolidateCaptureFormats(&device_info.supported_formats); 511 new_devices_info_cache.push_back(device_info); 512 } 513 514 on_devices_enumerated_callback.Run(new_devices_info_cache); 515} 516 517VideoCaptureManager::DeviceEntry* 518VideoCaptureManager::GetDeviceEntryForMediaStreamDevice( 519 const MediaStreamDevice& device_info) { 520 DCHECK_CURRENTLY_ON(BrowserThread::IO); 521 522 for (DeviceEntries::iterator it = devices_.begin(); 523 it != devices_.end(); ++it) { 524 DeviceEntry* device = *it; 525 if (device_info.type == device->stream_type && 526 device_info.id == device->id) { 527 return device; 528 } 529 } 530 return NULL; 531} 532 533VideoCaptureManager::DeviceEntry* 534VideoCaptureManager::GetDeviceEntryForController( 535 const VideoCaptureController* controller) const { 536 // Look up |controller| in |devices_|. 537 for (DeviceEntries::const_iterator it = devices_.begin(); 538 it != devices_.end(); ++it) { 539 if ((*it)->video_capture_controller.get() == controller) { 540 return *it; 541 } 542 } 543 return NULL; 544} 545 546void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) { 547 DCHECK_CURRENTLY_ON(BrowserThread::IO); 548 // Removal of the last client stops the device. 549 if (entry->video_capture_controller->GetClientCount() == 0) { 550 DVLOG(1) << "VideoCaptureManager stopping device (type = " 551 << entry->stream_type << ", id = " << entry->id << ")"; 552 553 // The DeviceEntry is removed from |devices_| immediately. The controller is 554 // deleted immediately, and the device is freed asynchronously. After this 555 // point, subsequent requests to open this same device ID will create a new 556 // DeviceEntry, VideoCaptureController, and VideoCaptureDevice. 557 devices_.erase(entry); 558 entry->video_capture_controller.reset(); 559 device_task_runner_->PostTask( 560 FROM_HERE, 561 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, 562 base::Owned(entry))); 563 } 564} 565 566VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry( 567 media::VideoCaptureSessionId capture_session_id) { 568 DCHECK_CURRENTLY_ON(BrowserThread::IO); 569 570 SessionMap::iterator session_it = sessions_.find(capture_session_id); 571 if (session_it == sessions_.end()) { 572 return NULL; 573 } 574 const MediaStreamDevice& device_info = session_it->second; 575 576 // Check if another session has already opened this device. If so, just 577 // use that opened device. 578 DeviceEntry* const existing_device = 579 GetDeviceEntryForMediaStreamDevice(device_info); 580 if (existing_device) { 581 DCHECK_EQ(device_info.type, existing_device->stream_type); 582 return existing_device; 583 } 584 585 scoped_ptr<VideoCaptureController> video_capture_controller( 586 new VideoCaptureController()); 587 DeviceEntry* new_device = new DeviceEntry(device_info.type, 588 device_info.id, 589 video_capture_controller.Pass()); 590 devices_.insert(new_device); 591 return new_device; 592} 593 594VideoCaptureManager::DeviceInfo* VideoCaptureManager::FindDeviceInfoById( 595 const std::string& id, 596 DeviceInfos& device_vector) { 597 for (DeviceInfos::iterator it = device_vector.begin(); 598 it != device_vector.end(); ++it) { 599 if (it->name.id() == id) 600 return &(*it); 601 } 602 return NULL; 603} 604 605void VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread( 606 DeviceEntry* entry, 607 gfx::NativeViewId window_id) { 608 DCHECK(IsOnDeviceThread()); 609 DCHECK(entry->stream_type == MEDIA_DESKTOP_VIDEO_CAPTURE); 610#if defined(ENABLE_SCREEN_CAPTURE) 611 DesktopCaptureDevice* device = 612 static_cast<DesktopCaptureDevice*>(entry->video_capture_device.get()); 613 device->SetNotificationWindowId(window_id); 614#endif 615} 616 617void VideoCaptureManager::SaveDesktopCaptureWindowIdOnDeviceThread( 618 media::VideoCaptureSessionId session_id, 619 gfx::NativeViewId window_id) { 620 DCHECK(IsOnDeviceThread()); 621 DCHECK(notification_window_ids_.find(session_id) == 622 notification_window_ids_.end()); 623 notification_window_ids_[session_id] = window_id; 624} 625 626} // namespace content 627