Use multiline attribute to check for IA2_STATE_MULTILINE.
[chromium-blink-merge.git] / content / browser / renderer_host / media / video_capture_manager.cc
blobf1c14ebc7e140c912cd8a2d3bf7b442456dcda30
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/renderer_host/media/video_capture_manager.h"
7 #include <algorithm>
8 #include <set>
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/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"
31 #if defined(USE_AURA)
32 #include "content/browser/media/capture/desktop_capture_device_aura.h"
33 #endif
34 #endif
36 namespace {
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
56 // pixel format.
57 void ConsolidateCaptureFormats(media::VideoCaptureFormats* formats) {
58 if (formats->empty())
59 return;
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",
93 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;
100 } // namespace
102 namespace content {
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),
110 id(id),
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(
146 int serial_id,
147 media::VideoCaptureSessionId session_id,
148 const media::VideoCaptureParams& params)
149 : serial_id_(serial_id),
150 session_id_(session_id),
151 params_(params),
152 abort_start_(false) {
155 VideoCaptureManager::VideoCaptureManager(
156 scoped_ptr<media::VideoCaptureDeviceFactory> factory)
157 : listener_(NULL),
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);
171 DCHECK(!listener_);
172 DCHECK(!device_task_runner_.get());
173 listener_ = listener;
174 device_task_runner_ = device_task_runner;
177 void VideoCaptureManager::Unregister() {
178 DCHECK(listener_);
179 listener_ = NULL;
182 void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) {
183 DCHECK_CURRENTLY_ON(BrowserThread::IO);
184 DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type;
185 DCHECK(listener_);
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,
194 this,
195 media::BindToCurrentLoop(base::Bind(
196 &VideoCaptureManager::OnDevicesInfoEnumerated,
197 this,
198 stream_type,
199 base::Owned(new base::ElapsedTimer()))),
200 stream_type,
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);
212 DCHECK(listener_);
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);
235 DCHECK(listener_);
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()) {
240 NOTREACHED();
241 return;
244 DeviceEntry* const existing_device = GetDeviceEntryForMediaStreamDevice(
245 session_it->second);
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
253 // close the device.
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,
266 DeviceEntry* entry,
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;
287 return;
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(
296 FROM_HERE,
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())
311 return;
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(),
326 FROM_HERE,
327 base::Bind(
328 &VideoCaptureManager::DoStartDeviceOnDeviceThread,
329 this,
330 request->session_id(),
331 entry->id,
332 entry->stream_type,
333 request->params(),
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(
341 int serial_id,
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();
358 } else {
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_);
398 if (found) {
399 video_capture_device =
400 video_capture_device_factory_->Create(found->name);
402 break;
404 case MEDIA_TAB_VIDEO_CAPTURE: {
405 video_capture_device.reset(
406 WebContentsVideoCaptureDevice::Create(id));
407 break;
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));
416 } else
417 #endif
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)
423 break;
425 default: {
426 NOTIMPLEMENTED();
427 break;
431 if (!video_capture_device) {
432 device_client->OnError("Could not create capture device");
433 return nullptr;
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);
453 if (!entry) {
454 done_cb.Run(base::WeakPtr<VideoCaptureController>());
455 return;
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);
480 DCHECK(controller);
481 DCHECK(client_handler);
483 DeviceEntry* entry = GetDeviceEntryForController(controller);
484 if (!entry) {
485 NOTREACHED();
486 return;
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);
494 } else {
495 LogVideoCaptureEvent(
496 VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DESKTOP_OR_TAB);
498 } else {
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);
505 break;
510 // Detach client from controller.
511 media::VideoCaptureSessionId session_id =
512 controller->RemoveClient(client_id, client_handler);
513 DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = "
514 << 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);
525 DCHECK(controller);
526 DCHECK(client_handler);
527 DeviceEntry* entry = GetDeviceEntryForController(controller);
528 if (!entry) {
529 NOTREACHED();
530 return;
533 // We only pause the MEDIA_DEVICE_VIDEO_CAPTURE entry to release camera to
534 // system.
535 if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE)
536 return;
538 controller->PauseOrResumeClient(client_id, client_handler, true);
539 if (controller->GetActiveClientCount() != 0)
540 return;
542 // There is no more client, release the camera.
543 DoStopDevice(entry);
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);
553 DCHECK(controller);
554 DCHECK(client_handler);
556 DeviceEntry* entry = GetDeviceEntryForController(controller);
557 if (!entry) {
558 NOTREACHED();
559 return;
562 // We only pause/resume the MEDIA_DEVICE_VIDEO_CAPTURE entry.
563 if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE)
564 return;
566 controller->PauseOrResumeClient(client_id, client_handler, false);
567 if (controller->GetActiveClientCount() != 1)
568 return;
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())
582 return false;
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_);
588 if (existing_device)
589 *supported_formats = existing_device->supported_formats;
590 return true;
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())
601 return false;
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);
607 if (device_in_use) {
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());
612 return true;
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()) {
629 return;
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.";
636 return;
639 if (!existing_device->video_capture_device()) {
640 DVLOG(2) << "Screen capture device not yet started.";
641 return;
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.";
649 return;
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.";
655 return;
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(
662 FROM_HERE,
663 base::Bind(&VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread,
664 this,
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);
683 if (!listener_) {
684 // Listener has been removed.
685 return;
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);
694 if (!listener_) {
695 // Listener has been removed.
696 return;
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);
706 UMA_HISTOGRAM_TIMES(
707 "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime",
708 timer->Elapsed());
709 if (!listener_) {
710 // Listener has been removed.
711 return;
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);
749 break;
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) {
776 return device;
779 return NULL;
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) {
789 return *it;
792 return NULL;
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.
806 DoStopDevice(entry);
807 DeviceEntries::iterator device_it = std::find(devices_.begin(),
808 devices_.end(),
809 entry);
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()) {
820 return NULL;
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,
838 device_info.id,
839 video_capture_controller.Pass());
840 devices_.push_back(new_device);
841 return 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)
849 return &(it);
851 return NULL;
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.";
863 #endif
866 } // namespace content