• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
IsCaptureFormatSmaller(const media::VideoCaptureFormat & format1,const media::VideoCaptureFormat & format2)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 
IsCaptureFormatSizeEqual(const media::VideoCaptureFormat & format1,const media::VideoCaptureFormat & format2)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.
ConsolidateCaptureFormats(media::VideoCaptureFormats * formats)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 
LogVideoCaptureEvent(VideoCaptureEvent event)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 
DeviceEntry(MediaStreamType stream_type,const std::string & id,scoped_ptr<VideoCaptureController> controller)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 
~DeviceEntry()106 VideoCaptureManager::DeviceEntry::~DeviceEntry() {}
107 
DeviceInfo()108 VideoCaptureManager::DeviceInfo::DeviceInfo() {}
109 
DeviceInfo(const media::VideoCaptureDevice::Name & name,const media::VideoCaptureFormats & supported_formats)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 
~DeviceInfo()116 VideoCaptureManager::DeviceInfo::~DeviceInfo() {}
117 
VideoCaptureManager(scoped_ptr<media::VideoCaptureDeviceFactory> factory)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 
~VideoCaptureManager()125 VideoCaptureManager::~VideoCaptureManager() {
126   DCHECK(devices_.empty());
127 }
128 
Register(MediaStreamProviderListener * listener,const scoped_refptr<base::SingleThreadTaskRunner> & device_task_runner)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 
Unregister()139 void VideoCaptureManager::Unregister() {
140   DCHECK(listener_);
141   listener_ = NULL;
142 }
143 
EnumerateDevices(MediaStreamType stream_type)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 
Open(const StreamDeviceInfo & device_info)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 
Close(int capture_session_id)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 
DoStartDeviceOnDeviceThread(media::VideoCaptureSessionId session_id,DeviceEntry * entry,const media::VideoCaptureParams & params,scoped_ptr<media::VideoCaptureDevice::Client> device_client)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 
StartCaptureForClient(media::VideoCaptureSessionId session_id,const media::VideoCaptureParams & params,base::ProcessHandle client_render_process,VideoCaptureControllerID client_id,VideoCaptureControllerEventHandler * client_handler,const DoneCB & done_cb)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 
StopCaptureForClient(VideoCaptureController * controller,VideoCaptureControllerID client_id,VideoCaptureControllerEventHandler * client_handler,bool aborted_due_to_error)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 
PauseCaptureForClient(VideoCaptureController * controller,VideoCaptureControllerID client_id,VideoCaptureControllerEventHandler * client_handler)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 
ResumeCaptureForClient(media::VideoCaptureSessionId session_id,const media::VideoCaptureParams & params,VideoCaptureController * controller,VideoCaptureControllerID client_id,VideoCaptureControllerEventHandler * client_handler)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 
GetDeviceSupportedFormats(media::VideoCaptureSessionId capture_session_id,media::VideoCaptureFormats * supported_formats)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 
GetDeviceFormatsInUse(media::VideoCaptureSessionId capture_session_id,media::VideoCaptureFormats * formats_in_use)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 
SetDesktopCaptureWindowId(media::VideoCaptureSessionId session_id,gfx::NativeViewId window_id)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 
DoStopDeviceOnDeviceThread(DeviceEntry * entry)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 
OnOpened(MediaStreamType stream_type,media::VideoCaptureSessionId capture_session_id)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 
OnClosed(MediaStreamType stream_type,media::VideoCaptureSessionId capture_session_id)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 
OnDevicesInfoEnumerated(MediaStreamType stream_type,base::ElapsedTimer * timer,const DeviceInfos & new_devices_info_cache)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 
IsOnDeviceThread() const574 bool VideoCaptureManager::IsOnDeviceThread() const {
575   return device_task_runner_->BelongsToCurrentThread();
576 }
577 
ConsolidateDevicesInfoOnDeviceThread(base::Callback<void (const DeviceInfos &)> on_devices_enumerated_callback,MediaStreamType stream_type,const DeviceInfos & old_device_info_cache,scoped_ptr<media::VideoCaptureDevice::Names> names_snapshot)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*
GetDeviceEntryForMediaStreamDevice(const MediaStreamDevice & device_info)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*
GetDeviceEntryForController(const VideoCaptureController * controller) const634 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 
DestroyDeviceEntryIfNoClients(DeviceEntry * entry)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 
GetOrCreateDeviceEntry(media::VideoCaptureSessionId capture_session_id)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 
FindDeviceInfoById(const std::string & id,DeviceInfos & device_vector)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 
SetDesktopCaptureWindowIdOnDeviceThread(DeviceEntry * entry,gfx::NativeViewId window_id)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 
SaveDesktopCaptureWindowIdOnDeviceThread(media::VideoCaptureSessionId session_id,gfx::NativeViewId window_id)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
732