blob: 3f7cad190c4aaa20674b4da53bc188f493787501 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/media/video_capture_manager.h"
#include <algorithm>
#include <set>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/task_runner_util.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "content/browser/media/capture/desktop_capture_device_uma_types.h"
#include "content/browser/media/capture/web_contents_video_capture_device.h"
#include "content/browser/media/media_internals.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/video_capture_gpu_jpeg_decoder.h"
#include "content/browser/renderer_host/media/video_frame_receiver_on_io_thread.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/desktop_media_id.h"
#include "content/public/common/media_stream_request.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/media_switches.h"
#include "media/capture/video/video_capture_buffer_pool_impl.h"
#include "media/capture/video/video_capture_buffer_tracker_factory_impl.h"
#include "media/capture/video/video_capture_device.h"
#include "media/capture/video/video_capture_device_client.h"
#include "media/capture/video/video_capture_device_factory.h"
#if defined(ENABLE_SCREEN_CAPTURE)
#if BUILDFLAG(ENABLE_WEBRTC) && !defined(OS_ANDROID)
#include "content/browser/media/capture/desktop_capture_device.h"
#endif
#if defined(USE_AURA)
#include "content/browser/media/capture/desktop_capture_device_aura.h"
#endif
#if defined(OS_ANDROID)
#include "content/browser/media/capture/screen_capture_device_android.h"
#endif
#endif // defined(ENABLE_SCREEN_CAPTURE)
namespace {
class VideoFrameConsumerFeedbackObserverOnTaskRunner
: public media::VideoFrameConsumerFeedbackObserver {
public:
VideoFrameConsumerFeedbackObserverOnTaskRunner(
media::VideoFrameConsumerFeedbackObserver* observer,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: observer_(observer), task_runner_(std::move(task_runner)) {}
void OnUtilizationReport(int frame_feedback_id, double utilization) override {
task_runner_->PostTask(
FROM_HERE,
base::Bind(
&media::VideoFrameConsumerFeedbackObserver::OnUtilizationReport,
base::Unretained(observer_), frame_feedback_id, utilization));
}
private:
media::VideoFrameConsumerFeedbackObserver* const observer_;
const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
};
// Compares two VideoCaptureFormat by checking smallest frame_size area, then
// by _largest_ frame_rate. Used to order a VideoCaptureFormats vector so that
// the first entry for a given resolution has the largest frame rate, as needed
// by the ConsolidateCaptureFormats() method.
bool IsCaptureFormatSmaller(const media::VideoCaptureFormat& format1,
const media::VideoCaptureFormat& format2) {
DCHECK(format1.frame_size.GetCheckedArea().IsValid());
DCHECK(format2.frame_size.GetCheckedArea().IsValid());
if (format1.frame_size.GetCheckedArea().ValueOrDefault(0) ==
format2.frame_size.GetCheckedArea().ValueOrDefault(0)) {
return format1.frame_rate > format2.frame_rate;
}
return format1.frame_size.GetCheckedArea().ValueOrDefault(0) <
format2.frame_size.GetCheckedArea().ValueOrDefault(0);
}
bool IsCaptureFormatSizeEqual(const media::VideoCaptureFormat& format1,
const media::VideoCaptureFormat& format2) {
DCHECK(format1.frame_size.GetCheckedArea().IsValid());
DCHECK(format2.frame_size.GetCheckedArea().IsValid());
return format1.frame_size.GetCheckedArea().ValueOrDefault(0) ==
format2.frame_size.GetCheckedArea().ValueOrDefault(0);
}
// This function receives a list of capture formats, removes duplicated
// resolutions while keeping the highest frame rate for each, and forcing I420
// pixel format.
void ConsolidateCaptureFormats(media::VideoCaptureFormats* formats) {
if (formats->empty())
return;
std::sort(formats->begin(), formats->end(), IsCaptureFormatSmaller);
// Due to the ordering imposed, the largest frame_rate is kept while removing
// duplicated resolutions.
media::VideoCaptureFormats::iterator last =
std::unique(formats->begin(), formats->end(), IsCaptureFormatSizeEqual);
formats->erase(last, formats->end());
// Mark all formats as I420, since this is what the renderer side will get
// anyhow: the actual pixel format is decided at the device level.
// Don't do this for Y16 format as it is handled separatelly.
for (auto& format : *formats) {
if (format.pixel_format != media::PIXEL_FORMAT_Y16)
format.pixel_format = media::PIXEL_FORMAT_I420;
}
}
// The maximum number of video frame buffers in-flight at any one time. This
// value should be based on the logical capacity of the capture pipeline, and
// not on hardware performance. For example, tab capture requires more buffers
// than webcam capture because the pipeline is longer (it includes read-backs
// pending in the GPU pipeline).
const int kMaxNumberOfBuffers = 3;
// TODO(miu): The value for tab capture should be determined programmatically.
// http://crbug.com/460318
const int kMaxNumberOfBuffersForTabCapture = 10;
// Used for logging capture events.
// Elements in this enum should not be deleted or rearranged; the only
// permitted operation is to add new elements before NUM_VIDEO_CAPTURE_EVENT.
enum VideoCaptureEvent {
VIDEO_CAPTURE_START_CAPTURE = 0,
VIDEO_CAPTURE_STOP_CAPTURE_OK = 1,
VIDEO_CAPTURE_STOP_CAPTURE_DUE_TO_ERROR = 2,
VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DEVICE = 3,
VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DESKTOP_OR_TAB = 4,
NUM_VIDEO_CAPTURE_EVENT
};
void LogVideoCaptureEvent(VideoCaptureEvent event) {
UMA_HISTOGRAM_ENUMERATION("Media.VideoCaptureManager.Event",
event,
NUM_VIDEO_CAPTURE_EVENT);
}
// Counter used for identifying a DeviceRequest to start a capture device.
static int g_device_start_id = 0;
const media::VideoCaptureSessionId kFakeSessionId = -1;
std::unique_ptr<media::VideoCaptureJpegDecoder> CreateGpuJpegDecoder(
const media::VideoCaptureJpegDecoder::DecodeDoneCB& decode_done_cb) {
return base::MakeUnique<content::VideoCaptureGpuJpegDecoder>(decode_done_cb);
}
} // namespace
namespace content {
// Instances of this struct go through several different phases during their
// lifetime.
// Phase 1: When first created (in GetOrCreateDeviceEntry()), this consists of
// only the |video_capture_controller|. Clients can already connect to the
// controller, but there is no |buffer_pool| or |video_capture_device| present.
// Phase 2: When a request to "start" the entry comes in (via
// HandleQueuedStartRequest()), |buffer_pool| is created and creation of
// |video_capture_device| is scheduled to run asynchronously on the Device
// Thread.
// Phase 3: As soon as the creation of the VideoCaptureDevice is complete, this
// newly created VideoCaptureDevice instance is connected to the
// VideoCaptureController via SetConsumerFeedbackObserver(). Furthermore, the
// |buffer_pool| is moved to the |video_capture_controller| as a
// FrameBufferPool via SetFrameBufferPool().
// Phase 4: This phase can only be reached on Android. When the application goes
// to the background, the |video_capture_device| is asynchronously stopped and
// released on the Device Thread. When the application is resumed, we
// transition to Phase 2.
struct VideoCaptureManager::DeviceEntry {
public:
DeviceEntry(MediaStreamType stream_type,
const std::string& id,
const media::VideoCaptureParams& params);
~DeviceEntry();
std::unique_ptr<media::VideoCaptureDevice::Client> CreateDeviceClient();
std::unique_ptr<media::FrameBufferPool> CreateFrameBufferPool();
const int serial_id;
const MediaStreamType stream_type;
const std::string id;
const media::VideoCaptureParams parameters;
VideoCaptureController video_capture_controller;
scoped_refptr<media::VideoCaptureBufferPool> buffer_pool;
std::unique_ptr<media::VideoCaptureDevice> video_capture_device;
};
// Bundles a media::VideoCaptureDeviceDescriptor with corresponding supported
// video formats.
struct VideoCaptureManager::DeviceInfo {
DeviceInfo();
DeviceInfo(media::VideoCaptureDeviceDescriptor descriptor);
DeviceInfo(const DeviceInfo& other);
~DeviceInfo();
DeviceInfo& operator=(const DeviceInfo& other);
media::VideoCaptureDeviceDescriptor descriptor;
media::VideoCaptureFormats supported_formats;
};
class BufferPoolFrameBufferPool : public media::FrameBufferPool {
public:
explicit BufferPoolFrameBufferPool(
scoped_refptr<media::VideoCaptureBufferPool> buffer_pool)
: buffer_pool_(std::move(buffer_pool)) {}
void SetBufferHold(int buffer_id) override {
buffer_pool_->HoldForConsumers(buffer_id, 1);
}
void ReleaseBufferHold(int buffer_id) override {
buffer_pool_->RelinquishConsumerHold(buffer_id, 1);
}
private:
scoped_refptr<media::VideoCaptureBufferPool> buffer_pool_;
};
// Class used for queuing request for starting a device.
class VideoCaptureManager::CaptureDeviceStartRequest {
public:
CaptureDeviceStartRequest(int serial_id,
media::VideoCaptureSessionId session_id,
const media::VideoCaptureParams& params);
int serial_id() const { return serial_id_; }
media::VideoCaptureSessionId session_id() const { return session_id_; }
media::VideoCaptureParams params() const { return params_; }
// Set to true if the device should be stopped before it has successfully
// been started.
bool abort_start() const { return abort_start_; }
void set_abort_start() { abort_start_ = true; }
private:
const int serial_id_;
const media::VideoCaptureSessionId session_id_;
const media::VideoCaptureParams params_;
// Set to true if the device should be stopped before it has successfully
// been started.
bool abort_start_;
};
VideoCaptureManager::DeviceEntry::DeviceEntry(
MediaStreamType stream_type,
const std::string& id,
const media::VideoCaptureParams& params)
: serial_id(g_device_start_id++),
stream_type(stream_type),
id(id),
parameters(params) {}
VideoCaptureManager::DeviceEntry::~DeviceEntry() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// DCHECK that this DeviceEntry does not still own a
// media::VideoCaptureDevice. media::VideoCaptureDevice must be deleted on
// the device thread.
DCHECK(video_capture_device == nullptr);
}
std::unique_ptr<media::VideoCaptureDevice::Client>
VideoCaptureManager::DeviceEntry::CreateDeviceClient() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
const int max_buffers = stream_type == MEDIA_TAB_VIDEO_CAPTURE
? kMaxNumberOfBuffersForTabCapture
: kMaxNumberOfBuffers;
buffer_pool = new media::VideoCaptureBufferPoolImpl(
base::MakeUnique<media::VideoCaptureBufferTrackerFactoryImpl>(),
max_buffers);
return base::MakeUnique<media::VideoCaptureDeviceClient>(
base::MakeUnique<VideoFrameReceiverOnIOThread>(
video_capture_controller.GetWeakPtrForIOThread()),
buffer_pool,
base::Bind(
&CreateGpuJpegDecoder,
base::Bind(&media::VideoFrameReceiver::OnIncomingCapturedVideoFrame,
video_capture_controller.GetWeakPtrForIOThread())));
}
std::unique_ptr<media::FrameBufferPool>
VideoCaptureManager::DeviceEntry::CreateFrameBufferPool() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(buffer_pool);
return base::MakeUnique<BufferPoolFrameBufferPool>(std::move(buffer_pool));
}
VideoCaptureManager::DeviceInfo::DeviceInfo() = default;
VideoCaptureManager::DeviceInfo::DeviceInfo(
media::VideoCaptureDeviceDescriptor descriptor)
: descriptor(descriptor) {}
VideoCaptureManager::DeviceInfo::DeviceInfo(
const VideoCaptureManager::DeviceInfo& other) = default;
VideoCaptureManager::DeviceInfo::~DeviceInfo() = default;
VideoCaptureManager::DeviceInfo& VideoCaptureManager::DeviceInfo::operator=(
const VideoCaptureManager::DeviceInfo& other) = default;
VideoCaptureManager::CaptureDeviceStartRequest::CaptureDeviceStartRequest(
int serial_id,
media::VideoCaptureSessionId session_id,
const media::VideoCaptureParams& params)
: serial_id_(serial_id),
session_id_(session_id),
params_(params),
abort_start_(false) {
}
VideoCaptureManager::VideoCaptureManager(
std::unique_ptr<media::VideoCaptureDeviceFactory> factory)
: listener_(nullptr),
new_capture_session_id_(1),
video_capture_device_factory_(std::move(factory)) {}
VideoCaptureManager::~VideoCaptureManager() {
DCHECK(devices_.empty());
DCHECK(device_start_queue_.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;
#if defined(OS_ANDROID)
application_state_has_running_activities_ = true;
app_status_listener_.reset(new base::android::ApplicationStatusListener(
base::Bind(&VideoCaptureManager::OnApplicationStateChange,
base::Unretained(this))));
#endif
}
void VideoCaptureManager::Unregister() {
DCHECK(listener_);
listener_ = nullptr;
}
void VideoCaptureManager::EnumerateDevices(
const EnumerationCallback& client_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << "VideoCaptureManager::EnumerateDevices";
// Bind a callback to ConsolidateDevicesInfoOnDeviceThread() with an argument
// for another callback to OnDevicesInfoEnumerated() to be run in the current
// loop, i.e. IO loop. Pass a timer for UMA histogram collection.
base::Callback<void(std::unique_ptr<VideoCaptureDeviceDescriptors>)>
devices_enumerated_callback = base::Bind(
&VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread, this,
media::BindToCurrentLoop(base::Bind(
&VideoCaptureManager::OnDevicesInfoEnumerated, this,
base::Owned(new base::ElapsedTimer()), client_callback)),
devices_info_cache_);
// OK to use base::Unretained() since we own the VCDFactory and |this| is
// bound in |devices_enumerated_callback|.
device_task_runner_->PostTask(
FROM_HERE,
base::Bind(&media::VideoCaptureDeviceFactory::EnumerateDeviceDescriptors,
base::Unretained(video_capture_device_factory_.get()),
devices_enumerated_callback));
}
int VideoCaptureManager::Open(const StreamDeviceInfo& device_info) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(listener_);
// Generate a new id for the session being opened.
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;
// We just save the stream info for processing later.
sessions_[capture_session_id] = device_info.device;
// Notify our listener asynchronously; this ensures that we return
// |capture_session_id| to the caller of this function before using that same
// id in a listener event.
base::ThreadTaskRunnerHandle::Get()->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;
SessionMap::iterator session_it = sessions_.find(capture_session_id);
if (session_it == sessions_.end()) {
NOTREACHED();
return;
}
DeviceEntry* const existing_device =
GetDeviceEntryByTypeAndId(session_it->second.type, session_it->second.id);
if (existing_device) {
// Remove any client that is still using the session. This is safe to call
// even if there are no clients using the session.
existing_device->video_capture_controller.StopSession(capture_session_id);
// StopSession() may have removed the last client, so we might need to
// close the device.
DestroyDeviceEntryIfNoClients(existing_device);
}
// Notify listeners asynchronously, and forget the session.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&VideoCaptureManager::OnClosed, this,
session_it->second.type, capture_session_id));
sessions_.erase(session_it);
}
void VideoCaptureManager::QueueStartDevice(
media::VideoCaptureSessionId session_id,
DeviceEntry* entry,
const media::VideoCaptureParams& params) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
device_start_queue_.push_back(
CaptureDeviceStartRequest(entry->serial_id, session_id, params));
if (device_start_queue_.size() == 1)
HandleQueuedStartRequest();
}
void VideoCaptureManager::DoStopDevice(DeviceEntry* entry) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// TODO(mcasas): use a helper function https://crbug.com/624854.
DCHECK(
std::find_if(devices_.begin(), devices_.end(),
[entry](const std::unique_ptr<DeviceEntry>& device_entry) {
return device_entry.get() == entry;
}) != devices_.end());
// Find the matching start request.
for (DeviceStartQueue::reverse_iterator request =
device_start_queue_.rbegin();
request != device_start_queue_.rend(); ++request) {
if (request->serial_id() == entry->serial_id) {
request->set_abort_start();
DVLOG(3) << "DoStopDevice, aborting start request for device "
<< entry->id << " serial_id = " << entry->serial_id;
return;
}
}
DVLOG(3) << "DoStopDevice. Send stop request for device = " << entry->id
<< " serial_id = " << entry->serial_id << ".";
entry->video_capture_controller.OnLog(
base::StringPrintf("Stopping device: id: %s", entry->id.c_str()));
entry->video_capture_controller.SetConsumerFeedbackObserver(nullptr);
entry->video_capture_controller.SetFrameBufferPool(nullptr);
// |entry->video_capture_device| can be null if creating the device has
// failed.
if (entry->video_capture_device) {
device_task_runner_->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
base::Passed(&entry->video_capture_device)));
}
}
void VideoCaptureManager::HandleQueuedStartRequest() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Remove all start requests that have been aborted.
while (device_start_queue_.begin() != device_start_queue_.end() &&
device_start_queue_.begin()->abort_start()) {
device_start_queue_.pop_front();
}
DeviceStartQueue::iterator request = device_start_queue_.begin();
if (request == device_start_queue_.end())
return;
const int serial_id = request->serial_id();
DeviceEntry* const entry = GetDeviceEntryBySerialId(serial_id);
DCHECK(entry);
DVLOG(3) << "HandleQueuedStartRequest, Post start to device thread, device = "
<< entry->id << " start id = " << entry->serial_id;
std::unique_ptr<media::VideoCaptureDevice::Client> device_client =
entry->CreateDeviceClient();
std::unique_ptr<media::FrameBufferPool> frame_buffer_pool =
entry->CreateFrameBufferPool();
base::Callback<std::unique_ptr<VideoCaptureDevice>(void)>
start_capture_function;
switch (entry->stream_type) {
case MEDIA_DEVICE_VIDEO_CAPTURE: {
// 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.
const DeviceInfo* found = GetDeviceInfoById(entry->id);
if (found) {
entry->video_capture_controller.OnLog(
base::StringPrintf("Starting device: id: %s, name: %s, api: %s",
found->descriptor.device_id.c_str(),
found->descriptor.GetNameAndModel().c_str(),
found->descriptor.GetCaptureApiTypeString()));
start_capture_function =
base::Bind(&VideoCaptureManager::DoStartDeviceCaptureOnDeviceThread,
this, found->descriptor, request->params(),
base::Passed(std::move(device_client)));
} else {
// Errors from DoStartDeviceCaptureOnDeviceThread go via
// VideoCaptureDeviceClient::OnError, which needs some thread
// dancing to get errors processed on the IO thread. But since
// we're on that thread, we call VideoCaptureController
// methods directly.
const std::string log_message = base::StringPrintf(
"Error on %s:%d: device %s unknown. Maybe recently disconnected?",
__FILE__, __LINE__, entry->id.c_str());
DLOG(ERROR) << log_message;
entry->video_capture_controller.OnLog(log_message);
entry->video_capture_controller.OnError();
// Drop the failed start request.
device_start_queue_.pop_front();
return;
}
break;
}
case MEDIA_TAB_VIDEO_CAPTURE:
start_capture_function = base::Bind(
&VideoCaptureManager::DoStartTabCaptureOnDeviceThread, this,
entry->id, request->params(), base::Passed(std::move(device_client)));
break;
case MEDIA_DESKTOP_VIDEO_CAPTURE:
start_capture_function = base::Bind(
&VideoCaptureManager::DoStartDesktopCaptureOnDeviceThread, this,
entry->id, request->params(), base::Passed(std::move(device_client)));
break;
default: {
NOTIMPLEMENTED();
return;
}
}
base::PostTaskAndReplyWithResult(
device_task_runner_.get(), FROM_HERE, start_capture_function,
base::Bind(&VideoCaptureManager::OnDeviceStarted, this,
request->serial_id(), base::Passed(&frame_buffer_pool)));
}
void VideoCaptureManager::OnDeviceStarted(
int serial_id,
std::unique_ptr<media::FrameBufferPool> frame_buffer_pool,
std::unique_ptr<VideoCaptureDevice> device) {
DVLOG(3) << __func__;
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK_EQ(serial_id, device_start_queue_.begin()->serial_id());
// |device| can be null if creation failed in
// DoStartDeviceCaptureOnDeviceThread.
if (device_start_queue_.front().abort_start()) {
// The device is no longer wanted. Stop the device again.
DVLOG(3) << "OnDeviceStarted but start request have been aborted.";
media::VideoCaptureDevice* device_ptr = device.get();
base::Closure closure =
base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this,
base::Passed(&device));
if (device_ptr && !device_task_runner_->PostTask(FROM_HERE, closure)) {
// PostTask failed. The device must be stopped anyway.
device_ptr->StopAndDeAllocate();
}
} else {
DeviceEntry* const entry = GetDeviceEntryBySerialId(serial_id);
DCHECK(entry);
DCHECK(!entry->video_capture_device);
if (device) {
entry->video_capture_controller.SetFrameBufferPool(
std::move(frame_buffer_pool));
// Passing raw pointer |device.get()| to the controller is safe,
// because we transfer ownership of it to |entry|. We are calling
// SetConsumerFeedbackObserver(nullptr) before releasing
// |entry->video_capture_device_| on the |device_task_runner_|.
entry->video_capture_controller.SetConsumerFeedbackObserver(
base::MakeUnique<VideoFrameConsumerFeedbackObserverOnTaskRunner>(
device.get(), device_task_runner_));
}
entry->video_capture_device = std::move(device);
if (entry->stream_type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
const media::VideoCaptureSessionId session_id =
device_start_queue_.front().session_id();
DCHECK(session_id != kFakeSessionId);
MaybePostDesktopCaptureWindowId(session_id);
}
auto it = photo_request_queue_.begin();
while (it != photo_request_queue_.end()) {
auto request = it++;
DeviceEntry* maybe_entry = GetDeviceEntryBySessionId(request->first);
if (maybe_entry && maybe_entry->video_capture_device) {
request->second.Run(maybe_entry->video_capture_device.get());
photo_request_queue_.erase(request);
}
}
}
device_start_queue_.pop_front();
HandleQueuedStartRequest();
}
std::unique_ptr<media::VideoCaptureDevice>
VideoCaptureManager::DoStartDeviceCaptureOnDeviceThread(
const VideoCaptureDeviceDescriptor& descriptor,
const media::VideoCaptureParams& params,
std::unique_ptr<VideoCaptureDevice::Client> device_client) {
SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime");
DCHECK(IsOnDeviceThread());
std::unique_ptr<VideoCaptureDevice> video_capture_device;
video_capture_device =
video_capture_device_factory_->CreateDevice(descriptor);
if (!video_capture_device) {
device_client->OnError(FROM_HERE, "Could not create capture device");
return nullptr;
}
video_capture_device->AllocateAndStart(params, std::move(device_client));
return video_capture_device;
}
std::unique_ptr<media::VideoCaptureDevice>
VideoCaptureManager::DoStartTabCaptureOnDeviceThread(
const std::string& id,
const media::VideoCaptureParams& params,
std::unique_ptr<VideoCaptureDevice::Client> device_client) {
SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime");
DCHECK(IsOnDeviceThread());
std::unique_ptr<VideoCaptureDevice> video_capture_device;
#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
video_capture_device = WebContentsVideoCaptureDevice::Create(id);
#endif
if (!video_capture_device) {
device_client->OnError(FROM_HERE, "Could not create capture device");
return nullptr;
}
video_capture_device->AllocateAndStart(params, std::move(device_client));
return video_capture_device;
}
std::unique_ptr<media::VideoCaptureDevice>
VideoCaptureManager::DoStartDesktopCaptureOnDeviceThread(
const std::string& id,
const media::VideoCaptureParams& params,
std::unique_ptr<VideoCaptureDevice::Client> device_client) {
SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime");
DCHECK(IsOnDeviceThread());
std::unique_ptr<VideoCaptureDevice> video_capture_device;
#if defined(ENABLE_SCREEN_CAPTURE)
DesktopMediaID desktop_id = DesktopMediaID::Parse(id);
if (desktop_id.is_null()) {
device_client->OnError(FROM_HERE, "Desktop media ID is null");
return nullptr;
}
if (desktop_id.type == DesktopMediaID::TYPE_WEB_CONTENTS) {
#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
video_capture_device = WebContentsVideoCaptureDevice::Create(id);
IncrementDesktopCaptureCounter(TAB_VIDEO_CAPTURER_CREATED);
if (desktop_id.audio_share) {
IncrementDesktopCaptureCounter(TAB_VIDEO_CAPTURER_CREATED_WITH_AUDIO);
} else {
IncrementDesktopCaptureCounter(TAB_VIDEO_CAPTURER_CREATED_WITHOUT_AUDIO);
}
#endif
} else {
#if defined(OS_ANDROID)
video_capture_device = base::MakeUnique<ScreenCaptureDeviceAndroid>();
#else
#if defined(USE_AURA)
video_capture_device = DesktopCaptureDeviceAura::Create(desktop_id);
#endif // defined(USE_AURA)
#if BUILDFLAG(ENABLE_WEBRTC)
if (!video_capture_device)
video_capture_device = DesktopCaptureDevice::Create(desktop_id);
#endif // BUILDFLAG(ENABLE_WEBRTC)
#endif // defined (OS_ANDROID)
}
#endif // defined(ENABLE_SCREEN_CAPTURE)
if (!video_capture_device) {
device_client->OnError(FROM_HERE, "Could not create capture device");
return nullptr;
}
video_capture_device->AllocateAndStart(params, std::move(device_client));
return video_capture_device;
}
void VideoCaptureManager::StartCaptureForClient(
media::VideoCaptureSessionId session_id,
const media::VideoCaptureParams& params,
VideoCaptureControllerID client_id,
VideoCaptureControllerEventHandler* client_handler,
const DoneCB& done_cb) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DVLOG(1) << __func__ << ", session_id = " << session_id << ", request: "
<< media::VideoCaptureFormat::ToString(params.requested_format);
DeviceEntry* entry = GetOrCreateDeviceEntry(session_id, params);
if (!entry) {
done_cb.Run(base::WeakPtr<VideoCaptureController>());
return;
}
LogVideoCaptureEvent(VIDEO_CAPTURE_START_CAPTURE);
// First client starts the device.
if (!entry->video_capture_controller.HasActiveClient() &&
!entry->video_capture_controller.HasPausedClient()) {
DVLOG(1) << "VideoCaptureManager starting device (type = "
<< entry->stream_type << ", id = " << entry->id << ")";
QueueStartDevice(session_id, entry, params);
}
// Run the callback first, as AddClient() may trigger OnFrameInfo().
done_cb.Run(entry->video_capture_controller.GetWeakPtrForIOThread());
entry->video_capture_controller.AddClient(client_id, client_handler,
session_id, params);
}
void VideoCaptureManager::StopCaptureForClient(
VideoCaptureController* controller,
VideoCaptureControllerID client_id,
VideoCaptureControllerEventHandler* client_handler,
bool aborted_due_to_error) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(controller);
DCHECK(client_handler);
DeviceEntry* entry = GetDeviceEntryByController(controller);
if (!entry) {
NOTREACHED();
return;
}
if (!aborted_due_to_error) {
if (controller->has_received_frames()) {
LogVideoCaptureEvent(VIDEO_CAPTURE_STOP_CAPTURE_OK);
} else if (entry->stream_type == MEDIA_DEVICE_VIDEO_CAPTURE) {
LogVideoCaptureEvent(
VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DEVICE);
} else {
LogVideoCaptureEvent(
VIDEO_CAPTURE_STOP_CAPTURE_OK_NO_FRAMES_PRODUCED_BY_DESKTOP_OR_TAB);
}
} else {
LogVideoCaptureEvent(VIDEO_CAPTURE_STOP_CAPTURE_DUE_TO_ERROR);
for (auto it : sessions_) {
if (it.second.type == entry->stream_type && it.second.id == entry->id) {
listener_->Aborted(it.second.type, it.first);
// Aborted() call might synchronously destroy |entry|, recheck.
entry = GetDeviceEntryByController(controller);
if (!entry)
return;
break;
}
}
}
// Detach client from controller.
const media::VideoCaptureSessionId session_id =
controller->RemoveClient(client_id, client_handler);
DVLOG(1) << __func__ << ", session_id = " << session_id;
// If controller has no more clients, delete controller and device.
DestroyDeviceEntryIfNoClients(entry);
}
void VideoCaptureManager::PauseCaptureForClient(
VideoCaptureController* controller,
VideoCaptureControllerID client_id,
VideoCaptureControllerEventHandler* client_handler) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(controller);
DCHECK(client_handler);
DeviceEntry* entry = GetDeviceEntryByController(controller);
if (!entry)
NOTREACHED() << "Got Null entry while pausing capture";
const bool had_active_client = controller->HasActiveClient();
controller->PauseClient(client_id, client_handler);
if (!had_active_client || controller->HasActiveClient())
return;
if (media::VideoCaptureDevice* device = entry->video_capture_device.get()) {
device_task_runner_->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureDevice::MaybeSuspend,
// Unretained is safe to use here because |device| would be
// null if it was scheduled for shutdown and destruction, and
// because this task is guaranteed to run before the task
// that destroys the |device|.
base::Unretained(device)));
}
}
void VideoCaptureManager::ResumeCaptureForClient(
media::VideoCaptureSessionId session_id,
const media::VideoCaptureParams& params,
VideoCaptureController* controller,
VideoCaptureControllerID client_id,
VideoCaptureControllerEventHandler* client_handler) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(controller);
DCHECK(client_handler);
DeviceEntry* entry = GetDeviceEntryByController(controller);
if (!entry)
NOTREACHED() << "Got Null entry while resuming capture";
const bool had_active_client = controller->HasActiveClient();
controller->ResumeClient(client_id, client_handler);
if (had_active_client || !controller->HasActiveClient())
return;
if (media::VideoCaptureDevice* device = entry->video_capture_device.get()) {
device_task_runner_->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureDevice::Resume,
// Unretained is safe to use here because |device| would be
// null if it was scheduled for shutdown and destruction, and
// because this task is guaranteed to run before the task
// that destroys the |device|.
base::Unretained(device)));
}
}
void VideoCaptureManager::RequestRefreshFrameForClient(
VideoCaptureController* controller) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (DeviceEntry* entry = GetDeviceEntryByController(controller)) {
if (media::VideoCaptureDevice* device = entry->video_capture_device.get()) {
device_task_runner_->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureDevice::RequestRefreshFrame,
// Unretained is safe to use here because |device| would be
// null if it was scheduled for shutdown and destruction,
// and because this task is guaranteed to run before the
// task that destroys the |device|.
base::Unretained(device)));
}
}
}
bool VideoCaptureManager::GetDeviceSupportedFormats(
media::VideoCaptureSessionId capture_session_id,
media::VideoCaptureFormats* supported_formats) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(supported_formats->empty());
SessionMap::iterator it = sessions_.find(capture_session_id);
if (it == sessions_.end())
return false;
DVLOG(1) << "GetDeviceSupportedFormats for device: " << it->second.name;
return GetDeviceSupportedFormats(it->second.id, supported_formats);
}
bool VideoCaptureManager::GetDeviceSupportedFormats(
const std::string& device_id,
media::VideoCaptureFormats* supported_formats) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(supported_formats->empty());
// Return all available formats of the device, regardless its started state.
DeviceInfo* existing_device = GetDeviceInfoById(device_id);
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());
SessionMap::iterator it = sessions_.find(capture_session_id);
if (it == sessions_.end())
return false;
DVLOG(1) << "GetDeviceFormatsInUse for device: " << it->second.name;
return GetDeviceFormatsInUse(it->second.type, it->second.id, formats_in_use);
}
bool VideoCaptureManager::GetDeviceFormatsInUse(
MediaStreamType stream_type,
const std::string& device_id,
media::VideoCaptureFormats* formats_in_use) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(formats_in_use->empty());
// Return the currently in-use format(s) of the device, if it's started.
DeviceEntry* device_in_use =
GetDeviceEntryByTypeAndId(stream_type, device_id);
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::SetDesktopCaptureWindowId(
media::VideoCaptureSessionId session_id,
gfx::NativeViewId window_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
VLOG(2) << "SetDesktopCaptureWindowId called for session " << session_id;
notification_window_ids_[session_id] = window_id;
MaybePostDesktopCaptureWindowId(session_id);
}
void VideoCaptureManager::MaybePostDesktopCaptureWindowId(
media::VideoCaptureSessionId session_id) {
SessionMap::iterator session_it = sessions_.find(session_id);
if (session_it == sessions_.end())
return;
DeviceEntry* const existing_device =
GetDeviceEntryByTypeAndId(session_it->second.type, session_it->second.id);
if (!existing_device) {
DVLOG(2) << "Failed to find an existing screen capture device.";
return;
}
if (!existing_device->video_capture_device) {
DVLOG(2) << "Screen capture device not yet started.";
return;
}
DCHECK_EQ(MEDIA_DESKTOP_VIDEO_CAPTURE, existing_device->stream_type);
DesktopMediaID id = DesktopMediaID::Parse(existing_device->id);
if (id.is_null())
return;
auto window_id_it = notification_window_ids_.find(session_id);
if (window_id_it == notification_window_ids_.end()) {
DVLOG(2) << "Notification window id not set for screen capture.";
return;
}
// Post |existing_device->video_capture_device| to the VideoCaptureDevice to
// the device_task_runner_. This is safe since the device is destroyed on the
// device_task_runner_.
device_task_runner_->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread,
this, existing_device->video_capture_device.get(),
window_id_it->second));
notification_window_ids_.erase(window_id_it);
}
void VideoCaptureManager::GetPhotoCapabilities(
int session_id,
VideoCaptureDevice::GetPhotoCapabilitiesCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
const DeviceEntry* entry = GetDeviceEntryBySessionId(session_id);
if (!entry)
return;
VideoCaptureDevice* device = entry->video_capture_device.get();
if (device) {
VideoCaptureManager::DoGetPhotoCapabilities(std::move(callback), device);
return;
}
// |entry| is known but |device| is nullptr, queue up a request for later.
photo_request_queue_.emplace_back(
session_id, base::Bind(&VideoCaptureManager::DoGetPhotoCapabilities, this,
base::Passed(&callback)));
}
void VideoCaptureManager::SetPhotoOptions(
int session_id,
media::mojom::PhotoSettingsPtr settings,
VideoCaptureDevice::SetPhotoOptionsCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
const DeviceEntry* entry = GetDeviceEntryBySessionId(session_id);
if (!entry)
return;
VideoCaptureDevice* device = entry->video_capture_device.get();
if (device) {
VideoCaptureManager::DoSetPhotoOptions(std::move(callback),
std::move(settings), device);
return;
}
// |entry| is known but |device| is nullptr, queue up a request for later.
photo_request_queue_.emplace_back(
session_id, base::Bind(&VideoCaptureManager::DoSetPhotoOptions, this,
base::Passed(&callback), base::Passed(&settings)));
}
void VideoCaptureManager::TakePhoto(
int session_id,
VideoCaptureDevice::TakePhotoCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
const DeviceEntry* entry = GetDeviceEntryBySessionId(session_id);
if (!entry)
return;
VideoCaptureDevice* device = entry->video_capture_device.get();
if (device) {
VideoCaptureManager::DoTakePhoto(std::move(callback), device);
return;
}
// |entry| is known but |device| is nullptr, queue up a request for later.
photo_request_queue_.emplace_back(
session_id, base::Bind(&VideoCaptureManager::DoTakePhoto, this,
base::Passed(&callback)));
}
void VideoCaptureManager::DoStopDeviceOnDeviceThread(
std::unique_ptr<VideoCaptureDevice> device) {
SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime");
DCHECK(IsOnDeviceThread());
device->StopAndDeAllocate();
DVLOG(3) << "DoStopDeviceOnDeviceThread";
}
void VideoCaptureManager::OnOpened(
MediaStreamType stream_type,
media::VideoCaptureSessionId capture_session_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!listener_) {
// Listener has been removed.
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_) {
// Listener has been removed.
return;
}
listener_->Closed(stream_type, capture_session_id);
}
void VideoCaptureManager::OnDevicesInfoEnumerated(
base::ElapsedTimer* timer,
const EnumerationCallback& client_callback,
const VideoCaptureManager::DeviceInfos& new_devices_info_cache) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
UMA_HISTOGRAM_TIMES(
"Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime",
timer->Elapsed());
devices_info_cache_ = new_devices_info_cache;
// Walk the |devices_info_cache_| and produce a
// media::VideoCaptureDeviceDescriptors for return purposes.
media::VideoCaptureDeviceDescriptors devices;
std::vector<std::tuple<media::VideoCaptureDeviceDescriptor,
media::VideoCaptureFormats>>
descriptors_and_formats;
for (const auto& it : devices_info_cache_) {
devices.emplace_back(it.descriptor);
descriptors_and_formats.emplace_back(it.descriptor, it.supported_formats);
}
if (!descriptors_and_formats.empty()) {
MediaInternals::GetInstance()->UpdateVideoCaptureDeviceCapabilities(
descriptors_and_formats);
}
client_callback.Run(devices);
}
bool VideoCaptureManager::IsOnDeviceThread() const {
return device_task_runner_->BelongsToCurrentThread();
}
void VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread(
base::Callback<void(const VideoCaptureManager::DeviceInfos&)>
on_devices_enumerated_callback,
const VideoCaptureManager::DeviceInfos& old_device_info_cache,
std::unique_ptr<VideoCaptureDeviceDescriptors> descriptors_snapshot) {
DCHECK(IsOnDeviceThread());
// 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.
VideoCaptureManager::DeviceInfos new_devices_info_cache;
for (const auto& device_info : old_device_info_cache) {
for (VideoCaptureDeviceDescriptors::iterator it =
descriptors_snapshot->begin();
it != descriptors_snapshot->end(); ++it) {
if (device_info.descriptor.device_id == it->device_id) {
new_devices_info_cache.push_back(device_info);
descriptors_snapshot->erase(it);
break;
}
}
}
// Get the device info for the new devices in |names_snapshot|.
for (const auto& it : *descriptors_snapshot) {
DeviceInfo device_info(it);
video_capture_device_factory_->GetSupportedFormats(
it, &device_info.supported_formats);
ConsolidateCaptureFormats(&device_info.supported_formats);
new_devices_info_cache.push_back(device_info);
}
on_devices_enumerated_callback.Run(new_devices_info_cache);
}
void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Removal of the last client stops the device.
if (!entry->video_capture_controller.HasActiveClient() &&
!entry->video_capture_controller.HasPausedClient()) {
DVLOG(1) << "VideoCaptureManager stopping device (type = "
<< entry->stream_type << ", id = " << entry->id << ")";
// The DeviceEntry is removed from |devices_| immediately. The controller is
// deleted immediately, and the device is freed asynchronously. After this
// point, subsequent requests to open this same device ID will create a new
// DeviceEntry, VideoCaptureController, and VideoCaptureDevice.
DoStopDevice(entry);
// TODO(mcasas): use a helper function https://crbug.com/624854.
DeviceEntries::iterator device_it =
std::find_if(devices_.begin(), devices_.end(),
[entry](const std::unique_ptr<DeviceEntry>& device_entry) {
return device_entry.get() == entry;
});
devices_.erase(device_it);
}
}
VideoCaptureManager::DeviceEntry*
VideoCaptureManager::GetDeviceEntryBySessionId(int session_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
SessionMap::const_iterator session_it = sessions_.find(session_id);
if (session_it == sessions_.end())
return nullptr;
return GetDeviceEntryByTypeAndId(session_it->second.type,
session_it->second.id);
}
VideoCaptureManager::DeviceEntry*
VideoCaptureManager::GetDeviceEntryByTypeAndId(
MediaStreamType type,
const std::string& device_id) const {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
for (const std::unique_ptr<DeviceEntry>& device : devices_) {
if (type == device->stream_type && device_id == device->id)
return device.get();
}
return nullptr;
}
VideoCaptureManager::DeviceEntry*
VideoCaptureManager::GetDeviceEntryByController(
const VideoCaptureController* controller) const {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Look up |controller| in |devices_|.
for (const std::unique_ptr<DeviceEntry>& device : devices_) {
if (&device->video_capture_controller == controller)
return device.get();
}
return nullptr;
}
VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetDeviceEntryBySerialId(
int serial_id) const {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
for (const std::unique_ptr<DeviceEntry>& device : devices_) {
if (device->serial_id == serial_id)
return device.get();
}
return nullptr;
}
VideoCaptureManager::DeviceInfo* VideoCaptureManager::GetDeviceInfoById(
const std::string& id) {
for (auto& it : devices_info_cache_) {
if (it.descriptor.device_id == id)
return &it;
}
return nullptr;
}
VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry(
media::VideoCaptureSessionId capture_session_id,
const media::VideoCaptureParams& params) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
SessionMap::iterator session_it = sessions_.find(capture_session_id);
if (session_it == sessions_.end())
return nullptr;
const MediaStreamDevice& device_info = session_it->second;
// Check if another session has already opened this device. If so, just
// use that opened device.
DeviceEntry* const existing_device =
GetDeviceEntryByTypeAndId(device_info.type, device_info.id);
if (existing_device) {
DCHECK_EQ(device_info.type, existing_device->stream_type);
return existing_device;
}
devices_.emplace_back(
new DeviceEntry(device_info.type, device_info.id, params));
return devices_.back().get();
}
void VideoCaptureManager::SetDesktopCaptureWindowIdOnDeviceThread(
media::VideoCaptureDevice* device,
gfx::NativeViewId window_id) {
DCHECK(IsOnDeviceThread());
#if defined(ENABLE_SCREEN_CAPTURE) && BUILDFLAG(ENABLE_WEBRTC) && !defined(OS_ANDROID)
DesktopCaptureDevice* desktop_device =
static_cast<DesktopCaptureDevice*>(device);
desktop_device->SetNotificationWindowId(window_id);
VLOG(2) << "Screen capture notification window passed on device thread.";
#endif
}
void VideoCaptureManager::DoGetPhotoCapabilities(
VideoCaptureDevice::GetPhotoCapabilitiesCallback callback,
VideoCaptureDevice* device) {
// Unretained() is safe to use here because |device| would be null if it
// was scheduled for shutdown and destruction, and because this task is
// guaranteed to run before the task that destroys the |device|.
device_task_runner_->PostTask(
FROM_HERE, base::Bind(&VideoCaptureDevice::GetPhotoCapabilities,
base::Unretained(device), base::Passed(&callback)));
}
void VideoCaptureManager::DoSetPhotoOptions(
VideoCaptureDevice::SetPhotoOptionsCallback callback,
media::mojom::PhotoSettingsPtr settings,
VideoCaptureDevice* device) {
// Unretained() is safe to use here because |device| would be null if it
// was scheduled for shutdown and destruction, and because this task is
// guaranteed to run before the task that destroys the |device|.
device_task_runner_->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureDevice::SetPhotoOptions, base::Unretained(device),
base::Passed(&settings), base::Passed(&callback)));
}
void VideoCaptureManager::DoTakePhoto(
VideoCaptureDevice::TakePhotoCallback callback,
VideoCaptureDevice* device) {
// Unretained() is safe to use here because |device| would be null if it
// was scheduled for shutdown and destruction, and because this task is
// guaranteed to run before the task that destroys the |device|.
device_task_runner_->PostTask(
FROM_HERE, base::Bind(&VideoCaptureDevice::TakePhoto,
base::Unretained(device), base::Passed(&callback)));
}
base::Optional<CameraCalibration> VideoCaptureManager::GetCameraCalibration(
const std::string& device_id) {
VideoCaptureManager::DeviceInfo* info = GetDeviceInfoById(device_id);
if (!info)
return base::Optional<CameraCalibration>();
return info->descriptor.camera_calibration;
}
#if defined(OS_ANDROID)
void VideoCaptureManager::OnApplicationStateChange(
base::android::ApplicationState state) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Only release/resume devices when the Application state changes from
// RUNNING->STOPPED->RUNNING.
if (state == base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES &&
!application_state_has_running_activities_) {
ResumeDevices();
application_state_has_running_activities_ = true;
} else if (state == base::android::APPLICATION_STATE_HAS_STOPPED_ACTIVITIES) {
ReleaseDevices();
application_state_has_running_activities_ = false;
}
}
void VideoCaptureManager::ReleaseDevices() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
for (auto& entry : devices_) {
// Do not stop Content Video Capture devices, e.g. Tab or Screen capture.
if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE)
continue;
DoStopDevice(entry.get());
}
}
void VideoCaptureManager::ResumeDevices() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
for (auto& entry : devices_) {
// Do not resume Content Video Capture devices, e.g. Tab or Screen capture.
// Do not try to restart already running devices.
if (entry->stream_type != MEDIA_DEVICE_VIDEO_CAPTURE ||
entry->video_capture_device)
continue;
// Check if the device is already in the start queue.
bool device_in_queue = false;
for (auto& request : device_start_queue_) {
if (request.serial_id() == entry->serial_id) {
device_in_queue = true;
break;
}
}
if (!device_in_queue) {
// Session ID is only valid for Screen capture. So we can fake it to
// resume video capture devices here.
QueueStartDevice(kFakeSessionId, entry.get(), entry->parameters);
}
}
}
#endif // defined(OS_ANDROID)
} // namespace content