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"
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"
36 #include "content/browser/media/capture/desktop_capture_device_aura.h"
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
61 void ConsolidateCaptureFormats(media::VideoCaptureFormats
* formats
) {
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::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",
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;
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
),
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(
153 media::VideoCaptureSessionId session_id
,
154 const media::VideoCaptureParams
& params
)
155 : serial_id_(serial_id
),
156 session_id_(session_id
),
158 abort_start_(false) {
161 VideoCaptureManager::VideoCaptureManager(
162 scoped_ptr
<media::VideoCaptureDeviceFactory
> factory
)
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
);
178 DCHECK(!device_task_runner_
.get());
179 listener_
= listener
;
180 device_task_runner_
= device_task_runner
;
183 void VideoCaptureManager::Unregister() {
188 void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type
) {
189 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
190 DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type
;
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
,
201 media::BindToCurrentLoop(base::Bind(
202 &VideoCaptureManager::OnDevicesInfoEnumerated
,
205 base::Owned(new base::ElapsedTimer()))),
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
);
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
);
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()) {
250 DeviceEntry
* const existing_device
= GetDeviceEntryForMediaStreamDevice(
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
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
,
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
;
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(
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())
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 DVLOG(3) << "HandleQueuedStartRequest, Post start to device thread, device = "
329 << entry
->id
<< " start id = " << entry
->serial_id
;
330 base::PostTaskAndReplyWithResult(
331 device_task_runner_
.get(),
334 &VideoCaptureManager::DoStartDeviceOnDeviceThread
,
336 request
->session_id(),
340 base::Passed(entry
->video_capture_controller()->NewDeviceClient(
341 device_task_runner_
))),
342 base::Bind(&VideoCaptureManager::OnDeviceStarted
, this,
343 request
->serial_id()));
346 void VideoCaptureManager::OnDeviceStarted(
348 scoped_ptr
<media::VideoCaptureDevice
> device
) {
349 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
350 DCHECK(serial_id
== device_start_queue_
.begin()->serial_id());
351 DVLOG(3) << "OnDeviceStarted";
352 if (device_start_queue_
.front().abort_start()) {
353 // |device| can be null if creation failed in DoStartDeviceOnDeviceThread.
354 // The device is no longer wanted. Stop the device again.
355 DVLOG(3) << "OnDeviceStarted but start request have been aborted.";
356 media::VideoCaptureDevice
* device_ptr
= device
.get();
357 base::Closure closure
=
358 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread
, this,
359 base::Passed(&device
));
360 if (device_ptr
&& !device_task_runner_
->PostTask(FROM_HERE
, closure
)) {
361 // PostTask failed. The device must be stopped anyway.
362 device_ptr
->StopAndDeAllocate();
365 DeviceEntries::iterator entry_it
= std::find_if(
366 devices_
.begin(), devices_
.end(),
367 [serial_id
] (const DeviceEntry
* e
) {
368 return e
->serial_id
== serial_id
;
370 DCHECK(entry_it
!= devices_
.end());
371 DeviceEntry
* entry
= *entry_it
;
372 DCHECK(!entry
->video_capture_device());
373 entry
->SetVideoCaptureDevice(device
.Pass());
375 if (entry
->stream_type
== MEDIA_DESKTOP_VIDEO_CAPTURE
) {
376 const media::VideoCaptureSessionId session_id
=
377 device_start_queue_
.front().session_id();
378 MaybePostDesktopCaptureWindowId(session_id
);
382 device_start_queue_
.pop_front();
383 HandleQueuedStartRequest();
386 scoped_ptr
<media::VideoCaptureDevice
>
387 VideoCaptureManager::DoStartDeviceOnDeviceThread(
388 media::VideoCaptureSessionId session_id
,
389 const std::string
& id
,
390 MediaStreamType stream_type
,
391 const media::VideoCaptureParams
& params
,
392 scoped_ptr
<media::VideoCaptureDevice::Client
> device_client
) {
393 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime");
394 DCHECK(IsOnDeviceThread());
396 scoped_ptr
<media::VideoCaptureDevice
> video_capture_device
;
397 switch (stream_type
) {
398 case MEDIA_DEVICE_VIDEO_CAPTURE
: {
399 // We look up the device id from the renderer in our local enumeration
400 // since the renderer does not have all the information that might be
401 // held in the browser-side VideoCaptureDevice::Name structure.
402 const media::VideoCaptureDeviceInfo
* found
=
403 FindDeviceInfoById(id
, devices_info_cache_
);
405 video_capture_device
=
406 video_capture_device_factory_
->Create(found
->name
);
410 case MEDIA_TAB_VIDEO_CAPTURE
: {
411 video_capture_device
.reset(
412 WebContentsVideoCaptureDevice::Create(id
));
415 case MEDIA_DESKTOP_VIDEO_CAPTURE
: {
416 #if defined(ENABLE_SCREEN_CAPTURE)
417 DesktopMediaID desktop_id
= DesktopMediaID::Parse(id
);
418 if (!desktop_id
.is_null()) {
419 #if defined(USE_AURA)
420 video_capture_device
= DesktopCaptureDeviceAura::Create(desktop_id
);
422 if (!video_capture_device
)
423 video_capture_device
= DesktopCaptureDevice::Create(desktop_id
);
425 #endif // defined(ENABLE_SCREEN_CAPTURE)
434 if (!video_capture_device
) {
435 device_client
->OnError("Could not create capture device");
439 video_capture_device
->AllocateAndStart(params
, device_client
.Pass());
440 return video_capture_device
.Pass();
443 void VideoCaptureManager::StartCaptureForClient(
444 media::VideoCaptureSessionId session_id
,
445 const media::VideoCaptureParams
& params
,
446 base::ProcessHandle client_render_process
,
447 VideoCaptureControllerID client_id
,
448 VideoCaptureControllerEventHandler
* client_handler
,
449 const DoneCB
& done_cb
) {
450 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
451 DVLOG(1) << "VideoCaptureManager::StartCaptureForClient #" << session_id
453 << media::VideoCaptureFormat::ToString(params
.requested_format
);
455 DeviceEntry
* entry
= GetOrCreateDeviceEntry(session_id
);
457 done_cb
.Run(base::WeakPtr
<VideoCaptureController
>());
461 DCHECK(entry
->video_capture_controller());
463 LogVideoCaptureEvent(VIDEO_CAPTURE_START_CAPTURE
);
465 // First client starts the device.
466 if (entry
->video_capture_controller()->GetActiveClientCount() == 0) {
467 DVLOG(1) << "VideoCaptureManager starting device (type = "
468 << entry
->stream_type
<< ", id = " << entry
->id
<< ")";
469 QueueStartDevice(session_id
, entry
, params
);
471 // Run the callback first, as AddClient() may trigger OnFrameInfo().
472 done_cb
.Run(entry
->video_capture_controller()->GetWeakPtrForIOThread());
473 entry
->video_capture_controller()->AddClient(
474 client_id
, client_handler
, client_render_process
, session_id
, params
);
477 void VideoCaptureManager::StopCaptureForClient(
478 VideoCaptureController
* controller
,
479 VideoCaptureControllerID client_id
,
480 VideoCaptureControllerEventHandler
* client_handler
,
481 bool aborted_due_to_error
) {
482 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
484 DCHECK(client_handler
);
486 DeviceEntry
* entry
= GetDeviceEntryForController(controller
);
491 if (!aborted_due_to_error
) {
492 if (controller
->has_received_frames()) {
493 LogVideoCaptureEvent(VIDEO_CAPTURE_STOP_CAPTURE_OK
);
494 } else if (entry
->stream_type
== MEDIA_DEVICE_VIDEO_CAPTURE
) {
495 LogVideoCaptureEvent(
496 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DEVICE
);
498 LogVideoCaptureEvent(
499 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DESKTOP_OR_TAB
);
502 LogVideoCaptureEvent(VIDEO_CAPTURE_STOP_CAPTURE_DUE_TO_ERROR
);
503 SessionMap::iterator it
;
504 for (it
= sessions_
.begin(); it
!= sessions_
.end(); ++it
) {
505 if (it
->second
.type
== entry
->stream_type
&&
506 it
->second
.id
== entry
->id
) {
507 listener_
->Aborted(it
->second
.type
, it
->first
);
513 // Detach client from controller.
514 media::VideoCaptureSessionId session_id
=
515 controller
->RemoveClient(client_id
, client_handler
);
516 DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = "
519 // If controller has no more clients, delete controller and device.
520 DestroyDeviceEntryIfNoClients(entry
);
523 void VideoCaptureManager::PauseCaptureForClient(
524 VideoCaptureController
* controller
,
525 VideoCaptureControllerID client_id
,
526 VideoCaptureControllerEventHandler
* client_handler
) {
527 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
529 DCHECK(client_handler
);
530 DeviceEntry
* entry
= GetDeviceEntryForController(controller
);
533 DVLOG(1) << "Got Null entry while pausing capture";
536 // Do not pause Content Video Capture devices, e.g. Tab or Screen capture.
537 if (entry
->stream_type
!= MEDIA_DEVICE_VIDEO_CAPTURE
)
540 controller
->PauseClient(client_id
, client_handler
);
541 // Release the capture device if there are no more clients.
542 if (!controller
->GetActiveClientCount())
546 void VideoCaptureManager::ResumeCaptureForClient(
547 media::VideoCaptureSessionId session_id
,
548 const media::VideoCaptureParams
& params
,
549 VideoCaptureController
* controller
,
550 VideoCaptureControllerID client_id
,
551 VideoCaptureControllerEventHandler
* client_handler
) {
552 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
554 DCHECK(client_handler
);
556 DeviceEntry
* entry
= GetDeviceEntryForController(controller
);
559 DVLOG(1) << "Got Null entry while resuming capture";
562 // Do not resume Content Video Capture devices, e.g. Tab or Screen capture.
563 if (entry
->stream_type
!= MEDIA_DEVICE_VIDEO_CAPTURE
)
566 // We can't resume a capturing client, which will crash with Camera2 API.
567 // Refer to crbug/514740 for more details.
568 if (!controller
->ResumeClient(client_id
, client_handler
))
570 if (controller
->GetActiveClientCount() != 1)
573 // This is first active client, allocate the camera.
574 QueueStartDevice(session_id
, entry
, params
);
577 bool VideoCaptureManager::GetDeviceSupportedFormats(
578 media::VideoCaptureSessionId capture_session_id
,
579 media::VideoCaptureFormats
* supported_formats
) {
580 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
581 DCHECK(supported_formats
->empty());
583 SessionMap::iterator it
= sessions_
.find(capture_session_id
);
584 if (it
== sessions_
.end())
586 DVLOG(1) << "GetDeviceSupportedFormats for device: " << it
->second
.name
;
588 // Return all available formats of the device, regardless its started state.
589 media::VideoCaptureDeviceInfo
* existing_device
=
590 FindDeviceInfoById(it
->second
.id
, devices_info_cache_
);
592 *supported_formats
= existing_device
->supported_formats
;
596 bool VideoCaptureManager::GetDeviceFormatsInUse(
597 media::VideoCaptureSessionId capture_session_id
,
598 media::VideoCaptureFormats
* formats_in_use
) {
599 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
600 DCHECK(formats_in_use
->empty());
602 SessionMap::iterator it
= sessions_
.find(capture_session_id
);
603 if (it
== sessions_
.end())
605 DVLOG(1) << "GetDeviceFormatsInUse for device: " << it
->second
.name
;
607 // Return the currently in-use format(s) of the device, if it's started.
608 DeviceEntry
* device_in_use
=
609 GetDeviceEntryForMediaStreamDevice(it
->second
);
611 // Currently only one format-in-use is supported at the VCC level.
612 formats_in_use
->push_back(
613 device_in_use
->video_capture_controller()->GetVideoCaptureFormat());
618 void VideoCaptureManager::SetDesktopCaptureWindowId(
619 media::VideoCaptureSessionId session_id
,
620 gfx::NativeViewId window_id
) {
621 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
622 VLOG(2) << "SetDesktopCaptureWindowId called for session " << session_id
;
624 notification_window_ids_
[session_id
] = window_id
;
625 MaybePostDesktopCaptureWindowId(session_id
);
628 void VideoCaptureManager::MaybePostDesktopCaptureWindowId(
629 media::VideoCaptureSessionId session_id
) {
630 SessionMap::iterator session_it
= sessions_
.find(session_id
);
631 if (session_it
== sessions_
.end()) {
635 DeviceEntry
* const existing_device
=
636 GetDeviceEntryForMediaStreamDevice(session_it
->second
);
637 if (!existing_device
) {
638 DVLOG(2) << "Failed to find an existing screen capture device.";
642 if (!existing_device
->video_capture_device()) {
643 DVLOG(2) << "Screen capture device not yet started.";
647 DCHECK_EQ(MEDIA_DESKTOP_VIDEO_CAPTURE
, existing_device
->stream_type
);
648 DesktopMediaID id
= DesktopMediaID::Parse(existing_device
->id
);
652 auto window_id_it
= notification_window_ids_
.find(session_id
);
653 if (window_id_it
== notification_window_ids_
.end()) {
654 DVLOG(2) << "Notification window id not set for screen capture.";
658 // Post |existing_device->video_capture_device| to the VideoCaptureDevice to
659 // the device_task_runner_. This is safe since the device is destroyed on the
660 // device_task_runner_.
661 device_task_runner_
->PostTask(
663 base::Bind(&VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread
,
665 existing_device
->video_capture_device(),
666 window_id_it
->second
));
668 notification_window_ids_
.erase(window_id_it
);
671 void VideoCaptureManager::DoStopDeviceOnDeviceThread(
672 scoped_ptr
<media::VideoCaptureDevice
> device
) {
673 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime");
674 DCHECK(IsOnDeviceThread());
675 device
->StopAndDeAllocate();
676 DVLOG(3) << "DoStopDeviceOnDeviceThread";
679 void VideoCaptureManager::OnOpened(
680 MediaStreamType stream_type
,
681 media::VideoCaptureSessionId capture_session_id
) {
682 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
684 // Listener has been removed.
687 listener_
->Opened(stream_type
, capture_session_id
);
690 void VideoCaptureManager::OnClosed(
691 MediaStreamType stream_type
,
692 media::VideoCaptureSessionId capture_session_id
) {
693 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
695 // Listener has been removed.
698 listener_
->Closed(stream_type
, capture_session_id
);
701 void VideoCaptureManager::OnDevicesInfoEnumerated(
702 MediaStreamType stream_type
,
703 base::ElapsedTimer
* timer
,
704 const media::VideoCaptureDeviceInfos
& new_devices_info_cache
) {
705 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
707 "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime",
710 // Listener has been removed.
713 devices_info_cache_
= new_devices_info_cache
;
715 MediaInternals::GetInstance()->UpdateVideoCaptureDeviceCapabilities(
716 devices_info_cache_
);
718 // Walk the |devices_info_cache_| and transform from VCD::Name to
719 // StreamDeviceInfo for return purposes.
720 StreamDeviceInfoArray devices
;
721 for (const auto& it
: devices_info_cache_
) {
722 devices
.push_back(StreamDeviceInfo(
723 stream_type
, it
.name
.GetNameAndModel(), it
.name
.id()));
725 listener_
->DevicesEnumerated(stream_type
, devices
);
728 bool VideoCaptureManager::IsOnDeviceThread() const {
729 return device_task_runner_
->BelongsToCurrentThread();
732 void VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread(
733 base::Callback
<void(const media::VideoCaptureDeviceInfos
&)>
734 on_devices_enumerated_callback
,
735 MediaStreamType stream_type
,
736 const media::VideoCaptureDeviceInfos
& old_device_info_cache
,
737 scoped_ptr
<media::VideoCaptureDevice::Names
> names_snapshot
) {
738 DCHECK(IsOnDeviceThread());
739 // Construct |new_devices_info_cache| with the cached devices that are still
740 // present in the system, and remove their names from |names_snapshot|, so we
741 // keep there the truly new devices.
742 media::VideoCaptureDeviceInfos new_devices_info_cache
;
743 for (const auto& device_info
: old_device_info_cache
) {
744 for (media::VideoCaptureDevice::Names::iterator it
=
745 names_snapshot
->begin(); it
!= names_snapshot
->end(); ++it
) {
746 if (device_info
.name
.id() == it
->id()) {
747 new_devices_info_cache
.push_back(device_info
);
748 names_snapshot
->erase(it
);
754 // Get the supported capture formats for the new devices in |names_snapshot|.
755 for (const auto& it
: *names_snapshot
) {
756 media::VideoCaptureDeviceInfo
device_info(it
, media::VideoCaptureFormats());
757 video_capture_device_factory_
->GetDeviceSupportedFormats(
758 it
, &(device_info
.supported_formats
));
759 ConsolidateCaptureFormats(&device_info
.supported_formats
);
760 new_devices_info_cache
.push_back(device_info
);
763 on_devices_enumerated_callback
.Run(new_devices_info_cache
);
766 VideoCaptureManager::DeviceEntry
*
767 VideoCaptureManager::GetDeviceEntryForMediaStreamDevice(
768 const MediaStreamDevice
& device_info
) {
769 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
771 for (DeviceEntries::iterator it
= devices_
.begin();
772 it
!= devices_
.end(); ++it
) {
773 DeviceEntry
* device
= *it
;
774 if (device_info
.type
== device
->stream_type
&&
775 device_info
.id
== device
->id
) {
782 VideoCaptureManager::DeviceEntry
*
783 VideoCaptureManager::GetDeviceEntryForController(
784 const VideoCaptureController
* controller
) const {
785 // Look up |controller| in |devices_|.
786 for (DeviceEntries::const_iterator it
= devices_
.begin();
787 it
!= devices_
.end(); ++it
) {
788 if ((*it
)->video_capture_controller() == controller
) {
795 void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry
* entry
) {
796 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
797 // Removal of the last active client stops the device. It is important to only
798 // check active clients and not all clients, because otherwise this class gets
799 // into a bad state and might crash next time VideoCaptureManager::Open() is
801 if (entry
->video_capture_controller()->GetActiveClientCount() == 0) {
802 DVLOG(1) << "VideoCaptureManager stopping device (type = "
803 << entry
->stream_type
<< ", id = " << entry
->id
<< ")";
805 // The DeviceEntry is removed from |devices_| immediately. The controller is
806 // deleted immediately, and the device is freed asynchronously. After this
807 // point, subsequent requests to open this same device ID will create a new
808 // DeviceEntry, VideoCaptureController, and VideoCaptureDevice.
810 DeviceEntries::iterator device_it
= std::find(devices_
.begin(),
813 devices_
.erase(device_it
);
817 VideoCaptureManager::DeviceEntry
* VideoCaptureManager::GetOrCreateDeviceEntry(
818 media::VideoCaptureSessionId capture_session_id
) {
819 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
821 SessionMap::iterator session_it
= sessions_
.find(capture_session_id
);
822 if (session_it
== sessions_
.end()) {
825 const MediaStreamDevice
& device_info
= session_it
->second
;
827 // Check if another session has already opened this device. If so, just
828 // use that opened device.
829 DeviceEntry
* const existing_device
=
830 GetDeviceEntryForMediaStreamDevice(device_info
);
831 if (existing_device
) {
832 DCHECK_EQ(device_info
.type
, existing_device
->stream_type
);
833 return existing_device
;
836 const int max_buffers
= device_info
.type
== MEDIA_TAB_VIDEO_CAPTURE
?
837 kMaxNumberOfBuffersForTabCapture
: kMaxNumberOfBuffers
;
838 scoped_ptr
<VideoCaptureController
> video_capture_controller(
839 new VideoCaptureController(max_buffers
));
840 DeviceEntry
* new_device
= new DeviceEntry(device_info
.type
,
842 video_capture_controller
.Pass());
843 devices_
.push_back(new_device
);
847 media::VideoCaptureDeviceInfo
* VideoCaptureManager::FindDeviceInfoById(
848 const std::string
& id
,
849 media::VideoCaptureDeviceInfos
& device_vector
) {
850 for (auto& it
: device_vector
) {
851 if (it
.name
.id() == id
)
857 void VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread(
858 media::VideoCaptureDevice
* device
,
859 gfx::NativeViewId window_id
) {
860 DCHECK(IsOnDeviceThread());
861 #if defined(ENABLE_SCREEN_CAPTURE)
862 DesktopCaptureDevice
* desktop_device
=
863 static_cast<DesktopCaptureDevice
*>(device
);
864 desktop_device
->SetNotificationWindowId(window_id
);
865 VLOG(2) << "Screen capture notification window passed on device thread.";
869 } // namespace content