Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / media / video_capture_manager.cc
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.
4
5 #include "content/browser/renderer_host/media/video_capture_manager.h"
6
7 #include <set>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/stl_util.h"
14 #include "base/task_runner_util.h"
15 #include "base/threading/sequenced_worker_pool.h"
16 #include "content/browser/media/capture/web_contents_video_capture_device.h"
17 #include "content/browser/media/media_internals.h"
18 #include "content/browser/renderer_host/media/video_capture_controller.h"
19 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/desktop_media_id.h"
22 #include "content/public/common/content_switches.h"
23 #include "content/public/common/media_stream_request.h"
24 #include "media/base/bind_to_current_loop.h"
25 #include "media/base/scoped_histogram_timer.h"
26 #include "media/video/capture/video_capture_device.h"
27 #include "media/video/capture/video_capture_device_factory.h"
28
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
35
36 namespace {
37
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();
47 }
48
49 bool IsCaptureFormatSizeEqual(const media::VideoCaptureFormat& format1,
50                               const media::VideoCaptureFormat& format2) {
51   return format1.frame_size.GetArea() == format2.frame_size.GetArea();
52 }
53
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;
71   }
72 }
73
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;
78
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
89 };
90
91 void LogVideoCaptureEvent(VideoCaptureEvent event) {
92   UMA_HISTOGRAM_ENUMERATION("Media.VideoCaptureManager.Event",
93                             event,
94                             NUM_VIDEO_CAPTURE_EVENT);
95 }
96
97 }  // namespace
98
99 namespace content {
100
101 VideoCaptureManager::DeviceEntry::DeviceEntry(
102     MediaStreamType stream_type,
103     const std::string& id,
104     scoped_ptr<VideoCaptureController> controller)
105     : stream_type(stream_type),
106       id(id),
107       video_capture_controller(controller.Pass()) {}
108
109 VideoCaptureManager::DeviceEntry::~DeviceEntry() {}
110
111
112 VideoCaptureManager::VideoCaptureManager(
113     scoped_ptr<media::VideoCaptureDeviceFactory> factory)
114     : listener_(NULL),
115       new_capture_session_id_(1),
116       video_capture_device_factory_(factory.Pass()) {
117 }
118
119 VideoCaptureManager::~VideoCaptureManager() {
120   DCHECK(devices_.empty());
121 }
122
123 void VideoCaptureManager::Register(
124     MediaStreamProviderListener* listener,
125     const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) {
126   DCHECK_CURRENTLY_ON(BrowserThread::IO);
127   DCHECK(!listener_);
128   DCHECK(!device_task_runner_.get());
129   listener_ = listener;
130   device_task_runner_ = device_task_runner;
131 }
132
133 void VideoCaptureManager::Unregister() {
134   DCHECK(listener_);
135   listener_ = NULL;
136 }
137
138 void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) {
139   DCHECK_CURRENTLY_ON(BrowserThread::IO);
140   DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type;
141   DCHECK(listener_);
142   DCHECK_EQ(stream_type, MEDIA_DEVICE_VIDEO_CAPTURE);
143
144   // Bind a callback to ConsolidateDevicesInfoOnDeviceThread() with an argument
145   // for another callback to OnDevicesInfoEnumerated() to be run in the current
146   // loop, i.e. IO loop. Pass a timer for UMA histogram collection.
147   base::Callback<void(scoped_ptr<media::VideoCaptureDevice::Names>)>
148       devices_enumerated_callback =
149           base::Bind(&VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread,
150                      this,
151                      media::BindToCurrentLoop(base::Bind(
152                          &VideoCaptureManager::OnDevicesInfoEnumerated,
153                          this,
154                          stream_type,
155                          base::Owned(new base::ElapsedTimer()))),
156                      stream_type,
157                      devices_info_cache_);
158   // OK to use base::Unretained() since we own the VCDFactory and |this| is
159   // bound in |devices_enumerated_callback|.
160   device_task_runner_->PostTask(FROM_HERE,
161       base::Bind(&media::VideoCaptureDeviceFactory::EnumerateDeviceNames,
162                  base::Unretained(video_capture_device_factory_.get()),
163                  devices_enumerated_callback));
164 }
165
166 int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) {
167   DCHECK_CURRENTLY_ON(BrowserThread::IO);
168   DCHECK(listener_);
169
170   // Generate a new id for the session being opened.
171   const media::VideoCaptureSessionId capture_session_id =
172       new_capture_session_id_++;
173
174   DCHECK(sessions_.find(capture_session_id) == sessions_.end());
175   DVLOG(1) << "VideoCaptureManager::Open, id " << capture_session_id;
176
177   // We just save the stream info for processing later.
178   sessions_[capture_session_id] = device_info.device;
179
180   // Notify our listener asynchronously; this ensures that we return
181   // |capture_session_id| to the caller of this function before using that same
182   // id in a listener event.
183   base::MessageLoop::current()->PostTask(FROM_HERE,
184       base::Bind(&VideoCaptureManager::OnOpened, this,
185                  device_info.device.type, capture_session_id));
186   return capture_session_id;
187 }
188
189 void VideoCaptureManager::Close(int capture_session_id) {
190   DCHECK_CURRENTLY_ON(BrowserThread::IO);
191   DCHECK(listener_);
192   DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id;
193
194   SessionMap::iterator session_it = sessions_.find(capture_session_id);
195   if (session_it == sessions_.end()) {
196     NOTREACHED();
197     return;
198   }
199
200   DeviceEntry* const existing_device = GetDeviceEntryForMediaStreamDevice(
201       session_it->second);
202   if (existing_device) {
203     // Remove any client that is still using the session. This is safe to call
204     // even if there are no clients using the session.
205     existing_device->video_capture_controller->StopSession(capture_session_id);
206
207     // StopSession() may have removed the last client, so we might need to
208     // close the device.
209     DestroyDeviceEntryIfNoClients(existing_device);
210   }
211
212   // Notify listeners asynchronously, and forget the session.
213   base::MessageLoop::current()->PostTask(FROM_HERE,
214       base::Bind(&VideoCaptureManager::OnClosed, this, session_it->second.type,
215                  capture_session_id));
216   sessions_.erase(session_it);
217 }
218
219 void VideoCaptureManager::DoStartDeviceOnDeviceThread(
220     media::VideoCaptureSessionId session_id,
221     DeviceEntry* entry,
222     const media::VideoCaptureParams& params,
223     scoped_ptr<media::VideoCaptureDevice::Client> device_client) {
224   SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime");
225   DCHECK(IsOnDeviceThread());
226
227   scoped_ptr<media::VideoCaptureDevice> video_capture_device;
228   switch (entry->stream_type) {
229     case MEDIA_DEVICE_VIDEO_CAPTURE: {
230       // We look up the device id from the renderer in our local enumeration
231       // since the renderer does not have all the information that might be
232       // held in the browser-side VideoCaptureDevice::Name structure.
233       media::VideoCaptureDeviceInfo* found =
234           FindDeviceInfoById(entry->id, devices_info_cache_);
235       if (found) {
236         video_capture_device =
237             video_capture_device_factory_->Create(found->name);
238       }
239       break;
240     }
241     case MEDIA_TAB_VIDEO_CAPTURE: {
242       video_capture_device.reset(
243           WebContentsVideoCaptureDevice::Create(entry->id));
244       break;
245     }
246     case MEDIA_DESKTOP_VIDEO_CAPTURE: {
247 #if defined(ENABLE_SCREEN_CAPTURE)
248       DesktopMediaID id = DesktopMediaID::Parse(entry->id);
249 #if defined(USE_AURA)
250       if (id.type == DesktopMediaID::TYPE_AURA_WINDOW) {
251         video_capture_device.reset(DesktopCaptureDeviceAura::Create(id));
252       } else
253 #endif
254       if (id.type != DesktopMediaID::TYPE_NONE &&
255           id.type != DesktopMediaID::TYPE_AURA_WINDOW) {
256         video_capture_device = DesktopCaptureDevice::Create(id);
257         if (notification_window_ids_.find(session_id) !=
258             notification_window_ids_.end()) {
259           static_cast<DesktopCaptureDevice*>(video_capture_device.get())
260               ->SetNotificationWindowId(notification_window_ids_[session_id]);
261           VLOG(2) << "Screen capture notification window passed for session "
262                   << session_id;
263         }
264       }
265 #endif  // defined(ENABLE_SCREEN_CAPTURE)
266       break;
267     }
268     default: {
269       NOTIMPLEMENTED();
270       break;
271     }
272   }
273
274   if (!video_capture_device) {
275     device_client->OnError("Could not create capture device");
276     return;
277   }
278
279   video_capture_device->AllocateAndStart(params, device_client.Pass());
280   entry->video_capture_device = video_capture_device.Pass();
281 }
282
283 void VideoCaptureManager::StartCaptureForClient(
284     media::VideoCaptureSessionId session_id,
285     const media::VideoCaptureParams& params,
286     base::ProcessHandle client_render_process,
287     VideoCaptureControllerID client_id,
288     VideoCaptureControllerEventHandler* client_handler,
289     const DoneCB& done_cb) {
290   DCHECK_CURRENTLY_ON(BrowserThread::IO);
291   DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, "
292            << params.requested_format.frame_size.ToString() << ", "
293            << params.requested_format.frame_rate << ", #" << session_id << ")";
294
295   DeviceEntry* entry = GetOrCreateDeviceEntry(session_id);
296   if (!entry) {
297     done_cb.Run(base::WeakPtr<VideoCaptureController>());
298     return;
299   }
300
301   DCHECK(entry->video_capture_controller);
302
303   LogVideoCaptureEvent(VIDEO_CAPTURE_START_CAPTURE);
304
305   // First client starts the device.
306   if (entry->video_capture_controller->GetActiveClientCount() == 0) {
307     DVLOG(1) << "VideoCaptureManager starting device (type = "
308              << entry->stream_type << ", id = " << entry->id << ")";
309
310     device_task_runner_->PostTask(
311         FROM_HERE,
312         base::Bind(
313             &VideoCaptureManager::DoStartDeviceOnDeviceThread,
314             this,
315             session_id,
316             entry,
317             params,
318             base::Passed(entry->video_capture_controller->NewDeviceClient())));
319   }
320   // Run the callback first, as AddClient() may trigger OnFrameInfo().
321   done_cb.Run(entry->video_capture_controller->GetWeakPtr());
322   entry->video_capture_controller->AddClient(
323       client_id, client_handler, client_render_process, session_id, params);
324 }
325
326 void VideoCaptureManager::StopCaptureForClient(
327     VideoCaptureController* controller,
328     VideoCaptureControllerID client_id,
329     VideoCaptureControllerEventHandler* client_handler,
330     bool aborted_due_to_error) {
331   DCHECK_CURRENTLY_ON(BrowserThread::IO);
332   DCHECK(controller);
333   DCHECK(client_handler);
334
335   DeviceEntry* entry = GetDeviceEntryForController(controller);
336   if (!entry) {
337     NOTREACHED();
338     return;
339   }
340   if (!aborted_due_to_error) {
341     if (controller->has_received_frames()) {
342       LogVideoCaptureEvent(VIDEO_CAPTURE_STOP_CAPTURE_OK);
343     } else if (entry->stream_type == MEDIA_DEVICE_VIDEO_CAPTURE) {
344       LogVideoCaptureEvent(
345           VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DEVICE);
346     } else {
347       LogVideoCaptureEvent(
348           VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DESKTOP_OR_TAB);
349     }
350   } else {
351     LogVideoCaptureEvent(VIDEO_CAPTURE_STOP_CAPTURE_DUE_TO_ERROR);
352     SessionMap::iterator it;
353     for (it = sessions_.begin(); it != sessions_.end(); ++it) {
354       if (it->second.type == entry->stream_type &&
355           it->second.id == entry->id) {
356         listener_->Aborted(it->second.type, it->first);
357         break;
358       }
359     }
360   }
361
362   // Detach client from controller.
363   media::VideoCaptureSessionId session_id =
364       controller->RemoveClient(client_id, client_handler);
365   DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = "
366            << session_id;
367
368   // If controller has no more clients, delete controller and device.
369   DestroyDeviceEntryIfNoClients(entry);
370 }
371
372 void VideoCaptureManager::PauseCaptureForClient(
373     VideoCaptureController* controller,
374     VideoCaptureControllerID client_id,
375     VideoCaptureControllerEventHandler* client_handler) {
376   DCHECK_CURRENTLY_ON(BrowserThread::IO);
377   DCHECK(controller);
378   DCHECK(client_handler);
379   DeviceEntry* entry = GetDeviceEntryForController(controller);
380   if (!entry) {
381     NOTREACHED();
382     return;
383   }
384
385   // We only pause the MEDIA_DEVICE_VIDEO_CAPTURE entry to release camera to
386   // system.
387   if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE)
388     return;
389
390   controller->PauseOrResumeClient(client_id, client_handler, true);
391   if (controller->GetActiveClientCount() != 0)
392     return;
393
394   // There is no more client, release the camera.
395   device_task_runner_->PostTask(
396       FROM_HERE,
397       base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
398                  base::Unretained(entry)));
399 }
400
401 void VideoCaptureManager::ResumeCaptureForClient(
402     media::VideoCaptureSessionId session_id,
403     const media::VideoCaptureParams& params,
404     VideoCaptureController* controller,
405     VideoCaptureControllerID client_id,
406     VideoCaptureControllerEventHandler* client_handler) {
407   DCHECK_CURRENTLY_ON(BrowserThread::IO);
408   DCHECK(controller);
409   DCHECK(client_handler);
410
411   DeviceEntry* entry = GetDeviceEntryForController(controller);
412   if (!entry) {
413     NOTREACHED();
414     return;
415   }
416
417   // We only pause/resume the MEDIA_DEVICE_VIDEO_CAPTURE entry.
418   if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE)
419     return;
420
421   controller->PauseOrResumeClient(client_id, client_handler, false);
422   if (controller->GetActiveClientCount() != 1)
423     return;
424
425   // This is first active client, allocate the camera.
426   device_task_runner_->PostTask(
427       FROM_HERE,
428       base::Bind(
429           &VideoCaptureManager::DoStartDeviceOnDeviceThread,
430           this,
431           session_id,
432           entry,
433           params,
434           base::Passed(entry->video_capture_controller->NewDeviceClient())));
435 }
436
437 bool VideoCaptureManager::GetDeviceSupportedFormats(
438     media::VideoCaptureSessionId capture_session_id,
439     media::VideoCaptureFormats* supported_formats) {
440   DCHECK_CURRENTLY_ON(BrowserThread::IO);
441   DCHECK(supported_formats->empty());
442
443   SessionMap::iterator it = sessions_.find(capture_session_id);
444   if (it == sessions_.end())
445     return false;
446   DVLOG(1) << "GetDeviceSupportedFormats for device: " << it->second.name;
447
448   // Return all available formats of the device, regardless its started state.
449   media::VideoCaptureDeviceInfo* existing_device =
450       FindDeviceInfoById(it->second.id, devices_info_cache_);
451   if (existing_device)
452     *supported_formats = existing_device->supported_formats;
453   return true;
454 }
455
456 bool VideoCaptureManager::GetDeviceFormatsInUse(
457     media::VideoCaptureSessionId capture_session_id,
458     media::VideoCaptureFormats* formats_in_use) {
459   DCHECK_CURRENTLY_ON(BrowserThread::IO);
460   DCHECK(formats_in_use->empty());
461
462   SessionMap::iterator it = sessions_.find(capture_session_id);
463   if (it == sessions_.end())
464     return false;
465   DVLOG(1) << "GetDeviceFormatsInUse for device: " << it->second.name;
466
467   // Return the currently in-use format(s) of the device, if it's started.
468   DeviceEntry* device_in_use =
469       GetDeviceEntryForMediaStreamDevice(it->second);
470   if (device_in_use) {
471     // Currently only one format-in-use is supported at the VCC level.
472     formats_in_use->push_back(
473         device_in_use->video_capture_controller->GetVideoCaptureFormat());
474   }
475   return true;
476 }
477
478 void VideoCaptureManager::SetDesktopCaptureWindowId(
479     media::VideoCaptureSessionId session_id,
480     gfx::NativeViewId window_id) {
481   DCHECK_CURRENTLY_ON(BrowserThread::IO);
482   VLOG(2) << "SetDesktopCaptureWindowId called for session " << session_id;
483
484   SessionMap::iterator session_it = sessions_.find(session_id);
485   if (session_it == sessions_.end()) {
486     VLOG(2) << "Session not found, will save the notification window.";
487     device_task_runner_->PostTask(
488         FROM_HERE,
489         base::Bind(
490             &VideoCaptureManager::SaveDesktopCaptureWindowIdOnDeviceThread,
491             this,
492             session_id,
493             window_id));
494     return;
495   }
496
497   DeviceEntry* const existing_device =
498       GetDeviceEntryForMediaStreamDevice(session_it->second);
499   if (!existing_device) {
500     VLOG(2) << "Failed to find an existing device.";
501     return;
502   }
503
504   DCHECK_EQ(MEDIA_DESKTOP_VIDEO_CAPTURE, existing_device->stream_type);
505   DesktopMediaID id = DesktopMediaID::Parse(existing_device->id);
506   if (id.type == DesktopMediaID::TYPE_NONE ||
507       id.type == DesktopMediaID::TYPE_AURA_WINDOW) {
508     VLOG(2) << "Video capture device type mismatch.";
509     return;
510   }
511
512   device_task_runner_->PostTask(
513       FROM_HERE,
514       base::Bind(&VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread,
515                  this,
516                  existing_device,
517                  window_id));
518 }
519
520 void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) {
521   SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime");
522   DCHECK(IsOnDeviceThread());
523   if (entry->video_capture_device) {
524     entry->video_capture_device->StopAndDeAllocate();
525   }
526   entry->video_capture_device.reset();
527 }
528
529 void VideoCaptureManager::OnOpened(
530     MediaStreamType stream_type,
531     media::VideoCaptureSessionId capture_session_id) {
532   DCHECK_CURRENTLY_ON(BrowserThread::IO);
533   if (!listener_) {
534     // Listener has been removed.
535     return;
536   }
537   listener_->Opened(stream_type, capture_session_id);
538 }
539
540 void VideoCaptureManager::OnClosed(
541     MediaStreamType stream_type,
542     media::VideoCaptureSessionId capture_session_id) {
543   DCHECK_CURRENTLY_ON(BrowserThread::IO);
544   if (!listener_) {
545     // Listener has been removed.
546     return;
547   }
548   listener_->Closed(stream_type, capture_session_id);
549 }
550
551 void VideoCaptureManager::OnDevicesInfoEnumerated(
552     MediaStreamType stream_type,
553     base::ElapsedTimer* timer,
554     const media::VideoCaptureDeviceInfos& new_devices_info_cache) {
555   DCHECK_CURRENTLY_ON(BrowserThread::IO);
556   UMA_HISTOGRAM_TIMES(
557       "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime",
558       timer->Elapsed());
559   if (!listener_) {
560     // Listener has been removed.
561     return;
562   }
563   devices_info_cache_ = new_devices_info_cache;
564
565   MediaInternals::GetInstance()->UpdateVideoCaptureDeviceCapabilities(
566       devices_info_cache_);
567
568   // Walk the |devices_info_cache_| and transform from VCD::Name to
569   // StreamDeviceInfo for return purposes.
570   StreamDeviceInfoArray devices;
571   for (const auto& it : devices_info_cache_) {
572     devices.push_back(StreamDeviceInfo(
573         stream_type, it.name.GetNameAndModel(), it.name.id()));
574   }
575   listener_->DevicesEnumerated(stream_type, devices);
576 }
577
578 bool VideoCaptureManager::IsOnDeviceThread() const {
579   return device_task_runner_->BelongsToCurrentThread();
580 }
581
582 void VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread(
583     base::Callback<void(const media::VideoCaptureDeviceInfos&)>
584         on_devices_enumerated_callback,
585     MediaStreamType stream_type,
586     const media::VideoCaptureDeviceInfos& old_device_info_cache,
587     scoped_ptr<media::VideoCaptureDevice::Names> names_snapshot) {
588   DCHECK(IsOnDeviceThread());
589   // Construct |new_devices_info_cache| with the cached devices that are still
590   // present in the system, and remove their names from |names_snapshot|, so we
591   // keep there the truly new devices.
592   media::VideoCaptureDeviceInfos new_devices_info_cache;
593   for (const auto& device_info : old_device_info_cache) {
594      for (media::VideoCaptureDevice::Names::iterator it =
595          names_snapshot->begin(); it != names_snapshot->end(); ++it) {
596       if (device_info.name.id() == it->id()) {
597         new_devices_info_cache.push_back(device_info);
598         names_snapshot->erase(it);
599         break;
600       }
601     }
602   }
603
604   // Get the supported capture formats for the new devices in |names_snapshot|.
605   for (const auto& it : *names_snapshot) {
606     media::VideoCaptureDeviceInfo device_info(it, media::VideoCaptureFormats());
607     video_capture_device_factory_->GetDeviceSupportedFormats(
608         it, &(device_info.supported_formats));
609     ConsolidateCaptureFormats(&device_info.supported_formats);
610     new_devices_info_cache.push_back(device_info);
611   }
612
613   on_devices_enumerated_callback.Run(new_devices_info_cache);
614 }
615
616 VideoCaptureManager::DeviceEntry*
617 VideoCaptureManager::GetDeviceEntryForMediaStreamDevice(
618     const MediaStreamDevice& device_info) {
619   DCHECK_CURRENTLY_ON(BrowserThread::IO);
620
621   for (DeviceEntries::iterator it = devices_.begin();
622        it != devices_.end(); ++it) {
623     DeviceEntry* device = *it;
624     if (device_info.type == device->stream_type &&
625         device_info.id == device->id) {
626       return device;
627     }
628   }
629   return NULL;
630 }
631
632 VideoCaptureManager::DeviceEntry*
633 VideoCaptureManager::GetDeviceEntryForController(
634     const VideoCaptureController* controller) const {
635   // Look up |controller| in |devices_|.
636   for (DeviceEntries::const_iterator it = devices_.begin();
637        it != devices_.end(); ++it) {
638     if ((*it)->video_capture_controller.get() == controller) {
639       return *it;
640     }
641   }
642   return NULL;
643 }
644
645 void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) {
646   DCHECK_CURRENTLY_ON(BrowserThread::IO);
647   // Removal of the last client stops the device.
648   if (entry->video_capture_controller->GetClientCount() == 0) {
649     DVLOG(1) << "VideoCaptureManager stopping device (type = "
650              << entry->stream_type << ", id = " << entry->id << ")";
651
652     // The DeviceEntry is removed from |devices_| immediately. The controller is
653     // deleted immediately, and the device is freed asynchronously. After this
654     // point, subsequent requests to open this same device ID will create a new
655     // DeviceEntry, VideoCaptureController, and VideoCaptureDevice.
656     devices_.erase(entry);
657     entry->video_capture_controller.reset();
658     device_task_runner_->PostTask(
659         FROM_HERE,
660         base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
661                    base::Owned(entry)));
662   }
663 }
664
665 VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry(
666     media::VideoCaptureSessionId capture_session_id) {
667   DCHECK_CURRENTLY_ON(BrowserThread::IO);
668
669   SessionMap::iterator session_it = sessions_.find(capture_session_id);
670   if (session_it == sessions_.end()) {
671     return NULL;
672   }
673   const MediaStreamDevice& device_info = session_it->second;
674
675   // Check if another session has already opened this device. If so, just
676   // use that opened device.
677   DeviceEntry* const existing_device =
678       GetDeviceEntryForMediaStreamDevice(device_info);
679   if (existing_device) {
680     DCHECK_EQ(device_info.type, existing_device->stream_type);
681     return existing_device;
682   }
683
684   const int max_buffers = device_info.type == MEDIA_TAB_VIDEO_CAPTURE ?
685       kMaxNumberOfBuffersForTabCapture : kMaxNumberOfBuffers;
686   scoped_ptr<VideoCaptureController> video_capture_controller(
687       new VideoCaptureController(max_buffers));
688   DeviceEntry* new_device = new DeviceEntry(device_info.type,
689                                             device_info.id,
690                                             video_capture_controller.Pass());
691   devices_.insert(new_device);
692   return new_device;
693 }
694
695 media::VideoCaptureDeviceInfo* VideoCaptureManager::FindDeviceInfoById(
696     const std::string& id,
697     media::VideoCaptureDeviceInfos& device_vector) {
698   for (auto& it : device_vector) {
699     if (it.name.id() == id)
700       return &(it);
701   }
702   return NULL;
703 }
704
705 void VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread(
706     DeviceEntry* entry,
707     gfx::NativeViewId window_id) {
708   DCHECK(IsOnDeviceThread());
709   DCHECK(entry->stream_type == MEDIA_DESKTOP_VIDEO_CAPTURE);
710 #if defined(ENABLE_SCREEN_CAPTURE)
711   DesktopCaptureDevice* device =
712       static_cast<DesktopCaptureDevice*>(entry->video_capture_device.get());
713   device->SetNotificationWindowId(window_id);
714   VLOG(2) << "Screen capture notification window passed on device thread.";
715 #endif
716 }
717
718 void VideoCaptureManager::SaveDesktopCaptureWindowIdOnDeviceThread(
719     media::VideoCaptureSessionId session_id,
720     gfx::NativeViewId window_id) {
721   DCHECK(IsOnDeviceThread());
722   DCHECK(notification_window_ids_.find(session_id) ==
723          notification_window_ids_.end());
724   notification_window_ids_[session_id] = window_id;
725   VLOG(2) << "Screen capture notification window saved for session "
726           << session_id << " on device thread.";
727 }
728
729 }  // namespace content