1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/renderer_host/media/video_capture_manager.h"
10 #include "base/command_line.h"
11 #include "base/logging.h"
12 #include "base/stl_util.h"
13 #include "base/threading/sequenced_worker_pool.h"
14 #include "content/browser/renderer_host/media/video_capture_controller.h"
15 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
16 #include "content/browser/renderer_host/media/web_contents_video_capture_device.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/common/content_switches.h"
19 #include "content/public/common/media_stream_request.h"
20 #include "media/base/scoped_histogram_timer.h"
21 #include "media/video/capture/fake_video_capture_device.h"
22 #include "media/video/capture/video_capture_device.h"
24 #if defined(ENABLE_SCREEN_CAPTURE)
25 #include "content/browser/renderer_host/media/screen_capture_device.h"
30 // Starting id for the first capture session.
31 // VideoCaptureManager::kStartOpenSessionId is used as default id without
32 // explicitly calling open device.
33 enum { kFirstSessionId
= VideoCaptureManager::kStartOpenSessionId
+ 1 };
35 struct VideoCaptureManager::Controller
{
37 VideoCaptureController
* vc_controller
,
38 VideoCaptureControllerEventHandler
* handler
)
39 : controller(vc_controller
),
40 ready_to_delete(false) {
41 handlers
.push_front(handler
);
45 scoped_refptr
<VideoCaptureController
> controller
;
50 VideoCaptureManager::VideoCaptureManager()
52 new_capture_session_id_(kFirstSessionId
),
53 use_fake_device_(false) {
56 VideoCaptureManager::~VideoCaptureManager() {
57 DCHECK(devices_
.empty());
58 DCHECK(controllers_
.empty());
61 void VideoCaptureManager::Register(MediaStreamProviderListener
* listener
,
62 base::MessageLoopProxy
* device_thread_loop
) {
63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
65 DCHECK(!device_loop_
.get());
67 device_loop_
= device_thread_loop
;
70 void VideoCaptureManager::Unregister() {
75 void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type
) {
76 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
78 device_loop_
->PostTask(
80 base::Bind(&VideoCaptureManager::OnEnumerateDevices
, this, stream_type
));
83 int VideoCaptureManager::Open(const StreamDeviceInfo
& device
) {
84 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
87 // Generate a new id for this device.
88 int video_capture_session_id
= new_capture_session_id_
++;
90 device_loop_
->PostTask(
92 base::Bind(&VideoCaptureManager::OnOpen
, this, video_capture_session_id
,
95 return video_capture_session_id
;
98 void VideoCaptureManager::Close(int capture_session_id
) {
99 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
101 DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id
;
102 device_loop_
->PostTask(
104 base::Bind(&VideoCaptureManager::OnClose
, this, capture_session_id
));
107 void VideoCaptureManager::Start(
108 const media::VideoCaptureParams
& capture_params
,
109 media::VideoCaptureDevice::EventHandler
* video_capture_receiver
) {
110 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
111 device_loop_
->PostTask(
113 base::Bind(&VideoCaptureManager::OnStart
, this, capture_params
,
114 video_capture_receiver
));
117 void VideoCaptureManager::Stop(
118 const media::VideoCaptureSessionId
& capture_session_id
,
119 base::Closure stopped_cb
) {
120 DVLOG(1) << "VideoCaptureManager::Stop, id " << capture_session_id
;
121 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
122 device_loop_
->PostTask(
124 base::Bind(&VideoCaptureManager::OnStop
, this, capture_session_id
,
128 void VideoCaptureManager::UseFakeDevice() {
129 use_fake_device_
= true;
132 void VideoCaptureManager::OnEnumerateDevices(MediaStreamType stream_type
) {
133 SCOPED_UMA_HISTOGRAM_TIMER(
134 "Media.VideoCaptureManager.OnEnumerateDevicesTime");
135 DCHECK(IsOnDeviceThread());
137 media::VideoCaptureDevice::Names device_names
;
138 GetAvailableDevices(stream_type
, &device_names
);
140 scoped_ptr
<StreamDeviceInfoArray
> devices(new StreamDeviceInfoArray());
141 for (media::VideoCaptureDevice::Names::iterator it
=
142 device_names
.begin(); it
!= device_names
.end(); ++it
) {
143 bool opened
= DeviceOpened(*it
);
144 devices
->push_back(StreamDeviceInfo(
145 stream_type
, it
->device_name
, it
->unique_id
, opened
));
148 PostOnDevicesEnumerated(stream_type
, devices
.Pass());
151 void VideoCaptureManager::OnOpen(int capture_session_id
,
152 const StreamDeviceInfo
& device
) {
153 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnOpenTime");
154 DCHECK(IsOnDeviceThread());
155 DCHECK(devices_
.find(capture_session_id
) == devices_
.end());
156 DVLOG(1) << "VideoCaptureManager::OnOpen, id " << capture_session_id
;
158 // Check if another session has already opened this device. If so, just
159 // use that opened device.
160 media::VideoCaptureDevice
* video_capture_device
= GetOpenedDevice(device
);
161 if (video_capture_device
) {
162 DeviceEntry
& new_entry
= devices_
[capture_session_id
];
163 new_entry
.stream_type
= device
.device
.type
;
164 new_entry
.capture_device
= video_capture_device
;
165 PostOnOpened(device
.device
.type
, capture_session_id
);
170 media::VideoCaptureDevice::Name vc_device_name
;
171 vc_device_name
.device_name
= device
.device
.name
;
172 vc_device_name
.unique_id
= device
.device
.id
;
174 if (use_fake_device_
) {
175 video_capture_device
=
176 media::FakeVideoCaptureDevice::Create(vc_device_name
);
178 switch (device
.device
.type
) {
179 case MEDIA_DEVICE_VIDEO_CAPTURE
: {
180 video_capture_device
=
181 media::VideoCaptureDevice::Create(vc_device_name
);
184 case MEDIA_TAB_VIDEO_CAPTURE
: {
185 video_capture_device
= WebContentsVideoCaptureDevice::Create(
186 vc_device_name
.unique_id
);
189 case MEDIA_SCREEN_VIDEO_CAPTURE
: {
190 #if defined(ENABLE_SCREEN_CAPTURE)
191 scoped_refptr
<base::SequencedWorkerPool
> blocking_pool
=
192 BrowserThread::GetBlockingPool();
193 video_capture_device
= new ScreenCaptureDevice(
194 blocking_pool
->GetSequencedTaskRunner(
195 blocking_pool
->GetSequenceToken()));
196 #endif // defined(ENABLE_SCREEN_CAPTURE)
205 if (!video_capture_device
) {
206 PostOnError(capture_session_id
, kDeviceNotAvailable
);
210 DeviceEntry
& new_entry
= devices_
[capture_session_id
];
211 new_entry
.stream_type
= device
.device
.type
;
212 new_entry
.capture_device
= video_capture_device
;
213 PostOnOpened(device
.device
.type
, capture_session_id
);
216 void VideoCaptureManager::OnClose(int capture_session_id
) {
217 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnCloseTime");
218 DCHECK(IsOnDeviceThread());
219 DVLOG(1) << "VideoCaptureManager::OnClose, id " << capture_session_id
;
221 VideoCaptureDevices::iterator device_it
= devices_
.find(capture_session_id
);
222 if (device_it
== devices_
.end()) {
225 const DeviceEntry removed_entry
= device_it
->second
;
226 devices_
.erase(device_it
);
228 Controllers::iterator cit
= controllers_
.find(removed_entry
.capture_device
);
229 if (cit
!= controllers_
.end()) {
230 BrowserThread::PostTask(
231 BrowserThread::IO
, FROM_HERE
,
232 base::Bind(&VideoCaptureController::StopSession
,
233 cit
->second
->controller
, capture_session_id
));
236 if (!DeviceInUse(removed_entry
.capture_device
)) {
237 // No other users of this device, deallocate (if not done already) and
238 // delete the device. No need to take care of the controller, that is done
240 removed_entry
.capture_device
->DeAllocate();
241 Controllers::iterator cit
= controllers_
.find(removed_entry
.capture_device
);
242 if (cit
!= controllers_
.end()) {
244 controllers_
.erase(cit
);
246 delete removed_entry
.capture_device
;
249 PostOnClosed(removed_entry
.stream_type
, capture_session_id
);
252 void VideoCaptureManager::OnStart(
253 const media::VideoCaptureParams capture_params
,
254 media::VideoCaptureDevice::EventHandler
* video_capture_receiver
) {
255 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnStartTime");
256 DCHECK(IsOnDeviceThread());
257 DCHECK(video_capture_receiver
!= NULL
);
258 DVLOG(1) << "VideoCaptureManager::OnStart, (" << capture_params
.width
259 << ", " << capture_params
.height
260 << ", " << capture_params
.frame_per_second
261 << ", " << capture_params
.session_id
264 media::VideoCaptureDevice
* video_capture_device
=
265 GetDeviceInternal(capture_params
.session_id
);
266 if (!video_capture_device
) {
267 // Invalid session id.
268 video_capture_receiver
->OnError();
271 Controllers::iterator cit
= controllers_
.find(video_capture_device
);
272 if (cit
!= controllers_
.end()) {
273 cit
->second
->ready_to_delete
= false;
276 // Possible errors are signaled to video_capture_receiver by
277 // video_capture_device. video_capture_receiver to perform actions.
278 video_capture_device
->Allocate(capture_params
.width
, capture_params
.height
,
279 capture_params
.frame_per_second
,
280 video_capture_receiver
);
281 video_capture_device
->Start();
284 void VideoCaptureManager::OnStop(
285 const media::VideoCaptureSessionId capture_session_id
,
286 base::Closure stopped_cb
) {
287 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.OnStopTime");
288 DCHECK(IsOnDeviceThread());
289 DVLOG(1) << "VideoCaptureManager::OnStop, id " << capture_session_id
;
291 VideoCaptureDevices::iterator it
= devices_
.find(capture_session_id
);
292 if (it
!= devices_
.end()) {
293 media::VideoCaptureDevice
* video_capture_device
= it
->second
.capture_device
;
294 // Possible errors are signaled to video_capture_receiver by
295 // video_capture_device. video_capture_receiver to perform actions.
296 video_capture_device
->Stop();
297 video_capture_device
->DeAllocate();
298 Controllers::iterator cit
= controllers_
.find(video_capture_device
);
299 if (cit
!= controllers_
.end()) {
300 cit
->second
->ready_to_delete
= true;
301 if (cit
->second
->handlers
.empty()) {
303 controllers_
.erase(cit
);
308 if (!stopped_cb
.is_null())
311 if (capture_session_id
== kStartOpenSessionId
) {
312 // This device was opened from Start(), not Open(). Close it!
313 OnClose(capture_session_id
);
317 void VideoCaptureManager::OnOpened(MediaStreamType stream_type
,
318 int capture_session_id
) {
319 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
321 // Listener has been removed.
324 listener_
->Opened(stream_type
, capture_session_id
);
327 void VideoCaptureManager::OnClosed(MediaStreamType stream_type
,
328 int capture_session_id
) {
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
331 // Listener has been removed.
334 listener_
->Closed(stream_type
, capture_session_id
);
337 void VideoCaptureManager::OnDevicesEnumerated(
338 MediaStreamType stream_type
,
339 scoped_ptr
<StreamDeviceInfoArray
> devices
) {
340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
342 // Listener has been removed.
345 listener_
->DevicesEnumerated(stream_type
, *devices
);
348 void VideoCaptureManager::OnError(MediaStreamType stream_type
,
349 int capture_session_id
,
350 MediaStreamProviderError error
) {
351 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
353 // Listener has been removed.
356 listener_
->Error(stream_type
, capture_session_id
, error
);
359 void VideoCaptureManager::PostOnOpened(
360 MediaStreamType stream_type
, int capture_session_id
) {
361 DCHECK(IsOnDeviceThread());
362 BrowserThread::PostTask(BrowserThread::IO
,
364 base::Bind(&VideoCaptureManager::OnOpened
, this,
365 stream_type
, capture_session_id
));
368 void VideoCaptureManager::PostOnClosed(
369 MediaStreamType stream_type
, int capture_session_id
) {
370 DCHECK(IsOnDeviceThread());
371 BrowserThread::PostTask(BrowserThread::IO
,
373 base::Bind(&VideoCaptureManager::OnClosed
, this,
374 stream_type
, capture_session_id
));
377 void VideoCaptureManager::PostOnDevicesEnumerated(
378 MediaStreamType stream_type
,
379 scoped_ptr
<StreamDeviceInfoArray
> devices
) {
380 DCHECK(IsOnDeviceThread());
381 BrowserThread::PostTask(
382 BrowserThread::IO
, FROM_HERE
,
383 base::Bind(&VideoCaptureManager::OnDevicesEnumerated
,
384 this, stream_type
, base::Passed(&devices
)));
387 void VideoCaptureManager::PostOnError(int capture_session_id
,
388 MediaStreamProviderError error
) {
389 DCHECK(IsOnDeviceThread());
390 MediaStreamType stream_type
= MEDIA_DEVICE_VIDEO_CAPTURE
;
391 VideoCaptureDevices::const_iterator it
= devices_
.find(capture_session_id
);
392 if (it
!= devices_
.end())
393 stream_type
= it
->second
.stream_type
;
394 BrowserThread::PostTask(BrowserThread::IO
,
396 base::Bind(&VideoCaptureManager::OnError
, this,
397 stream_type
, capture_session_id
, error
));
400 bool VideoCaptureManager::IsOnDeviceThread() const {
401 return device_loop_
->BelongsToCurrentThread();
404 void VideoCaptureManager::GetAvailableDevices(
405 MediaStreamType stream_type
,
406 media::VideoCaptureDevice::Names
* device_names
) {
407 DCHECK(IsOnDeviceThread());
409 switch (stream_type
) {
410 case MEDIA_DEVICE_VIDEO_CAPTURE
:
411 if (!use_fake_device_
) {
412 media::VideoCaptureDevice::GetDeviceNames(device_names
);
414 media::FakeVideoCaptureDevice::GetDeviceNames(device_names
);
418 case MEDIA_SCREEN_VIDEO_CAPTURE
:
419 device_names
->clear();
428 bool VideoCaptureManager::DeviceOpened(
429 const media::VideoCaptureDevice::Name
& device_name
) {
430 DCHECK(IsOnDeviceThread());
432 for (VideoCaptureDevices::iterator it
= devices_
.begin();
433 it
!= devices_
.end(); ++it
) {
434 if (device_name
.unique_id
==
435 it
->second
.capture_device
->device_name().unique_id
) {
436 // We've found the device!
443 media::VideoCaptureDevice
* VideoCaptureManager::GetOpenedDevice(
444 const StreamDeviceInfo
& device_info
) {
445 DCHECK(IsOnDeviceThread());
447 for (VideoCaptureDevices::iterator it
= devices_
.begin();
448 it
!= devices_
.end(); it
++) {
449 if (device_info
.device
.id
==
450 it
->second
.capture_device
->device_name().unique_id
) {
451 return it
->second
.capture_device
;
457 bool VideoCaptureManager::DeviceInUse(
458 const media::VideoCaptureDevice
* video_capture_device
) {
459 DCHECK(IsOnDeviceThread());
461 for (VideoCaptureDevices::iterator it
= devices_
.begin();
462 it
!= devices_
.end(); ++it
) {
463 if (video_capture_device
== it
->second
.capture_device
) {
464 // We've found the device!
471 void VideoCaptureManager::AddController(
472 const media::VideoCaptureParams
& capture_params
,
473 VideoCaptureControllerEventHandler
* handler
,
474 base::Callback
<void(VideoCaptureController
*)> added_cb
) {
476 device_loop_
->PostTask(
478 base::Bind(&VideoCaptureManager::DoAddControllerOnDeviceThread
,
479 this, capture_params
, handler
, added_cb
));
482 void VideoCaptureManager::DoAddControllerOnDeviceThread(
483 const media::VideoCaptureParams capture_params
,
484 VideoCaptureControllerEventHandler
* handler
,
485 base::Callback
<void(VideoCaptureController
*)> added_cb
) {
486 DCHECK(IsOnDeviceThread());
488 media::VideoCaptureDevice
* video_capture_device
=
489 GetDeviceInternal(capture_params
.session_id
);
490 scoped_refptr
<VideoCaptureController
> controller
;
491 if (video_capture_device
) {
492 Controllers::iterator cit
= controllers_
.find(video_capture_device
);
493 if (cit
== controllers_
.end()) {
494 controller
= new VideoCaptureController(this);
495 controllers_
[video_capture_device
] =
496 new Controller(controller
.get(), handler
);
498 controllers_
[video_capture_device
]->handlers
.push_front(handler
);
499 controller
= controllers_
[video_capture_device
]->controller
;
502 added_cb
.Run(controller
.get());
505 void VideoCaptureManager::RemoveController(
506 VideoCaptureController
* controller
,
507 VideoCaptureControllerEventHandler
* handler
) {
509 device_loop_
->PostTask(
511 base::Bind(&VideoCaptureManager::DoRemoveControllerOnDeviceThread
, this,
512 make_scoped_refptr(controller
), handler
));
515 void VideoCaptureManager::DoRemoveControllerOnDeviceThread(
516 VideoCaptureController
* controller
,
517 VideoCaptureControllerEventHandler
* handler
) {
518 DCHECK(IsOnDeviceThread());
520 for (Controllers::iterator cit
= controllers_
.begin();
521 cit
!= controllers_
.end(); ++cit
) {
522 if (controller
== cit
->second
->controller
.get()) {
523 Handlers
& handlers
= cit
->second
->handlers
;
524 for (Handlers::iterator hit
= handlers
.begin();
525 hit
!= handlers
.end(); ++hit
) {
526 if ((*hit
) == handler
) {
531 if (handlers
.empty() && cit
->second
->ready_to_delete
) {
533 controllers_
.erase(cit
);
540 media::VideoCaptureDevice
* VideoCaptureManager::GetDeviceInternal(
541 int capture_session_id
) {
542 DCHECK(IsOnDeviceThread());
543 VideoCaptureDevices::iterator dit
= devices_
.find(capture_session_id
);
544 if (dit
!= devices_
.end()) {
545 return dit
->second
.capture_device
;
548 // Solution for not using MediaStreamManager.
549 // This session id won't be returned by Open().
550 if (capture_session_id
== kStartOpenSessionId
) {
551 media::VideoCaptureDevice::Names device_names
;
552 GetAvailableDevices(MEDIA_DEVICE_VIDEO_CAPTURE
, &device_names
);
553 if (device_names
.empty()) {
554 // No devices available.
557 StreamDeviceInfo
device(MEDIA_DEVICE_VIDEO_CAPTURE
,
558 device_names
.front().device_name
,
559 device_names
.front().unique_id
, false);
561 // Call OnOpen to open using the first device in the list.
562 OnOpen(capture_session_id
, device
);
564 VideoCaptureDevices::iterator dit
= devices_
.find(capture_session_id
);
565 if (dit
!= devices_
.end()) {
566 return dit
->second
.capture_device
;
572 } // namespace content