This source file includes following definitions.
- IsCaptureFormatSmaller
- IsCaptureFormatSizeEqual
- ConsolidateCaptureFormats
- video_capture_controller
- supported_formats
- artificial_device_source_for_testing_
- Register
- Unregister
- EnumerateDevices
- Open
- Close
- UseFakeDevice
- DoStartDeviceOnDeviceThread
- StartCaptureForClient
- StopCaptureForClient
- GetDeviceSupportedFormats
- GetDeviceFormatsInUse
- SetDesktopCaptureWindowId
- DoStopDeviceOnDeviceThread
- OnOpened
- OnClosed
- OnDevicesInfoEnumerated
- IsOnDeviceThread
- GetAvailableDevicesInfoOnDeviceThread
- GetDeviceEntryForMediaStreamDevice
- GetDeviceEntryForController
- DestroyDeviceEntryIfNoClients
- GetOrCreateDeviceEntry
- FindDeviceInfoById
- SetDesktopCaptureWindowIdOnDeviceThread
- SaveDesktopCaptureWindowIdOnDeviceThread
#include "content/browser/renderer_host/media/video_capture_manager.h"
#include <set>
#include "base/bind.h"
#include "base/command_line.h"
#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/media/capture/web_contents_video_capture_device.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/public/browser/browser_thread.h"
#include "content/public/browser/desktop_media_id.h"
#include "content/public/common/content_switches.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/media/capture/desktop_capture_device.h"
#if defined(USE_AURA)
#include "content/browser/media/capture/desktop_capture_device_aura.h"
#endif
#endif
namespace {
bool IsCaptureFormatSmaller(const media::VideoCaptureFormat& format1,
const media::VideoCaptureFormat& format2) {
if (format1.frame_size.GetArea() == format2.frame_size.GetArea())
return format1.frame_rate > format2.frame_rate;
return format1.frame_size.GetArea() < format2.frame_size.GetArea();
}
bool IsCaptureFormatSizeEqual(const media::VideoCaptureFormat& format1,
const media::VideoCaptureFormat& format2) {
return format1.frame_size.GetArea() == format2.frame_size.GetArea();
}
void ConsolidateCaptureFormats(media::VideoCaptureFormats* formats) {
if (formats->empty())
return;
std::sort(formats->begin(), formats->end(), IsCaptureFormatSmaller);
media::VideoCaptureFormats::iterator last =
std::unique(formats->begin(), formats->end(), IsCaptureFormatSizeEqual);
formats->erase(last, formats->end());
for (media::VideoCaptureFormats::iterator it = formats->begin();
it != formats->end(); ++it) {
it->pixel_format = media::PIXEL_FORMAT_I420;
}
}
}
namespace content {
VideoCaptureManager::DeviceEntry::DeviceEntry(
MediaStreamType stream_type,
const std::string& id,
scoped_ptr<VideoCaptureController> controller)
: stream_type(stream_type),
id(id),
video_capture_controller(controller.Pass()) {}
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),
artificial_device_source_for_testing_(DISABLED) {
}
VideoCaptureManager::~VideoCaptureManager() {
DCHECK(devices_.empty());
}
void VideoCaptureManager::Register(
MediaStreamProviderListener* listener,
const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(!listener_);
DCHECK(!device_task_runner_.get());
listener_ = listener;
device_task_runner_ = device_task_runner;
}
void VideoCaptureManager::Unregister() {
DCHECK(listener_);
listener_ = NULL;
}
void VideoCaptureManager::EnumerateDevices(MediaStreamType stream_type) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "VideoCaptureManager::EnumerateDevices, type " << stream_type;
DCHECK(listener_);
base::PostTaskAndReplyWithResult(
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_CURRENTLY_ON(BrowserThread::IO);
DCHECK(listener_);
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;
sessions_[capture_session_id] = device_info.device;
base::MessageLoop::current()->PostTask(FROM_HERE,
base::Bind(&VideoCaptureManager::OnOpened, this,
device_info.device.type, capture_session_id));
return capture_session_id;
}
void VideoCaptureManager::Close(int capture_session_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(listener_);
DVLOG(1) << "VideoCaptureManager::Close, id " << capture_session_id;
std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator
session_it = sessions_.find(capture_session_id);
if (session_it == sessions_.end()) {
NOTREACHED();
return;
}
DeviceEntry* const existing_device = GetDeviceEntryForMediaStreamDevice(
session_it->second);
if (existing_device) {
existing_device->video_capture_controller->StopSession(capture_session_id);
DestroyDeviceEntryIfNoClients(existing_device);
}
base::MessageLoop::current()->PostTask(FROM_HERE,
base::Bind(&VideoCaptureManager::OnClosed, this, session_it->second.type,
capture_session_id));
sessions_.erase(session_it);
}
void VideoCaptureManager::UseFakeDevice() {
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kUseFileForFakeVideoCapture)) {
artificial_device_source_for_testing_ = Y4M_FILE;
} else {
artificial_device_source_for_testing_ = TEST_PATTERN;
}
}
void VideoCaptureManager::DoStartDeviceOnDeviceThread(
media::VideoCaptureSessionId session_id,
DeviceEntry* entry,
const media::VideoCaptureParams& params,
scoped_ptr<media::VideoCaptureDevice::Client> device_client) {
SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime");
DCHECK(IsOnDeviceThread());
scoped_ptr<media::VideoCaptureDevice> video_capture_device;
switch (entry->stream_type) {
case MEDIA_DEVICE_VIDEO_CAPTURE: {
DeviceInfo* found = FindDeviceInfoById(entry->id, devices_info_cache_);
if (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_TAB_VIDEO_CAPTURE: {
video_capture_device.reset(
WebContentsVideoCaptureDevice::Create(entry->id));
break;
}
case MEDIA_DESKTOP_VIDEO_CAPTURE: {
#if defined(ENABLE_SCREEN_CAPTURE)
DesktopMediaID id = DesktopMediaID::Parse(entry->id);
#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);
if (notification_window_ids_.find(session_id) !=
notification_window_ids_.end()) {
static_cast<DesktopCaptureDevice*>(video_capture_device.get())
->SetNotificationWindowId(notification_window_ids_[session_id]);
}
}
#endif
break;
}
default: {
NOTIMPLEMENTED();
break;
}
}
if (!video_capture_device) {
device_client->OnError("Could not create capture device");
return;
}
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_CURRENTLY_ON(BrowserThread::IO);
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;
}
DCHECK(entry->video_capture_controller);
if (entry->video_capture_controller->GetClientCount() == 0) {
DVLOG(1) << "VideoCaptureManager starting device (type = "
<< entry->stream_type << ", id = " << entry->id << ")";
device_task_runner_->PostTask(
FROM_HERE,
base::Bind(
&VideoCaptureManager::DoStartDeviceOnDeviceThread,
this,
session_id,
entry,
params,
base::Passed(entry->video_capture_controller->NewDeviceClient())));
}
done_cb.Run(entry->video_capture_controller->GetWeakPtr());
entry->video_capture_controller->AddClient(
client_id, client_handler, client_render_process, session_id, params);
}
void VideoCaptureManager::StopCaptureForClient(
VideoCaptureController* controller,
VideoCaptureControllerID client_id,
VideoCaptureControllerEventHandler* client_handler) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(controller);
DCHECK(client_handler);
DeviceEntry* entry = GetDeviceEntryForController(controller);
if (!entry) {
NOTREACHED();
return;
}
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_CURRENTLY_ON(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;
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_CURRENTLY_ON(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;
DeviceEntry* device_in_use =
GetDeviceEntryForMediaStreamDevice(it->second);
if (device_in_use) {
formats_in_use->push_back(
device_in_use->video_capture_controller->GetVideoCaptureFormat());
}
return true;
}
void VideoCaptureManager::SetDesktopCaptureWindowId(
media::VideoCaptureSessionId session_id,
gfx::NativeViewId window_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator
session_it = sessions_.find(session_id);
if (session_it == sessions_.end()) {
device_task_runner_->PostTask(
FROM_HERE,
base::Bind(
&VideoCaptureManager::SaveDesktopCaptureWindowIdOnDeviceThread,
this,
session_id,
window_id));
return;
}
DeviceEntry* const existing_device =
GetDeviceEntryForMediaStreamDevice(session_it->second);
if (!existing_device)
return;
DCHECK_EQ(MEDIA_DESKTOP_VIDEO_CAPTURE, existing_device->stream_type);
DesktopMediaID id = DesktopMediaID::Parse(existing_device->id);
if (id.type == DesktopMediaID::TYPE_NONE ||
id.type == DesktopMediaID::TYPE_AURA_WINDOW) {
return;
}
device_task_runner_->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread,
this,
existing_device,
window_id));
}
void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) {
SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime");
DCHECK(IsOnDeviceThread());
if (entry->video_capture_device) {
entry->video_capture_device->StopAndDeAllocate();
}
entry->video_capture_device.reset();
}
void VideoCaptureManager::OnOpened(
MediaStreamType stream_type,
media::VideoCaptureSessionId capture_session_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!listener_) {
return;
}
listener_->Opened(stream_type, capture_session_id);
}
void VideoCaptureManager::OnClosed(
MediaStreamType stream_type,
media::VideoCaptureSessionId capture_session_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!listener_) {
return;
}
listener_->Closed(stream_type, capture_session_id);
}
void VideoCaptureManager::OnDevicesInfoEnumerated(
MediaStreamType stream_type,
const DeviceInfos& new_devices_info_cache) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!listener_) {
return;
}
devices_info_cache_ = new_devices_info_cache;
StreamDeviceInfoArray devices;
for (DeviceInfos::const_iterator it = devices_info_cache_.begin();
it != devices_info_cache_.end(); ++it) {
devices.push_back(StreamDeviceInfo(
stream_type, it->name.GetNameAndModel(), it->name.id()));
}
listener_->DevicesEnumerated(stream_type, devices);
}
bool VideoCaptureManager::IsOnDeviceThread() const {
return device_task_runner_->BelongsToCurrentThread();
}
VideoCaptureManager::DeviceInfos
VideoCaptureManager::GetAvailableDevicesInfoOnDeviceThread(
MediaStreamType stream_type,
const DeviceInfos& old_device_info_cache) {
SCOPED_UMA_HISTOGRAM_TIMER(
"Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime");
DCHECK(IsOnDeviceThread());
media::VideoCaptureDevice::Names names_snapshot;
switch (stream_type) {
case MEDIA_DEVICE_VIDEO_CAPTURE:
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;
}
break;
case MEDIA_DESKTOP_VIDEO_CAPTURE:
break;
default:
NOTREACHED();
break;
}
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;
}
}
}
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;
}
ConsolidateCaptureFormats(&device_info.supported_formats);
new_devices_info_cache.push_back(device_info);
}
return new_devices_info_cache;
}
VideoCaptureManager::DeviceEntry*
VideoCaptureManager::GetDeviceEntryForMediaStreamDevice(
const MediaStreamDevice& device_info) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
for (DeviceEntries::iterator it = devices_.begin();
it != devices_.end(); ++it) {
DeviceEntry* device = *it;
if (device_info.type == device->stream_type &&
device_info.id == device->id) {
return device;
}
}
return NULL;
}
VideoCaptureManager::DeviceEntry*
VideoCaptureManager::GetDeviceEntryForController(
const VideoCaptureController* controller) {
for (DeviceEntries::iterator it = devices_.begin();
it != devices_.end(); ++it) {
if ((*it)->video_capture_controller.get() == controller) {
return *it;
}
}
return NULL;
}
void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (entry->video_capture_controller->GetClientCount() == 0) {
DVLOG(1) << "VideoCaptureManager stopping device (type = "
<< entry->stream_type << ", id = " << entry->id << ")";
devices_.erase(entry);
entry->video_capture_controller.reset();
device_task_runner_->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
base::Owned(entry)));
}
}
VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry(
media::VideoCaptureSessionId capture_session_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
std::map<media::VideoCaptureSessionId, MediaStreamDevice>::iterator
session_it = sessions_.find(capture_session_id);
if (session_it == sessions_.end()) {
return NULL;
}
const MediaStreamDevice& device_info = session_it->second;
DeviceEntry* const existing_device =
GetDeviceEntryForMediaStreamDevice(device_info);
if (existing_device) {
DCHECK_EQ(device_info.type, existing_device->stream_type);
return existing_device;
}
scoped_ptr<VideoCaptureController> video_capture_controller(
new VideoCaptureController());
DeviceEntry* new_device = new DeviceEntry(device_info.type,
device_info.id,
video_capture_controller.Pass());
devices_.insert(new_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;
}
void VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread(
DeviceEntry* entry,
gfx::NativeViewId window_id) {
DCHECK(IsOnDeviceThread());
DCHECK(entry->stream_type == MEDIA_DESKTOP_VIDEO_CAPTURE);
#if defined(ENABLE_SCREEN_CAPTURE)
DesktopCaptureDevice* device =
static_cast<DesktopCaptureDevice*>(entry->video_capture_device.get());
device->SetNotificationWindowId(window_id);
#endif
}
void VideoCaptureManager::SaveDesktopCaptureWindowIdOnDeviceThread(
media::VideoCaptureSessionId session_id,
gfx::NativeViewId window_id) {
DCHECK(IsOnDeviceThread());
DCHECK(notification_window_ids_.find(session_id) ==
notification_window_ids_.end());
notification_window_ids_[session_id] = window_id;
}
}