blob: 13344097c5214682f7b52c146a966f2fae9735bc [file] [log] [blame]
// Copyright 2017 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/service_video_capture_device_launcher.h"
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/task/post_task.h"
#include "content/browser/renderer_host/media/service_launched_video_capture_device.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "media/capture/video/video_capture_device.h"
#include "media/capture/video/video_frame_receiver_on_task_runner.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "services/video_capture/public/cpp/receiver_media_to_mojo_adapter.h"
namespace content {
namespace {
void ConcludeLaunchDeviceWithSuccess(
video_capture::mojom::VideoSourcePtr source,
video_capture::mojom::PushVideoStreamSubscriptionPtr subscription,
base::OnceClosure connection_lost_cb,
VideoCaptureDeviceLauncher::Callbacks* callbacks,
base::OnceClosure done_cb) {
subscription->Activate();
callbacks->OnDeviceLaunched(
std::make_unique<ServiceLaunchedVideoCaptureDevice>(
std::move(source), std::move(subscription),
std::move(connection_lost_cb)));
std::move(done_cb).Run();
}
void ConcludeLaunchDeviceWithFailure(
bool abort_requested,
media::VideoCaptureError error,
scoped_refptr<RefCountedVideoSourceProvider> service_connection,
VideoCaptureDeviceLauncher::Callbacks* callbacks,
base::OnceClosure done_cb) {
service_connection.reset();
if (abort_requested)
callbacks->OnDeviceLaunchAborted();
else
callbacks->OnDeviceLaunchFailed(error);
std::move(done_cb).Run();
}
} // anonymous namespace
ServiceVideoCaptureDeviceLauncher::ServiceVideoCaptureDeviceLauncher(
ConnectToDeviceFactoryCB connect_to_source_provider_cb)
: connect_to_source_provider_cb_(std::move(connect_to_source_provider_cb)),
state_(State::READY_TO_LAUNCH),
callbacks_(nullptr) {}
ServiceVideoCaptureDeviceLauncher::~ServiceVideoCaptureDeviceLauncher() {
DCHECK(sequence_checker_.CalledOnValidSequence());
DCHECK(state_ == State::READY_TO_LAUNCH);
}
void ServiceVideoCaptureDeviceLauncher::LaunchDeviceAsync(
const std::string& device_id,
blink::mojom::MediaStreamType stream_type,
const media::VideoCaptureParams& params,
base::WeakPtr<media::VideoFrameReceiver> receiver,
base::OnceClosure connection_lost_cb,
Callbacks* callbacks,
base::OnceClosure done_cb) {
DCHECK(sequence_checker_.CalledOnValidSequence());
DCHECK(state_ == State::READY_TO_LAUNCH);
if (stream_type != blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
// This launcher only supports MediaStreamType::DEVICE_VIDEO_CAPTURE.
NOTREACHED();
return;
}
connect_to_source_provider_cb_.Run(&service_connection_);
if (!service_connection_->source_provider().is_bound()) {
// This can happen when the connection to the service was lost.
ConcludeLaunchDeviceWithFailure(
false,
media::VideoCaptureError::
kServiceDeviceLauncherLostConnectionToDeviceFactoryDuringDeviceStart,
std::move(service_connection_), callbacks, std::move(done_cb));
return;
}
if (receiver) {
std::ostringstream string_stream;
string_stream
<< "ServiceVideoCaptureDeviceLauncher::LaunchDeviceAsync: Asking "
"video capture service to create source for device_id = "
<< device_id;
receiver->OnLog(string_stream.str());
}
// Ownership of |done_cb| is moved to |this|. It is not sufficient to attach
// it to the callback passed to CreatePushSubscription(), because the
// connection to the service may get torn down before |callbacks| are
// invoked.
done_cb_ = std::move(done_cb);
callbacks_ = callbacks;
video_capture::mojom::VideoSourcePtr source;
service_connection_->source_provider()->GetVideoSource(
device_id, mojo::MakeRequest(&source));
auto receiver_adapter =
std::make_unique<video_capture::ReceiverMediaToMojoAdapter>(
std::make_unique<media::VideoFrameReceiverOnTaskRunner>(
std::move(receiver), base::CreateSingleThreadTaskRunnerWithTraits(
{BrowserThread::IO})));
video_capture::mojom::ReceiverPtr receiver_proxy;
mojo::MakeStrongBinding<video_capture::mojom::Receiver>(
std::move(receiver_adapter), mojo::MakeRequest(&receiver_proxy));
video_capture::mojom::PushVideoStreamSubscriptionPtr subscription;
// Create message pipe so that we can subsequently call
// subscription.set_connection_error_handler().
auto subscription_request = mojo::MakeRequest(&subscription);
// Use of Unretained(this) is safe, because |done_cb_| guarantees that |this|
// stays alive.
subscription.set_connection_error_handler(
base::BindOnce(&ServiceVideoCaptureDeviceLauncher::
OnConnectionLostWhileWaitingForCallback,
base::Unretained(this)));
// TODO(crbug.com/925083)
media::VideoCaptureParams new_params = params;
new_params.power_line_frequency =
media::VideoCaptureDevice::GetPowerLineFrequency(params);
// Note that we set |force_reopen_with_new_settings| to true in order
// to avoid the situation that a requests to open (or reopen) a device
// that has just been closed with different settings ends up getting the old
// settings, because from the perspective of the service, the device was still
// in use. In order to be able to set |force_reopen_with_new_settings|, we
// have to refactor code here and upstream to wait for a callback from the
// service indicating that the device closing is complete.
source->CreatePushSubscription(
std::move(receiver_proxy), new_params,
true /*force_reopen_with_new_settings*/, std::move(subscription_request),
base::BindOnce(
// Use of Unretained |this| is safe, because |done_cb_| guarantees
// that |this| stays alive.
&ServiceVideoCaptureDeviceLauncher::OnCreatePushSubscriptionCallback,
base::Unretained(this), std::move(source), std::move(subscription),
std::move(connection_lost_cb)));
state_ = State::DEVICE_START_IN_PROGRESS;
}
void ServiceVideoCaptureDeviceLauncher::AbortLaunch() {
DCHECK(sequence_checker_.CalledOnValidSequence());
if (state_ == State::DEVICE_START_IN_PROGRESS)
state_ = State::DEVICE_START_ABORTING;
}
void ServiceVideoCaptureDeviceLauncher::OnCreatePushSubscriptionCallback(
video_capture::mojom::VideoSourcePtr source,
video_capture::mojom::PushVideoStreamSubscriptionPtr subscription,
base::OnceClosure connection_lost_cb,
video_capture::mojom::CreatePushSubscriptionResultCode result_code,
const media::VideoCaptureParams& params) {
DCHECK(sequence_checker_.CalledOnValidSequence());
DCHECK(callbacks_);
DCHECK(done_cb_);
subscription.set_connection_error_handler(base::DoNothing());
const bool abort_requested = (state_ == State::DEVICE_START_ABORTING);
state_ = State::READY_TO_LAUNCH;
Callbacks* callbacks = callbacks_;
callbacks_ = nullptr;
switch (result_code) {
case video_capture::mojom::CreatePushSubscriptionResultCode::
kCreatedWithRequestedSettings: // Fall through.
case video_capture::mojom::CreatePushSubscriptionResultCode::
kCreatedWithDifferentSettings:
if (abort_requested) {
subscription.reset();
source.reset();
service_connection_.reset();
callbacks->OnDeviceLaunchAborted();
std::move(done_cb_).Run();
return;
}
ConcludeLaunchDeviceWithSuccess(
std::move(source), std::move(subscription),
std::move(connection_lost_cb), callbacks, std::move(done_cb_));
return;
case video_capture::mojom::CreatePushSubscriptionResultCode::kFailed:
ConcludeLaunchDeviceWithFailure(
abort_requested,
media::VideoCaptureError::
kServiceDeviceLauncherServiceRespondedWithDeviceNotFound,
std::move(service_connection_), callbacks, std::move(done_cb_));
return;
}
}
void ServiceVideoCaptureDeviceLauncher::
OnConnectionLostWhileWaitingForCallback() {
DCHECK(sequence_checker_.CalledOnValidSequence());
DCHECK(callbacks_);
const bool abort_requested = (state_ == State::DEVICE_START_ABORTING);
state_ = State::READY_TO_LAUNCH;
Callbacks* callbacks = callbacks_;
callbacks_ = nullptr;
ConcludeLaunchDeviceWithFailure(
abort_requested,
media::VideoCaptureError::
kServiceDeviceLauncherConnectionLostWhileWaitingForCallback,
std::move(service_connection_), callbacks, std::move(done_cb_));
}
} // namespace content