Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / content / browser / renderer_host / media / video_capture_manager.cc
blob6c8a94450a142984bd292fc508feb5464862f10c
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.
5 #include "content/browser/renderer_host/media/video_capture_manager.h"
7 #include <algorithm>
8 #include <set>
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/command_line.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/stl_util.h"
18 #include "base/task_runner_util.h"
19 #include "base/thread_task_runner_handle.h"
20 #include "base/threading/sequenced_worker_pool.h"
21 #include "content/browser/media/capture/web_contents_video_capture_device.h"
22 #include "content/browser/media/media_internals.h"
23 #include "content/browser/renderer_host/media/video_capture_controller.h"
24 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/desktop_media_id.h"
27 #include "content/public/common/media_stream_request.h"
28 #include "media/base/bind_to_current_loop.h"
29 #include "media/base/media_switches.h"
30 #include "media/capture/video/video_capture_device.h"
31 #include "media/capture/video/video_capture_device_factory.h"
33 #if defined(ENABLE_SCREEN_CAPTURE)
34 #include "content/browser/media/capture/desktop_capture_device.h"
35 #if defined(USE_AURA)
36 #include "content/browser/media/capture/desktop_capture_device_aura.h"
37 #endif
38 #endif
40 namespace {
42 // Compares two VideoCaptureFormat by checking smallest frame_size area, then
43 // by _largest_ frame_rate. Used to order a VideoCaptureFormats vector so that
44 // the first entry for a given resolution has the largest frame rate, as needed
45 // by the ConsolidateCaptureFormats() method.
46 bool IsCaptureFormatSmaller(const media::VideoCaptureFormat& format1,
47 const media::VideoCaptureFormat& format2) {
48 if (format1.frame_size.GetArea() == format2.frame_size.GetArea())
49 return format1.frame_rate > format2.frame_rate;
50 return format1.frame_size.GetArea() < format2.frame_size.GetArea();
53 bool IsCaptureFormatSizeEqual(const media::VideoCaptureFormat& format1,
54 const media::VideoCaptureFormat& format2) {
55 return format1.frame_size.GetArea() == format2.frame_size.GetArea();
58 // This function receives a list of capture formats, removes duplicated
59 // resolutions while keeping the highest frame rate for each, and forcing I420
60 // pixel format.
61 void ConsolidateCaptureFormats(media::VideoCaptureFormats* formats) {
62 if (formats->empty())
63 return;
64 std::sort(formats->begin(), formats->end(), IsCaptureFormatSmaller);
65 // Due to the ordering imposed, the largest frame_rate is kept while removing
66 // duplicated resolutions.
67 media::VideoCaptureFormats::iterator last =
68 std::unique(formats->begin(), formats->end(), IsCaptureFormatSizeEqual);
69 formats->erase(last, formats->end());
70 // Mark all formats as I420, since this is what the renderer side will get
71 // anyhow: the actual pixel format is decided at the device level.
72 for (media::VideoCaptureFormats::iterator it = formats->begin();
73 it != formats->end(); ++it) {
74 it->pixel_format = media::VIDEO_CAPTURE_PIXEL_FORMAT_I420;
78 // The maximum number of buffers in the capture pipeline. See
79 // VideoCaptureController ctor comments for more details.
80 const int kMaxNumberOfBuffers = 3;
81 // TODO(miu): The value for tab capture should be determined programmatically.
82 // http://crbug.com/460318
83 const int kMaxNumberOfBuffersForTabCapture = 10;
85 // Used for logging capture events.
86 // Elements in this enum should not be deleted or rearranged; the only
87 // permitted operation is to add new elements before NUM_VIDEO_CAPTURE_EVENT.
88 enum VideoCaptureEvent {
89 VIDEO_CAPTURE_START_CAPTURE = 0,
90 VIDEO_CAPTURE_STOP_CAPTURE_OK = 1,
91 VIDEO_CAPTURE_STOP_CAPTURE_DUE_TO_ERROR = 2,
92 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DEVICE = 3,
93 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DESKTOP_OR_TAB = 4,
94 NUM_VIDEO_CAPTURE_EVENT
97 void LogVideoCaptureEvent(VideoCaptureEvent event) {
98 UMA_HISTOGRAM_ENUMERATION("Media.VideoCaptureManager.Event",
99 event,
100 NUM_VIDEO_CAPTURE_EVENT);
103 // Counter used for identifying a DeviceRequest to start a capture device.
104 static int g_device_start_id = 0;
106 } // namespace
108 namespace content {
110 VideoCaptureManager::DeviceEntry::DeviceEntry(
111 MediaStreamType stream_type,
112 const std::string& id,
113 scoped_ptr<VideoCaptureController> controller)
114 : serial_id(g_device_start_id++),
115 stream_type(stream_type),
116 id(id),
117 video_capture_controller_(controller.Pass()) {}
119 VideoCaptureManager::DeviceEntry::~DeviceEntry() {
120 DCHECK(thread_checker_.CalledOnValidThread());
121 // DCHECK that this DeviceEntry does not still own a
122 // media::VideoCaptureDevice. media::VideoCaptureDevice must be deleted on
123 // the device thread.
124 DCHECK(video_capture_device_ == nullptr);
127 void VideoCaptureManager::DeviceEntry::SetVideoCaptureDevice(
128 scoped_ptr<media::VideoCaptureDevice> device) {
129 DCHECK(thread_checker_.CalledOnValidThread());
130 video_capture_device_.swap(device);
133 scoped_ptr<media::VideoCaptureDevice>
134 VideoCaptureManager::DeviceEntry::ReleaseVideoCaptureDevice() {
135 DCHECK(thread_checker_.CalledOnValidThread());
136 return video_capture_device_.Pass();
139 VideoCaptureController*
140 VideoCaptureManager::DeviceEntry::video_capture_controller() {
141 DCHECK(thread_checker_.CalledOnValidThread());
142 return video_capture_controller_.get();
145 media::VideoCaptureDevice*
146 VideoCaptureManager::DeviceEntry::video_capture_device() {
147 DCHECK(thread_checker_.CalledOnValidThread());
148 return video_capture_device_.get();
151 VideoCaptureManager::CaptureDeviceStartRequest::CaptureDeviceStartRequest(
152 int serial_id,
153 media::VideoCaptureSessionId session_id,
154 const media::VideoCaptureParams& params)
155 : serial_id_(serial_id),
156 session_id_(session_id),
157 params_(params),
158 abort_start_(false) {
161 VideoCaptureManager::VideoCaptureManager(
162 scoped_ptr<media::VideoCaptureDeviceFactory> factory)
163 : listener_(NULL),
164 new_capture_session_id_(1),
165 video_capture_device_factory_(factory.Pass()) {
168 VideoCaptureManager::~VideoCaptureManager() {
169 DCHECK(devices_.empty());
170 DCHECK(device_start_queue_.empty());
173 void VideoCaptureManager::Register(
174 MediaStreamProviderListener* listener,
175 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) {
176 DCHECK_CURRENTLY_ON(BrowserThread::IO);
177 DCHECK(!listener_);
178 DCHECK(!device_task_runner_.get());
179 listener_ = listener;
180 device_task_runner_ = device_task_runner;
183 void VideoCaptureManager::Unregister() {
184 DCHECK(listener_);
185 listener_ = NULL;
188 void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) {
189 DCHECK_CURRENTLY_ON(BrowserThread::IO);
190 DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type;
191 DCHECK(listener_);
192 DCHECK_EQ(stream_type, MEDIA_DEVICE_VIDEO_CAPTURE);
194 // Bind a callback to ConsolidateDevicesInfoOnDeviceThread() with an argument
195 // for another callback to OnDevicesInfoEnumerated() to be run in the current
196 // loop, i.e. IO loop. Pass a timer for UMA histogram collection.
197 base::Callback<void(scoped_ptr<media::VideoCaptureDevice::Names>)>
198 devices_enumerated_callback =
199 base::Bind(&VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread,
200 this,
201 media::BindToCurrentLoop(base::Bind(
202 &VideoCaptureManager::OnDevicesInfoEnumerated,
203 this,
204 stream_type,
205 base::Owned(new base::ElapsedTimer()))),
206 stream_type,
207 devices_info_cache_);
208 // OK to use base::Unretained() since we own the VCDFactory and |this| is
209 // bound in |devices_enumerated_callback|.
210 device_task_runner_->PostTask(FROM_HERE,
211 base::Bind(&media::VideoCaptureDeviceFactory::EnumerateDeviceNames,
212 base::Unretained(video_capture_device_factory_.get()),
213 devices_enumerated_callback));
216 int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) {
217 DCHECK_CURRENTLY_ON(BrowserThread::IO);
218 DCHECK(listener_);
220 // Generate a new id for the session being opened.
221 const media::VideoCaptureSessionId capture_session_id =
222 new_capture_session_id_++;
224 DCHECK(sessions_.find(capture_session_id) == sessions_.end());
225 DVLOG(1) << "VideoCaptureManager::Open, id " << capture_session_id;
227 // We just save the stream info for processing later.
228 sessions_[capture_session_id] = device_info.device;
230 // Notify our listener asynchronously; this ensures that we return
231 // |capture_session_id| to the caller of this function before using that same
232 // id in a listener event.
233 base::ThreadTaskRunnerHandle::Get()->PostTask(
234 FROM_HERE, base::Bind(&VideoCaptureManager::OnOpened, this,
235 device_info.device.type, capture_session_id));
236 return capture_session_id;
239 void VideoCaptureManager::Close(int capture_session_id) {
240 DCHECK_CURRENTLY_ON(BrowserThread::IO);
241 DCHECK(listener_);
242 DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id;
244 SessionMap::iterator session_it = sessions_.find(capture_session_id);
245 if (session_it == sessions_.end()) {
246 NOTREACHED();
247 return;
250 DeviceEntry* const existing_device = GetDeviceEntryForMediaStreamDevice(
251 session_it->second);
252 if (existing_device) {
253 // Remove any client that is still using the session. This is safe to call
254 // even if there are no clients using the session.
255 existing_device->video_capture_controller()
256 ->StopSession(capture_session_id);
258 // StopSession() may have removed the last client, so we might need to
259 // close the device.
260 DestroyDeviceEntryIfNoClients(existing_device);
263 // Notify listeners asynchronously, and forget the session.
264 base::ThreadTaskRunnerHandle::Get()->PostTask(
265 FROM_HERE, base::Bind(&VideoCaptureManager::OnClosed, this,
266 session_it->second.type, capture_session_id));
267 sessions_.erase(session_it);
270 void VideoCaptureManager::QueueStartDevice(
271 media::VideoCaptureSessionId session_id,
272 DeviceEntry* entry,
273 const media::VideoCaptureParams& params) {
274 DCHECK_CURRENTLY_ON(BrowserThread::IO);
275 device_start_queue_.push_back(
276 CaptureDeviceStartRequest(entry->serial_id, session_id, params));
277 if (device_start_queue_.size() == 1)
278 HandleQueuedStartRequest();
281 void VideoCaptureManager::DoStopDevice(DeviceEntry* entry) {
282 DCHECK_CURRENTLY_ON(BrowserThread::IO);
283 DCHECK(std::find(devices_.begin(), devices_.end(), entry) != devices_.end());
285 // Find the matching start request.
286 for (DeviceStartQueue::reverse_iterator request =
287 device_start_queue_.rbegin();
288 request != device_start_queue_.rend(); ++request) {
289 if (request->serial_id() == entry->serial_id) {
290 request->set_abort_start();
291 DVLOG(3) << "DoStopDevice, aborting start request for device "
292 << entry->id << " serial_id = " << entry->serial_id;
293 return;
297 DVLOG(3) << "DoStopDevice. Send stop request for device = " << entry->id
298 << " serial_id = " << entry->serial_id << ".";
299 if (entry->video_capture_device()) {
300 // |entry->video_capture_device| can be null if creating the device fails.
301 device_task_runner_->PostTask(
302 FROM_HERE,
303 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
304 base::Passed(entry->ReleaseVideoCaptureDevice())));
308 void VideoCaptureManager::HandleQueuedStartRequest() {
309 DCHECK_CURRENTLY_ON(BrowserThread::IO);
310 // Remove all start requests that have been aborted.
311 while (device_start_queue_.begin() != device_start_queue_.end() &&
312 device_start_queue_.begin()->abort_start()) {
313 device_start_queue_.pop_front();
315 DeviceStartQueue::iterator request = device_start_queue_.begin();
316 if (request == device_start_queue_.end())
317 return;
319 const int serial_id = request->serial_id();
320 DeviceEntries::iterator entry_it = std::find_if(
321 devices_.begin(), devices_.end(),
322 [serial_id] (const DeviceEntry* e) {
323 return e->serial_id == serial_id;
325 DCHECK(entry_it != devices_.end());
326 DeviceEntry* entry = (*entry_it);
328 media::VideoCaptureParams params = request->params();
329 params.use_gpu_memory_buffers =
330 base::CommandLine::ForCurrentProcess()->HasSwitch(
331 switches::kUseGpuMemoryBuffersForCapture);
333 DVLOG(3) << "HandleQueuedStartRequest, Post start to device thread, device = "
334 << entry->id << " start id = " << entry->serial_id;
335 base::PostTaskAndReplyWithResult(
336 device_task_runner_.get(),
337 FROM_HERE,
338 base::Bind(
339 &VideoCaptureManager::DoStartDeviceOnDeviceThread,
340 this,
341 request->session_id(),
342 entry->id,
343 entry->stream_type,
344 params,
345 base::Passed(entry->video_capture_controller()->NewDeviceClient(
346 device_task_runner_))),
347 base::Bind(&VideoCaptureManager::OnDeviceStarted, this,
348 request->serial_id()));
351 void VideoCaptureManager::OnDeviceStarted(
352 int serial_id,
353 scoped_ptr<media::VideoCaptureDevice> device) {
354 DCHECK_CURRENTLY_ON(BrowserThread::IO);
355 DCHECK(serial_id == device_start_queue_.begin()->serial_id());
356 DVLOG(3) << "OnDeviceStarted";
357 if (device_start_queue_.front().abort_start()) {
358 // |device| can be null if creation failed in DoStartDeviceOnDeviceThread.
359 // The device is no longer wanted. Stop the device again.
360 DVLOG(3) << "OnDeviceStarted but start request have been aborted.";
361 media::VideoCaptureDevice* device_ptr = device.get();
362 base::Closure closure =
363 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
364 base::Passed(&device));
365 if (device_ptr && !device_task_runner_->PostTask(FROM_HERE, closure)) {
366 // PostTask failed. The device must be stopped anyway.
367 device_ptr->StopAndDeAllocate();
369 } else {
370 DeviceEntries::iterator entry_it = std::find_if(
371 devices_.begin(), devices_.end(),
372 [serial_id] (const DeviceEntry* e) {
373 return e->serial_id == serial_id;
375 DCHECK(entry_it != devices_.end());
376 DeviceEntry* entry = *entry_it;
377 DCHECK(!entry->video_capture_device());
378 entry->SetVideoCaptureDevice(device.Pass());
380 if (entry->stream_type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
381 const media::VideoCaptureSessionId session_id =
382 device_start_queue_.front().session_id();
383 MaybePostDesktopCaptureWindowId(session_id);
387 device_start_queue_.pop_front();
388 HandleQueuedStartRequest();
391 scoped_ptr<media::VideoCaptureDevice>
392 VideoCaptureManager::DoStartDeviceOnDeviceThread(
393 media::VideoCaptureSessionId session_id,
394 const std::string& id,
395 MediaStreamType stream_type,
396 const media::VideoCaptureParams& params,
397 scoped_ptr<media::VideoCaptureDevice::Client> device_client) {
398 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime");
399 DCHECK(IsOnDeviceThread());
401 scoped_ptr<media::VideoCaptureDevice> video_capture_device;
402 switch (stream_type) {
403 case MEDIA_DEVICE_VIDEO_CAPTURE: {
404 // We look up the device id from the renderer in our local enumeration
405 // since the renderer does not have all the information that might be
406 // held in the browser-side VideoCaptureDevice::Name structure.
407 const media::VideoCaptureDeviceInfo* found =
408 FindDeviceInfoById(id, devices_info_cache_);
409 if (found) {
410 video_capture_device =
411 video_capture_device_factory_->Create(found->name);
413 break;
415 case MEDIA_TAB_VIDEO_CAPTURE: {
416 video_capture_device.reset(
417 WebContentsVideoCaptureDevice::Create(id));
418 break;
420 case MEDIA_DESKTOP_VIDEO_CAPTURE: {
421 #if defined(ENABLE_SCREEN_CAPTURE)
422 DesktopMediaID desktop_id = DesktopMediaID::Parse(id);
423 #if defined(USE_AURA)
424 if (desktop_id.type == DesktopMediaID::TYPE_AURA_WINDOW) {
425 video_capture_device.reset(
426 DesktopCaptureDeviceAura::Create(desktop_id));
427 } else
428 #endif
429 if (desktop_id.type != DesktopMediaID::TYPE_NONE &&
430 desktop_id.type != DesktopMediaID::TYPE_AURA_WINDOW) {
431 video_capture_device = DesktopCaptureDevice::Create(desktop_id);
433 #endif // defined(ENABLE_SCREEN_CAPTURE)
434 break;
436 default: {
437 NOTIMPLEMENTED();
438 break;
442 if (!video_capture_device) {
443 device_client->OnError("Could not create capture device");
444 return nullptr;
447 video_capture_device->AllocateAndStart(params, device_client.Pass());
448 return video_capture_device.Pass();
451 void VideoCaptureManager::StartCaptureForClient(
452 media::VideoCaptureSessionId session_id,
453 const media::VideoCaptureParams& params,
454 base::ProcessHandle client_render_process,
455 VideoCaptureControllerID client_id,
456 VideoCaptureControllerEventHandler* client_handler,
457 const DoneCB& done_cb) {
458 DCHECK_CURRENTLY_ON(BrowserThread::IO);
459 DVLOG(1) << "VideoCaptureManager::StartCaptureForClient #" << session_id
460 << ", request: "
461 << media::VideoCaptureFormat::ToString(params.requested_format);
463 DeviceEntry* entry = GetOrCreateDeviceEntry(session_id);
464 if (!entry) {
465 done_cb.Run(base::WeakPtr<VideoCaptureController>());
466 return;
469 DCHECK(entry->video_capture_controller());
471 LogVideoCaptureEvent(VIDEO_CAPTURE_START_CAPTURE);
473 // First client starts the device.
474 if (entry->video_capture_controller()->GetActiveClientCount() == 0) {
475 DVLOG(1) << "VideoCaptureManager starting device (type = "
476 << entry->stream_type << ", id = " << entry->id << ")";
477 QueueStartDevice(session_id, entry, params);
479 // Run the callback first, as AddClient() may trigger OnFrameInfo().
480 done_cb.Run(entry->video_capture_controller()->GetWeakPtrForIOThread());
481 entry->video_capture_controller()->AddClient(
482 client_id, client_handler, client_render_process, session_id, params);
485 void VideoCaptureManager::StopCaptureForClient(
486 VideoCaptureController* controller,
487 VideoCaptureControllerID client_id,
488 VideoCaptureControllerEventHandler* client_handler,
489 bool aborted_due_to_error) {
490 DCHECK_CURRENTLY_ON(BrowserThread::IO);
491 DCHECK(controller);
492 DCHECK(client_handler);
494 DeviceEntry* entry = GetDeviceEntryForController(controller);
495 if (!entry) {
496 NOTREACHED();
497 return;
499 if (!aborted_due_to_error) {
500 if (controller->has_received_frames()) {
501 LogVideoCaptureEvent(VIDEO_CAPTURE_STOP_CAPTURE_OK);
502 } else if (entry->stream_type == MEDIA_DEVICE_VIDEO_CAPTURE) {
503 LogVideoCaptureEvent(
504 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DEVICE);
505 } else {
506 LogVideoCaptureEvent(
507 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DESKTOP_OR_TAB);
509 } else {
510 LogVideoCaptureEvent(VIDEO_CAPTURE_STOP_CAPTURE_DUE_TO_ERROR);
511 SessionMap::iterator it;
512 for (it = sessions_.begin(); it != sessions_.end(); ++it) {
513 if (it->second.type == entry->stream_type &&
514 it->second.id == entry->id) {
515 listener_->Aborted(it->second.type, it->first);
516 break;
521 // Detach client from controller.
522 media::VideoCaptureSessionId session_id =
523 controller->RemoveClient(client_id, client_handler);
524 DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = "
525 << session_id;
527 // If controller has no more clients, delete controller and device.
528 DestroyDeviceEntryIfNoClients(entry);
531 void VideoCaptureManager::PauseCaptureForClient(
532 VideoCaptureController* controller,
533 VideoCaptureControllerID client_id,
534 VideoCaptureControllerEventHandler* client_handler) {
535 DCHECK_CURRENTLY_ON(BrowserThread::IO);
536 DCHECK(controller);
537 DCHECK(client_handler);
538 DeviceEntry* entry = GetDeviceEntryForController(controller);
539 if (!entry) {
540 NOTREACHED();
541 return;
544 // We only pause the MEDIA_DEVICE_VIDEO_CAPTURE entry to release camera to
545 // system.
546 if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE)
547 return;
549 controller->PauseOrResumeClient(client_id, client_handler, true);
550 if (controller->GetActiveClientCount() != 0)
551 return;
553 // There is no more client, release the camera.
554 DoStopDevice(entry);
557 void VideoCaptureManager::ResumeCaptureForClient(
558 media::VideoCaptureSessionId session_id,
559 const media::VideoCaptureParams& params,
560 VideoCaptureController* controller,
561 VideoCaptureControllerID client_id,
562 VideoCaptureControllerEventHandler* client_handler) {
563 DCHECK_CURRENTLY_ON(BrowserThread::IO);
564 DCHECK(controller);
565 DCHECK(client_handler);
567 DeviceEntry* entry = GetDeviceEntryForController(controller);
568 if (!entry) {
569 NOTREACHED();
570 return;
573 // We only pause/resume the MEDIA_DEVICE_VIDEO_CAPTURE entry.
574 if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE)
575 return;
577 controller->PauseOrResumeClient(client_id, client_handler, false);
578 if (controller->GetActiveClientCount() != 1)
579 return;
581 // This is first active client, allocate the camera.
582 QueueStartDevice(session_id, entry, params);
585 bool VideoCaptureManager::GetDeviceSupportedFormats(
586 media::VideoCaptureSessionId capture_session_id,
587 media::VideoCaptureFormats* supported_formats) {
588 DCHECK_CURRENTLY_ON(BrowserThread::IO);
589 DCHECK(supported_formats->empty());
591 SessionMap::iterator it = sessions_.find(capture_session_id);
592 if (it == sessions_.end())
593 return false;
594 DVLOG(1) << "GetDeviceSupportedFormats for device: " << it->second.name;
596 // Return all available formats of the device, regardless its started state.
597 media::VideoCaptureDeviceInfo* existing_device =
598 FindDeviceInfoById(it->second.id, devices_info_cache_);
599 if (existing_device)
600 *supported_formats = existing_device->supported_formats;
601 return true;
604 bool VideoCaptureManager::GetDeviceFormatsInUse(
605 media::VideoCaptureSessionId capture_session_id,
606 media::VideoCaptureFormats* formats_in_use) {
607 DCHECK_CURRENTLY_ON(BrowserThread::IO);
608 DCHECK(formats_in_use->empty());
610 SessionMap::iterator it = sessions_.find(capture_session_id);
611 if (it == sessions_.end())
612 return false;
613 DVLOG(1) << "GetDeviceFormatsInUse for device: " << it->second.name;
615 // Return the currently in-use format(s) of the device, if it's started.
616 DeviceEntry* device_in_use =
617 GetDeviceEntryForMediaStreamDevice(it->second);
618 if (device_in_use) {
619 // Currently only one format-in-use is supported at the VCC level.
620 formats_in_use->push_back(
621 device_in_use->video_capture_controller()->GetVideoCaptureFormat());
623 return true;
626 void VideoCaptureManager::SetDesktopCaptureWindowId(
627 media::VideoCaptureSessionId session_id,
628 gfx::NativeViewId window_id) {
629 DCHECK_CURRENTLY_ON(BrowserThread::IO);
630 VLOG(2) << "SetDesktopCaptureWindowId called for session " << session_id;
632 notification_window_ids_[session_id] = window_id;
633 MaybePostDesktopCaptureWindowId(session_id);
636 void VideoCaptureManager::MaybePostDesktopCaptureWindowId(
637 media::VideoCaptureSessionId session_id) {
638 SessionMap::iterator session_it = sessions_.find(session_id);
639 if (session_it == sessions_.end()) {
640 return;
643 DeviceEntry* const existing_device =
644 GetDeviceEntryForMediaStreamDevice(session_it->second);
645 if (!existing_device) {
646 DVLOG(2) << "Failed to find an existing screen capture device.";
647 return;
650 if (!existing_device->video_capture_device()) {
651 DVLOG(2) << "Screen capture device not yet started.";
652 return;
655 DCHECK_EQ(MEDIA_DESKTOP_VIDEO_CAPTURE, existing_device->stream_type);
656 DesktopMediaID id = DesktopMediaID::Parse(existing_device->id);
657 if (id.type == DesktopMediaID::TYPE_NONE ||
658 id.type == DesktopMediaID::TYPE_AURA_WINDOW) {
659 VLOG(2) << "Video capture device type mismatch.";
660 return;
663 auto window_id_it = notification_window_ids_.find(session_id);
664 if (window_id_it == notification_window_ids_.end()) {
665 DVLOG(2) << "Notification window id not set for screen capture.";
666 return;
669 // Post |existing_device->video_capture_device| to the VideoCaptureDevice to
670 // the device_task_runner_. This is safe since the device is destroyed on the
671 // device_task_runner_.
672 device_task_runner_->PostTask(
673 FROM_HERE,
674 base::Bind(&VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread,
675 this,
676 existing_device->video_capture_device(),
677 window_id_it->second));
679 notification_window_ids_.erase(window_id_it);
682 void VideoCaptureManager::DoStopDeviceOnDeviceThread(
683 scoped_ptr<media::VideoCaptureDevice> device) {
684 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime");
685 DCHECK(IsOnDeviceThread());
686 device->StopAndDeAllocate();
687 DVLOG(3) << "DoStopDeviceOnDeviceThread";
690 void VideoCaptureManager::OnOpened(
691 MediaStreamType stream_type,
692 media::VideoCaptureSessionId capture_session_id) {
693 DCHECK_CURRENTLY_ON(BrowserThread::IO);
694 if (!listener_) {
695 // Listener has been removed.
696 return;
698 listener_->Opened(stream_type, capture_session_id);
701 void VideoCaptureManager::OnClosed(
702 MediaStreamType stream_type,
703 media::VideoCaptureSessionId capture_session_id) {
704 DCHECK_CURRENTLY_ON(BrowserThread::IO);
705 if (!listener_) {
706 // Listener has been removed.
707 return;
709 listener_->Closed(stream_type, capture_session_id);
712 void VideoCaptureManager::OnDevicesInfoEnumerated(
713 MediaStreamType stream_type,
714 base::ElapsedTimer* timer,
715 const media::VideoCaptureDeviceInfos& new_devices_info_cache) {
716 DCHECK_CURRENTLY_ON(BrowserThread::IO);
717 UMA_HISTOGRAM_TIMES(
718 "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime",
719 timer->Elapsed());
720 if (!listener_) {
721 // Listener has been removed.
722 return;
724 devices_info_cache_ = new_devices_info_cache;
726 MediaInternals::GetInstance()->UpdateVideoCaptureDeviceCapabilities(
727 devices_info_cache_);
729 // Walk the |devices_info_cache_| and transform from VCD::Name to
730 // StreamDeviceInfo for return purposes.
731 StreamDeviceInfoArray devices;
732 for (const auto& it : devices_info_cache_) {
733 devices.push_back(StreamDeviceInfo(
734 stream_type, it.name.GetNameAndModel(), it.name.id()));
736 listener_->DevicesEnumerated(stream_type, devices);
739 bool VideoCaptureManager::IsOnDeviceThread() const {
740 return device_task_runner_->BelongsToCurrentThread();
743 void VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread(
744 base::Callback<void(const media::VideoCaptureDeviceInfos&)>
745 on_devices_enumerated_callback,
746 MediaStreamType stream_type,
747 const media::VideoCaptureDeviceInfos& old_device_info_cache,
748 scoped_ptr<media::VideoCaptureDevice::Names> names_snapshot) {
749 DCHECK(IsOnDeviceThread());
750 // Construct |new_devices_info_cache| with the cached devices that are still
751 // present in the system, and remove their names from |names_snapshot|, so we
752 // keep there the truly new devices.
753 media::VideoCaptureDeviceInfos new_devices_info_cache;
754 for (const auto& device_info : old_device_info_cache) {
755 for (media::VideoCaptureDevice::Names::iterator it =
756 names_snapshot->begin(); it != names_snapshot->end(); ++it) {
757 if (device_info.name.id() == it->id()) {
758 new_devices_info_cache.push_back(device_info);
759 names_snapshot->erase(it);
760 break;
765 // Get the supported capture formats for the new devices in |names_snapshot|.
766 for (const auto& it : *names_snapshot) {
767 media::VideoCaptureDeviceInfo device_info(it, media::VideoCaptureFormats());
768 video_capture_device_factory_->GetDeviceSupportedFormats(
769 it, &(device_info.supported_formats));
770 ConsolidateCaptureFormats(&device_info.supported_formats);
771 new_devices_info_cache.push_back(device_info);
774 on_devices_enumerated_callback.Run(new_devices_info_cache);
777 VideoCaptureManager::DeviceEntry*
778 VideoCaptureManager::GetDeviceEntryForMediaStreamDevice(
779 const MediaStreamDevice& device_info) {
780 DCHECK_CURRENTLY_ON(BrowserThread::IO);
782 for (DeviceEntries::iterator it = devices_.begin();
783 it != devices_.end(); ++it) {
784 DeviceEntry* device = *it;
785 if (device_info.type == device->stream_type &&
786 device_info.id == device->id) {
787 return device;
790 return NULL;
793 VideoCaptureManager::DeviceEntry*
794 VideoCaptureManager::GetDeviceEntryForController(
795 const VideoCaptureController* controller) const {
796 // Look up |controller| in |devices_|.
797 for (DeviceEntries::const_iterator it = devices_.begin();
798 it != devices_.end(); ++it) {
799 if ((*it)->video_capture_controller() == controller) {
800 return *it;
803 return NULL;
806 void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) {
807 DCHECK_CURRENTLY_ON(BrowserThread::IO);
808 // Removal of the last client stops the device.
809 if (entry->video_capture_controller()->GetClientCount() == 0) {
810 DVLOG(1) << "VideoCaptureManager stopping device (type = "
811 << entry->stream_type << ", id = " << entry->id << ")";
813 // The DeviceEntry is removed from |devices_| immediately. The controller is
814 // deleted immediately, and the device is freed asynchronously. After this
815 // point, subsequent requests to open this same device ID will create a new
816 // DeviceEntry, VideoCaptureController, and VideoCaptureDevice.
817 DoStopDevice(entry);
818 DeviceEntries::iterator device_it = std::find(devices_.begin(),
819 devices_.end(),
820 entry);
821 devices_.erase(device_it);
825 VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry(
826 media::VideoCaptureSessionId capture_session_id) {
827 DCHECK_CURRENTLY_ON(BrowserThread::IO);
829 SessionMap::iterator session_it = sessions_.find(capture_session_id);
830 if (session_it == sessions_.end()) {
831 return NULL;
833 const MediaStreamDevice& device_info = session_it->second;
835 // Check if another session has already opened this device. If so, just
836 // use that opened device.
837 DeviceEntry* const existing_device =
838 GetDeviceEntryForMediaStreamDevice(device_info);
839 if (existing_device) {
840 DCHECK_EQ(device_info.type, existing_device->stream_type);
841 return existing_device;
844 const int max_buffers = device_info.type == MEDIA_TAB_VIDEO_CAPTURE ?
845 kMaxNumberOfBuffersForTabCapture : kMaxNumberOfBuffers;
846 scoped_ptr<VideoCaptureController> video_capture_controller(
847 new VideoCaptureController(max_buffers));
848 DeviceEntry* new_device = new DeviceEntry(device_info.type,
849 device_info.id,
850 video_capture_controller.Pass());
851 devices_.push_back(new_device);
852 return new_device;
855 media::VideoCaptureDeviceInfo* VideoCaptureManager::FindDeviceInfoById(
856 const std::string& id,
857 media::VideoCaptureDeviceInfos& device_vector) {
858 for (auto& it : device_vector) {
859 if (it.name.id() == id)
860 return &(it);
862 return NULL;
865 void VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread(
866 media::VideoCaptureDevice* device,
867 gfx::NativeViewId window_id) {
868 DCHECK(IsOnDeviceThread());
869 #if defined(ENABLE_SCREEN_CAPTURE)
870 DesktopCaptureDevice* desktop_device =
871 static_cast<DesktopCaptureDevice*>(device);
872 desktop_device->SetNotificationWindowId(window_id);
873 VLOG(2) << "Screen capture notification window passed on device thread.";
874 #endif
877 } // namespace content