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/logging.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/stl_util.h"
16 #include "base/task_runner_util.h"
17 #include "base/threading/sequenced_worker_pool.h"
18 #include "content/browser/media/capture/web_contents_video_capture_device.h"
19 #include "content/browser/media/media_internals.h"
20 #include "content/browser/renderer_host/media/video_capture_controller.h"
21 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/desktop_media_id.h"
24 #include "content/public/common/media_stream_request.h"
25 #include "media/base/bind_to_current_loop.h"
26 #include "media/video/capture/video_capture_device.h"
27 #include "media/video/capture/video_capture_device_factory.h"
29 #if defined(ENABLE_SCREEN_CAPTURE)
30 #include "content/browser/media/capture/desktop_capture_device.h"
32 #include "content/browser/media/capture/desktop_capture_device_aura.h"
38 // Compares two VideoCaptureFormat by checking smallest frame_size area, then
39 // by _largest_ frame_rate. Used to order a VideoCaptureFormats vector so that
40 // the first entry for a given resolution has the largest frame rate, as needed
41 // by the ConsolidateCaptureFormats() method.
42 bool IsCaptureFormatSmaller(const media::VideoCaptureFormat
& format1
,
43 const media::VideoCaptureFormat
& format2
) {
44 if (format1
.frame_size
.GetArea() == format2
.frame_size
.GetArea())
45 return format1
.frame_rate
> format2
.frame_rate
;
46 return format1
.frame_size
.GetArea() < format2
.frame_size
.GetArea();
49 bool IsCaptureFormatSizeEqual(const media::VideoCaptureFormat
& format1
,
50 const media::VideoCaptureFormat
& format2
) {
51 return format1
.frame_size
.GetArea() == format2
.frame_size
.GetArea();
54 // This function receives a list of capture formats, removes duplicated
55 // resolutions while keeping the highest frame rate for each, and forcing I420
57 void ConsolidateCaptureFormats(media::VideoCaptureFormats
* formats
) {
60 std::sort(formats
->begin(), formats
->end(), IsCaptureFormatSmaller
);
61 // Due to the ordering imposed, the largest frame_rate is kept while removing
62 // duplicated resolutions.
63 media::VideoCaptureFormats::iterator last
=
64 std::unique(formats
->begin(), formats
->end(), IsCaptureFormatSizeEqual
);
65 formats
->erase(last
, formats
->end());
66 // Mark all formats as I420, since this is what the renderer side will get
67 // anyhow: the actual pixel format is decided at the device level.
68 for (media::VideoCaptureFormats::iterator it
= formats
->begin();
69 it
!= formats
->end(); ++it
) {
70 it
->pixel_format
= media::PIXEL_FORMAT_I420
;
74 // The maximum number of buffers in the capture pipeline. See
75 // VideoCaptureController ctor comments for more details.
76 const int kMaxNumberOfBuffers
= 3;
77 const int kMaxNumberOfBuffersForTabCapture
= 5;
79 // Used for logging capture events.
80 // Elements in this enum should not be deleted or rearranged; the only
81 // permitted operation is to add new elements before NUM_VIDEO_CAPTURE_EVENT.
82 enum VideoCaptureEvent
{
83 VIDEO_CAPTURE_START_CAPTURE
= 0,
84 VIDEO_CAPTURE_STOP_CAPTURE_OK
= 1,
85 VIDEO_CAPTURE_STOP_CAPTURE_DUE_TO_ERROR
= 2,
86 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DEVICE
= 3,
87 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DESKTOP_OR_TAB
= 4,
88 NUM_VIDEO_CAPTURE_EVENT
91 void LogVideoCaptureEvent(VideoCaptureEvent event
) {
92 UMA_HISTOGRAM_ENUMERATION("Media.VideoCaptureManager.Event",
94 NUM_VIDEO_CAPTURE_EVENT
);
97 // Counter used for identifying a DeviceRequest to start a capture device.
98 static int g_device_start_id
= 0;
104 VideoCaptureManager::DeviceEntry::DeviceEntry(
105 MediaStreamType stream_type
,
106 const std::string
& id
,
107 scoped_ptr
<VideoCaptureController
> controller
)
108 : serial_id(g_device_start_id
++),
109 stream_type(stream_type
),
111 video_capture_controller_(controller
.Pass()) {}
113 VideoCaptureManager::DeviceEntry::~DeviceEntry() {
114 DCHECK(thread_checker_
.CalledOnValidThread());
115 // DCHECK that this DeviceEntry does not still own a
116 // media::VideoCaptureDevice. media::VideoCaptureDevice must be deleted on
117 // the device thread.
118 DCHECK(video_capture_device_
== nullptr);
121 void VideoCaptureManager::DeviceEntry::SetVideoCaptureDevice(
122 scoped_ptr
<media::VideoCaptureDevice
> device
) {
123 DCHECK(thread_checker_
.CalledOnValidThread());
124 video_capture_device_
.swap(device
);
127 scoped_ptr
<media::VideoCaptureDevice
>
128 VideoCaptureManager::DeviceEntry::ReleaseVideoCaptureDevice() {
129 DCHECK(thread_checker_
.CalledOnValidThread());
130 return video_capture_device_
.Pass();
133 VideoCaptureController
*
134 VideoCaptureManager::DeviceEntry::video_capture_controller() {
135 DCHECK(thread_checker_
.CalledOnValidThread());
136 return video_capture_controller_
.get();
139 media::VideoCaptureDevice
*
140 VideoCaptureManager::DeviceEntry::video_capture_device() {
141 DCHECK(thread_checker_
.CalledOnValidThread());
142 return video_capture_device_
.get();
145 VideoCaptureManager::CaptureDeviceStartRequest::CaptureDeviceStartRequest(
147 media::VideoCaptureSessionId session_id
,
148 const media::VideoCaptureParams
& params
)
149 : serial_id_(serial_id
),
150 session_id_(session_id
),
152 abort_start_(false) {
155 VideoCaptureManager::VideoCaptureManager(
156 scoped_ptr
<media::VideoCaptureDeviceFactory
> factory
)
158 new_capture_session_id_(1),
159 video_capture_device_factory_(factory
.Pass()) {
162 VideoCaptureManager::~VideoCaptureManager() {
163 DCHECK(devices_
.empty());
164 DCHECK(device_start_queue_
.empty());
167 void VideoCaptureManager::Register(
168 MediaStreamProviderListener
* listener
,
169 const scoped_refptr
<base::SingleThreadTaskRunner
>& device_task_runner
) {
170 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
172 DCHECK(!device_task_runner_
.get());
173 listener_
= listener
;
174 device_task_runner_
= device_task_runner
;
177 void VideoCaptureManager::Unregister() {
182 void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type
) {
183 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
184 DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type
;
186 DCHECK_EQ(stream_type
, MEDIA_DEVICE_VIDEO_CAPTURE
);
188 // Bind a callback to ConsolidateDevicesInfoOnDeviceThread() with an argument
189 // for another callback to OnDevicesInfoEnumerated() to be run in the current
190 // loop, i.e. IO loop. Pass a timer for UMA histogram collection.
191 base::Callback
<void(scoped_ptr
<media::VideoCaptureDevice::Names
>)>
192 devices_enumerated_callback
=
193 base::Bind(&VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread
,
195 media::BindToCurrentLoop(base::Bind(
196 &VideoCaptureManager::OnDevicesInfoEnumerated
,
199 base::Owned(new base::ElapsedTimer()))),
201 devices_info_cache_
);
202 // OK to use base::Unretained() since we own the VCDFactory and |this| is
203 // bound in |devices_enumerated_callback|.
204 device_task_runner_
->PostTask(FROM_HERE
,
205 base::Bind(&media::VideoCaptureDeviceFactory::EnumerateDeviceNames
,
206 base::Unretained(video_capture_device_factory_
.get()),
207 devices_enumerated_callback
));
210 int VideoCaptureManager::Open(const StreamDeviceInfo
& device_info
) {
211 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
214 // Generate a new id for the session being opened.
215 const media::VideoCaptureSessionId capture_session_id
=
216 new_capture_session_id_
++;
218 DCHECK(sessions_
.find(capture_session_id
) == sessions_
.end());
219 DVLOG(1) << "VideoCaptureManager::Open, id " << capture_session_id
;
221 // We just save the stream info for processing later.
222 sessions_
[capture_session_id
] = device_info
.device
;
224 // Notify our listener asynchronously; this ensures that we return
225 // |capture_session_id| to the caller of this function before using that same
226 // id in a listener event.
227 base::MessageLoop::current()->PostTask(FROM_HERE
,
228 base::Bind(&VideoCaptureManager::OnOpened
, this,
229 device_info
.device
.type
, capture_session_id
));
230 return capture_session_id
;
233 void VideoCaptureManager::Close(int capture_session_id
) {
234 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
236 DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id
;
238 SessionMap::iterator session_it
= sessions_
.find(capture_session_id
);
239 if (session_it
== sessions_
.end()) {
244 DeviceEntry
* const existing_device
= GetDeviceEntryForMediaStreamDevice(
246 if (existing_device
) {
247 // Remove any client that is still using the session. This is safe to call
248 // even if there are no clients using the session.
249 existing_device
->video_capture_controller()
250 ->StopSession(capture_session_id
);
252 // StopSession() may have removed the last client, so we might need to
254 DestroyDeviceEntryIfNoClients(existing_device
);
257 // Notify listeners asynchronously, and forget the session.
258 base::MessageLoop::current()->PostTask(FROM_HERE
,
259 base::Bind(&VideoCaptureManager::OnClosed
, this, session_it
->second
.type
,
260 capture_session_id
));
261 sessions_
.erase(session_it
);
264 void VideoCaptureManager::QueueStartDevice(
265 media::VideoCaptureSessionId session_id
,
267 const media::VideoCaptureParams
& params
) {
268 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
269 device_start_queue_
.push_back(
270 CaptureDeviceStartRequest(entry
->serial_id
, session_id
, params
));
271 if (device_start_queue_
.size() == 1)
272 HandleQueuedStartRequest();
275 void VideoCaptureManager::DoStopDevice(DeviceEntry
* entry
) {
276 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
277 DCHECK(std::find(devices_
.begin(), devices_
.end(), entry
) != devices_
.end());
279 // Find the matching start request.
280 for (DeviceStartQueue::reverse_iterator request
=
281 device_start_queue_
.rbegin();
282 request
!= device_start_queue_
.rend(); ++request
) {
283 if (request
->serial_id() == entry
->serial_id
) {
284 request
->set_abort_start();
285 DVLOG(3) << "DoStopDevice, aborting start request for device "
286 << entry
->id
<< " serial_id = " << entry
->serial_id
;
291 DVLOG(3) << "DoStopDevice. Send stop request for device = " << entry
->id
292 << " serial_id = " << entry
->serial_id
<< ".";
293 if (entry
->video_capture_device()) {
294 // |entry->video_capture_device| can be null if creating the device fails.
295 device_task_runner_
->PostTask(
297 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread
, this,
298 base::Passed(entry
->ReleaseVideoCaptureDevice())));
302 void VideoCaptureManager::HandleQueuedStartRequest() {
303 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
304 // Remove all start requests that have been aborted.
305 while (device_start_queue_
.begin() != device_start_queue_
.end() &&
306 device_start_queue_
.begin()->abort_start()) {
307 device_start_queue_
.pop_front();
309 DeviceStartQueue::iterator request
= device_start_queue_
.begin();
310 if (request
== device_start_queue_
.end())
313 const int serial_id
= request
->serial_id();
314 DeviceEntries::iterator entry_it
= std::find_if(
315 devices_
.begin(), devices_
.end(),
316 [serial_id
] (const DeviceEntry
* e
) {
317 return e
->serial_id
== serial_id
;
319 DCHECK(entry_it
!= devices_
.end());
320 DeviceEntry
* entry
= (*entry_it
);
322 DVLOG(3) << "HandleQueuedStartRequest, Post start to device thread, device = "
323 << entry
->id
<< " start id = " << entry
->serial_id
;
324 base::PostTaskAndReplyWithResult(
325 device_task_runner_
.get(),
328 &VideoCaptureManager::DoStartDeviceOnDeviceThread
,
330 request
->session_id(),
334 base::Passed(entry
->video_capture_controller()->NewDeviceClient(
335 device_task_runner_
, request
->params().requested_format
))),
336 base::Bind(&VideoCaptureManager::OnDeviceStarted
, this,
337 request
->serial_id()));
340 void VideoCaptureManager::OnDeviceStarted(
342 scoped_ptr
<media::VideoCaptureDevice
> device
) {
343 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
344 DCHECK(serial_id
== device_start_queue_
.begin()->serial_id());
345 DVLOG(3) << "OnDeviceStarted";
346 if (device_start_queue_
.front().abort_start()) {
347 // |device| can be null if creation failed in DoStartDeviceOnDeviceThread.
348 // The device is no longer wanted. Stop the device again.
349 DVLOG(3) << "OnDeviceStarted but start request have been aborted.";
350 media::VideoCaptureDevice
* device_ptr
= device
.get();
351 base::Closure closure
=
352 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread
, this,
353 base::Passed(&device
));
354 if (device_ptr
&& !device_task_runner_
->PostTask(FROM_HERE
, closure
)) {
355 // PostTask failed. The device must be stopped anyway.
356 device_ptr
->StopAndDeAllocate();
359 DeviceEntries::iterator entry_it
= std::find_if(
360 devices_
.begin(), devices_
.end(),
361 [serial_id
] (const DeviceEntry
* e
) {
362 return e
->serial_id
== serial_id
;
364 DCHECK(entry_it
!= devices_
.end());
365 DeviceEntry
* entry
= *entry_it
;
366 DCHECK(!entry
->video_capture_device());
367 entry
->SetVideoCaptureDevice(device
.Pass());
369 if (entry
->stream_type
== MEDIA_DESKTOP_VIDEO_CAPTURE
) {
370 const media::VideoCaptureSessionId session_id
=
371 device_start_queue_
.front().session_id();
372 MaybePostDesktopCaptureWindowId(session_id
);
376 device_start_queue_
.pop_front();
377 HandleQueuedStartRequest();
380 scoped_ptr
<media::VideoCaptureDevice
>
381 VideoCaptureManager::DoStartDeviceOnDeviceThread(
382 media::VideoCaptureSessionId session_id
,
383 const std::string
& id
,
384 MediaStreamType stream_type
,
385 const media::VideoCaptureParams
& params
,
386 scoped_ptr
<media::VideoCaptureDevice::Client
> device_client
) {
387 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime");
388 DCHECK(IsOnDeviceThread());
390 scoped_ptr
<media::VideoCaptureDevice
> video_capture_device
;
391 switch (stream_type
) {
392 case MEDIA_DEVICE_VIDEO_CAPTURE
: {
393 // We look up the device id from the renderer in our local enumeration
394 // since the renderer does not have all the information that might be
395 // held in the browser-side VideoCaptureDevice::Name structure.
396 const media::VideoCaptureDeviceInfo
* found
=
397 FindDeviceInfoById(id
, devices_info_cache_
);
399 video_capture_device
=
400 video_capture_device_factory_
->Create(found
->name
);
404 case MEDIA_TAB_VIDEO_CAPTURE
: {
405 video_capture_device
.reset(
406 WebContentsVideoCaptureDevice::Create(id
));
409 case MEDIA_DESKTOP_VIDEO_CAPTURE
: {
410 #if defined(ENABLE_SCREEN_CAPTURE)
411 DesktopMediaID desktop_id
= DesktopMediaID::Parse(id
);
412 #if defined(USE_AURA)
413 if (desktop_id
.type
== DesktopMediaID::TYPE_AURA_WINDOW
) {
414 video_capture_device
.reset(
415 DesktopCaptureDeviceAura::Create(desktop_id
));
418 if (desktop_id
.type
!= DesktopMediaID::TYPE_NONE
&&
419 desktop_id
.type
!= DesktopMediaID::TYPE_AURA_WINDOW
) {
420 video_capture_device
= DesktopCaptureDevice::Create(desktop_id
);
422 #endif // defined(ENABLE_SCREEN_CAPTURE)
431 if (!video_capture_device
) {
432 device_client
->OnError("Could not create capture device");
436 video_capture_device
->AllocateAndStart(params
, device_client
.Pass());
437 return video_capture_device
.Pass();
440 void VideoCaptureManager::StartCaptureForClient(
441 media::VideoCaptureSessionId session_id
,
442 const media::VideoCaptureParams
& params
,
443 base::ProcessHandle client_render_process
,
444 VideoCaptureControllerID client_id
,
445 VideoCaptureControllerEventHandler
* client_handler
,
446 const DoneCB
& done_cb
) {
447 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
448 DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, "
449 << params
.requested_format
.frame_size
.ToString() << ", "
450 << params
.requested_format
.frame_rate
<< ", #" << session_id
<< ")";
452 DeviceEntry
* entry
= GetOrCreateDeviceEntry(session_id
);
454 done_cb
.Run(base::WeakPtr
<VideoCaptureController
>());
458 DCHECK(entry
->video_capture_controller());
460 LogVideoCaptureEvent(VIDEO_CAPTURE_START_CAPTURE
);
462 // First client starts the device.
463 if (entry
->video_capture_controller()->GetActiveClientCount() == 0) {
464 DVLOG(1) << "VideoCaptureManager starting device (type = "
465 << entry
->stream_type
<< ", id = " << entry
->id
<< ")";
466 QueueStartDevice(session_id
, entry
, params
);
468 // Run the callback first, as AddClient() may trigger OnFrameInfo().
469 done_cb
.Run(entry
->video_capture_controller()->GetWeakPtrForIOThread());
470 entry
->video_capture_controller()->AddClient(
471 client_id
, client_handler
, client_render_process
, session_id
, params
);
474 void VideoCaptureManager::StopCaptureForClient(
475 VideoCaptureController
* controller
,
476 VideoCaptureControllerID client_id
,
477 VideoCaptureControllerEventHandler
* client_handler
,
478 bool aborted_due_to_error
) {
479 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
481 DCHECK(client_handler
);
483 DeviceEntry
* entry
= GetDeviceEntryForController(controller
);
488 if (!aborted_due_to_error
) {
489 if (controller
->has_received_frames()) {
490 LogVideoCaptureEvent(VIDEO_CAPTURE_STOP_CAPTURE_OK
);
491 } else if (entry
->stream_type
== MEDIA_DEVICE_VIDEO_CAPTURE
) {
492 LogVideoCaptureEvent(
493 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DEVICE
);
495 LogVideoCaptureEvent(
496 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DESKTOP_OR_TAB
);
499 LogVideoCaptureEvent(VIDEO_CAPTURE_STOP_CAPTURE_DUE_TO_ERROR
);
500 SessionMap::iterator it
;
501 for (it
= sessions_
.begin(); it
!= sessions_
.end(); ++it
) {
502 if (it
->second
.type
== entry
->stream_type
&&
503 it
->second
.id
== entry
->id
) {
504 listener_
->Aborted(it
->second
.type
, it
->first
);
510 // Detach client from controller.
511 media::VideoCaptureSessionId session_id
=
512 controller
->RemoveClient(client_id
, client_handler
);
513 DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = "
516 // If controller has no more clients, delete controller and device.
517 DestroyDeviceEntryIfNoClients(entry
);
520 void VideoCaptureManager::PauseCaptureForClient(
521 VideoCaptureController
* controller
,
522 VideoCaptureControllerID client_id
,
523 VideoCaptureControllerEventHandler
* client_handler
) {
524 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
526 DCHECK(client_handler
);
527 DeviceEntry
* entry
= GetDeviceEntryForController(controller
);
533 // We only pause the MEDIA_DEVICE_VIDEO_CAPTURE entry to release camera to
535 if (entry
->stream_type
!= MEDIA_DEVICE_VIDEO_CAPTURE
)
538 controller
->PauseOrResumeClient(client_id
, client_handler
, true);
539 if (controller
->GetActiveClientCount() != 0)
542 // There is no more client, release the camera.
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
);
562 // We only pause/resume the MEDIA_DEVICE_VIDEO_CAPTURE entry.
563 if (entry
->stream_type
!= MEDIA_DEVICE_VIDEO_CAPTURE
)
566 controller
->PauseOrResumeClient(client_id
, client_handler
, false);
567 if (controller
->GetActiveClientCount() != 1)
570 // This is first active client, allocate the camera.
571 QueueStartDevice(session_id
, entry
, params
);
574 bool VideoCaptureManager::GetDeviceSupportedFormats(
575 media::VideoCaptureSessionId capture_session_id
,
576 media::VideoCaptureFormats
* supported_formats
) {
577 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
578 DCHECK(supported_formats
->empty());
580 SessionMap::iterator it
= sessions_
.find(capture_session_id
);
581 if (it
== sessions_
.end())
583 DVLOG(1) << "GetDeviceSupportedFormats for device: " << it
->second
.name
;
585 // Return all available formats of the device, regardless its started state.
586 media::VideoCaptureDeviceInfo
* existing_device
=
587 FindDeviceInfoById(it
->second
.id
, devices_info_cache_
);
589 *supported_formats
= existing_device
->supported_formats
;
593 bool VideoCaptureManager::GetDeviceFormatsInUse(
594 media::VideoCaptureSessionId capture_session_id
,
595 media::VideoCaptureFormats
* formats_in_use
) {
596 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
597 DCHECK(formats_in_use
->empty());
599 SessionMap::iterator it
= sessions_
.find(capture_session_id
);
600 if (it
== sessions_
.end())
602 DVLOG(1) << "GetDeviceFormatsInUse for device: " << it
->second
.name
;
604 // Return the currently in-use format(s) of the device, if it's started.
605 DeviceEntry
* device_in_use
=
606 GetDeviceEntryForMediaStreamDevice(it
->second
);
608 // Currently only one format-in-use is supported at the VCC level.
609 formats_in_use
->push_back(
610 device_in_use
->video_capture_controller()->GetVideoCaptureFormat());
615 void VideoCaptureManager::SetDesktopCaptureWindowId(
616 media::VideoCaptureSessionId session_id
,
617 gfx::NativeViewId window_id
) {
618 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
619 VLOG(2) << "SetDesktopCaptureWindowId called for session " << session_id
;
621 notification_window_ids_
[session_id
] = window_id
;
622 MaybePostDesktopCaptureWindowId(session_id
);
625 void VideoCaptureManager::MaybePostDesktopCaptureWindowId(
626 media::VideoCaptureSessionId session_id
) {
627 SessionMap::iterator session_it
= sessions_
.find(session_id
);
628 if (session_it
== sessions_
.end()) {
632 DeviceEntry
* const existing_device
=
633 GetDeviceEntryForMediaStreamDevice(session_it
->second
);
634 if (!existing_device
) {
635 DVLOG(2) << "Failed to find an existing screen capture device.";
639 if (!existing_device
->video_capture_device()) {
640 DVLOG(2) << "Screen capture device not yet started.";
644 DCHECK_EQ(MEDIA_DESKTOP_VIDEO_CAPTURE
, existing_device
->stream_type
);
645 DesktopMediaID id
= DesktopMediaID::Parse(existing_device
->id
);
646 if (id
.type
== DesktopMediaID::TYPE_NONE
||
647 id
.type
== DesktopMediaID::TYPE_AURA_WINDOW
) {
648 VLOG(2) << "Video capture device type mismatch.";
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 client stops the device.
798 if (entry
->video_capture_controller()->GetClientCount() == 0) {
799 DVLOG(1) << "VideoCaptureManager stopping device (type = "
800 << entry
->stream_type
<< ", id = " << entry
->id
<< ")";
802 // The DeviceEntry is removed from |devices_| immediately. The controller is
803 // deleted immediately, and the device is freed asynchronously. After this
804 // point, subsequent requests to open this same device ID will create a new
805 // DeviceEntry, VideoCaptureController, and VideoCaptureDevice.
807 DeviceEntries::iterator device_it
= std::find(devices_
.begin(),
810 devices_
.erase(device_it
);
814 VideoCaptureManager::DeviceEntry
* VideoCaptureManager::GetOrCreateDeviceEntry(
815 media::VideoCaptureSessionId capture_session_id
) {
816 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
818 SessionMap::iterator session_it
= sessions_
.find(capture_session_id
);
819 if (session_it
== sessions_
.end()) {
822 const MediaStreamDevice
& device_info
= session_it
->second
;
824 // Check if another session has already opened this device. If so, just
825 // use that opened device.
826 DeviceEntry
* const existing_device
=
827 GetDeviceEntryForMediaStreamDevice(device_info
);
828 if (existing_device
) {
829 DCHECK_EQ(device_info
.type
, existing_device
->stream_type
);
830 return existing_device
;
833 const int max_buffers
= device_info
.type
== MEDIA_TAB_VIDEO_CAPTURE
?
834 kMaxNumberOfBuffersForTabCapture
: kMaxNumberOfBuffers
;
835 scoped_ptr
<VideoCaptureController
> video_capture_controller(
836 new VideoCaptureController(max_buffers
));
837 DeviceEntry
* new_device
= new DeviceEntry(device_info
.type
,
839 video_capture_controller
.Pass());
840 devices_
.push_back(new_device
);
844 media::VideoCaptureDeviceInfo
* VideoCaptureManager::FindDeviceInfoById(
845 const std::string
& id
,
846 media::VideoCaptureDeviceInfos
& device_vector
) {
847 for (auto& it
: device_vector
) {
848 if (it
.name
.id() == id
)
854 void VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread(
855 media::VideoCaptureDevice
* device
,
856 gfx::NativeViewId window_id
) {
857 DCHECK(IsOnDeviceThread());
858 #if defined(ENABLE_SCREEN_CAPTURE)
859 DesktopCaptureDevice
* desktop_device
=
860 static_cast<DesktopCaptureDevice
*>(device
);
861 desktop_device
->SetNotificationWindowId(window_id
);
862 VLOG(2) << "Screen capture notification window passed on device thread.";
866 } // namespace content