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/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/renderer_host/media/video_capture_controller.h"
17 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
18 #include "content/browser/renderer_host/media/web_contents_video_capture_device.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/media_switches.h"
24 #include "media/base/scoped_histogram_timer.h"
25 #include "media/video/capture/fake_video_capture_device.h"
26 #include "media/video/capture/file_video_capture_device.h"
27 #include "media/video/capture/video_capture_device.h"
29 #if defined(ENABLE_SCREEN_CAPTURE)
30 #include "content/browser/renderer_host/media/desktop_capture_device.h"
32 #include "content/browser/renderer_host/media/desktop_capture_device_aura.h"
38 VideoCaptureManager::DeviceEntry::DeviceEntry(
39 MediaStreamType stream_type,
40 const std::string& id,
41 scoped_ptr<VideoCaptureController> controller)
42 : stream_type(stream_type),
44 video_capture_controller(controller.Pass()) {}
46 VideoCaptureManager::DeviceEntry::~DeviceEntry() {}
48 VideoCaptureManager::DeviceInfo::DeviceInfo() {}
50 VideoCaptureManager::DeviceInfo::DeviceInfo(
51 const media::VideoCaptureDevice::Name& name,
52 const media::VideoCaptureFormats& supported_formats)
54 supported_formats(supported_formats) {}
56 VideoCaptureManager::DeviceInfo::~DeviceInfo() {}
58 VideoCaptureManager::VideoCaptureManager()
60 new_capture_session_id_(1),
61 artificial_device_source_for_testing_(DISABLED) {
64 VideoCaptureManager::~VideoCaptureManager() {
65 DCHECK(devices_.empty());
68 void VideoCaptureManager::Register(
69 MediaStreamProviderListener* listener,
70 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) {
71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
73 DCHECK(!device_task_runner_.get());
75 device_task_runner_ = device_task_runner;
78 void VideoCaptureManager::Unregister() {
83 void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) {
84 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
85 DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type;
87 base::PostTaskAndReplyWithResult(
88 device_task_runner_, FROM_HERE,
89 base::Bind(&VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread,
90 this, stream_type, devices_info_cache_),
91 base::Bind(&VideoCaptureManager::OnDevicesInfoEnumerated, this,
95 int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) {
96 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
99 // Generate a new id for the session being opened.
100 const media::VideoCaptureSessionId capture_session_id =
101 new_capture_session_id_++;
103 DCHECK(sessions_.find(capture_session_id) == sessions_.end());
104 DVLOG(1) << "VideoCaptureManager::Open, id " << capture_session_id;
106 // We just save the stream info for processing later.
107 sessions_[capture_session_id] = device_info.device;
109 // Notify our listener asynchronously; this ensures that we return
110 // |capture_session_id| to the caller of this function before using that same
111 // id in a listener event.
112 base::MessageLoop::current()->PostTask(FROM_HERE,
113 base::Bind(&VideoCaptureManager::OnOpened, this,
114 device_info.device.type, capture_session_id));
115 return capture_session_id;
118 void VideoCaptureManager::Close(int capture_session_id) {
119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
121 DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id;
123 std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator
124 session_it = sessions_.find(capture_session_id);
125 if (session_it == sessions_.end()) {
130 DeviceEntry* const existing_device = GetDeviceEntryForMediaStreamDevice(
132 if (existing_device) {
133 // Remove any client that is still using the session. This is safe to call
134 // even if there are no clients using the session.
135 existing_device->video_capture_controller->StopSession(capture_session_id);
137 // StopSession() may have removed the last client, so we might need to
139 DestroyDeviceEntryIfNoClients(existing_device);
142 // Notify listeners asynchronously, and forget the session.
143 base::MessageLoop::current()->PostTask(FROM_HERE,
144 base::Bind(&VideoCaptureManager::OnClosed, this, session_it->second.type,
145 capture_session_id));
146 sessions_.erase(session_it);
149 void VideoCaptureManager::UseFakeDevice() {
150 if (CommandLine::ForCurrentProcess()->HasSwitch(
151 switches::kUseFileForFakeVideoCapture)) {
152 artificial_device_source_for_testing_ = Y4M_FILE;
154 artificial_device_source_for_testing_ = TEST_PATTERN;
158 void VideoCaptureManager::DoStartDeviceOnDeviceThread(
160 const media::VideoCaptureParams& params,
161 scoped_ptr<media::VideoCaptureDevice::Client> device_client) {
162 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime");
163 DCHECK(IsOnDeviceThread());
165 scoped_ptr<media::VideoCaptureDevice> video_capture_device;
166 switch (entry->stream_type) {
167 case MEDIA_DEVICE_VIDEO_CAPTURE: {
168 // We look up the device id from the renderer in our local enumeration
169 // since the renderer does not have all the information that might be
170 // held in the browser-side VideoCaptureDevice::Name structure.
171 DeviceInfo* found = FindDeviceInfoById(entry->id, devices_info_cache_);
173 switch (artificial_device_source_for_testing_) {
175 video_capture_device.reset(
176 media::VideoCaptureDevice::Create(found->name));
179 video_capture_device.reset(
180 media::FakeVideoCaptureDevice::Create(found->name));
183 video_capture_device.reset(
184 media::FileVideoCaptureDevice::Create(found->name));
190 case MEDIA_TAB_VIDEO_CAPTURE: {
191 video_capture_device.reset(
192 WebContentsVideoCaptureDevice::Create(entry->id));
195 case MEDIA_DESKTOP_VIDEO_CAPTURE: {
196 #if defined(ENABLE_SCREEN_CAPTURE)
197 DesktopMediaID id = DesktopMediaID::Parse(entry->id);
198 #if defined(USE_AURA)
199 if (id.type == DesktopMediaID::TYPE_AURA_WINDOW) {
200 video_capture_device.reset(DesktopCaptureDeviceAura::Create(id));
203 if (id.type != DesktopMediaID::TYPE_NONE &&
204 id.type != DesktopMediaID::TYPE_AURA_WINDOW) {
205 video_capture_device = DesktopCaptureDevice::Create(id);
207 #endif // defined(ENABLE_SCREEN_CAPTURE)
216 if (!video_capture_device) {
217 device_client->OnError("Could not create capture device");
221 video_capture_device->AllocateAndStart(params, device_client.Pass());
222 entry->video_capture_device = video_capture_device.Pass();
225 void VideoCaptureManager::StartCaptureForClient(
226 media::VideoCaptureSessionId session_id,
227 const media::VideoCaptureParams& params,
228 base::ProcessHandle client_render_process,
229 VideoCaptureControllerID client_id,
230 VideoCaptureControllerEventHandler* client_handler,
231 const DoneCB& done_cb) {
232 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
233 DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, "
234 << params.requested_format.frame_size.ToString() << ", "
235 << params.requested_format.frame_rate << ", #" << session_id << ")";
237 DeviceEntry* entry = GetOrCreateDeviceEntry(session_id);
239 done_cb.Run(base::WeakPtr<VideoCaptureController>());
243 DCHECK(entry->video_capture_controller);
245 // First client starts the device.
246 if (entry->video_capture_controller->GetClientCount() == 0) {
247 DVLOG(1) << "VideoCaptureManager starting device (type = "
248 << entry->stream_type << ", id = " << entry->id << ")";
250 device_task_runner_->PostTask(
253 &VideoCaptureManager::DoStartDeviceOnDeviceThread,
257 base::Passed(entry->video_capture_controller->NewDeviceClient())));
259 // Run the callback first, as AddClient() may trigger OnFrameInfo().
260 done_cb.Run(entry->video_capture_controller->GetWeakPtr());
261 entry->video_capture_controller->AddClient(
262 client_id, client_handler, client_render_process, session_id, params);
265 void VideoCaptureManager::StopCaptureForClient(
266 VideoCaptureController* controller,
267 VideoCaptureControllerID client_id,
268 VideoCaptureControllerEventHandler* client_handler) {
269 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
271 DCHECK(client_handler);
273 DeviceEntry* entry = GetDeviceEntryForController(controller);
279 // Detach client from controller.
280 media::VideoCaptureSessionId session_id =
281 controller->RemoveClient(client_id, client_handler);
282 DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = "
285 // If controller has no more clients, delete controller and device.
286 DestroyDeviceEntryIfNoClients(entry);
289 bool VideoCaptureManager::GetDeviceSupportedFormats(
290 media::VideoCaptureSessionId capture_session_id,
291 media::VideoCaptureFormats* supported_formats) {
292 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
293 DCHECK(supported_formats->empty());
295 std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator it =
296 sessions_.find(capture_session_id);
297 if (it == sessions_.end())
299 DVLOG(1) << "GetDeviceSupportedFormats for device: " << it->second.name;
301 // Return all available formats of the device, regardless its started state.
302 DeviceInfo* existing_device =
303 FindDeviceInfoById(it->second.id, devices_info_cache_);
305 *supported_formats = existing_device->supported_formats;
309 bool VideoCaptureManager::GetDeviceFormatsInUse(
310 media::VideoCaptureSessionId capture_session_id,
311 media::VideoCaptureFormats* formats_in_use) {
312 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
313 DCHECK(formats_in_use->empty());
315 std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator it =
316 sessions_.find(capture_session_id);
317 if (it == sessions_.end())
319 DVLOG(1) << "GetDeviceFormatsInUse for device: " << it->second.name;
321 // Return the currently in-use format(s) of the device, if it's started.
322 DeviceEntry* device_in_use =
323 GetDeviceEntryForMediaStreamDevice(it->second);
325 // Currently only one format-in-use is supported at the VCC level.
326 formats_in_use->push_back(
327 device_in_use->video_capture_controller->GetVideoCaptureFormat());
332 void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) {
333 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime");
334 DCHECK(IsOnDeviceThread());
335 if (entry->video_capture_device) {
336 entry->video_capture_device->StopAndDeAllocate();
338 entry->video_capture_device.reset();
341 void VideoCaptureManager::OnOpened(
342 MediaStreamType stream_type,
343 media::VideoCaptureSessionId capture_session_id) {
344 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
346 // Listener has been removed.
349 listener_->Opened(stream_type, capture_session_id);
352 void VideoCaptureManager::OnClosed(
353 MediaStreamType stream_type,
354 media::VideoCaptureSessionId capture_session_id) {
355 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
357 // Listener has been removed.
360 listener_->Closed(stream_type, capture_session_id);
363 void VideoCaptureManager::OnDevicesInfoEnumerated(
364 MediaStreamType stream_type,
365 const DeviceInfos& new_devices_info_cache) {
366 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
369 // Listener has been removed.
372 devices_info_cache_ = new_devices_info_cache;
374 // Walk the |devices_info_cache_| and transform from VCD::Name to
375 // StreamDeviceInfo for return purposes.
376 StreamDeviceInfoArray devices;
377 for (DeviceInfos::const_iterator it = devices_info_cache_.begin();
378 it != devices_info_cache_.end(); ++it) {
379 devices.push_back(StreamDeviceInfo(
380 stream_type, it->name.GetNameAndModel(), it->name.id()));
382 listener_->DevicesEnumerated(stream_type, devices);
385 bool VideoCaptureManager::IsOnDeviceThread() const {
386 return device_task_runner_->BelongsToCurrentThread();
389 VideoCaptureManager::DeviceInfos
390 VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread(
391 MediaStreamType stream_type,
392 const DeviceInfos& old_device_info_cache) {
393 SCOPED_UMA_HISTOGRAM_TIMER(
394 "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime");
395 DCHECK(IsOnDeviceThread());
396 media::VideoCaptureDevice::Names names_snapshot;
397 switch (stream_type) {
398 case MEDIA_DEVICE_VIDEO_CAPTURE:
399 // Cache the latest enumeration of video capture devices.
400 // We'll refer to this list again in OnOpen to avoid having to
401 // enumerate the devices again.
402 switch (artificial_device_source_for_testing_) {
404 media::VideoCaptureDevice::GetDeviceNames(&names_snapshot);
407 media::FakeVideoCaptureDevice::GetDeviceNames(&names_snapshot);
410 media::FileVideoCaptureDevice::GetDeviceNames(&names_snapshot);
415 case MEDIA_DESKTOP_VIDEO_CAPTURE:
424 // Construct |new_devices_info_cache| with the cached devices that are still
425 // present in the system, and remove their names from |names_snapshot|, so we
426 // keep there the truly new devices.
427 DeviceInfos new_devices_info_cache;
428 for (DeviceInfos::const_iterator it_device_info =
429 old_device_info_cache.begin();
430 it_device_info != old_device_info_cache.end(); ++it_device_info) {
431 for (media::VideoCaptureDevice::Names::iterator it =
432 names_snapshot.begin();
433 it != names_snapshot.end(); ++it) {
434 if (it_device_info->name.id() == it->id()) {
435 new_devices_info_cache.push_back(*it_device_info);
436 names_snapshot.erase(it);
442 // Get the supported capture formats for the new devices in |names_snapshot|.
443 for (media::VideoCaptureDevice::Names::const_iterator it =
444 names_snapshot.begin();
445 it != names_snapshot.end(); ++it) {
446 media::VideoCaptureFormats supported_formats;
447 DeviceInfo device_info(*it, media::VideoCaptureFormats());
448 switch (artificial_device_source_for_testing_) {
450 media::VideoCaptureDevice::GetDeviceSupportedFormats(
451 *it, &(device_info.supported_formats));
454 media::FakeVideoCaptureDevice::GetDeviceSupportedFormats(
455 *it, &(device_info.supported_formats));
458 media::FileVideoCaptureDevice::GetDeviceSupportedFormats(
459 *it, &(device_info.supported_formats));
462 new_devices_info_cache.push_back(device_info);
464 return new_devices_info_cache;
467 VideoCaptureManager::DeviceEntry*
468 VideoCaptureManager::GetDeviceEntryForMediaStreamDevice(
469 const MediaStreamDevice& device_info) {
470 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
472 for (DeviceEntries::iterator it = devices_.begin();
473 it != devices_.end(); ++it) {
474 DeviceEntry* device = *it;
475 if (device_info.type == device->stream_type &&
476 device_info.id == device->id) {
483 VideoCaptureManager::DeviceEntry*
484 VideoCaptureManager::GetDeviceEntryForController(
485 const VideoCaptureController* controller) {
486 // Look up |controller| in |devices_|.
487 for (DeviceEntries::iterator it = devices_.begin();
488 it != devices_.end(); ++it) {
489 if ((*it)->video_capture_controller.get() == controller) {
496 void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) {
497 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
498 // Removal of the last client stops the device.
499 if (entry->video_capture_controller->GetClientCount() == 0) {
500 DVLOG(1) << "VideoCaptureManager stopping device (type = "
501 << entry->stream_type << ", id = " << entry->id << ")";
503 // The DeviceEntry is removed from |devices_| immediately. The controller is
504 // deleted immediately, and the device is freed asynchronously. After this
505 // point, subsequent requests to open this same device ID will create a new
506 // DeviceEntry, VideoCaptureController, and VideoCaptureDevice.
507 devices_.erase(entry);
508 entry->video_capture_controller.reset();
509 device_task_runner_->PostTask(
511 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
512 base::Owned(entry)));
516 VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry(
517 media::VideoCaptureSessionId capture_session_id) {
518 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
520 std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator
521 session_it = sessions_.find(capture_session_id);
522 if (session_it == sessions_.end()) {
525 const MediaStreamDevice& device_info = session_it->second;
527 // Check if another session has already opened this device. If so, just
528 // use that opened device.
529 DeviceEntry* const existing_device =
530 GetDeviceEntryForMediaStreamDevice(device_info);
531 if (existing_device) {
532 DCHECK_EQ(device_info.type, existing_device->stream_type);
533 return existing_device;
536 scoped_ptr<VideoCaptureController> video_capture_controller(
537 new VideoCaptureController());
538 DeviceEntry* new_device = new DeviceEntry(device_info.type,
540 video_capture_controller.Pass());
541 devices_.insert(new_device);
545 VideoCaptureManager::DeviceInfo* VideoCaptureManager::FindDeviceInfoById(
546 const std::string& id,
547 DeviceInfos& device_vector) {
548 for (DeviceInfos::iterator it = device_vector.begin();
549 it != device_vector.end(); ++it) {
550 if (it->name.id() == id)
556 } // namespace content