#include "services/video_capture/video_source_impl.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/metrics/histogram_functions.h"
#include "media/capture/video/video_capture_device_client.h"
#include "services/video_capture/push_video_stream_subscription_impl.h"
namespace video_capture {
VideoSourceImpl::VideoSourceImpl(
DeviceFactory* device_factory,
const std::string& device_id,
base::RepeatingClosure on_last_binding_closed_cb)
: device_factory_(device_factory),
device_id_(device_id),
on_last_binding_closed_cb_(std::move(on_last_binding_closed_cb)),
device_status_(DeviceStatus::kNotStarted) {
receivers_.set_disconnect_handler(base::BindRepeating(
&VideoSourceImpl::OnClientDisconnected, base::Unretained(this)));
}
VideoSourceImpl::~VideoSourceImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
receivers_.set_disconnect_handler(base::DoNothing());
}
void VideoSourceImpl::AddToReceiverSet(
mojo::PendingReceiver<VideoSource> receiver) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
receivers_.Add(this, std::move(receiver));
}
void VideoSourceImpl::CreatePushSubscription(
mojo::PendingRemote<mojom::VideoFrameHandler> subscriber,
const media::VideoCaptureParams& requested_settings,
bool force_reopen_with_new_settings,
mojo::PendingReceiver<mojom::PushVideoStreamSubscription>
subscription_receiver,
CreatePushSubscriptionCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (device_status_ == DeviceStatus::kNotStarted) {
device_startup_start_time_ = base::TimeTicks::Now();
}
auto subscription = std::make_unique<PushVideoStreamSubscriptionImpl>(
std::move(subscription_receiver), std::move(subscriber),
requested_settings, std::move(callback), &broadcaster_);
auto* subscription_ptr = subscription.get();
subscription->SetOnClosedHandler(base::BindOnce(
&VideoSourceImpl::OnPushSubscriptionClosedOrDisconnectedOrDiscarded,
weak_factory_.GetWeakPtr(), subscription_ptr));
push_subscriptions_.insert(
std::make_pair(subscription_ptr, std::move(subscription)));
switch (device_status_) {
case DeviceStatus::kNotStarted:
StartDeviceWithSettings(requested_settings);
return;
case DeviceStatus::kStartingAsynchronously:
if (force_reopen_with_new_settings)
device_start_settings_ = requested_settings;
return;
case DeviceStatus::kStarted:
CHECK(device_);
if (!force_reopen_with_new_settings ||
requested_settings == device_start_settings_) {
subscription_ptr->OnDeviceStartSucceededWithSettings(
device_start_settings_, device_);
return;
}
restart_device_once_when_stop_complete_ = true;
device_start_settings_ = requested_settings;
StopDeviceAsynchronously();
return;
case DeviceStatus::kStoppingAsynchronously:
restart_device_once_when_stop_complete_ = true;
device_start_settings_ = requested_settings;
return;
}
}
void VideoSourceImpl::OnClientDisconnected() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!receivers_.empty()) {
return;
}
if (device_status_ != DeviceStatus::kStoppingAsynchronously &&
device_status_ != DeviceStatus::kNotStarted) {
device_factory_->StopDevice(device_id_);
}
on_last_binding_closed_cb_.Run();
}
void VideoSourceImpl::StartDeviceWithSettings(
const media::VideoCaptureParams& requested_settings) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto scoped_trace = ScopedCaptureTrace::CreateIfEnabled(
"VideoSourceImpl::StartDeviceWithSettings");
if (scoped_trace)
scoped_trace->AddStep("CreateDevice");
device_start_settings_ = requested_settings;
device_status_ = DeviceStatus::kStartingAsynchronously;
device_factory_->CreateDevice(
device_id_,
base::BindOnce(&VideoSourceImpl::OnCreateDeviceResponse,
weak_factory_.GetWeakPtr(), std::move(scoped_trace)));
}
void VideoSourceImpl::OnCreateDeviceResponse(
std::unique_ptr<ScopedCaptureTrace> scoped_trace,
DeviceInfo info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!device_);
if (info.result_code == media::VideoCaptureError::kNone) {
UmaHistogramTimes("Media.VideoCapture.CreateDeviceSuccessLatency",
base::TimeTicks::Now() - device_startup_start_time_);
device_ = info.device;
if (scoped_trace)
scoped_trace->AddStep("StartDevice");
info.device->StartInProcess(device_start_settings_,
broadcaster_.GetWeakPtr());
UmaHistogramTimes("Media.VideoCapture.StartSourceSuccessLatency",
base::TimeTicks::Now() - device_startup_start_time_);
device_status_ = DeviceStatus::kStarted;
if (push_subscriptions_.empty()) {
StopDeviceAsynchronously();
return;
}
for (auto& entry : push_subscriptions_) {
auto& subscription = entry.second;
subscription->OnDeviceStartSucceededWithSettings(device_start_settings_,
device_);
}
return;
}
for (auto& entry : push_subscriptions_) {
auto& subscription = entry.second;
subscription->OnDeviceStartFailed(info.result_code);
}
push_subscriptions_.clear();
device_status_ = DeviceStatus::kNotStarted;
return;
}
void VideoSourceImpl::OnPushSubscriptionClosedOrDisconnectedOrDiscarded(
PushVideoStreamSubscriptionImpl* subscription,
base::OnceClosure done_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto subscription_ownership = std::move(push_subscriptions_[subscription]);
push_subscriptions_.erase(subscription);
if (push_subscriptions_.empty()) {
switch (device_status_) {
case DeviceStatus::kNotStarted:
break;
case DeviceStatus::kStartingAsynchronously:
break;
case DeviceStatus::kStarted:
StopDeviceAsynchronously();
break;
case DeviceStatus::kStoppingAsynchronously:
break;
}
}
std::move(done_cb).Run();
}
void VideoSourceImpl::StopDeviceAsynchronously() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (restart_device_once_when_stop_complete_) {
broadcaster_.HideSourceRestartFromClients(base::BindOnce(
&VideoSourceImpl::OnStopDeviceComplete, base::Unretained(this)));
} else {
broadcaster_.SetOnStoppedHandler(base::BindOnce(
&VideoSourceImpl::OnStopDeviceComplete, base::Unretained(this)));
}
device_factory_->StopDevice(device_id_);
device_status_ = DeviceStatus::kStoppingAsynchronously;
}
void VideoSourceImpl::OnStopDeviceComplete() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
device_status_ = DeviceStatus::kNotStarted;
device_ = nullptr;
if (!restart_device_once_when_stop_complete_)
return;
restart_device_once_when_stop_complete_ = false;
StartDeviceWithSettings(device_start_settings_);
}
}