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 defined(USE_AURA)
424 if (desktop_id
.type
== DesktopMediaID::TYPE_AURA_WINDOW
) {
425 video_capture_device
.reset(
426 DesktopCaptureDeviceAura::Create(desktop_id
));
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)
442 if (!video_capture_device
) {
443 device_client
->OnError("Could not create capture device");
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
461 << media::VideoCaptureFormat::ToString(params
.requested_format
);
463 DeviceEntry
* entry
= GetOrCreateDeviceEntry(session_id
);
465 done_cb
.Run(base::WeakPtr
<VideoCaptureController
>());
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
);
492 DCHECK(client_handler
);
494 DeviceEntry
* entry
= GetDeviceEntryForController(controller
);
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
);
506 LogVideoCaptureEvent(
507 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DESKTOP_OR_TAB
);
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
);
521 // Detach client from controller.
522 media::VideoCaptureSessionId session_id
=
523 controller
->RemoveClient(client_id
, client_handler
);
524 DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, 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
);
537 DCHECK(client_handler
);
538 DeviceEntry
* entry
= GetDeviceEntryForController(controller
);
544 // We only pause the MEDIA_DEVICE_VIDEO_CAPTURE entry to release camera to
546 if (entry
->stream_type
!= MEDIA_DEVICE_VIDEO_CAPTURE
)
549 controller
->PauseOrResumeClient(client_id
, client_handler
, true);
550 if (controller
->GetActiveClientCount() != 0)
553 // There is no more client, release the camera.
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
);
565 DCHECK(client_handler
);
567 DeviceEntry
* entry
= GetDeviceEntryForController(controller
);
573 // We only pause/resume the MEDIA_DEVICE_VIDEO_CAPTURE entry.
574 if (entry
->stream_type
!= MEDIA_DEVICE_VIDEO_CAPTURE
)
577 controller
->PauseOrResumeClient(client_id
, client_handler
, false);
578 if (controller
->GetActiveClientCount() != 1)
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())
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_
);
600 *supported_formats
= existing_device
->supported_formats
;
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())
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
);
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());
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()) {
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.";
650 if (!existing_device
->video_capture_device()) {
651 DVLOG(2) << "Screen capture device not yet started.";
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.";
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.";
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(
674 base::Bind(&VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread
,
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
);
695 // Listener has been removed.
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
);
706 // Listener has been removed.
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
);
718 "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime",
721 // Listener has been removed.
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
);
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
) {
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
) {
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.
818 DeviceEntries::iterator device_it
= std::find(devices_
.begin(),
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()) {
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
,
850 video_capture_controller
.Pass());
851 devices_
.push_back(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
)
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.";
877 } // namespace content