blob: c5a4d77a65284275b0d307eaf9dc54c2955fc2d0 [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 "third_party/blink/renderer/modules/mediastream/apply_constraints_processor.h"
#include <utility>
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "third_party/blink/public/mojom/mediastream/media_devices.mojom-blink.h"
#include "third_party/blink/public/platform/modules/mediastream/web_media_stream_track.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h"
#include "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.h"
#include "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_content.h"
#include "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.h"
#include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h"
#include "third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h"
#include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
namespace {
void RequestFailed(blink::ApplyConstraintsRequest* request,
const String& constraint,
const String& message) {
DCHECK(request);
request->RequestFailed(constraint, message);
}
void RequestSucceeded(blink::ApplyConstraintsRequest* request) {
DCHECK(request);
request->RequestSucceeded();
}
} // namespace
ApplyConstraintsProcessor::ApplyConstraintsProcessor(
MediaDevicesDispatcherCallback media_devices_dispatcher_cb,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: media_devices_dispatcher_cb_(std::move(media_devices_dispatcher_cb)),
task_runner_(std::move(task_runner)) {}
ApplyConstraintsProcessor::~ApplyConstraintsProcessor() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
void ApplyConstraintsProcessor::ProcessRequest(
blink::ApplyConstraintsRequest* request,
base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!request_completed_cb_);
DCHECK(!current_request_);
DCHECK(request->Track());
if (!request->Track()->Source()) {
CannotApplyConstraints(
"Track has no source. ApplyConstraints not possible.");
return;
}
request_completed_cb_ = std::move(callback);
current_request_ = request;
if (current_request_->Track()->Source()->GetType() ==
MediaStreamSource::kTypeVideo) {
ProcessVideoRequest();
} else {
DCHECK_EQ(current_request_->Track()->Source()->GetType(),
MediaStreamSource::kTypeAudio);
ProcessAudioRequest();
}
}
void ApplyConstraintsProcessor::ProcessAudioRequest() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(current_request_);
DCHECK_EQ(current_request_->Track()->Source()->GetType(),
MediaStreamSource::kTypeAudio);
DCHECK(request_completed_cb_);
blink::MediaStreamAudioSource* audio_source = GetCurrentAudioSource();
if (!audio_source) {
CannotApplyConstraints("The track is not connected to any source");
return;
}
blink::AudioCaptureSettings settings =
SelectSettingsAudioCapture(audio_source, current_request_->Constraints());
if (settings.HasValue()) {
ApplyConstraintsSucceeded();
} else {
ApplyConstraintsFailed(settings.failed_constraint_name());
}
}
void ApplyConstraintsProcessor::ProcessVideoRequest() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(current_request_);
DCHECK_EQ(current_request_->Track()->Source()->GetType(),
MediaStreamSource::kTypeVideo);
DCHECK(request_completed_cb_);
video_source_ = GetCurrentVideoSource();
if (!video_source_) {
CannotApplyConstraints("The track is not connected to any source");
return;
}
const blink::MediaStreamDevice& device_info = video_source_->device();
if (device_info.type == blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
ProcessVideoDeviceRequest();
} else {
FinalizeVideoRequest();
}
}
void ApplyConstraintsProcessor::ProcessVideoDeviceRequest() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (AbortIfVideoRequestStateInvalid())
return;
// TODO(guidou): Support restarting the source even if there is more than
// one track in the source. https://crbug.com/768205
if (video_source_->NumTracks() > 1U) {
FinalizeVideoRequest();
return;
}
// It might be necessary to restart the video source. Before doing that,
// check if the current format is the best format to satisfy the new
// constraints. If this is the case, then the source does not need to be
// restarted. To determine if the current format is the best, it is necessary
// to know all the formats potentially supported by the source.
GetMediaDevicesDispatcher()->GetAllVideoInputDeviceFormats(
String(video_source_->device().id.data()),
WTF::Bind(&ApplyConstraintsProcessor::MaybeStopSourceForRestart,
WrapWeakPersistent(this)));
}
void ApplyConstraintsProcessor::MaybeStopSourceForRestart(
const Vector<media::VideoCaptureFormat>& formats) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (AbortIfVideoRequestStateInvalid())
return;
blink::VideoCaptureSettings settings = SelectVideoSettings(formats);
if (!settings.HasValue()) {
ApplyConstraintsFailed(settings.failed_constraint_name());
return;
}
if (video_source_->GetCurrentFormat() == settings.Format()) {
video_source_->ReconfigureTrack(GetCurrentVideoTrack(),
settings.track_adapter_settings());
ApplyConstraintsSucceeded();
} else {
video_source_->StopForRestart(
WTF::Bind(&ApplyConstraintsProcessor::MaybeSourceStoppedForRestart,
WrapWeakPersistent(this)));
}
}
void ApplyConstraintsProcessor::MaybeSourceStoppedForRestart(
blink::MediaStreamVideoSource::RestartResult result) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (AbortIfVideoRequestStateInvalid())
return;
if (result == blink::MediaStreamVideoSource::RestartResult::IS_RUNNING) {
FinalizeVideoRequest();
return;
}
DCHECK_EQ(result, blink::MediaStreamVideoSource::RestartResult::IS_STOPPED);
GetMediaDevicesDispatcher()->GetAvailableVideoInputDeviceFormats(
String(video_source_->device().id.data()),
WTF::Bind(&ApplyConstraintsProcessor::FindNewFormatAndRestart,
WrapWeakPersistent(this)));
}
void ApplyConstraintsProcessor::FindNewFormatAndRestart(
const Vector<media::VideoCaptureFormat>& formats) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (AbortIfVideoRequestStateInvalid())
return;
blink::VideoCaptureSettings settings = SelectVideoSettings(formats);
DCHECK(video_source_->GetCurrentFormat());
// |settings| should have a value. If it does not due to some unexpected
// reason (perhaps a race with another renderer process), restart the source
// with the old format.
video_source_->Restart(
settings.HasValue() ? settings.Format()
: *video_source_->GetCurrentFormat(),
WTF::Bind(&ApplyConstraintsProcessor::MaybeSourceRestarted,
WrapWeakPersistent(this)));
}
void ApplyConstraintsProcessor::MaybeSourceRestarted(
blink::MediaStreamVideoSource::RestartResult result) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (AbortIfVideoRequestStateInvalid())
return;
if (result == blink::MediaStreamVideoSource::RestartResult::IS_RUNNING) {
FinalizeVideoRequest();
} else {
DCHECK_EQ(result, blink::MediaStreamVideoSource::RestartResult::IS_STOPPED);
CannotApplyConstraints("Source failed to restart");
video_source_->StopSource();
}
}
void ApplyConstraintsProcessor::FinalizeVideoRequest() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (AbortIfVideoRequestStateInvalid())
return;
media::VideoCaptureFormat format;
if (video_source_->GetCurrentFormat()) {
format = *video_source_->GetCurrentFormat();
} else {
format = GetCurrentVideoTrack()->GetComputedSourceFormat();
}
blink::VideoCaptureSettings settings = SelectVideoSettings({format});
if (settings.HasValue()) {
if (settings.min_frame_rate().has_value()) {
GetCurrentVideoTrack()->SetMinimumFrameRate(
settings.min_frame_rate().value());
}
video_source_->ReconfigureTrack(GetCurrentVideoTrack(),
settings.track_adapter_settings());
ApplyConstraintsSucceeded();
} else {
ApplyConstraintsFailed(settings.failed_constraint_name());
}
}
blink::VideoCaptureSettings ApplyConstraintsProcessor::SelectVideoSettings(
Vector<media::VideoCaptureFormat> formats) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(current_request_);
DCHECK_EQ(current_request_->Track()->Source()->GetType(),
MediaStreamSource::kTypeVideo);
DCHECK(request_completed_cb_);
DCHECK_GT(formats.size(), 0U);
blink::VideoInputDeviceCapabilities device_capabilities;
device_capabilities.device_id = current_request_->Track()->Source()->Id();
device_capabilities.group_id = current_request_->Track()->Source()->GroupId();
device_capabilities.facing_mode =
GetCurrentVideoSource()
? static_cast<mojom::blink::FacingMode>(
GetCurrentVideoSource()->device().video_facing)
: mojom::blink::FacingMode::NONE;
device_capabilities.formats = std::move(formats);
blink::VideoDeviceCaptureCapabilities video_capabilities;
video_capabilities.noise_reduction_capabilities.push_back(
GetCurrentVideoTrack()->noise_reduction());
video_capabilities.device_capabilities.push_back(
std::move(device_capabilities));
// Run SelectSettings using the track's current settings as the default
// values. However, initialize |settings| with the default values as a
// fallback in case GetSettings returns nothing and leaves |settings|
// unmodified.
MediaStreamTrackPlatform::Settings settings;
settings.width = blink::MediaStreamVideoSource::kDefaultWidth;
settings.height = blink::MediaStreamVideoSource::kDefaultHeight;
settings.frame_rate = blink::MediaStreamVideoSource::kDefaultFrameRate;
GetCurrentVideoTrack()->GetSettings(settings);
return SelectSettingsVideoDeviceCapture(
video_capabilities, current_request_->Constraints(), settings.width,
settings.height, settings.frame_rate);
}
blink::MediaStreamAudioSource*
ApplyConstraintsProcessor::GetCurrentAudioSource() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(current_request_);
DCHECK(current_request_->Track());
return blink::MediaStreamAudioSource::From(
current_request_->Track()->Source());
}
blink::MediaStreamVideoTrack*
ApplyConstraintsProcessor::GetCurrentVideoTrack() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
MediaStreamVideoTrack* track =
MediaStreamVideoTrack::From(current_request_->Track());
DCHECK(track);
return track;
}
blink::MediaStreamVideoSource*
ApplyConstraintsProcessor::GetCurrentVideoSource() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return GetCurrentVideoTrack()->source();
}
bool ApplyConstraintsProcessor::AbortIfVideoRequestStateInvalid() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(current_request_);
DCHECK_EQ(current_request_->Track()->Source()->GetType(),
MediaStreamSource::kTypeVideo);
DCHECK(request_completed_cb_);
if (GetCurrentVideoSource() != video_source_) {
CannotApplyConstraints(
"Track stopped or source changed. ApplyConstraints not possible.");
return true;
}
return false;
}
void ApplyConstraintsProcessor::ApplyConstraintsSucceeded() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
task_runner_->PostTask(
FROM_HERE, WTF::Bind(&ApplyConstraintsProcessor::CleanupRequest,
WrapWeakPersistent(this),
WTF::Bind(&RequestSucceeded,
WrapPersistent(current_request_.Get()))));
}
void ApplyConstraintsProcessor::ApplyConstraintsFailed(
const char* failed_constraint_name) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
task_runner_->PostTask(
FROM_HERE,
WTF::Bind(
&ApplyConstraintsProcessor::CleanupRequest, WrapWeakPersistent(this),
WTF::Bind(&RequestFailed, WrapPersistent(current_request_.Get()),
String(failed_constraint_name),
String("Cannot satisfy constraints"))));
}
void ApplyConstraintsProcessor::CannotApplyConstraints(const String& message) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
task_runner_->PostTask(
FROM_HERE,
WTF::Bind(
&ApplyConstraintsProcessor::CleanupRequest, WrapWeakPersistent(this),
WTF::Bind(&RequestFailed, WrapPersistent(current_request_.Get()),
String(), message)));
}
void ApplyConstraintsProcessor::CleanupRequest(
base::OnceClosure user_media_request_callback) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(current_request_);
DCHECK(request_completed_cb_);
std::move(request_completed_cb_).Run();
std::move(user_media_request_callback).Run();
current_request_ = nullptr;
video_source_ = nullptr;
}
blink::mojom::blink::MediaDevicesDispatcherHost*
ApplyConstraintsProcessor::GetMediaDevicesDispatcher() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return media_devices_dispatcher_cb_.Run();
}
} // namespace blink