Upstream version 10.39.225.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/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
35 namespace {
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.
41 bool 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
48 bool 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.
56 void 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.
75 const int kMaxNumberOfBuffers = 3;
76 const 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.
81 enum 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
88 void LogVideoCaptureEvent(VideoCaptureEvent event) {
89   UMA_HISTOGRAM_ENUMERATION("Media.VideoCaptureManager.Event",
90                             event,
91                             NUM_VIDEO_CAPTURE_EVENT);
92 }
93
94 }  // namespace
95
96 namespace content {
97
98 VideoCaptureManager::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
106 VideoCaptureManager::DeviceEntry::~DeviceEntry() {}
107
108 VideoCaptureManager::DeviceInfo::DeviceInfo() {}
109
110 VideoCaptureManager::DeviceInfo::DeviceInfo(
111     const media::VideoCaptureDevice::Name& name,
112     const media::VideoCaptureFormats& supported_formats)
113     : name(name),
114       supported_formats(supported_formats) {}
115
116 VideoCaptureManager::DeviceInfo::~DeviceInfo() {}
117
118 VideoCaptureManager::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
125 VideoCaptureManager::~VideoCaptureManager() {
126   DCHECK(devices_.empty());
127 }
128
129 void 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
139 void VideoCaptureManager::Unregister() {
140   DCHECK(listener_);
141   listener_ = NULL;
142 }
143
144 void 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
172 int 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
195 void 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
225 void 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
288 void 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
331 void 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
370 void 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
399 void 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
435 bool 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
454 bool 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
476 void 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
518 void 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
527 void 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
538 void 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
549 void 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
574 bool VideoCaptureManager::IsOnDeviceThread() const {
575   return device_task_runner_->BelongsToCurrentThread();
576 }
577
578 void 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
617 VideoCaptureManager::DeviceEntry*
618 VideoCaptureManager::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
633 VideoCaptureManager::DeviceEntry*
634 VideoCaptureManager::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
646 void 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
666 VideoCaptureManager::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
696 VideoCaptureManager::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
707 void 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
720 void 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