blob: 52a4824eae868024515bed8ace1a9eeb9b968eda [file] [log] [blame]
// Copyright 2014 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/renderer/media/stream/media_stream_constraints_util.h"
#include <algorithm>
#include <utility>
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/common/media_stream_request.h"
#include "content/renderer/media/stream/media_stream_constraints_util_sets.h"
#include "content/renderer/media/stream/media_stream_constraints_util_video_device.h"
#include "third_party/blink/public/platform/web_string.h"
namespace content {
namespace {
// TODO(c.padhi): Allow frame rates lower than 1Hz,
// see https://crbug.com/814131.
const float kMinDeviceCaptureFrameRate = 1.0f;
template <typename P, typename T>
bool ScanConstraintsForExactValue(const blink::WebMediaConstraints& constraints,
P picker,
T* value) {
if (constraints.IsNull())
return false;
const auto& the_field = constraints.Basic().*picker;
if (the_field.HasExact()) {
*value = the_field.Exact();
return true;
}
for (const auto& advanced_constraint : constraints.Advanced()) {
const auto& advanced_field = advanced_constraint.*picker;
if (advanced_field.HasExact()) {
*value = advanced_field.Exact();
return true;
}
}
return false;
}
template <typename P, typename T>
bool ScanConstraintsForMaxValue(const blink::WebMediaConstraints& constraints,
P picker,
T* value) {
if (constraints.IsNull())
return false;
const auto& the_field = constraints.Basic().*picker;
if (the_field.HasMax()) {
*value = the_field.Max();
return true;
}
if (the_field.HasExact()) {
*value = the_field.Exact();
return true;
}
for (const auto& advanced_constraint : constraints.Advanced()) {
const auto& advanced_field = advanced_constraint.*picker;
if (advanced_field.HasMax()) {
*value = advanced_field.Max();
return true;
}
if (advanced_field.HasExact()) {
*value = advanced_field.Exact();
return true;
}
}
return false;
}
template <typename P, typename T>
bool ScanConstraintsForMinValue(const blink::WebMediaConstraints& constraints,
P picker,
T* value) {
if (constraints.IsNull())
return false;
const auto& the_field = constraints.Basic().*picker;
if (the_field.HasMin()) {
*value = the_field.Min();
return true;
}
if (the_field.HasExact()) {
*value = the_field.Exact();
return true;
}
for (const auto& advanced_constraint : constraints.Advanced()) {
const auto& advanced_field = advanced_constraint.*picker;
if (advanced_field.HasMin()) {
*value = advanced_field.Min();
return true;
}
if (advanced_field.HasExact()) {
*value = advanced_field.Exact();
return true;
}
}
return false;
}
} // namespace
VideoCaptureSettings::VideoCaptureSettings() : VideoCaptureSettings("") {}
VideoCaptureSettings::VideoCaptureSettings(const char* failed_constraint_name)
: failed_constraint_name_(failed_constraint_name) {
DCHECK(failed_constraint_name_);
}
VideoCaptureSettings::VideoCaptureSettings(
std::string device_id,
media::VideoCaptureParams capture_params,
base::Optional<bool> noise_reduction,
const VideoTrackAdapterSettings& track_adapter_settings,
base::Optional<double> min_frame_rate,
base::Optional<double> max_frame_rate)
: failed_constraint_name_(nullptr),
device_id_(std::move(device_id)),
capture_params_(capture_params),
noise_reduction_(noise_reduction),
track_adapter_settings_(track_adapter_settings),
min_frame_rate_(min_frame_rate),
max_frame_rate_(max_frame_rate) {
DCHECK(!min_frame_rate ||
*min_frame_rate_ <= capture_params.requested_format.frame_rate);
DCHECK_LE(track_adapter_settings.max_width,
capture_params.requested_format.frame_size.width());
DCHECK_LE(track_adapter_settings.max_height,
capture_params.requested_format.frame_size.height());
}
VideoCaptureSettings::VideoCaptureSettings(const VideoCaptureSettings& other) =
default;
VideoCaptureSettings::VideoCaptureSettings(VideoCaptureSettings&& other) =
default;
VideoCaptureSettings::~VideoCaptureSettings() = default;
VideoCaptureSettings& VideoCaptureSettings::operator=(
const VideoCaptureSettings& other) = default;
VideoCaptureSettings& VideoCaptureSettings::operator=(
VideoCaptureSettings&& other) = default;
AudioCaptureSettings::AudioCaptureSettings() : AudioCaptureSettings("") {}
AudioCaptureSettings::AudioCaptureSettings(const char* failed_constraint_name)
: failed_constraint_name_(failed_constraint_name) {
DCHECK(failed_constraint_name_);
}
AudioCaptureSettings::AudioCaptureSettings(
std::string device_id,
const media::AudioParameters& audio_parameters,
bool enable_hotword,
bool disable_local_echo,
bool enable_automatic_output_device_selection,
const AudioProcessingProperties& audio_processing_properties)
: failed_constraint_name_(nullptr),
device_id_(std::move(device_id)),
audio_parameters_(audio_parameters),
hotword_enabled_(enable_hotword),
disable_local_echo_(disable_local_echo),
render_to_associated_sink_(enable_automatic_output_device_selection),
audio_processing_properties_(audio_processing_properties) {}
AudioCaptureSettings::AudioCaptureSettings(const AudioCaptureSettings& other) =
default;
AudioCaptureSettings& AudioCaptureSettings::operator=(
const AudioCaptureSettings& other) = default;
AudioCaptureSettings::AudioCaptureSettings(AudioCaptureSettings&& other) =
default;
AudioCaptureSettings& AudioCaptureSettings::operator=(
AudioCaptureSettings&& other) = default;
bool GetConstraintValueAsBoolean(
const blink::WebMediaConstraints& constraints,
const blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*picker,
bool* value) {
return ScanConstraintsForExactValue(constraints, picker, value);
}
bool GetConstraintValueAsInteger(
const blink::WebMediaConstraints& constraints,
const blink::LongConstraint blink::WebMediaTrackConstraintSet::*picker,
int* value) {
return ScanConstraintsForExactValue(constraints, picker, value);
}
bool GetConstraintMinAsInteger(
const blink::WebMediaConstraints& constraints,
const blink::LongConstraint blink::WebMediaTrackConstraintSet::*picker,
int* value) {
return ScanConstraintsForMinValue(constraints, picker, value);
}
bool GetConstraintMaxAsInteger(
const blink::WebMediaConstraints& constraints,
const blink::LongConstraint blink::WebMediaTrackConstraintSet::*picker,
int* value) {
return ScanConstraintsForMaxValue(constraints, picker, value);
}
bool GetConstraintValueAsDouble(
const blink::WebMediaConstraints& constraints,
const blink::DoubleConstraint blink::WebMediaTrackConstraintSet::*picker,
double* value) {
return ScanConstraintsForExactValue(constraints, picker, value);
}
bool GetConstraintMinAsDouble(
const blink::WebMediaConstraints& constraints,
const blink::DoubleConstraint blink::WebMediaTrackConstraintSet::*picker,
double* value) {
return ScanConstraintsForMinValue(constraints, picker, value);
}
bool GetConstraintMaxAsDouble(
const blink::WebMediaConstraints& constraints,
const blink::DoubleConstraint blink::WebMediaTrackConstraintSet::*picker,
double* value) {
return ScanConstraintsForMaxValue(constraints, picker, value);
}
bool GetConstraintValueAsString(
const blink::WebMediaConstraints& constraints,
const blink::StringConstraint blink::WebMediaTrackConstraintSet::*picker,
std::string* value) {
blink::WebVector<blink::WebString> return_value;
if (ScanConstraintsForExactValue(constraints, picker, &return_value)) {
*value = return_value[0].Utf8();
return true;
}
return false;
}
rtc::Optional<bool> ConstraintToOptional(
const blink::WebMediaConstraints& constraints,
const blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*picker) {
bool value;
if (GetConstraintValueAsBoolean(constraints, picker, &value)) {
return rtc::Optional<bool>(value);
}
return rtc::Optional<bool>();
}
std::string GetMediaStreamSource(
const blink::WebMediaConstraints& constraints) {
std::string source;
if (constraints.Basic().media_stream_source.HasIdeal() &&
constraints.Basic().media_stream_source.Ideal().size() > 0) {
source = constraints.Basic().media_stream_source.Ideal()[0].Utf8();
}
if (constraints.Basic().media_stream_source.HasExact() &&
constraints.Basic().media_stream_source.Exact().size() > 0) {
source = constraints.Basic().media_stream_source.Exact()[0].Utf8();
}
return source;
}
bool IsDeviceCapture(const blink::WebMediaConstraints& constraints) {
return GetMediaStreamSource(constraints).empty();
}
VideoTrackAdapterSettings SelectVideoTrackAdapterSettings(
const blink::WebMediaTrackConstraintSet& basic_constraint_set,
const ResolutionSet& resolution_set,
const NumericRangeSet<double>& frame_rate_set,
const media::VideoCaptureFormat& source_format) {
ResolutionSet::Point resolution = resolution_set.SelectClosestPointToIdeal(
basic_constraint_set, source_format.frame_size.height(),
source_format.frame_size.width());
int track_max_height = static_cast<int>(std::round(resolution.height()));
int track_max_width = static_cast<int>(std::round(resolution.width()));
double track_min_aspect_ratio =
std::max(resolution_set.min_aspect_ratio(),
static_cast<double>(resolution_set.min_width()) /
static_cast<double>(resolution_set.max_height()));
double track_max_aspect_ratio =
std::min(resolution_set.max_aspect_ratio(),
static_cast<double>(resolution_set.max_width()) /
static_cast<double>(resolution_set.min_height()));
// VideoTrackAdapter uses a frame rate of 0.0 to disable frame-rate
// adjustment.
double track_max_frame_rate = frame_rate_set.Max().value_or(0.0);
if (basic_constraint_set.frame_rate.HasIdeal()) {
track_max_frame_rate = basic_constraint_set.frame_rate.Ideal();
if (frame_rate_set.Min() && track_max_frame_rate < *frame_rate_set.Min())
track_max_frame_rate = *frame_rate_set.Min();
if (frame_rate_set.Max() && track_max_frame_rate > *frame_rate_set.Max())
track_max_frame_rate = *frame_rate_set.Max();
}
// Disable frame-rate adjustment if the requested rate is greater than the
// source rate.
if (track_max_frame_rate >= source_format.frame_rate)
track_max_frame_rate = 0.0;
return VideoTrackAdapterSettings(
track_max_width, track_max_height, track_min_aspect_ratio,
track_max_aspect_ratio, track_max_frame_rate);
}
double NumericConstraintFitnessDistance(double value1, double value2) {
if (std::fabs(value1 - value2) <= blink::DoubleConstraint::kConstraintEpsilon)
return 0.0;
return std::fabs(value1 - value2) /
std::max(std::fabs(value1), std::fabs(value2));
}
double StringConstraintFitnessDistance(
const blink::WebString& value,
const blink::StringConstraint& constraint) {
if (!constraint.HasIdeal())
return 0.0;
for (auto& ideal_value : constraint.Ideal()) {
if (value == ideal_value)
return 0.0;
}
return 1.0;
}
blink::WebMediaStreamSource::Capabilities ComputeCapabilitiesForVideoSource(
const blink::WebString& device_id,
const media::VideoCaptureFormats& formats,
media::VideoFacingMode facing_mode,
bool is_device_capture,
const base::Optional<std::string>& group_id) {
blink::WebMediaStreamSource::Capabilities capabilities;
capabilities.device_id = std::move(device_id);
if (is_device_capture) {
capabilities.facing_mode = ToWebFacingMode(facing_mode);
if (group_id)
capabilities.group_id = blink::WebString::FromUTF8(*group_id);
}
if (!formats.empty()) {
int max_width = 1;
int max_height = 1;
float min_frame_rate =
is_device_capture ? kMinDeviceCaptureFrameRate : 0.0f;
float max_frame_rate = min_frame_rate;
for (const auto& format : formats) {
max_width = std::max(max_width, format.frame_size.width());
max_height = std::max(max_height, format.frame_size.height());
max_frame_rate = std::max(max_frame_rate, format.frame_rate);
}
capabilities.width = {1, max_width};
capabilities.height = {1, max_height};
capabilities.aspect_ratio = {1.0 / max_height,
static_cast<double>(max_width)};
capabilities.frame_rate = {min_frame_rate, max_frame_rate};
}
return capabilities;
}
} // namespace content