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