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(MediaStreamProviderListener* listener,
69 base::MessageLoopProxy* device_thread_loop) {
70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
72 DCHECK(!device_loop_.get());
74 device_loop_ = device_thread_loop;
77 void VideoCaptureManager::Unregister() {
82 void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) {
83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
84 DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type;
86 base::PostTaskAndReplyWithResult(
87 device_loop_, FROM_HERE,
88 base::Bind(&VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread,
89 this, stream_type, devices_info_cache_),
90 base::Bind(&VideoCaptureManager::OnDevicesInfoEnumerated, this,
94 int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) {
95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
98 // Generate a new id for the session being opened.
99 const media::VideoCaptureSessionId capture_session_id =
100 new_capture_session_id_++;
102 DCHECK(sessions_.find(capture_session_id) == sessions_.end());
103 DVLOG(1) << "VideoCaptureManager::Open, id " << capture_session_id;
105 // We just save the stream info for processing later.
106 sessions_[capture_session_id] = device_info.device;
108 // Notify our listener asynchronously; this ensures that we return
109 // |capture_session_id| to the caller of this function before using that same
110 // id in a listener event.
111 base::MessageLoop::current()->PostTask(FROM_HERE,
112 base::Bind(&VideoCaptureManager::OnOpened, this,
113 device_info.device.type, capture_session_id));
114 return capture_session_id;
117 void VideoCaptureManager::Close(int capture_session_id) {
118 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
120 DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id;
122 std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator
123 session_it = sessions_.find(capture_session_id);
124 if (session_it == sessions_.end()) {
129 DeviceEntry* const existing_device = GetDeviceEntryForMediaStreamDevice(
131 if (existing_device) {
132 // Remove any client that is still using the session. This is safe to call
133 // even if there are no clients using the session.
134 existing_device->video_capture_controller->StopSession(capture_session_id);
136 // StopSession() may have removed the last client, so we might need to
138 DestroyDeviceEntryIfNoClients(existing_device);
141 // Notify listeners asynchronously, and forget the session.
142 base::MessageLoop::current()->PostTask(FROM_HERE,
143 base::Bind(&VideoCaptureManager::OnClosed, this, session_it->second.type,
144 capture_session_id));
145 sessions_.erase(session_it);
148 void VideoCaptureManager::UseFakeDevice() {
149 if (CommandLine::ForCurrentProcess()->HasSwitch(
150 switches::kUseFileForFakeVideoCapture)) {
151 artificial_device_source_for_testing_ = Y4M_FILE;
153 artificial_device_source_for_testing_ = TEST_PATTERN;
157 void VideoCaptureManager::DoStartDeviceOnDeviceThread(
159 const media::VideoCaptureParams& params,
160 scoped_ptr<media::VideoCaptureDevice::Client> device_client) {
161 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime");
162 DCHECK(IsOnDeviceThread());
164 scoped_ptr<media::VideoCaptureDevice> video_capture_device;
165 switch (entry->stream_type) {
166 case MEDIA_DEVICE_VIDEO_CAPTURE: {
167 // We look up the device id from the renderer in our local enumeration
168 // since the renderer does not have all the information that might be
169 // held in the browser-side VideoCaptureDevice::Name structure.
170 DeviceInfo* found = FindDeviceInfoById(entry->id, devices_info_cache_);
172 switch (artificial_device_source_for_testing_) {
174 video_capture_device.reset(
175 media::VideoCaptureDevice::Create(found->name));
178 video_capture_device.reset(
179 media::FakeVideoCaptureDevice::Create(found->name));
182 video_capture_device.reset(
183 media::FileVideoCaptureDevice::Create(found->name));
189 case MEDIA_TAB_VIDEO_CAPTURE: {
190 video_capture_device.reset(
191 WebContentsVideoCaptureDevice::Create(entry->id));
194 case MEDIA_DESKTOP_VIDEO_CAPTURE: {
195 #if defined(ENABLE_SCREEN_CAPTURE)
196 DesktopMediaID id = DesktopMediaID::Parse(entry->id);
197 #if defined(USE_AURA)
198 if (id.type == DesktopMediaID::TYPE_AURA_WINDOW) {
199 video_capture_device.reset(DesktopCaptureDeviceAura::Create(id));
202 if (id.type != DesktopMediaID::TYPE_NONE &&
203 id.type != DesktopMediaID::TYPE_AURA_WINDOW) {
204 video_capture_device = DesktopCaptureDevice::Create(id);
206 #endif // defined(ENABLE_SCREEN_CAPTURE)
215 if (!video_capture_device) {
216 device_client->OnError("Could not create capture device");
220 video_capture_device->AllocateAndStart(params, device_client.Pass());
221 entry->video_capture_device = video_capture_device.Pass();
224 void VideoCaptureManager::StartCaptureForClient(
225 media::VideoCaptureSessionId session_id,
226 const media::VideoCaptureParams& params,
227 base::ProcessHandle client_render_process,
228 VideoCaptureControllerID client_id,
229 VideoCaptureControllerEventHandler* client_handler,
230 const DoneCB& done_cb) {
231 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
232 DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, "
233 << params.requested_format.frame_size.ToString() << ", "
234 << params.requested_format.frame_rate << ", #" << session_id << ")";
236 DeviceEntry* entry = GetOrCreateDeviceEntry(session_id);
238 done_cb.Run(base::WeakPtr<VideoCaptureController>());
242 DCHECK(entry->video_capture_controller);
244 // First client starts the device.
245 if (entry->video_capture_controller->GetClientCount() == 0) {
246 DVLOG(1) << "VideoCaptureManager starting device (type = "
247 << entry->stream_type << ", id = " << entry->id << ")";
249 device_loop_->PostTask(
252 &VideoCaptureManager::DoStartDeviceOnDeviceThread,
256 base::Passed(entry->video_capture_controller->NewDeviceClient())));
258 // Run the callback first, as AddClient() may trigger OnFrameInfo().
259 done_cb.Run(entry->video_capture_controller->GetWeakPtr());
260 entry->video_capture_controller->AddClient(
261 client_id, client_handler, client_render_process, session_id, params);
264 void VideoCaptureManager::StopCaptureForClient(
265 VideoCaptureController* controller,
266 VideoCaptureControllerID client_id,
267 VideoCaptureControllerEventHandler* client_handler) {
268 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
270 DCHECK(client_handler);
272 DeviceEntry* entry = GetDeviceEntryForController(controller);
278 // Detach client from controller.
279 media::VideoCaptureSessionId session_id =
280 controller->RemoveClient(client_id, client_handler);
281 DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = "
284 // If controller has no more clients, delete controller and device.
285 DestroyDeviceEntryIfNoClients(entry);
288 bool VideoCaptureManager::GetDeviceSupportedFormats(
289 media::VideoCaptureSessionId capture_session_id,
290 media::VideoCaptureFormats* supported_formats) {
291 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
292 DCHECK(supported_formats->empty());
294 std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator it =
295 sessions_.find(capture_session_id);
296 if (it == sessions_.end())
298 DVLOG(1) << "GetDeviceSupportedFormats for device: " << it->second.name;
300 // Return all available formats of the device, regardless its started state.
301 DeviceInfo* existing_device =
302 FindDeviceInfoById(it->second.id, devices_info_cache_);
304 *supported_formats = existing_device->supported_formats;
308 bool VideoCaptureManager::GetDeviceFormatInUse(
309 media::VideoCaptureSessionId capture_session_id,
310 media::VideoCaptureFormat* format_in_use) {
311 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
313 std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator it =
314 sessions_.find(capture_session_id);
315 if (it == sessions_.end())
317 DVLOG(1) << "GetDeviceFormatInUse for device: " << it->second.name;
319 // Return the currently in-use format of the device, if it's started.
320 DeviceEntry* device_in_use =
321 GetDeviceEntryForMediaStreamDevice(it->second);
325 device_in_use->video_capture_controller->GetVideoCaptureFormat();
329 void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) {
330 SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime");
331 DCHECK(IsOnDeviceThread());
332 if (entry->video_capture_device) {
333 entry->video_capture_device->StopAndDeAllocate();
335 entry->video_capture_device.reset();
338 void VideoCaptureManager::OnOpened(
339 MediaStreamType stream_type,
340 media::VideoCaptureSessionId capture_session_id) {
341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
343 // Listener has been removed.
346 listener_->Opened(stream_type, capture_session_id);
349 void VideoCaptureManager::OnClosed(
350 MediaStreamType stream_type,
351 media::VideoCaptureSessionId capture_session_id) {
352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
354 // Listener has been removed.
357 listener_->Closed(stream_type, capture_session_id);
360 void VideoCaptureManager::OnDevicesInfoEnumerated(
361 MediaStreamType stream_type,
362 const DeviceInfos& new_devices_info_cache) {
363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
366 // Listener has been removed.
369 devices_info_cache_ = new_devices_info_cache;
371 // Walk the |devices_info_cache_| and transform from VCD::Name to
372 // StreamDeviceInfo for return purposes.
373 StreamDeviceInfoArray devices;
374 for (DeviceInfos::const_iterator it = devices_info_cache_.begin();
375 it != devices_info_cache_.end(); ++it) {
376 devices.push_back(StreamDeviceInfo(
377 stream_type, it->name.GetNameAndModel(), it->name.id()));
379 listener_->DevicesEnumerated(stream_type, devices);
382 bool VideoCaptureManager::IsOnDeviceThread() const {
383 return device_loop_->BelongsToCurrentThread();
386 VideoCaptureManager::DeviceInfos
387 VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread(
388 MediaStreamType stream_type,
389 const DeviceInfos& old_device_info_cache) {
390 SCOPED_UMA_HISTOGRAM_TIMER(
391 "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime");
392 DCHECK(IsOnDeviceThread());
393 media::VideoCaptureDevice::Names names_snapshot;
394 switch (stream_type) {
395 case MEDIA_DEVICE_VIDEO_CAPTURE:
396 // Cache the latest enumeration of video capture devices.
397 // We'll refer to this list again in OnOpen to avoid having to
398 // enumerate the devices again.
399 switch (artificial_device_source_for_testing_) {
401 media::VideoCaptureDevice::GetDeviceNames(&names_snapshot);
404 media::FakeVideoCaptureDevice::GetDeviceNames(&names_snapshot);
407 media::FileVideoCaptureDevice::GetDeviceNames(&names_snapshot);
412 case MEDIA_DESKTOP_VIDEO_CAPTURE:
421 // Construct |new_devices_info_cache| with the cached devices that are still
422 // present in the system, and remove their names from |names_snapshot|, so we
423 // keep there the truly new devices.
424 DeviceInfos new_devices_info_cache;
425 for (DeviceInfos::const_iterator it_device_info =
426 old_device_info_cache.begin();
427 it_device_info != old_device_info_cache.end(); ++it_device_info) {
428 for (media::VideoCaptureDevice::Names::iterator it =
429 names_snapshot.begin();
430 it != names_snapshot.end(); ++it) {
431 if (it_device_info->name.id() == it->id()) {
432 new_devices_info_cache.push_back(*it_device_info);
433 names_snapshot.erase(it);
439 // Get the supported capture formats for the new devices in |names_snapshot|.
440 for (media::VideoCaptureDevice::Names::const_iterator it =
441 names_snapshot.begin();
442 it != names_snapshot.end(); ++it) {
443 media::VideoCaptureFormats supported_formats;
444 DeviceInfo device_info(*it, media::VideoCaptureFormats());
445 switch (artificial_device_source_for_testing_) {
447 media::VideoCaptureDevice::GetDeviceSupportedFormats(
448 *it, &(device_info.supported_formats));
451 media::FakeVideoCaptureDevice::GetDeviceSupportedFormats(
452 *it, &(device_info.supported_formats));
455 media::FileVideoCaptureDevice::GetDeviceSupportedFormats(
456 *it, &(device_info.supported_formats));
459 new_devices_info_cache.push_back(device_info);
461 return new_devices_info_cache;
464 VideoCaptureManager::DeviceEntry*
465 VideoCaptureManager::GetDeviceEntryForMediaStreamDevice(
466 const MediaStreamDevice& device_info) {
467 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
469 for (DeviceEntries::iterator it = devices_.begin();
470 it != devices_.end(); ++it) {
471 DeviceEntry* device = *it;
472 if (device_info.type == device->stream_type &&
473 device_info.id == device->id) {
480 VideoCaptureManager::DeviceEntry*
481 VideoCaptureManager::GetDeviceEntryForController(
482 const VideoCaptureController* controller) {
483 // Look up |controller| in |devices_|.
484 for (DeviceEntries::iterator it = devices_.begin();
485 it != devices_.end(); ++it) {
486 if ((*it)->video_capture_controller.get() == controller) {
493 void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) {
494 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
495 // Removal of the last client stops the device.
496 if (entry->video_capture_controller->GetClientCount() == 0) {
497 DVLOG(1) << "VideoCaptureManager stopping device (type = "
498 << entry->stream_type << ", id = " << entry->id << ")";
500 // The DeviceEntry is removed from |devices_| immediately. The controller is
501 // deleted immediately, and the device is freed asynchronously. After this
502 // point, subsequent requests to open this same device ID will create a new
503 // DeviceEntry, VideoCaptureController, and VideoCaptureDevice.
504 devices_.erase(entry);
505 entry->video_capture_controller.reset();
506 device_loop_->PostTask(
508 base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
509 base::Owned(entry)));
513 VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry(
514 media::VideoCaptureSessionId capture_session_id) {
515 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
517 std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator
518 session_it = sessions_.find(capture_session_id);
519 if (session_it == sessions_.end()) {
522 const MediaStreamDevice& device_info = session_it->second;
524 // Check if another session has already opened this device. If so, just
525 // use that opened device.
526 DeviceEntry* const existing_device =
527 GetDeviceEntryForMediaStreamDevice(device_info);
528 if (existing_device) {
529 DCHECK_EQ(device_info.type, existing_device->stream_type);
530 return existing_device;
533 scoped_ptr<VideoCaptureController> video_capture_controller(
534 new VideoCaptureController());
535 DeviceEntry* new_device = new DeviceEntry(device_info.type,
537 video_capture_controller.Pass());
538 devices_.insert(new_device);
542 VideoCaptureManager::DeviceInfo* VideoCaptureManager::FindDeviceInfoById(
543 const std::string& id,
544 DeviceInfos& device_vector) {
545 for (DeviceInfos::iterator it = device_vector.begin();
546 it != device_vector.end(); ++it) {
547 if (it->name.id() == id)
553 } // namespace content