#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/stl_util.h"
+#include "base/task_runner_util.h"
#include "base/threading/sequenced_worker_pool.h"
#include "content/browser/renderer_host/media/video_capture_controller.h"
#include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
#include "content/browser/renderer_host/media/web_contents_video_capture_device.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/desktop_media_id.h"
#include "content/public/common/content_switches.h"
-#include "content/public/common/desktop_media_id.h"
#include "content/public/common/media_stream_request.h"
+#include "media/base/media_switches.h"
#include "media/base/scoped_histogram_timer.h"
#include "media/video/capture/fake_video_capture_device.h"
+#include "media/video/capture/file_video_capture_device.h"
#include "media/video/capture/video_capture_device.h"
#if defined(ENABLE_SCREEN_CAPTURE)
#include "content/browser/renderer_host/media/desktop_capture_device.h"
+#if defined(USE_AURA)
+#include "content/browser/renderer_host/media/desktop_capture_device_aura.h"
+#endif
#endif
namespace content {
VideoCaptureManager::DeviceEntry::~DeviceEntry() {}
+VideoCaptureManager::DeviceInfo::DeviceInfo() {}
+
+VideoCaptureManager::DeviceInfo::DeviceInfo(
+ const media::VideoCaptureDevice::Name& name,
+ const media::VideoCaptureFormats& supported_formats)
+ : name(name),
+ supported_formats(supported_formats) {}
+
+VideoCaptureManager::DeviceInfo::~DeviceInfo() {}
+
VideoCaptureManager::VideoCaptureManager()
: listener_(NULL),
new_capture_session_id_(1),
- use_fake_device_(false) {
+ artificial_device_source_for_testing_(DISABLED) {
}
VideoCaptureManager::~VideoCaptureManager() {
DCHECK(devices_.empty());
}
-void VideoCaptureManager::Register(MediaStreamProviderListener* listener,
- base::MessageLoopProxy* device_thread_loop) {
+void VideoCaptureManager::Register(
+ MediaStreamProviderListener* listener,
+ const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(!listener_);
- DCHECK(!device_loop_.get());
+ DCHECK(!device_task_runner_.get());
listener_ = listener;
- device_loop_ = device_thread_loop;
+ device_task_runner_ = device_task_runner;
}
void VideoCaptureManager::Unregister() {
DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type;
DCHECK(listener_);
base::PostTaskAndReplyWithResult(
- device_loop_, FROM_HERE,
- base::Bind(&VideoCaptureManager::GetAvailableDevicesOnDeviceThread, this,
- stream_type),
- base::Bind(&VideoCaptureManager::OnDevicesEnumerated, this, stream_type));
+ device_task_runner_, FROM_HERE,
+ base::Bind(&VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread,
+ this, stream_type, devices_info_cache_),
+ base::Bind(&VideoCaptureManager::OnDevicesInfoEnumerated, this,
+ stream_type));
}
int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) {
DCHECK(listener_);
// Generate a new id for the session being opened.
- const int capture_session_id = new_capture_session_id_++;
+ const media::VideoCaptureSessionId capture_session_id =
+ new_capture_session_id_++;
DCHECK(sessions_.find(capture_session_id) == sessions_.end());
DVLOG(1) << "VideoCaptureManager::Open, id " << capture_session_id;
DCHECK(listener_);
DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id;
- std::map<int, MediaStreamDevice>::iterator session_it =
- sessions_.find(capture_session_id);
+ std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator
+ session_it = sessions_.find(capture_session_id);
if (session_it == sessions_.end()) {
NOTREACHED();
return;
}
void VideoCaptureManager::UseFakeDevice() {
- use_fake_device_ = true;
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kUseFileForFakeVideoCapture)) {
+ artificial_device_source_for_testing_ = Y4M_FILE;
+ } else {
+ artificial_device_source_for_testing_ = TEST_PATTERN;
+ }
}
void VideoCaptureManager::DoStartDeviceOnDeviceThread(
DeviceEntry* entry,
- const media::VideoCaptureCapability& capture_params,
+ const media::VideoCaptureParams& params,
scoped_ptr<media::VideoCaptureDevice::Client> device_client) {
SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime");
DCHECK(IsOnDeviceThread());
// We look up the device id from the renderer in our local enumeration
// since the renderer does not have all the information that might be
// held in the browser-side VideoCaptureDevice::Name structure.
- media::VideoCaptureDevice::Name* found =
- video_capture_devices_.FindById(entry->id);
+ DeviceInfo* found = FindDeviceInfoById(entry->id, devices_info_cache_);
if (found) {
- video_capture_device.reset(use_fake_device_ ?
- media::FakeVideoCaptureDevice::Create(*found) :
- media::VideoCaptureDevice::Create(*found));
+ switch (artificial_device_source_for_testing_) {
+ case DISABLED:
+ video_capture_device.reset(
+ media::VideoCaptureDevice::Create(found->name));
+ break;
+ case TEST_PATTERN:
+ video_capture_device.reset(
+ media::FakeVideoCaptureDevice::Create(found->name));
+ break;
+ case Y4M_FILE:
+ video_capture_device.reset(
+ media::FileVideoCaptureDevice::Create(found->name));
+ break;
+ }
}
break;
}
case MEDIA_DESKTOP_VIDEO_CAPTURE: {
#if defined(ENABLE_SCREEN_CAPTURE)
DesktopMediaID id = DesktopMediaID::Parse(entry->id);
- if (id.type != DesktopMediaID::TYPE_NONE) {
+#if defined(USE_AURA)
+ if (id.type == DesktopMediaID::TYPE_AURA_WINDOW) {
+ video_capture_device.reset(DesktopCaptureDeviceAura::Create(id));
+ } else
+#endif
+ if (id.type != DesktopMediaID::TYPE_NONE &&
+ id.type != DesktopMediaID::TYPE_AURA_WINDOW) {
video_capture_device = DesktopCaptureDevice::Create(id);
}
#endif // defined(ENABLE_SCREEN_CAPTURE)
}
if (!video_capture_device) {
- device_client->OnError();
+ device_client->OnError("Could not create capture device");
return;
}
- video_capture_device->AllocateAndStart(capture_params, device_client.Pass());
+ video_capture_device->AllocateAndStart(params, device_client.Pass());
entry->video_capture_device = video_capture_device.Pass();
}
void VideoCaptureManager::StartCaptureForClient(
+ media::VideoCaptureSessionId session_id,
const media::VideoCaptureParams& params,
base::ProcessHandle client_render_process,
VideoCaptureControllerID client_id,
VideoCaptureControllerEventHandler* client_handler,
const DoneCB& done_cb) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, ("
- << params.requested_format.width
- << ", " << params.requested_format.height
- << ", " << params.requested_format.frame_rate
- << ", #" << params.session_id
- << ")";
-
- DeviceEntry* entry = GetOrCreateDeviceEntry(params.session_id);
+ DVLOG(1) << "VideoCaptureManager::StartCaptureForClient, "
+ << params.requested_format.frame_size.ToString() << ", "
+ << params.requested_format.frame_rate << ", #" << session_id << ")";
+
+ DeviceEntry* entry = GetOrCreateDeviceEntry(session_id);
if (!entry) {
done_cb.Run(base::WeakPtr<VideoCaptureController>());
return;
DVLOG(1) << "VideoCaptureManager starting device (type = "
<< entry->stream_type << ", id = " << entry->id << ")";
- media::VideoCaptureCapability params_as_capability;
- params_as_capability.width = params.requested_format.width;
- params_as_capability.height = params.requested_format.height;
- params_as_capability.frame_rate = params.requested_format.frame_rate;
- params_as_capability.frame_size_type =
- params.requested_format.frame_size_type;
-
- device_loop_->PostTask(FROM_HERE, base::Bind(
- &VideoCaptureManager::DoStartDeviceOnDeviceThread, this,
- entry, params_as_capability,
- base::Passed(entry->video_capture_controller->NewDeviceClient())));
+ device_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &VideoCaptureManager::DoStartDeviceOnDeviceThread,
+ this,
+ entry,
+ params,
+ base::Passed(entry->video_capture_controller->NewDeviceClient())));
}
// Run the callback first, as AddClient() may trigger OnFrameInfo().
done_cb.Run(entry->video_capture_controller->GetWeakPtr());
- entry->video_capture_controller->AddClient(client_id,
- client_handler,
- client_render_process,
- params);
+ entry->video_capture_controller->AddClient(
+ client_id, client_handler, client_render_process, session_id, params);
}
void VideoCaptureManager::StopCaptureForClient(
}
// Detach client from controller.
- int session_id = controller->RemoveClient(client_id, client_handler);
+ media::VideoCaptureSessionId session_id =
+ controller->RemoveClient(client_id, client_handler);
DVLOG(1) << "VideoCaptureManager::StopCaptureForClient, session_id = "
<< session_id;
DestroyDeviceEntryIfNoClients(entry);
}
+bool VideoCaptureManager::GetDeviceSupportedFormats(
+ media::VideoCaptureSessionId capture_session_id,
+ media::VideoCaptureFormats* supported_formats) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(supported_formats->empty());
+
+ std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator it =
+ sessions_.find(capture_session_id);
+ if (it == sessions_.end())
+ return false;
+ DVLOG(1) << "GetDeviceSupportedFormats for device: " << it->second.name;
+
+ // Return all available formats of the device, regardless its started state.
+ DeviceInfo* existing_device =
+ FindDeviceInfoById(it->second.id, devices_info_cache_);
+ if (existing_device)
+ *supported_formats = existing_device->supported_formats;
+ return true;
+}
+
+bool VideoCaptureManager::GetDeviceFormatsInUse(
+ media::VideoCaptureSessionId capture_session_id,
+ media::VideoCaptureFormats* formats_in_use) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(formats_in_use->empty());
+
+ std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator it =
+ sessions_.find(capture_session_id);
+ if (it == sessions_.end())
+ return false;
+ DVLOG(1) << "GetDeviceFormatsInUse for device: " << it->second.name;
+
+ // Return the currently in-use format(s) of the device, if it's started.
+ DeviceEntry* device_in_use =
+ GetDeviceEntryForMediaStreamDevice(it->second);
+ if (device_in_use) {
+ // Currently only one format-in-use is supported at the VCC level.
+ formats_in_use->push_back(
+ device_in_use->video_capture_controller->GetVideoCaptureFormat());
+ }
+ return true;
+}
+
void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) {
SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime");
DCHECK(IsOnDeviceThread());
entry->video_capture_device.reset();
}
-void VideoCaptureManager::OnOpened(MediaStreamType stream_type,
- int capture_session_id) {
+void VideoCaptureManager::OnOpened(
+ MediaStreamType stream_type,
+ media::VideoCaptureSessionId capture_session_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!listener_) {
// Listener has been removed.
listener_->Opened(stream_type, capture_session_id);
}
-void VideoCaptureManager::OnClosed(MediaStreamType stream_type,
- int capture_session_id) {
+void VideoCaptureManager::OnClosed(
+ MediaStreamType stream_type,
+ media::VideoCaptureSessionId capture_session_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!listener_) {
// Listener has been removed.
listener_->Closed(stream_type, capture_session_id);
}
-void VideoCaptureManager::OnDevicesEnumerated(
+void VideoCaptureManager::OnDevicesInfoEnumerated(
MediaStreamType stream_type,
- const media::VideoCaptureDevice::Names& device_names) {
+ const DeviceInfos& new_devices_info_cache) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!listener_) {
// Listener has been removed.
return;
}
+ devices_info_cache_ = new_devices_info_cache;
- // Transform from VCD::Name to StreamDeviceInfo.
+ // Walk the |devices_info_cache_| and transform from VCD::Name to
+ // StreamDeviceInfo for return purposes.
StreamDeviceInfoArray devices;
- for (media::VideoCaptureDevice::Names::const_iterator it =
- device_names.begin(); it != device_names.end(); ++it) {
+ for (DeviceInfos::const_iterator it = devices_info_cache_.begin();
+ it != devices_info_cache_.end(); ++it) {
devices.push_back(StreamDeviceInfo(
- stream_type, it->GetNameAndModel(), it->id()));
+ stream_type, it->name.GetNameAndModel(), it->name.id()));
}
-
listener_->DevicesEnumerated(stream_type, devices);
}
bool VideoCaptureManager::IsOnDeviceThread() const {
- return device_loop_->BelongsToCurrentThread();
+ return device_task_runner_->BelongsToCurrentThread();
}
-media::VideoCaptureDevice::Names
-VideoCaptureManager::GetAvailableDevicesOnDeviceThread(
- MediaStreamType stream_type) {
+VideoCaptureManager::DeviceInfos
+VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread(
+ MediaStreamType stream_type,
+ const DeviceInfos& old_device_info_cache) {
SCOPED_UMA_HISTOGRAM_TIMER(
- "Media.VideoCaptureManager.GetAvailableDevicesTime");
+ "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime");
DCHECK(IsOnDeviceThread());
- media::VideoCaptureDevice::Names result;
-
+ media::VideoCaptureDevice::Names names_snapshot;
switch (stream_type) {
case MEDIA_DEVICE_VIDEO_CAPTURE:
// Cache the latest enumeration of video capture devices.
// We'll refer to this list again in OnOpen to avoid having to
// enumerate the devices again.
- if (!use_fake_device_) {
- media::VideoCaptureDevice::GetDeviceNames(&result);
- } else {
- media::FakeVideoCaptureDevice::GetDeviceNames(&result);
+ switch (artificial_device_source_for_testing_) {
+ case DISABLED:
+ media::VideoCaptureDevice::GetDeviceNames(&names_snapshot);
+ break;
+ case TEST_PATTERN:
+ media::FakeVideoCaptureDevice::GetDeviceNames(&names_snapshot);
+ break;
+ case Y4M_FILE:
+ media::FileVideoCaptureDevice::GetDeviceNames(&names_snapshot);
+ break;
}
-
- // TODO(nick): The correctness of device start depends on this cache being
- // maintained, but it seems a little odd to keep a cache here. Can we
- // eliminate it?
- video_capture_devices_ = result;
break;
case MEDIA_DESKTOP_VIDEO_CAPTURE:
NOTREACHED();
break;
}
- return result;
+
+ // Construct |new_devices_info_cache| with the cached devices that are still
+ // present in the system, and remove their names from |names_snapshot|, so we
+ // keep there the truly new devices.
+ DeviceInfos new_devices_info_cache;
+ for (DeviceInfos::const_iterator it_device_info =
+ old_device_info_cache.begin();
+ it_device_info != old_device_info_cache.end(); ++it_device_info) {
+ for (media::VideoCaptureDevice::Names::iterator it =
+ names_snapshot.begin();
+ it != names_snapshot.end(); ++it) {
+ if (it_device_info->name.id() == it->id()) {
+ new_devices_info_cache.push_back(*it_device_info);
+ names_snapshot.erase(it);
+ break;
+ }
+ }
+ }
+
+ // Get the supported capture formats for the new devices in |names_snapshot|.
+ for (media::VideoCaptureDevice::Names::const_iterator it =
+ names_snapshot.begin();
+ it != names_snapshot.end(); ++it) {
+ media::VideoCaptureFormats supported_formats;
+ DeviceInfo device_info(*it, media::VideoCaptureFormats());
+ switch (artificial_device_source_for_testing_) {
+ case DISABLED:
+ media::VideoCaptureDevice::GetDeviceSupportedFormats(
+ *it, &(device_info.supported_formats));
+ break;
+ case TEST_PATTERN:
+ media::FakeVideoCaptureDevice::GetDeviceSupportedFormats(
+ *it, &(device_info.supported_formats));
+ break;
+ case Y4M_FILE:
+ media::FileVideoCaptureDevice::GetDeviceSupportedFormats(
+ *it, &(device_info.supported_formats));
+ break;
+ }
+ new_devices_info_cache.push_back(device_info);
+ }
+ return new_devices_info_cache;
}
VideoCaptureManager::DeviceEntry*
// DeviceEntry, VideoCaptureController, and VideoCaptureDevice.
devices_.erase(entry);
entry->video_capture_controller.reset();
- device_loop_->PostTask(
+ device_task_runner_->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
base::Owned(entry)));
}
VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry(
- int capture_session_id) {
+ media::VideoCaptureSessionId capture_session_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- std::map<int, MediaStreamDevice>::iterator session_it =
- sessions_.find(capture_session_id);
+ std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator
+ session_it = sessions_.find(capture_session_id);
if (session_it == sessions_.end()) {
return NULL;
}
// Check if another session has already opened this device. If so, just
// use that opened device.
DeviceEntry* const existing_device =
- GetDeviceEntryForMediaStreamDevice(device_info);
+ GetDeviceEntryForMediaStreamDevice(device_info);
if (existing_device) {
DCHECK_EQ(device_info.type, existing_device->stream_type);
return existing_device;
return new_device;
}
+VideoCaptureManager::DeviceInfo* VideoCaptureManager::FindDeviceInfoById(
+ const std::string& id,
+ DeviceInfos& device_vector) {
+ for (DeviceInfos::iterator it = device_vector.begin();
+ it != device_vector.end(); ++it) {
+ if (it->name.id() == id)
+ return &(*it);
+ }
+ return NULL;
+}
+
} // namespace content