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::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",
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 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(),
339 &VideoCaptureManager::DoStartDeviceOnDeviceThread
,
341 request
->session_id(),
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(
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();
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_
);
410 video_capture_device
=
411 video_capture_device_factory_
->Create(found
->name
);
415 case MEDIA_TAB_VIDEO_CAPTURE
: {
416 video_capture_device
.reset(
417 WebContentsVideoCaptureDevice::Create(id
));
420 case MEDIA_DESKTOP_VIDEO_CAPTURE
: {
421 #if defined(ENABLE_SCREEN_CAPTURE)
422 DesktopMediaID desktop_id
= DesktopMediaID::Parse(id
);
423 if (!desktop_id
.is_null()) {
424 #if defined(USE_AURA)
425 video_capture_device
= DesktopCaptureDeviceAura::Create(desktop_id
);
427 if (!video_capture_device
)
428 video_capture_device
= DesktopCaptureDevice::Create(desktop_id
);
430 #endif // defined(ENABLE_SCREEN_CAPTURE)
439 if (!video_capture_device
) {
440 device_client
->OnError("Could not create capture device");
444 video_capture_device
->AllocateAndStart(params
, device_client
.Pass());
445 return video_capture_device
.Pass();
448 void VideoCaptureManager::StartCaptureForClient(
449 media::VideoCaptureSessionId session_id
,
450 const media::VideoCaptureParams
& params
,
451 base::ProcessHandle client_render_process
,
452 VideoCaptureControllerID client_id
,
453 VideoCaptureControllerEventHandler
* client_handler
,
454 const DoneCB
& done_cb
) {
455 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
456 DVLOG(1) << "VideoCaptureManager::StartCaptureForClient #" << session_id
458 << media::VideoCaptureFormat::ToString(params
.requested_format
);
460 DeviceEntry
* entry
= GetOrCreateDeviceEntry(session_id
);
462 done_cb
.Run(base::WeakPtr
<VideoCaptureController
>());
466 DCHECK(entry
->video_capture_controller());
468 LogVideoCaptureEvent(VIDEO_CAPTURE_START_CAPTURE
);
470 // First client starts the device.
471 if (entry
->video_capture_controller()->GetActiveClientCount() == 0) {
472 DVLOG(1) << "VideoCaptureManager starting device (type = "
473 << entry
->stream_type
<< ", id = " << entry
->id
<< ")";
474 QueueStartDevice(session_id
, entry
, params
);
476 // Run the callback first, as AddClient() may trigger OnFrameInfo().
477 done_cb
.Run(entry
->video_capture_controller()->GetWeakPtrForIOThread());
478 entry
->video_capture_controller()->AddClient(
479 client_id
, client_handler
, client_render_process
, session_id
, params
);
482 void VideoCaptureManager::StopCaptureForClient(
483 VideoCaptureController
* controller
,
484 VideoCaptureControllerID client_id
,
485 VideoCaptureControllerEventHandler
* client_handler
,
486 bool aborted_due_to_error
) {
487 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
489 DCHECK(client_handler
);
491 DeviceEntry
* entry
= GetDeviceEntryForController(controller
);
496 if (!aborted_due_to_error
) {
497 if (controller
->has_received_frames()) {
498 LogVideoCaptureEvent(VIDEO_CAPTURE_STOP_CAPTURE_OK
);
499 } else if (entry
->stream_type
== MEDIA_DEVICE_VIDEO_CAPTURE
) {
500 LogVideoCaptureEvent(
501 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DEVICE
);
503 LogVideoCaptureEvent(
504 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DESKTOP_OR_TAB
);
507 LogVideoCaptureEvent(VIDEO_CAPTURE_STOP_CAPTURE_DUE_TO_ERROR
);
508 SessionMap::iterator it
;
509 for (it
= sessions_
.begin(); it
!= sessions_
.end(); ++it
) {
510 if (it
->second
.type
== entry
->stream_type
&&
511 it
->second
.id
== entry
->id
) {
512 listener_
->Aborted(it
->second
.type
, it
->first
);
518 // Detach client from controller.
519 media::VideoCaptureSessionId session_id
=
520 controller
->RemoveClient(client_id
, client_handler
);
521 DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = "
524 // If controller has no more clients, delete controller and device.
525 DestroyDeviceEntryIfNoClients(entry
);
528 void VideoCaptureManager::PauseCaptureForClient(
529 VideoCaptureController
* controller
,
530 VideoCaptureControllerID client_id
,
531 VideoCaptureControllerEventHandler
* client_handler
) {
532 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
534 DCHECK(client_handler
);
535 DeviceEntry
* entry
= GetDeviceEntryForController(controller
);
541 // We only pause the MEDIA_DEVICE_VIDEO_CAPTURE entry to release camera to
543 if (entry
->stream_type
!= MEDIA_DEVICE_VIDEO_CAPTURE
)
546 controller
->PauseOrResumeClient(client_id
, client_handler
, true);
547 if (controller
->GetActiveClientCount() != 0)
550 // There is no more client, release the camera.
554 void VideoCaptureManager::ResumeCaptureForClient(
555 media::VideoCaptureSessionId session_id
,
556 const media::VideoCaptureParams
& params
,
557 VideoCaptureController
* controller
,
558 VideoCaptureControllerID client_id
,
559 VideoCaptureControllerEventHandler
* client_handler
) {
560 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
562 DCHECK(client_handler
);
564 DeviceEntry
* entry
= GetDeviceEntryForController(controller
);
570 // We only pause/resume the MEDIA_DEVICE_VIDEO_CAPTURE entry.
571 if (entry
->stream_type
!= MEDIA_DEVICE_VIDEO_CAPTURE
)
574 controller
->PauseOrResumeClient(client_id
, client_handler
, false);
575 if (controller
->GetActiveClientCount() != 1)
578 // This is first active client, allocate the camera.
579 QueueStartDevice(session_id
, entry
, params
);
582 bool VideoCaptureManager::GetDeviceSupportedFormats(
583 media::VideoCaptureSessionId capture_session_id
,
584 media::VideoCaptureFormats
* supported_formats
) {
585 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
586 DCHECK(supported_formats
->empty());
588 SessionMap::iterator it
= sessions_
.find(capture_session_id
);
589 if (it
== sessions_
.end())
591 DVLOG(1) << "GetDeviceSupportedFormats for device: " << it
->second
.name
;
593 // Return all available formats of the device, regardless its started state.
594 media::VideoCaptureDeviceInfo
* existing_device
=
595 FindDeviceInfoById(it
->second
.id
, devices_info_cache_
);
597 *supported_formats
= existing_device
->supported_formats
;
601 bool VideoCaptureManager::GetDeviceFormatsInUse(
602 media::VideoCaptureSessionId capture_session_id
,
603 media::VideoCaptureFormats
* formats_in_use
) {
604 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
605 DCHECK(formats_in_use
->empty());
607 SessionMap::iterator it
= sessions_
.find(capture_session_id
);
608 if (it
== sessions_
.end())
610 DVLOG(1) << "GetDeviceFormatsInUse for device: " << it
->second
.name
;
612 // Return the currently in-use format(s) of the device, if it's started.
613 DeviceEntry
* device_in_use
=
614 GetDeviceEntryForMediaStreamDevice(it
->second
);
616 // Currently only one format-in-use is supported at the VCC level.
617 formats_in_use
->push_back(
618 device_in_use
->video_capture_controller()->GetVideoCaptureFormat());
623 void VideoCaptureManager::SetDesktopCaptureWindowId(
624 media::VideoCaptureSessionId session_id
,
625 gfx::NativeViewId window_id
) {
626 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
627 VLOG(2) << "SetDesktopCaptureWindowId called for session " << session_id
;
629 notification_window_ids_
[session_id
] = window_id
;
630 MaybePostDesktopCaptureWindowId(session_id
);
633 void VideoCaptureManager::MaybePostDesktopCaptureWindowId(
634 media::VideoCaptureSessionId session_id
) {
635 SessionMap::iterator session_it
= sessions_
.find(session_id
);
636 if (session_it
== sessions_
.end()) {
640 DeviceEntry
* const existing_device
=
641 GetDeviceEntryForMediaStreamDevice(session_it
->second
);
642 if (!existing_device
) {
643 DVLOG(2) << "Failed to find an existing screen capture device.";
647 if (!existing_device
->video_capture_device()) {
648 DVLOG(2) << "Screen capture device not yet started.";
652 DCHECK_EQ(MEDIA_DESKTOP_VIDEO_CAPTURE
, existing_device
->stream_type
);
653 DesktopMediaID id
= DesktopMediaID::Parse(existing_device
->id
);
657 auto window_id_it
= notification_window_ids_
.find(session_id
);
658 if (window_id_it
== notification_window_ids_
.end()) {
659 DVLOG(2) << "Notification window id not set for screen capture.";
663 // Post |existing_device->video_capture_device| to the VideoCaptureDevice to
664 // the device_task_runner_. This is safe since the device is destroyed on the
665 // device_task_runner_.
666 device_task_runner_
->PostTask(
668 base::Bind(&VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread
,
670 existing_device
->video_capture_device(),
671 window_id_it
->second
));
673 notification_window_ids_
.erase(window_id_it
);
676 void VideoCaptureManager::DoStopDeviceOnDeviceThread(
677 scoped_ptr
<media::VideoCaptureDevice
> device
) {
678 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime");
679 DCHECK(IsOnDeviceThread());
680 device
->StopAndDeAllocate();
681 DVLOG(3) << "DoStopDeviceOnDeviceThread";
684 void VideoCaptureManager::OnOpened(
685 MediaStreamType stream_type
,
686 media::VideoCaptureSessionId capture_session_id
) {
687 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
689 // Listener has been removed.
692 listener_
->Opened(stream_type
, capture_session_id
);
695 void VideoCaptureManager::OnClosed(
696 MediaStreamType stream_type
,
697 media::VideoCaptureSessionId capture_session_id
) {
698 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
700 // Listener has been removed.
703 listener_
->Closed(stream_type
, capture_session_id
);
706 void VideoCaptureManager::OnDevicesInfoEnumerated(
707 MediaStreamType stream_type
,
708 base::ElapsedTimer
* timer
,
709 const media::VideoCaptureDeviceInfos
& new_devices_info_cache
) {
710 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
712 "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime",
715 // Listener has been removed.
718 devices_info_cache_
= new_devices_info_cache
;
720 MediaInternals::GetInstance()->UpdateVideoCaptureDeviceCapabilities(
721 devices_info_cache_
);
723 // Walk the |devices_info_cache_| and transform from VCD::Name to
724 // StreamDeviceInfo for return purposes.
725 StreamDeviceInfoArray devices
;
726 for (const auto& it
: devices_info_cache_
) {
727 devices
.push_back(StreamDeviceInfo(
728 stream_type
, it
.name
.GetNameAndModel(), it
.name
.id()));
730 listener_
->DevicesEnumerated(stream_type
, devices
);
733 bool VideoCaptureManager::IsOnDeviceThread() const {
734 return device_task_runner_
->BelongsToCurrentThread();
737 void VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread(
738 base::Callback
<void(const media::VideoCaptureDeviceInfos
&)>
739 on_devices_enumerated_callback
,
740 MediaStreamType stream_type
,
741 const media::VideoCaptureDeviceInfos
& old_device_info_cache
,
742 scoped_ptr
<media::VideoCaptureDevice::Names
> names_snapshot
) {
743 DCHECK(IsOnDeviceThread());
744 // Construct |new_devices_info_cache| with the cached devices that are still
745 // present in the system, and remove their names from |names_snapshot|, so we
746 // keep there the truly new devices.
747 media::VideoCaptureDeviceInfos new_devices_info_cache
;
748 for (const auto& device_info
: old_device_info_cache
) {
749 for (media::VideoCaptureDevice::Names::iterator it
=
750 names_snapshot
->begin(); it
!= names_snapshot
->end(); ++it
) {
751 if (device_info
.name
.id() == it
->id()) {
752 new_devices_info_cache
.push_back(device_info
);
753 names_snapshot
->erase(it
);
759 // Get the supported capture formats for the new devices in |names_snapshot|.
760 for (const auto& it
: *names_snapshot
) {
761 media::VideoCaptureDeviceInfo
device_info(it
, media::VideoCaptureFormats());
762 video_capture_device_factory_
->GetDeviceSupportedFormats(
763 it
, &(device_info
.supported_formats
));
764 ConsolidateCaptureFormats(&device_info
.supported_formats
);
765 new_devices_info_cache
.push_back(device_info
);
768 on_devices_enumerated_callback
.Run(new_devices_info_cache
);
771 VideoCaptureManager::DeviceEntry
*
772 VideoCaptureManager::GetDeviceEntryForMediaStreamDevice(
773 const MediaStreamDevice
& device_info
) {
774 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
776 for (DeviceEntries::iterator it
= devices_
.begin();
777 it
!= devices_
.end(); ++it
) {
778 DeviceEntry
* device
= *it
;
779 if (device_info
.type
== device
->stream_type
&&
780 device_info
.id
== device
->id
) {
787 VideoCaptureManager::DeviceEntry
*
788 VideoCaptureManager::GetDeviceEntryForController(
789 const VideoCaptureController
* controller
) const {
790 // Look up |controller| in |devices_|.
791 for (DeviceEntries::const_iterator it
= devices_
.begin();
792 it
!= devices_
.end(); ++it
) {
793 if ((*it
)->video_capture_controller() == controller
) {
800 void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry
* entry
) {
801 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
802 // Removal of the last client stops the device.
803 if (entry
->video_capture_controller()->GetClientCount() == 0) {
804 DVLOG(1) << "VideoCaptureManager stopping device (type = "
805 << entry
->stream_type
<< ", id = " << entry
->id
<< ")";
807 // The DeviceEntry is removed from |devices_| immediately. The controller is
808 // deleted immediately, and the device is freed asynchronously. After this
809 // point, subsequent requests to open this same device ID will create a new
810 // DeviceEntry, VideoCaptureController, and VideoCaptureDevice.
812 DeviceEntries::iterator device_it
= std::find(devices_
.begin(),
815 devices_
.erase(device_it
);
819 VideoCaptureManager::DeviceEntry
* VideoCaptureManager::GetOrCreateDeviceEntry(
820 media::VideoCaptureSessionId capture_session_id
) {
821 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
823 SessionMap::iterator session_it
= sessions_
.find(capture_session_id
);
824 if (session_it
== sessions_
.end()) {
827 const MediaStreamDevice
& device_info
= session_it
->second
;
829 // Check if another session has already opened this device. If so, just
830 // use that opened device.
831 DeviceEntry
* const existing_device
=
832 GetDeviceEntryForMediaStreamDevice(device_info
);
833 if (existing_device
) {
834 DCHECK_EQ(device_info
.type
, existing_device
->stream_type
);
835 return existing_device
;
838 const int max_buffers
= device_info
.type
== MEDIA_TAB_VIDEO_CAPTURE
?
839 kMaxNumberOfBuffersForTabCapture
: kMaxNumberOfBuffers
;
840 scoped_ptr
<VideoCaptureController
> video_capture_controller(
841 new VideoCaptureController(max_buffers
));
842 DeviceEntry
* new_device
= new DeviceEntry(device_info
.type
,
844 video_capture_controller
.Pass());
845 devices_
.push_back(new_device
);
849 media::VideoCaptureDeviceInfo
* VideoCaptureManager::FindDeviceInfoById(
850 const std::string
& id
,
851 media::VideoCaptureDeviceInfos
& device_vector
) {
852 for (auto& it
: device_vector
) {
853 if (it
.name
.id() == id
)
859 void VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread(
860 media::VideoCaptureDevice
* device
,
861 gfx::NativeViewId window_id
) {
862 DCHECK(IsOnDeviceThread());
863 #if defined(ENABLE_SCREEN_CAPTURE)
864 DesktopCaptureDevice
* desktop_device
=
865 static_cast<DesktopCaptureDevice
*>(device
);
866 desktop_device
->SetNotificationWindowId(window_id
);
867 VLOG(2) << "Screen capture notification window passed on device thread.";
871 } // namespace content