Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
3
TMessagesProj/jni/voip/webrtc/call/adaptation/OWNERS
Normal file
3
TMessagesProj/jni/voip/webrtc/call/adaptation/OWNERS
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
eshr@webrtc.org
|
||||
hbos@webrtc.org
|
||||
ilnik@webrtc.org
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "call/adaptation/adaptation_constraint.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
AdaptationConstraint::~AdaptationConstraint() {}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef CALL_ADAPTATION_ADAPTATION_CONSTRAINT_H_
|
||||
#define CALL_ADAPTATION_ADAPTATION_CONSTRAINT_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "api/adaptation/resource.h"
|
||||
#include "call/adaptation/video_source_restrictions.h"
|
||||
#include "call/adaptation/video_stream_input_state.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Adaptation constraints have the ability to prevent applying a proposed
|
||||
// adaptation (expressed as restrictions before/after adaptation).
|
||||
class AdaptationConstraint {
|
||||
public:
|
||||
virtual ~AdaptationConstraint();
|
||||
|
||||
virtual std::string Name() const = 0;
|
||||
|
||||
// TODO(https://crbug.com/webrtc/11172): When we have multi-stream adaptation
|
||||
// support, this interface needs to indicate which stream the adaptation
|
||||
// applies to.
|
||||
virtual bool IsAdaptationUpAllowed(
|
||||
const VideoStreamInputState& input_state,
|
||||
const VideoSourceRestrictions& restrictions_before,
|
||||
const VideoSourceRestrictions& restrictions_after) const = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // CALL_ADAPTATION_ADAPTATION_CONSTRAINT_H_
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "call/adaptation/broadcast_resource_listener.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/make_ref_counted.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// The AdapterResource redirects resource usage measurements from its parent to
|
||||
// a single ResourceListener.
|
||||
class BroadcastResourceListener::AdapterResource : public Resource {
|
||||
public:
|
||||
explicit AdapterResource(absl::string_view name) : name_(std::move(name)) {}
|
||||
~AdapterResource() override { RTC_DCHECK(!listener_); }
|
||||
|
||||
// The parent is letting us know we have a usage neasurement.
|
||||
void OnResourceUsageStateMeasured(ResourceUsageState usage_state) {
|
||||
MutexLock lock(&lock_);
|
||||
if (!listener_)
|
||||
return;
|
||||
listener_->OnResourceUsageStateMeasured(rtc::scoped_refptr<Resource>(this),
|
||||
usage_state);
|
||||
}
|
||||
|
||||
// Resource implementation.
|
||||
std::string Name() const override { return name_; }
|
||||
void SetResourceListener(ResourceListener* listener) override {
|
||||
MutexLock lock(&lock_);
|
||||
RTC_DCHECK(!listener_ || !listener);
|
||||
listener_ = listener;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string name_;
|
||||
Mutex lock_;
|
||||
ResourceListener* listener_ RTC_GUARDED_BY(lock_) = nullptr;
|
||||
};
|
||||
|
||||
BroadcastResourceListener::BroadcastResourceListener(
|
||||
rtc::scoped_refptr<Resource> source_resource)
|
||||
: source_resource_(source_resource), is_listening_(false) {
|
||||
RTC_DCHECK(source_resource_);
|
||||
}
|
||||
|
||||
BroadcastResourceListener::~BroadcastResourceListener() {
|
||||
RTC_DCHECK(!is_listening_);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<Resource> BroadcastResourceListener::SourceResource() const {
|
||||
return source_resource_;
|
||||
}
|
||||
|
||||
void BroadcastResourceListener::StartListening() {
|
||||
MutexLock lock(&lock_);
|
||||
RTC_DCHECK(!is_listening_);
|
||||
source_resource_->SetResourceListener(this);
|
||||
is_listening_ = true;
|
||||
}
|
||||
|
||||
void BroadcastResourceListener::StopListening() {
|
||||
MutexLock lock(&lock_);
|
||||
RTC_DCHECK(is_listening_);
|
||||
RTC_DCHECK(adapters_.empty());
|
||||
source_resource_->SetResourceListener(nullptr);
|
||||
is_listening_ = false;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<Resource>
|
||||
BroadcastResourceListener::CreateAdapterResource() {
|
||||
MutexLock lock(&lock_);
|
||||
RTC_DCHECK(is_listening_);
|
||||
rtc::scoped_refptr<AdapterResource> adapter =
|
||||
rtc::make_ref_counted<AdapterResource>(source_resource_->Name() +
|
||||
"Adapter");
|
||||
adapters_.push_back(adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
void BroadcastResourceListener::RemoveAdapterResource(
|
||||
rtc::scoped_refptr<Resource> resource) {
|
||||
MutexLock lock(&lock_);
|
||||
auto it = std::find(adapters_.begin(), adapters_.end(), resource);
|
||||
RTC_DCHECK(it != adapters_.end());
|
||||
adapters_.erase(it);
|
||||
}
|
||||
|
||||
std::vector<rtc::scoped_refptr<Resource>>
|
||||
BroadcastResourceListener::GetAdapterResources() {
|
||||
std::vector<rtc::scoped_refptr<Resource>> resources;
|
||||
MutexLock lock(&lock_);
|
||||
for (const auto& adapter : adapters_) {
|
||||
resources.push_back(adapter);
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
void BroadcastResourceListener::OnResourceUsageStateMeasured(
|
||||
rtc::scoped_refptr<Resource> resource,
|
||||
ResourceUsageState usage_state) {
|
||||
RTC_DCHECK_EQ(resource, source_resource_);
|
||||
MutexLock lock(&lock_);
|
||||
for (const auto& adapter : adapters_) {
|
||||
adapter->OnResourceUsageStateMeasured(usage_state);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef CALL_ADAPTATION_BROADCAST_RESOURCE_LISTENER_H_
|
||||
#define CALL_ADAPTATION_BROADCAST_RESOURCE_LISTENER_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "api/adaptation/resource.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Responsible for forwarding 1 resource usage measurement to N listeners by
|
||||
// creating N "adapter" resources.
|
||||
//
|
||||
// Example:
|
||||
// If we have ResourceA, ResourceListenerX and ResourceListenerY we can create a
|
||||
// BroadcastResourceListener that listens to ResourceA, use CreateAdapter() to
|
||||
// spawn adapter resources ResourceX and ResourceY and let ResourceListenerX
|
||||
// listen to ResourceX and ResourceListenerY listen to ResourceY. When ResourceA
|
||||
// makes a measurement it will be echoed by both ResourceX and ResourceY.
|
||||
//
|
||||
// TODO(https://crbug.com/webrtc/11565): When the ResourceAdaptationProcessor is
|
||||
// moved to call there will only be one ResourceAdaptationProcessor that needs
|
||||
// to listen to the injected resources. When this is the case, delete this class
|
||||
// and DCHECK that a Resource's listener is never overwritten.
|
||||
class BroadcastResourceListener : public ResourceListener {
|
||||
public:
|
||||
explicit BroadcastResourceListener(
|
||||
rtc::scoped_refptr<Resource> source_resource);
|
||||
~BroadcastResourceListener() override;
|
||||
|
||||
rtc::scoped_refptr<Resource> SourceResource() const;
|
||||
void StartListening();
|
||||
void StopListening();
|
||||
|
||||
// Creates a Resource that redirects any resource usage measurements that
|
||||
// BroadcastResourceListener receives to its listener.
|
||||
rtc::scoped_refptr<Resource> CreateAdapterResource();
|
||||
|
||||
// Unregister the adapter from the BroadcastResourceListener; it will no
|
||||
// longer receive resource usage measurement and will no longer be referenced.
|
||||
// Use this to prevent memory leaks of old adapters.
|
||||
void RemoveAdapterResource(rtc::scoped_refptr<Resource> resource);
|
||||
std::vector<rtc::scoped_refptr<Resource>> GetAdapterResources();
|
||||
|
||||
// ResourceListener implementation.
|
||||
void OnResourceUsageStateMeasured(rtc::scoped_refptr<Resource> resource,
|
||||
ResourceUsageState usage_state) override;
|
||||
|
||||
private:
|
||||
class AdapterResource;
|
||||
friend class AdapterResource;
|
||||
|
||||
const rtc::scoped_refptr<Resource> source_resource_;
|
||||
Mutex lock_;
|
||||
bool is_listening_ RTC_GUARDED_BY(lock_);
|
||||
// The AdapterResource unregisters itself prior to destruction, guaranteeing
|
||||
// that these pointers are safe to use.
|
||||
std::vector<rtc::scoped_refptr<AdapterResource>> adapters_
|
||||
RTC_GUARDED_BY(lock_);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // CALL_ADAPTATION_BROADCAST_RESOURCE_LISTENER_H_
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "call/adaptation/degradation_preference_provider.h"
|
||||
|
||||
webrtc::DegradationPreferenceProvider::~DegradationPreferenceProvider() =
|
||||
default;
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef CALL_ADAPTATION_DEGRADATION_PREFERENCE_PROVIDER_H_
|
||||
#define CALL_ADAPTATION_DEGRADATION_PREFERENCE_PROVIDER_H_
|
||||
|
||||
#include "api/rtp_parameters.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class DegradationPreferenceProvider {
|
||||
public:
|
||||
virtual ~DegradationPreferenceProvider();
|
||||
|
||||
virtual DegradationPreference degradation_preference() const = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // CALL_ADAPTATION_DEGRADATION_PREFERENCE_PROVIDER_H_
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "call/adaptation/encoder_settings.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
EncoderSettings::EncoderSettings(VideoEncoder::EncoderInfo encoder_info,
|
||||
VideoEncoderConfig encoder_config,
|
||||
VideoCodec video_codec)
|
||||
: encoder_info_(std::move(encoder_info)),
|
||||
encoder_config_(std::move(encoder_config)),
|
||||
video_codec_(std::move(video_codec)) {}
|
||||
|
||||
EncoderSettings::EncoderSettings(const EncoderSettings& other)
|
||||
: encoder_info_(other.encoder_info_),
|
||||
encoder_config_(other.encoder_config_.Copy()),
|
||||
video_codec_(other.video_codec_) {}
|
||||
|
||||
EncoderSettings& EncoderSettings::operator=(const EncoderSettings& other) {
|
||||
encoder_info_ = other.encoder_info_;
|
||||
encoder_config_ = other.encoder_config_.Copy();
|
||||
video_codec_ = other.video_codec_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const VideoEncoder::EncoderInfo& EncoderSettings::encoder_info() const {
|
||||
return encoder_info_;
|
||||
}
|
||||
|
||||
const VideoEncoderConfig& EncoderSettings::encoder_config() const {
|
||||
return encoder_config_;
|
||||
}
|
||||
|
||||
const VideoCodec& EncoderSettings::video_codec() const {
|
||||
return video_codec_;
|
||||
}
|
||||
|
||||
VideoCodecType GetVideoCodecTypeOrGeneric(
|
||||
const absl::optional<EncoderSettings>& settings) {
|
||||
return settings.has_value() ? settings->encoder_config().codec_type
|
||||
: kVideoCodecGeneric;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef CALL_ADAPTATION_ENCODER_SETTINGS_H_
|
||||
#define CALL_ADAPTATION_ENCODER_SETTINGS_H_
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/video_codecs/video_codec.h"
|
||||
#include "api/video_codecs/video_encoder.h"
|
||||
#include "video/config/video_encoder_config.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Information about an encoder available when reconfiguring the encoder.
|
||||
class EncoderSettings {
|
||||
public:
|
||||
EncoderSettings(VideoEncoder::EncoderInfo encoder_info,
|
||||
VideoEncoderConfig encoder_config,
|
||||
VideoCodec video_codec);
|
||||
EncoderSettings(const EncoderSettings& other);
|
||||
EncoderSettings& operator=(const EncoderSettings& other);
|
||||
|
||||
// Encoder capabilities, implementation info, etc.
|
||||
const VideoEncoder::EncoderInfo& encoder_info() const;
|
||||
// Configuration parameters, ultimately coming from the API and negotiation.
|
||||
const VideoEncoderConfig& encoder_config() const;
|
||||
// Lower level config, heavily based on the VideoEncoderConfig.
|
||||
const VideoCodec& video_codec() const;
|
||||
|
||||
private:
|
||||
VideoEncoder::EncoderInfo encoder_info_;
|
||||
VideoEncoderConfig encoder_config_;
|
||||
VideoCodec video_codec_;
|
||||
};
|
||||
|
||||
VideoCodecType GetVideoCodecTypeOrGeneric(
|
||||
const absl::optional<EncoderSettings>& settings);
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // CALL_ADAPTATION_ENCODER_SETTINGS_H_
|
||||
|
|
@ -0,0 +1,378 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "call/adaptation/resource_adaptation_processor.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/video/video_adaptation_counters.h"
|
||||
#include "call/adaptation/video_stream_adapter.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
ResourceAdaptationProcessor::ResourceListenerDelegate::ResourceListenerDelegate(
|
||||
ResourceAdaptationProcessor* processor)
|
||||
: task_queue_(TaskQueueBase::Current()), processor_(processor) {
|
||||
RTC_DCHECK(task_queue_);
|
||||
}
|
||||
|
||||
void ResourceAdaptationProcessor::ResourceListenerDelegate::
|
||||
OnProcessorDestroyed() {
|
||||
RTC_DCHECK_RUN_ON(task_queue_);
|
||||
processor_ = nullptr;
|
||||
}
|
||||
|
||||
void ResourceAdaptationProcessor::ResourceListenerDelegate::
|
||||
OnResourceUsageStateMeasured(rtc::scoped_refptr<Resource> resource,
|
||||
ResourceUsageState usage_state) {
|
||||
if (!task_queue_->IsCurrent()) {
|
||||
task_queue_->PostTask(
|
||||
[this_ref = rtc::scoped_refptr<ResourceListenerDelegate>(this),
|
||||
resource, usage_state] {
|
||||
this_ref->OnResourceUsageStateMeasured(resource, usage_state);
|
||||
});
|
||||
return;
|
||||
}
|
||||
RTC_DCHECK_RUN_ON(task_queue_);
|
||||
if (processor_) {
|
||||
processor_->OnResourceUsageStateMeasured(resource, usage_state);
|
||||
}
|
||||
}
|
||||
|
||||
ResourceAdaptationProcessor::MitigationResultAndLogMessage::
|
||||
MitigationResultAndLogMessage()
|
||||
: result(MitigationResult::kAdaptationApplied), message() {}
|
||||
|
||||
ResourceAdaptationProcessor::MitigationResultAndLogMessage::
|
||||
MitigationResultAndLogMessage(MitigationResult result,
|
||||
absl::string_view message)
|
||||
: result(result), message(message) {}
|
||||
|
||||
ResourceAdaptationProcessor::ResourceAdaptationProcessor(
|
||||
VideoStreamAdapter* stream_adapter)
|
||||
: task_queue_(TaskQueueBase::Current()),
|
||||
resource_listener_delegate_(
|
||||
rtc::make_ref_counted<ResourceListenerDelegate>(this)),
|
||||
resources_(),
|
||||
stream_adapter_(stream_adapter),
|
||||
last_reported_source_restrictions_(),
|
||||
previous_mitigation_results_() {
|
||||
RTC_DCHECK(task_queue_);
|
||||
stream_adapter_->AddRestrictionsListener(this);
|
||||
}
|
||||
|
||||
ResourceAdaptationProcessor::~ResourceAdaptationProcessor() {
|
||||
RTC_DCHECK_RUN_ON(task_queue_);
|
||||
RTC_DCHECK(resources_.empty())
|
||||
<< "There are resource(s) attached to a ResourceAdaptationProcessor "
|
||||
<< "being destroyed.";
|
||||
stream_adapter_->RemoveRestrictionsListener(this);
|
||||
resource_listener_delegate_->OnProcessorDestroyed();
|
||||
}
|
||||
|
||||
void ResourceAdaptationProcessor::AddResourceLimitationsListener(
|
||||
ResourceLimitationsListener* limitations_listener) {
|
||||
RTC_DCHECK_RUN_ON(task_queue_);
|
||||
RTC_DCHECK(std::find(resource_limitations_listeners_.begin(),
|
||||
resource_limitations_listeners_.end(),
|
||||
limitations_listener) ==
|
||||
resource_limitations_listeners_.end());
|
||||
resource_limitations_listeners_.push_back(limitations_listener);
|
||||
}
|
||||
|
||||
void ResourceAdaptationProcessor::RemoveResourceLimitationsListener(
|
||||
ResourceLimitationsListener* limitations_listener) {
|
||||
RTC_DCHECK_RUN_ON(task_queue_);
|
||||
auto it =
|
||||
std::find(resource_limitations_listeners_.begin(),
|
||||
resource_limitations_listeners_.end(), limitations_listener);
|
||||
RTC_DCHECK(it != resource_limitations_listeners_.end());
|
||||
resource_limitations_listeners_.erase(it);
|
||||
}
|
||||
|
||||
void ResourceAdaptationProcessor::AddResource(
|
||||
rtc::scoped_refptr<Resource> resource) {
|
||||
RTC_DCHECK(resource);
|
||||
{
|
||||
MutexLock crit(&resources_lock_);
|
||||
RTC_DCHECK(absl::c_find(resources_, resource) == resources_.end())
|
||||
<< "Resource \"" << resource->Name() << "\" was already registered.";
|
||||
resources_.push_back(resource);
|
||||
}
|
||||
resource->SetResourceListener(resource_listener_delegate_.get());
|
||||
RTC_LOG(LS_INFO) << "Registered resource \"" << resource->Name() << "\".";
|
||||
}
|
||||
|
||||
std::vector<rtc::scoped_refptr<Resource>>
|
||||
ResourceAdaptationProcessor::GetResources() const {
|
||||
MutexLock crit(&resources_lock_);
|
||||
return resources_;
|
||||
}
|
||||
|
||||
void ResourceAdaptationProcessor::RemoveResource(
|
||||
rtc::scoped_refptr<Resource> resource) {
|
||||
RTC_DCHECK(resource);
|
||||
RTC_LOG(LS_INFO) << "Removing resource \"" << resource->Name() << "\".";
|
||||
resource->SetResourceListener(nullptr);
|
||||
{
|
||||
MutexLock crit(&resources_lock_);
|
||||
auto it = absl::c_find(resources_, resource);
|
||||
RTC_DCHECK(it != resources_.end()) << "Resource \"" << resource->Name()
|
||||
<< "\" was not a registered resource.";
|
||||
resources_.erase(it);
|
||||
}
|
||||
RemoveLimitationsImposedByResource(std::move(resource));
|
||||
}
|
||||
|
||||
void ResourceAdaptationProcessor::RemoveLimitationsImposedByResource(
|
||||
rtc::scoped_refptr<Resource> resource) {
|
||||
if (!task_queue_->IsCurrent()) {
|
||||
task_queue_->PostTask(
|
||||
[this, resource]() { RemoveLimitationsImposedByResource(resource); });
|
||||
return;
|
||||
}
|
||||
RTC_DCHECK_RUN_ON(task_queue_);
|
||||
auto resource_adaptation_limits =
|
||||
adaptation_limits_by_resources_.find(resource);
|
||||
if (resource_adaptation_limits != adaptation_limits_by_resources_.end()) {
|
||||
VideoStreamAdapter::RestrictionsWithCounters adaptation_limits =
|
||||
resource_adaptation_limits->second;
|
||||
adaptation_limits_by_resources_.erase(resource_adaptation_limits);
|
||||
if (adaptation_limits_by_resources_.empty()) {
|
||||
// Only the resource being removed was adapted so clear restrictions.
|
||||
stream_adapter_->ClearRestrictions();
|
||||
return;
|
||||
}
|
||||
|
||||
VideoStreamAdapter::RestrictionsWithCounters most_limited =
|
||||
FindMostLimitedResources().second;
|
||||
|
||||
if (adaptation_limits.counters.Total() <= most_limited.counters.Total()) {
|
||||
// The removed limitations were less limited than the most limited
|
||||
// resource. Don't change the current restrictions.
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply the new most limited resource as the next restrictions.
|
||||
Adaptation adapt_to = stream_adapter_->GetAdaptationTo(
|
||||
most_limited.counters, most_limited.restrictions);
|
||||
RTC_DCHECK_EQ(adapt_to.status(), Adaptation::Status::kValid);
|
||||
stream_adapter_->ApplyAdaptation(adapt_to, nullptr);
|
||||
|
||||
RTC_LOG(LS_INFO)
|
||||
<< "Most limited resource removed. Restoring restrictions to "
|
||||
"next most limited restrictions: "
|
||||
<< most_limited.restrictions.ToString() << " with counters "
|
||||
<< most_limited.counters.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceAdaptationProcessor::OnResourceUsageStateMeasured(
|
||||
rtc::scoped_refptr<Resource> resource,
|
||||
ResourceUsageState usage_state) {
|
||||
RTC_DCHECK_RUN_ON(task_queue_);
|
||||
RTC_DCHECK(resource);
|
||||
// `resource` could have been removed after signalling.
|
||||
{
|
||||
MutexLock crit(&resources_lock_);
|
||||
if (absl::c_find(resources_, resource) == resources_.end()) {
|
||||
RTC_LOG(LS_INFO) << "Ignoring signal from removed resource \""
|
||||
<< resource->Name() << "\".";
|
||||
return;
|
||||
}
|
||||
}
|
||||
MitigationResultAndLogMessage result_and_message;
|
||||
switch (usage_state) {
|
||||
case ResourceUsageState::kOveruse:
|
||||
result_and_message = OnResourceOveruse(resource);
|
||||
break;
|
||||
case ResourceUsageState::kUnderuse:
|
||||
result_and_message = OnResourceUnderuse(resource);
|
||||
break;
|
||||
}
|
||||
// Maybe log the result of the operation.
|
||||
auto it = previous_mitigation_results_.find(resource.get());
|
||||
if (it != previous_mitigation_results_.end() &&
|
||||
it->second == result_and_message.result) {
|
||||
// This resource has previously reported the same result and we haven't
|
||||
// successfully adapted since - don't log to avoid spam.
|
||||
return;
|
||||
}
|
||||
RTC_LOG(LS_INFO) << "Resource \"" << resource->Name() << "\" signalled "
|
||||
<< ResourceUsageStateToString(usage_state) << ". "
|
||||
<< result_and_message.message;
|
||||
if (result_and_message.result == MitigationResult::kAdaptationApplied) {
|
||||
previous_mitigation_results_.clear();
|
||||
} else {
|
||||
previous_mitigation_results_.insert(
|
||||
std::make_pair(resource.get(), result_and_message.result));
|
||||
}
|
||||
}
|
||||
|
||||
ResourceAdaptationProcessor::MitigationResultAndLogMessage
|
||||
ResourceAdaptationProcessor::OnResourceUnderuse(
|
||||
rtc::scoped_refptr<Resource> reason_resource) {
|
||||
RTC_DCHECK_RUN_ON(task_queue_);
|
||||
// How can this stream be adapted up?
|
||||
Adaptation adaptation = stream_adapter_->GetAdaptationUp();
|
||||
if (adaptation.status() != Adaptation::Status::kValid) {
|
||||
rtc::StringBuilder message;
|
||||
message << "Not adapting up because VideoStreamAdapter returned "
|
||||
<< Adaptation::StatusToString(adaptation.status());
|
||||
return MitigationResultAndLogMessage(MitigationResult::kRejectedByAdapter,
|
||||
message.Release());
|
||||
}
|
||||
// Check that resource is most limited.
|
||||
std::vector<rtc::scoped_refptr<Resource>> most_limited_resources;
|
||||
VideoStreamAdapter::RestrictionsWithCounters most_limited_restrictions;
|
||||
std::tie(most_limited_resources, most_limited_restrictions) =
|
||||
FindMostLimitedResources();
|
||||
|
||||
// If the most restricted resource is less limited than current restrictions
|
||||
// then proceed with adapting up.
|
||||
if (!most_limited_resources.empty() &&
|
||||
most_limited_restrictions.counters.Total() >=
|
||||
stream_adapter_->adaptation_counters().Total()) {
|
||||
// If `reason_resource` is not one of the most limiting resources then abort
|
||||
// adaptation.
|
||||
if (absl::c_find(most_limited_resources, reason_resource) ==
|
||||
most_limited_resources.end()) {
|
||||
rtc::StringBuilder message;
|
||||
message << "Resource \"" << reason_resource->Name()
|
||||
<< "\" was not the most limited resource.";
|
||||
return MitigationResultAndLogMessage(
|
||||
MitigationResult::kNotMostLimitedResource, message.Release());
|
||||
}
|
||||
|
||||
if (most_limited_resources.size() > 1) {
|
||||
// If there are multiple most limited resources, all must signal underuse
|
||||
// before the adaptation is applied.
|
||||
UpdateResourceLimitations(reason_resource, adaptation.restrictions(),
|
||||
adaptation.counters());
|
||||
rtc::StringBuilder message;
|
||||
message << "Resource \"" << reason_resource->Name()
|
||||
<< "\" was not the only most limited resource.";
|
||||
return MitigationResultAndLogMessage(
|
||||
MitigationResult::kSharedMostLimitedResource, message.Release());
|
||||
}
|
||||
}
|
||||
// Apply adaptation.
|
||||
stream_adapter_->ApplyAdaptation(adaptation, reason_resource);
|
||||
rtc::StringBuilder message;
|
||||
message << "Adapted up successfully. Unfiltered adaptations: "
|
||||
<< stream_adapter_->adaptation_counters().ToString();
|
||||
return MitigationResultAndLogMessage(MitigationResult::kAdaptationApplied,
|
||||
message.Release());
|
||||
}
|
||||
|
||||
ResourceAdaptationProcessor::MitigationResultAndLogMessage
|
||||
ResourceAdaptationProcessor::OnResourceOveruse(
|
||||
rtc::scoped_refptr<Resource> reason_resource) {
|
||||
RTC_DCHECK_RUN_ON(task_queue_);
|
||||
// How can this stream be adapted up?
|
||||
Adaptation adaptation = stream_adapter_->GetAdaptationDown();
|
||||
if (adaptation.status() == Adaptation::Status::kLimitReached) {
|
||||
// Add resource as most limited.
|
||||
VideoStreamAdapter::RestrictionsWithCounters restrictions;
|
||||
std::tie(std::ignore, restrictions) = FindMostLimitedResources();
|
||||
UpdateResourceLimitations(reason_resource, restrictions.restrictions,
|
||||
restrictions.counters);
|
||||
}
|
||||
if (adaptation.status() != Adaptation::Status::kValid) {
|
||||
rtc::StringBuilder message;
|
||||
message << "Not adapting down because VideoStreamAdapter returned "
|
||||
<< Adaptation::StatusToString(adaptation.status());
|
||||
return MitigationResultAndLogMessage(MitigationResult::kRejectedByAdapter,
|
||||
message.Release());
|
||||
}
|
||||
// Apply adaptation.
|
||||
UpdateResourceLimitations(reason_resource, adaptation.restrictions(),
|
||||
adaptation.counters());
|
||||
stream_adapter_->ApplyAdaptation(adaptation, reason_resource);
|
||||
rtc::StringBuilder message;
|
||||
message << "Adapted down successfully. Unfiltered adaptations: "
|
||||
<< stream_adapter_->adaptation_counters().ToString();
|
||||
return MitigationResultAndLogMessage(MitigationResult::kAdaptationApplied,
|
||||
message.Release());
|
||||
}
|
||||
|
||||
std::pair<std::vector<rtc::scoped_refptr<Resource>>,
|
||||
VideoStreamAdapter::RestrictionsWithCounters>
|
||||
ResourceAdaptationProcessor::FindMostLimitedResources() const {
|
||||
std::vector<rtc::scoped_refptr<Resource>> most_limited_resources;
|
||||
VideoStreamAdapter::RestrictionsWithCounters most_limited_restrictions{
|
||||
VideoSourceRestrictions(), VideoAdaptationCounters()};
|
||||
|
||||
for (const auto& resource_and_adaptation_limit_ :
|
||||
adaptation_limits_by_resources_) {
|
||||
const auto& restrictions_with_counters =
|
||||
resource_and_adaptation_limit_.second;
|
||||
if (restrictions_with_counters.counters.Total() >
|
||||
most_limited_restrictions.counters.Total()) {
|
||||
most_limited_restrictions = restrictions_with_counters;
|
||||
most_limited_resources.clear();
|
||||
most_limited_resources.push_back(resource_and_adaptation_limit_.first);
|
||||
} else if (most_limited_restrictions.counters ==
|
||||
restrictions_with_counters.counters) {
|
||||
most_limited_resources.push_back(resource_and_adaptation_limit_.first);
|
||||
}
|
||||
}
|
||||
return std::make_pair(std::move(most_limited_resources),
|
||||
most_limited_restrictions);
|
||||
}
|
||||
|
||||
void ResourceAdaptationProcessor::UpdateResourceLimitations(
|
||||
rtc::scoped_refptr<Resource> reason_resource,
|
||||
const VideoSourceRestrictions& restrictions,
|
||||
const VideoAdaptationCounters& counters) {
|
||||
auto& adaptation_limits = adaptation_limits_by_resources_[reason_resource];
|
||||
if (adaptation_limits.restrictions == restrictions &&
|
||||
adaptation_limits.counters == counters) {
|
||||
return;
|
||||
}
|
||||
adaptation_limits = {restrictions, counters};
|
||||
|
||||
std::map<rtc::scoped_refptr<Resource>, VideoAdaptationCounters> limitations;
|
||||
for (const auto& p : adaptation_limits_by_resources_) {
|
||||
limitations.insert(std::make_pair(p.first, p.second.counters));
|
||||
}
|
||||
for (auto limitations_listener : resource_limitations_listeners_) {
|
||||
limitations_listener->OnResourceLimitationChanged(reason_resource,
|
||||
limitations);
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceAdaptationProcessor::OnVideoSourceRestrictionsUpdated(
|
||||
VideoSourceRestrictions restrictions,
|
||||
const VideoAdaptationCounters& adaptation_counters,
|
||||
rtc::scoped_refptr<Resource> reason,
|
||||
const VideoSourceRestrictions& unfiltered_restrictions) {
|
||||
RTC_DCHECK_RUN_ON(task_queue_);
|
||||
if (reason) {
|
||||
UpdateResourceLimitations(reason, unfiltered_restrictions,
|
||||
adaptation_counters);
|
||||
} else if (adaptation_counters.Total() == 0) {
|
||||
// Adaptations are cleared.
|
||||
adaptation_limits_by_resources_.clear();
|
||||
previous_mitigation_results_.clear();
|
||||
for (auto limitations_listener : resource_limitations_listeners_) {
|
||||
limitations_listener->OnResourceLimitationChanged(nullptr, {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef CALL_ADAPTATION_RESOURCE_ADAPTATION_PROCESSOR_H_
|
||||
#define CALL_ADAPTATION_RESOURCE_ADAPTATION_PROCESSOR_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/adaptation/resource.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "api/video/video_adaptation_counters.h"
|
||||
#include "api/video/video_frame.h"
|
||||
#include "call/adaptation/resource_adaptation_processor_interface.h"
|
||||
#include "call/adaptation/video_source_restrictions.h"
|
||||
#include "call/adaptation/video_stream_adapter.h"
|
||||
#include "call/adaptation/video_stream_input_state.h"
|
||||
#include "call/adaptation/video_stream_input_state_provider.h"
|
||||
#include "video/video_stream_encoder_observer.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// The Resource Adaptation Processor is responsible for reacting to resource
|
||||
// usage measurements (e.g. overusing or underusing CPU). When a resource is
|
||||
// overused the Processor is responsible for performing mitigations in order to
|
||||
// consume less resources.
|
||||
//
|
||||
// Today we have one Processor per VideoStreamEncoder and the Processor is only
|
||||
// capable of restricting resolution or frame rate of the encoded stream. In the
|
||||
// future we should have a single Processor responsible for all encoded streams,
|
||||
// and it should be capable of reconfiguring other things than just
|
||||
// VideoSourceRestrictions (e.g. reduce render frame rate).
|
||||
// See Resource-Adaptation hotlist:
|
||||
// https://bugs.chromium.org/u/590058293/hotlists/Resource-Adaptation
|
||||
//
|
||||
// The ResourceAdaptationProcessor is single-threaded. It may be constructed on
|
||||
// any thread but MUST subsequently be used and destroyed on a single sequence,
|
||||
// i.e. the "resource adaptation task queue". Resources can be added and removed
|
||||
// from any thread.
|
||||
class ResourceAdaptationProcessor : public ResourceAdaptationProcessorInterface,
|
||||
public VideoSourceRestrictionsListener,
|
||||
public ResourceListener {
|
||||
public:
|
||||
explicit ResourceAdaptationProcessor(
|
||||
VideoStreamAdapter* video_stream_adapter);
|
||||
~ResourceAdaptationProcessor() override;
|
||||
|
||||
// ResourceAdaptationProcessorInterface implementation.
|
||||
void AddResourceLimitationsListener(
|
||||
ResourceLimitationsListener* limitations_listener) override;
|
||||
void RemoveResourceLimitationsListener(
|
||||
ResourceLimitationsListener* limitations_listener) override;
|
||||
void AddResource(rtc::scoped_refptr<Resource> resource) override;
|
||||
std::vector<rtc::scoped_refptr<Resource>> GetResources() const override;
|
||||
void RemoveResource(rtc::scoped_refptr<Resource> resource) override;
|
||||
|
||||
// ResourceListener implementation.
|
||||
// Triggers OnResourceUnderuse() or OnResourceOveruse().
|
||||
void OnResourceUsageStateMeasured(rtc::scoped_refptr<Resource> resource,
|
||||
ResourceUsageState usage_state) override;
|
||||
|
||||
// VideoSourceRestrictionsListener implementation.
|
||||
void OnVideoSourceRestrictionsUpdated(
|
||||
VideoSourceRestrictions restrictions,
|
||||
const VideoAdaptationCounters& adaptation_counters,
|
||||
rtc::scoped_refptr<Resource> reason,
|
||||
const VideoSourceRestrictions& unfiltered_restrictions) override;
|
||||
|
||||
private:
|
||||
// If resource usage measurements happens off the adaptation task queue, this
|
||||
// class takes care of posting the measurement for the processor to handle it
|
||||
// on the adaptation task queue.
|
||||
class ResourceListenerDelegate : public rtc::RefCountInterface,
|
||||
public ResourceListener {
|
||||
public:
|
||||
explicit ResourceListenerDelegate(ResourceAdaptationProcessor* processor);
|
||||
|
||||
void OnProcessorDestroyed();
|
||||
|
||||
// ResourceListener implementation.
|
||||
void OnResourceUsageStateMeasured(rtc::scoped_refptr<Resource> resource,
|
||||
ResourceUsageState usage_state) override;
|
||||
|
||||
private:
|
||||
TaskQueueBase* task_queue_;
|
||||
ResourceAdaptationProcessor* processor_ RTC_GUARDED_BY(task_queue_);
|
||||
};
|
||||
|
||||
enum class MitigationResult {
|
||||
kNotMostLimitedResource,
|
||||
kSharedMostLimitedResource,
|
||||
kRejectedByAdapter,
|
||||
kAdaptationApplied,
|
||||
};
|
||||
|
||||
struct MitigationResultAndLogMessage {
|
||||
MitigationResultAndLogMessage();
|
||||
MitigationResultAndLogMessage(MitigationResult result,
|
||||
absl::string_view message);
|
||||
MitigationResult result;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
// Performs the adaptation by getting the next target, applying it and
|
||||
// informing listeners of the new VideoSourceRestriction and adaptation
|
||||
// counters.
|
||||
MitigationResultAndLogMessage OnResourceUnderuse(
|
||||
rtc::scoped_refptr<Resource> reason_resource);
|
||||
MitigationResultAndLogMessage OnResourceOveruse(
|
||||
rtc::scoped_refptr<Resource> reason_resource);
|
||||
|
||||
void UpdateResourceLimitations(rtc::scoped_refptr<Resource> reason_resource,
|
||||
const VideoSourceRestrictions& restrictions,
|
||||
const VideoAdaptationCounters& counters)
|
||||
RTC_RUN_ON(task_queue_);
|
||||
|
||||
// Searches `adaptation_limits_by_resources_` for each resource with the
|
||||
// highest total adaptation counts. Adaptation up may only occur if the
|
||||
// resource performing the adaptation is the only most limited resource. This
|
||||
// function returns the list of all most limited resources as well as the
|
||||
// corresponding adaptation of that resource.
|
||||
std::pair<std::vector<rtc::scoped_refptr<Resource>>,
|
||||
VideoStreamAdapter::RestrictionsWithCounters>
|
||||
FindMostLimitedResources() const RTC_RUN_ON(task_queue_);
|
||||
|
||||
void RemoveLimitationsImposedByResource(
|
||||
rtc::scoped_refptr<Resource> resource);
|
||||
|
||||
TaskQueueBase* task_queue_;
|
||||
rtc::scoped_refptr<ResourceListenerDelegate> resource_listener_delegate_;
|
||||
// Input and output.
|
||||
mutable Mutex resources_lock_;
|
||||
std::vector<rtc::scoped_refptr<Resource>> resources_
|
||||
RTC_GUARDED_BY(resources_lock_);
|
||||
std::vector<ResourceLimitationsListener*> resource_limitations_listeners_
|
||||
RTC_GUARDED_BY(task_queue_);
|
||||
// Purely used for statistics, does not ensure mapped resources stay alive.
|
||||
std::map<rtc::scoped_refptr<Resource>,
|
||||
VideoStreamAdapter::RestrictionsWithCounters>
|
||||
adaptation_limits_by_resources_ RTC_GUARDED_BY(task_queue_);
|
||||
// Responsible for generating and applying possible adaptations.
|
||||
VideoStreamAdapter* const stream_adapter_ RTC_GUARDED_BY(task_queue_);
|
||||
VideoSourceRestrictions last_reported_source_restrictions_
|
||||
RTC_GUARDED_BY(task_queue_);
|
||||
// Keeps track of previous mitigation results per resource since the last
|
||||
// successful adaptation. Used to avoid RTC_LOG spam.
|
||||
std::map<Resource*, MitigationResult> previous_mitigation_results_
|
||||
RTC_GUARDED_BY(task_queue_);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // CALL_ADAPTATION_RESOURCE_ADAPTATION_PROCESSOR_H_
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "call/adaptation/resource_adaptation_processor_interface.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
ResourceAdaptationProcessorInterface::~ResourceAdaptationProcessorInterface() =
|
||||
default;
|
||||
|
||||
ResourceLimitationsListener::~ResourceLimitationsListener() = default;
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef CALL_ADAPTATION_RESOURCE_ADAPTATION_PROCESSOR_INTERFACE_H_
|
||||
#define CALL_ADAPTATION_RESOURCE_ADAPTATION_PROCESSOR_INTERFACE_H_
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/adaptation/resource.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "api/video/video_adaptation_counters.h"
|
||||
#include "api/video/video_frame.h"
|
||||
#include "call/adaptation/adaptation_constraint.h"
|
||||
#include "call/adaptation/encoder_settings.h"
|
||||
#include "call/adaptation/video_source_restrictions.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class ResourceLimitationsListener {
|
||||
public:
|
||||
virtual ~ResourceLimitationsListener();
|
||||
|
||||
// The limitations on a resource were changed. This does not mean the current
|
||||
// video restrictions have changed.
|
||||
virtual void OnResourceLimitationChanged(
|
||||
rtc::scoped_refptr<Resource> resource,
|
||||
const std::map<rtc::scoped_refptr<Resource>, VideoAdaptationCounters>&
|
||||
resource_limitations) = 0;
|
||||
};
|
||||
|
||||
// The Resource Adaptation Processor is responsible for reacting to resource
|
||||
// usage measurements (e.g. overusing or underusing CPU). When a resource is
|
||||
// overused the Processor is responsible for performing mitigations in order to
|
||||
// consume less resources.
|
||||
class ResourceAdaptationProcessorInterface {
|
||||
public:
|
||||
virtual ~ResourceAdaptationProcessorInterface();
|
||||
|
||||
virtual void AddResourceLimitationsListener(
|
||||
ResourceLimitationsListener* limitations_listener) = 0;
|
||||
virtual void RemoveResourceLimitationsListener(
|
||||
ResourceLimitationsListener* limitations_listener) = 0;
|
||||
// Starts or stops listening to resources, effectively enabling or disabling
|
||||
// processing. May be called from anywhere.
|
||||
// TODO(https://crbug.com/webrtc/11172): Automatically register and unregister
|
||||
// with AddResource() and RemoveResource() instead. When the processor is
|
||||
// multi-stream aware, stream-specific resouces will get added and removed
|
||||
// over time.
|
||||
virtual void AddResource(rtc::scoped_refptr<Resource> resource) = 0;
|
||||
virtual std::vector<rtc::scoped_refptr<Resource>> GetResources() const = 0;
|
||||
virtual void RemoveResource(rtc::scoped_refptr<Resource> resource) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // CALL_ADAPTATION_RESOURCE_ADAPTATION_PROCESSOR_INTERFACE_H_
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "call/adaptation/video_source_restrictions.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
VideoSourceRestrictions::VideoSourceRestrictions()
|
||||
: max_pixels_per_frame_(absl::nullopt),
|
||||
target_pixels_per_frame_(absl::nullopt),
|
||||
max_frame_rate_(absl::nullopt) {}
|
||||
|
||||
VideoSourceRestrictions::VideoSourceRestrictions(
|
||||
absl::optional<size_t> max_pixels_per_frame,
|
||||
absl::optional<size_t> target_pixels_per_frame,
|
||||
absl::optional<double> max_frame_rate)
|
||||
: max_pixels_per_frame_(std::move(max_pixels_per_frame)),
|
||||
target_pixels_per_frame_(std::move(target_pixels_per_frame)),
|
||||
max_frame_rate_(std::move(max_frame_rate)) {
|
||||
RTC_DCHECK(!max_pixels_per_frame_.has_value() ||
|
||||
max_pixels_per_frame_.value() <
|
||||
static_cast<size_t>(std::numeric_limits<int>::max()));
|
||||
RTC_DCHECK(!max_frame_rate_.has_value() ||
|
||||
max_frame_rate_.value() < std::numeric_limits<int>::max());
|
||||
RTC_DCHECK(!max_frame_rate_.has_value() || max_frame_rate_.value() > 0.0);
|
||||
}
|
||||
|
||||
std::string VideoSourceRestrictions::ToString() const {
|
||||
rtc::StringBuilder ss;
|
||||
ss << "{";
|
||||
if (max_frame_rate_)
|
||||
ss << " max_fps=" << max_frame_rate_.value();
|
||||
if (max_pixels_per_frame_)
|
||||
ss << " max_pixels_per_frame=" << max_pixels_per_frame_.value();
|
||||
if (target_pixels_per_frame_)
|
||||
ss << " target_pixels_per_frame=" << target_pixels_per_frame_.value();
|
||||
ss << " }";
|
||||
return ss.Release();
|
||||
}
|
||||
|
||||
const absl::optional<size_t>& VideoSourceRestrictions::max_pixels_per_frame()
|
||||
const {
|
||||
return max_pixels_per_frame_;
|
||||
}
|
||||
|
||||
const absl::optional<size_t>& VideoSourceRestrictions::target_pixels_per_frame()
|
||||
const {
|
||||
return target_pixels_per_frame_;
|
||||
}
|
||||
|
||||
const absl::optional<double>& VideoSourceRestrictions::max_frame_rate() const {
|
||||
return max_frame_rate_;
|
||||
}
|
||||
|
||||
void VideoSourceRestrictions::set_max_pixels_per_frame(
|
||||
absl::optional<size_t> max_pixels_per_frame) {
|
||||
max_pixels_per_frame_ = std::move(max_pixels_per_frame);
|
||||
}
|
||||
|
||||
void VideoSourceRestrictions::set_target_pixels_per_frame(
|
||||
absl::optional<size_t> target_pixels_per_frame) {
|
||||
target_pixels_per_frame_ = std::move(target_pixels_per_frame);
|
||||
}
|
||||
|
||||
void VideoSourceRestrictions::set_max_frame_rate(
|
||||
absl::optional<double> max_frame_rate) {
|
||||
max_frame_rate_ = std::move(max_frame_rate);
|
||||
}
|
||||
|
||||
void VideoSourceRestrictions::UpdateMin(const VideoSourceRestrictions& other) {
|
||||
if (max_pixels_per_frame_.has_value()) {
|
||||
max_pixels_per_frame_ = std::min(*max_pixels_per_frame_,
|
||||
other.max_pixels_per_frame().value_or(
|
||||
std::numeric_limits<size_t>::max()));
|
||||
} else {
|
||||
max_pixels_per_frame_ = other.max_pixels_per_frame();
|
||||
}
|
||||
if (target_pixels_per_frame_.has_value()) {
|
||||
target_pixels_per_frame_ = std::min(
|
||||
*target_pixels_per_frame_, other.target_pixels_per_frame().value_or(
|
||||
std::numeric_limits<size_t>::max()));
|
||||
} else {
|
||||
target_pixels_per_frame_ = other.target_pixels_per_frame();
|
||||
}
|
||||
if (max_frame_rate_.has_value()) {
|
||||
max_frame_rate_ = std::min(
|
||||
*max_frame_rate_,
|
||||
other.max_frame_rate().value_or(std::numeric_limits<double>::max()));
|
||||
} else {
|
||||
max_frame_rate_ = other.max_frame_rate();
|
||||
}
|
||||
}
|
||||
|
||||
bool DidRestrictionsIncrease(VideoSourceRestrictions before,
|
||||
VideoSourceRestrictions after) {
|
||||
bool decreased_resolution = DidDecreaseResolution(before, after);
|
||||
bool decreased_framerate = DidDecreaseFrameRate(before, after);
|
||||
bool same_resolution =
|
||||
before.max_pixels_per_frame() == after.max_pixels_per_frame();
|
||||
bool same_framerate = before.max_frame_rate() == after.max_frame_rate();
|
||||
|
||||
return (decreased_resolution && decreased_framerate) ||
|
||||
(decreased_resolution && same_framerate) ||
|
||||
(same_resolution && decreased_framerate);
|
||||
}
|
||||
|
||||
bool DidRestrictionsDecrease(VideoSourceRestrictions before,
|
||||
VideoSourceRestrictions after) {
|
||||
bool increased_resolution = DidIncreaseResolution(before, after);
|
||||
bool increased_framerate = DidIncreaseFrameRate(before, after);
|
||||
bool same_resolution =
|
||||
before.max_pixels_per_frame() == after.max_pixels_per_frame();
|
||||
bool same_framerate = before.max_frame_rate() == after.max_frame_rate();
|
||||
|
||||
return (increased_resolution && increased_framerate) ||
|
||||
(increased_resolution && same_framerate) ||
|
||||
(same_resolution && increased_framerate);
|
||||
}
|
||||
|
||||
bool DidIncreaseResolution(VideoSourceRestrictions restrictions_before,
|
||||
VideoSourceRestrictions restrictions_after) {
|
||||
if (!restrictions_before.max_pixels_per_frame().has_value())
|
||||
return false;
|
||||
if (!restrictions_after.max_pixels_per_frame().has_value())
|
||||
return true;
|
||||
return restrictions_after.max_pixels_per_frame().value() >
|
||||
restrictions_before.max_pixels_per_frame().value();
|
||||
}
|
||||
|
||||
bool DidDecreaseResolution(VideoSourceRestrictions restrictions_before,
|
||||
VideoSourceRestrictions restrictions_after) {
|
||||
if (!restrictions_after.max_pixels_per_frame().has_value())
|
||||
return false;
|
||||
if (!restrictions_before.max_pixels_per_frame().has_value())
|
||||
return true;
|
||||
return restrictions_after.max_pixels_per_frame().value() <
|
||||
restrictions_before.max_pixels_per_frame().value();
|
||||
}
|
||||
|
||||
bool DidIncreaseFrameRate(VideoSourceRestrictions restrictions_before,
|
||||
VideoSourceRestrictions restrictions_after) {
|
||||
if (!restrictions_before.max_frame_rate().has_value())
|
||||
return false;
|
||||
if (!restrictions_after.max_frame_rate().has_value())
|
||||
return true;
|
||||
return restrictions_after.max_frame_rate().value() >
|
||||
restrictions_before.max_frame_rate().value();
|
||||
}
|
||||
|
||||
bool DidDecreaseFrameRate(VideoSourceRestrictions restrictions_before,
|
||||
VideoSourceRestrictions restrictions_after) {
|
||||
if (!restrictions_after.max_frame_rate().has_value())
|
||||
return false;
|
||||
if (!restrictions_before.max_frame_rate().has_value())
|
||||
return true;
|
||||
return restrictions_after.max_frame_rate().value() <
|
||||
restrictions_before.max_frame_rate().value();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef CALL_ADAPTATION_VIDEO_SOURCE_RESTRICTIONS_H_
|
||||
#define CALL_ADAPTATION_VIDEO_SOURCE_RESTRICTIONS_H_
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Describes optional restrictions to the resolution and frame rate of a video
|
||||
// source.
|
||||
class VideoSourceRestrictions {
|
||||
public:
|
||||
// Constructs without any restrictions.
|
||||
VideoSourceRestrictions();
|
||||
// All values must be positive or nullopt.
|
||||
// TODO(hbos): Support expressing "disable this stream"?
|
||||
VideoSourceRestrictions(absl::optional<size_t> max_pixels_per_frame,
|
||||
absl::optional<size_t> target_pixels_per_frame,
|
||||
absl::optional<double> max_frame_rate);
|
||||
|
||||
bool operator==(const VideoSourceRestrictions& rhs) const {
|
||||
return max_pixels_per_frame_ == rhs.max_pixels_per_frame_ &&
|
||||
target_pixels_per_frame_ == rhs.target_pixels_per_frame_ &&
|
||||
max_frame_rate_ == rhs.max_frame_rate_;
|
||||
}
|
||||
bool operator!=(const VideoSourceRestrictions& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
std::string ToString() const;
|
||||
|
||||
// The source must produce a resolution less than or equal to
|
||||
// max_pixels_per_frame().
|
||||
const absl::optional<size_t>& max_pixels_per_frame() const;
|
||||
// The source should produce a resolution as close to the
|
||||
// target_pixels_per_frame() as possible, provided this does not exceed
|
||||
// max_pixels_per_frame().
|
||||
// The actual pixel count selected depends on the capabilities of the source.
|
||||
// TODO(hbos): Clarify how "target" is used. One possible implementation: open
|
||||
// the camera in the smallest resolution that is greater than or equal to the
|
||||
// target and scale it down to the target if it is greater. Is this an
|
||||
// accurate description of what this does today, or do we do something else?
|
||||
const absl::optional<size_t>& target_pixels_per_frame() const;
|
||||
const absl::optional<double>& max_frame_rate() const;
|
||||
|
||||
void set_max_pixels_per_frame(absl::optional<size_t> max_pixels_per_frame);
|
||||
void set_target_pixels_per_frame(
|
||||
absl::optional<size_t> target_pixels_per_frame);
|
||||
void set_max_frame_rate(absl::optional<double> max_frame_rate);
|
||||
|
||||
// Update `this` with min(`this`, `other`).
|
||||
void UpdateMin(const VideoSourceRestrictions& other);
|
||||
|
||||
private:
|
||||
// These map to rtc::VideoSinkWants's `max_pixel_count` and
|
||||
// `target_pixel_count`.
|
||||
absl::optional<size_t> max_pixels_per_frame_;
|
||||
absl::optional<size_t> target_pixels_per_frame_;
|
||||
absl::optional<double> max_frame_rate_;
|
||||
};
|
||||
|
||||
bool DidRestrictionsIncrease(VideoSourceRestrictions before,
|
||||
VideoSourceRestrictions after);
|
||||
bool DidRestrictionsDecrease(VideoSourceRestrictions before,
|
||||
VideoSourceRestrictions after);
|
||||
bool DidIncreaseResolution(VideoSourceRestrictions restrictions_before,
|
||||
VideoSourceRestrictions restrictions_after);
|
||||
bool DidDecreaseResolution(VideoSourceRestrictions restrictions_before,
|
||||
VideoSourceRestrictions restrictions_after);
|
||||
bool DidIncreaseFrameRate(VideoSourceRestrictions restrictions_before,
|
||||
VideoSourceRestrictions restrictions_after);
|
||||
bool DidDecreaseFrameRate(VideoSourceRestrictions restrictions_before,
|
||||
VideoSourceRestrictions restrictions_after);
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // CALL_ADAPTATION_VIDEO_SOURCE_RESTRICTIONS_H_
|
||||
|
|
@ -0,0 +1,753 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "call/adaptation/video_stream_adapter.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "absl/types/variant.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/video/video_adaptation_counters.h"
|
||||
#include "api/video/video_adaptation_reason.h"
|
||||
#include "api/video_codecs/video_encoder.h"
|
||||
#include "call/adaptation/video_source_restrictions.h"
|
||||
#include "call/adaptation/video_stream_input_state.h"
|
||||
#include "modules/video_coding/svc/scalability_mode_util.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/numerics/safe_conversions.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
const int kMinFrameRateFps = 2;
|
||||
|
||||
namespace {
|
||||
|
||||
// For frame rate, the steps we take are 2/3 (down) and 3/2 (up).
|
||||
int GetLowerFrameRateThan(int fps) {
|
||||
RTC_DCHECK(fps != std::numeric_limits<int>::max());
|
||||
return (fps * 2) / 3;
|
||||
}
|
||||
// TODO(hbos): Use absl::optional<> instead?
|
||||
int GetHigherFrameRateThan(int fps) {
|
||||
return fps != std::numeric_limits<int>::max()
|
||||
? (fps * 3) / 2
|
||||
: std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
int GetIncreasedMaxPixelsWanted(int target_pixels) {
|
||||
if (target_pixels == std::numeric_limits<int>::max())
|
||||
return std::numeric_limits<int>::max();
|
||||
// When we decrease resolution, we go down to at most 3/5 of current pixels.
|
||||
// Thus to increase resolution, we need 3/5 to get back to where we started.
|
||||
// When going up, the desired max_pixels_per_frame() has to be significantly
|
||||
// higher than the target because the source's native resolutions might not
|
||||
// match the target. We pick 12/5 of the target.
|
||||
//
|
||||
// (This value was historically 4 times the old target, which is (3/5)*4 of
|
||||
// the new target - or 12/5 - assuming the target is adjusted according to
|
||||
// the above steps.)
|
||||
RTC_DCHECK(target_pixels != std::numeric_limits<int>::max());
|
||||
return (target_pixels * 12) / 5;
|
||||
}
|
||||
|
||||
bool CanDecreaseResolutionTo(int target_pixels,
|
||||
int target_pixels_min,
|
||||
const VideoStreamInputState& input_state,
|
||||
const VideoSourceRestrictions& restrictions) {
|
||||
int max_pixels_per_frame =
|
||||
rtc::dchecked_cast<int>(restrictions.max_pixels_per_frame().value_or(
|
||||
std::numeric_limits<int>::max()));
|
||||
return target_pixels < max_pixels_per_frame &&
|
||||
target_pixels_min >= input_state.min_pixels_per_frame();
|
||||
}
|
||||
|
||||
bool CanIncreaseResolutionTo(int target_pixels,
|
||||
const VideoSourceRestrictions& restrictions) {
|
||||
int max_pixels_wanted = GetIncreasedMaxPixelsWanted(target_pixels);
|
||||
int max_pixels_per_frame =
|
||||
rtc::dchecked_cast<int>(restrictions.max_pixels_per_frame().value_or(
|
||||
std::numeric_limits<int>::max()));
|
||||
return max_pixels_wanted > max_pixels_per_frame;
|
||||
}
|
||||
|
||||
bool CanDecreaseFrameRateTo(int max_frame_rate,
|
||||
const VideoSourceRestrictions& restrictions) {
|
||||
const int fps_wanted = std::max(kMinFrameRateFps, max_frame_rate);
|
||||
return fps_wanted <
|
||||
rtc::dchecked_cast<int>(restrictions.max_frame_rate().value_or(
|
||||
std::numeric_limits<int>::max()));
|
||||
}
|
||||
|
||||
bool CanIncreaseFrameRateTo(int max_frame_rate,
|
||||
const VideoSourceRestrictions& restrictions) {
|
||||
return max_frame_rate >
|
||||
rtc::dchecked_cast<int>(restrictions.max_frame_rate().value_or(
|
||||
std::numeric_limits<int>::max()));
|
||||
}
|
||||
|
||||
bool MinPixelLimitReached(const VideoStreamInputState& input_state) {
|
||||
if (input_state.single_active_stream_pixels().has_value()) {
|
||||
return GetLowerResolutionThan(
|
||||
input_state.single_active_stream_pixels().value()) <
|
||||
input_state.min_pixels_per_frame();
|
||||
}
|
||||
return input_state.frame_size_pixels().has_value() &&
|
||||
GetLowerResolutionThan(input_state.frame_size_pixels().value()) <
|
||||
input_state.min_pixels_per_frame();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
VideoSourceRestrictionsListener::~VideoSourceRestrictionsListener() = default;
|
||||
|
||||
VideoSourceRestrictions FilterRestrictionsByDegradationPreference(
|
||||
VideoSourceRestrictions source_restrictions,
|
||||
DegradationPreference degradation_preference) {
|
||||
switch (degradation_preference) {
|
||||
case DegradationPreference::BALANCED:
|
||||
break;
|
||||
case DegradationPreference::MAINTAIN_FRAMERATE:
|
||||
source_restrictions.set_max_frame_rate(absl::nullopt);
|
||||
break;
|
||||
case DegradationPreference::MAINTAIN_RESOLUTION:
|
||||
source_restrictions.set_max_pixels_per_frame(absl::nullopt);
|
||||
source_restrictions.set_target_pixels_per_frame(absl::nullopt);
|
||||
break;
|
||||
case DegradationPreference::DISABLED:
|
||||
source_restrictions.set_max_pixels_per_frame(absl::nullopt);
|
||||
source_restrictions.set_target_pixels_per_frame(absl::nullopt);
|
||||
source_restrictions.set_max_frame_rate(absl::nullopt);
|
||||
}
|
||||
return source_restrictions;
|
||||
}
|
||||
|
||||
// For resolution, the steps we take are 3/5 (down) and 5/3 (up).
|
||||
// Notice the asymmetry of which restriction property is set depending on if
|
||||
// we are adapting up or down:
|
||||
// - VideoSourceRestrictor::DecreaseResolution() sets the max_pixels_per_frame()
|
||||
// to the desired target and target_pixels_per_frame() to null.
|
||||
// - VideoSourceRestrictor::IncreaseResolutionTo() sets the
|
||||
// target_pixels_per_frame() to the desired target, and max_pixels_per_frame()
|
||||
// is set according to VideoSourceRestrictor::GetIncreasedMaxPixelsWanted().
|
||||
int GetLowerResolutionThan(int pixel_count) {
|
||||
RTC_DCHECK(pixel_count != std::numeric_limits<int>::max());
|
||||
return (pixel_count * 3) / 5;
|
||||
}
|
||||
|
||||
// TODO(hbos): Use absl::optional<> instead?
|
||||
int GetHigherResolutionThan(int pixel_count) {
|
||||
return pixel_count != std::numeric_limits<int>::max()
|
||||
? (pixel_count * 5) / 3
|
||||
: std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
// static
|
||||
const char* Adaptation::StatusToString(Adaptation::Status status) {
|
||||
switch (status) {
|
||||
case Adaptation::Status::kValid:
|
||||
return "kValid";
|
||||
case Adaptation::Status::kLimitReached:
|
||||
return "kLimitReached";
|
||||
case Adaptation::Status::kAwaitingPreviousAdaptation:
|
||||
return "kAwaitingPreviousAdaptation";
|
||||
case Status::kInsufficientInput:
|
||||
return "kInsufficientInput";
|
||||
case Status::kAdaptationDisabled:
|
||||
return "kAdaptationDisabled";
|
||||
case Status::kRejectedByConstraint:
|
||||
return "kRejectedByConstraint";
|
||||
}
|
||||
RTC_CHECK_NOTREACHED();
|
||||
}
|
||||
|
||||
Adaptation::Adaptation(int validation_id,
|
||||
VideoSourceRestrictions restrictions,
|
||||
VideoAdaptationCounters counters,
|
||||
VideoStreamInputState input_state)
|
||||
: validation_id_(validation_id),
|
||||
status_(Status::kValid),
|
||||
input_state_(std::move(input_state)),
|
||||
restrictions_(std::move(restrictions)),
|
||||
counters_(std::move(counters)) {}
|
||||
|
||||
Adaptation::Adaptation(int validation_id, Status invalid_status)
|
||||
: validation_id_(validation_id), status_(invalid_status) {
|
||||
RTC_DCHECK_NE(status_, Status::kValid);
|
||||
}
|
||||
|
||||
Adaptation::Status Adaptation::status() const {
|
||||
return status_;
|
||||
}
|
||||
|
||||
const VideoStreamInputState& Adaptation::input_state() const {
|
||||
return input_state_;
|
||||
}
|
||||
|
||||
const VideoSourceRestrictions& Adaptation::restrictions() const {
|
||||
return restrictions_;
|
||||
}
|
||||
|
||||
const VideoAdaptationCounters& Adaptation::counters() const {
|
||||
return counters_;
|
||||
}
|
||||
|
||||
VideoStreamAdapter::VideoStreamAdapter(
|
||||
VideoStreamInputStateProvider* input_state_provider,
|
||||
VideoStreamEncoderObserver* encoder_stats_observer,
|
||||
const FieldTrialsView& field_trials)
|
||||
: input_state_provider_(input_state_provider),
|
||||
encoder_stats_observer_(encoder_stats_observer),
|
||||
balanced_settings_(field_trials),
|
||||
adaptation_validation_id_(0),
|
||||
degradation_preference_(DegradationPreference::DISABLED),
|
||||
awaiting_frame_size_change_(absl::nullopt) {
|
||||
sequence_checker_.Detach();
|
||||
RTC_DCHECK(input_state_provider_);
|
||||
RTC_DCHECK(encoder_stats_observer_);
|
||||
}
|
||||
|
||||
VideoStreamAdapter::~VideoStreamAdapter() {
|
||||
RTC_DCHECK(adaptation_constraints_.empty())
|
||||
<< "There are constaint(s) attached to a VideoStreamAdapter being "
|
||||
"destroyed.";
|
||||
}
|
||||
|
||||
VideoSourceRestrictions VideoStreamAdapter::source_restrictions() const {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
return current_restrictions_.restrictions;
|
||||
}
|
||||
|
||||
const VideoAdaptationCounters& VideoStreamAdapter::adaptation_counters() const {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
return current_restrictions_.counters;
|
||||
}
|
||||
|
||||
void VideoStreamAdapter::ClearRestrictions() {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
// Invalidate any previously returned Adaptation.
|
||||
RTC_LOG(LS_INFO) << "Resetting restrictions";
|
||||
++adaptation_validation_id_;
|
||||
current_restrictions_ = {VideoSourceRestrictions(),
|
||||
VideoAdaptationCounters()};
|
||||
awaiting_frame_size_change_ = absl::nullopt;
|
||||
BroadcastVideoRestrictionsUpdate(input_state_provider_->InputState(),
|
||||
nullptr);
|
||||
}
|
||||
|
||||
void VideoStreamAdapter::AddRestrictionsListener(
|
||||
VideoSourceRestrictionsListener* restrictions_listener) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
RTC_DCHECK(std::find(restrictions_listeners_.begin(),
|
||||
restrictions_listeners_.end(),
|
||||
restrictions_listener) == restrictions_listeners_.end());
|
||||
restrictions_listeners_.push_back(restrictions_listener);
|
||||
}
|
||||
|
||||
void VideoStreamAdapter::RemoveRestrictionsListener(
|
||||
VideoSourceRestrictionsListener* restrictions_listener) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
auto it = std::find(restrictions_listeners_.begin(),
|
||||
restrictions_listeners_.end(), restrictions_listener);
|
||||
RTC_DCHECK(it != restrictions_listeners_.end());
|
||||
restrictions_listeners_.erase(it);
|
||||
}
|
||||
|
||||
void VideoStreamAdapter::AddAdaptationConstraint(
|
||||
AdaptationConstraint* adaptation_constraint) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
RTC_DCHECK(std::find(adaptation_constraints_.begin(),
|
||||
adaptation_constraints_.end(),
|
||||
adaptation_constraint) == adaptation_constraints_.end());
|
||||
adaptation_constraints_.push_back(adaptation_constraint);
|
||||
}
|
||||
|
||||
void VideoStreamAdapter::RemoveAdaptationConstraint(
|
||||
AdaptationConstraint* adaptation_constraint) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
auto it = std::find(adaptation_constraints_.begin(),
|
||||
adaptation_constraints_.end(), adaptation_constraint);
|
||||
RTC_DCHECK(it != adaptation_constraints_.end());
|
||||
adaptation_constraints_.erase(it);
|
||||
}
|
||||
|
||||
void VideoStreamAdapter::SetDegradationPreference(
|
||||
DegradationPreference degradation_preference) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
if (degradation_preference_ == degradation_preference)
|
||||
return;
|
||||
// Invalidate any previously returned Adaptation.
|
||||
++adaptation_validation_id_;
|
||||
bool balanced_switch =
|
||||
degradation_preference == DegradationPreference::BALANCED ||
|
||||
degradation_preference_ == DegradationPreference::BALANCED;
|
||||
degradation_preference_ = degradation_preference;
|
||||
if (balanced_switch) {
|
||||
// ClearRestrictions() calls BroadcastVideoRestrictionsUpdate(nullptr).
|
||||
ClearRestrictions();
|
||||
} else {
|
||||
BroadcastVideoRestrictionsUpdate(input_state_provider_->InputState(),
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
struct VideoStreamAdapter::RestrictionsOrStateVisitor {
|
||||
Adaptation operator()(const RestrictionsWithCounters& r) const {
|
||||
return Adaptation(adaptation_validation_id, r.restrictions, r.counters,
|
||||
input_state);
|
||||
}
|
||||
Adaptation operator()(const Adaptation::Status& status) const {
|
||||
RTC_DCHECK_NE(status, Adaptation::Status::kValid);
|
||||
return Adaptation(adaptation_validation_id, status);
|
||||
}
|
||||
|
||||
const int adaptation_validation_id;
|
||||
const VideoStreamInputState& input_state;
|
||||
};
|
||||
|
||||
Adaptation VideoStreamAdapter::RestrictionsOrStateToAdaptation(
|
||||
VideoStreamAdapter::RestrictionsOrState step_or_state,
|
||||
const VideoStreamInputState& input_state) const {
|
||||
RTC_DCHECK(!step_or_state.valueless_by_exception());
|
||||
return absl::visit(
|
||||
RestrictionsOrStateVisitor{adaptation_validation_id_, input_state},
|
||||
step_or_state);
|
||||
}
|
||||
|
||||
Adaptation VideoStreamAdapter::GetAdaptationUp(
|
||||
const VideoStreamInputState& input_state) const {
|
||||
RestrictionsOrState step = GetAdaptationUpStep(input_state);
|
||||
// If an adaptation proposed, check with the constraints that it is ok.
|
||||
if (absl::holds_alternative<RestrictionsWithCounters>(step)) {
|
||||
RestrictionsWithCounters restrictions =
|
||||
absl::get<RestrictionsWithCounters>(step);
|
||||
for (const auto* constraint : adaptation_constraints_) {
|
||||
if (!constraint->IsAdaptationUpAllowed(input_state,
|
||||
current_restrictions_.restrictions,
|
||||
restrictions.restrictions)) {
|
||||
RTC_LOG(LS_INFO) << "Not adapting up because constraint \""
|
||||
<< constraint->Name() << "\" disallowed it";
|
||||
step = Adaptation::Status::kRejectedByConstraint;
|
||||
}
|
||||
}
|
||||
}
|
||||
return RestrictionsOrStateToAdaptation(step, input_state);
|
||||
}
|
||||
|
||||
Adaptation VideoStreamAdapter::GetAdaptationUp() {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
VideoStreamInputState input_state = input_state_provider_->InputState();
|
||||
++adaptation_validation_id_;
|
||||
Adaptation adaptation = GetAdaptationUp(input_state);
|
||||
return adaptation;
|
||||
}
|
||||
|
||||
VideoStreamAdapter::RestrictionsOrState VideoStreamAdapter::GetAdaptationUpStep(
|
||||
const VideoStreamInputState& input_state) const {
|
||||
if (!HasSufficientInputForAdaptation(input_state)) {
|
||||
return Adaptation::Status::kInsufficientInput;
|
||||
}
|
||||
// Don't adapt if we're awaiting a previous adaptation to have an effect.
|
||||
if (awaiting_frame_size_change_ &&
|
||||
awaiting_frame_size_change_->pixels_increased &&
|
||||
degradation_preference_ == DegradationPreference::MAINTAIN_FRAMERATE &&
|
||||
input_state.frame_size_pixels().value() <=
|
||||
awaiting_frame_size_change_->frame_size_pixels) {
|
||||
return Adaptation::Status::kAwaitingPreviousAdaptation;
|
||||
}
|
||||
|
||||
// Maybe propose targets based on degradation preference.
|
||||
switch (degradation_preference_) {
|
||||
case DegradationPreference::BALANCED: {
|
||||
// Attempt to increase target frame rate.
|
||||
RestrictionsOrState increase_frame_rate =
|
||||
IncreaseFramerate(input_state, current_restrictions_);
|
||||
if (absl::holds_alternative<RestrictionsWithCounters>(
|
||||
increase_frame_rate)) {
|
||||
return increase_frame_rate;
|
||||
}
|
||||
// else, increase resolution.
|
||||
[[fallthrough]];
|
||||
}
|
||||
case DegradationPreference::MAINTAIN_FRAMERATE: {
|
||||
// Attempt to increase pixel count.
|
||||
return IncreaseResolution(input_state, current_restrictions_);
|
||||
}
|
||||
case DegradationPreference::MAINTAIN_RESOLUTION: {
|
||||
// Scale up framerate.
|
||||
return IncreaseFramerate(input_state, current_restrictions_);
|
||||
}
|
||||
case DegradationPreference::DISABLED:
|
||||
return Adaptation::Status::kAdaptationDisabled;
|
||||
}
|
||||
RTC_CHECK_NOTREACHED();
|
||||
}
|
||||
|
||||
Adaptation VideoStreamAdapter::GetAdaptationDown() {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
VideoStreamInputState input_state = input_state_provider_->InputState();
|
||||
++adaptation_validation_id_;
|
||||
RestrictionsOrState restrictions_or_state =
|
||||
GetAdaptationDownStep(input_state, current_restrictions_);
|
||||
if (MinPixelLimitReached(input_state)) {
|
||||
encoder_stats_observer_->OnMinPixelLimitReached();
|
||||
}
|
||||
// Check for min_fps
|
||||
if (degradation_preference_ == DegradationPreference::BALANCED &&
|
||||
absl::holds_alternative<RestrictionsWithCounters>(
|
||||
restrictions_or_state)) {
|
||||
restrictions_or_state = AdaptIfFpsDiffInsufficient(
|
||||
input_state,
|
||||
absl::get<RestrictionsWithCounters>(restrictions_or_state));
|
||||
}
|
||||
return RestrictionsOrStateToAdaptation(restrictions_or_state, input_state);
|
||||
}
|
||||
|
||||
VideoStreamAdapter::RestrictionsOrState
|
||||
VideoStreamAdapter::AdaptIfFpsDiffInsufficient(
|
||||
const VideoStreamInputState& input_state,
|
||||
const RestrictionsWithCounters& restrictions) const {
|
||||
RTC_DCHECK_EQ(degradation_preference_, DegradationPreference::BALANCED);
|
||||
int frame_size_pixels = input_state.single_active_stream_pixels().value_or(
|
||||
input_state.frame_size_pixels().value());
|
||||
absl::optional<int> min_fps_diff =
|
||||
balanced_settings_.MinFpsDiff(frame_size_pixels);
|
||||
if (current_restrictions_.counters.fps_adaptations <
|
||||
restrictions.counters.fps_adaptations &&
|
||||
min_fps_diff && input_state.frames_per_second() > 0) {
|
||||
int fps_diff = input_state.frames_per_second() -
|
||||
restrictions.restrictions.max_frame_rate().value();
|
||||
if (fps_diff < min_fps_diff.value()) {
|
||||
return GetAdaptationDownStep(input_state, restrictions);
|
||||
}
|
||||
}
|
||||
return restrictions;
|
||||
}
|
||||
|
||||
VideoStreamAdapter::RestrictionsOrState
|
||||
VideoStreamAdapter::GetAdaptationDownStep(
|
||||
const VideoStreamInputState& input_state,
|
||||
const RestrictionsWithCounters& current_restrictions) const {
|
||||
if (!HasSufficientInputForAdaptation(input_state)) {
|
||||
return Adaptation::Status::kInsufficientInput;
|
||||
}
|
||||
// Don't adapt if we're awaiting a previous adaptation to have an effect or
|
||||
// if we switched degradation preference.
|
||||
if (awaiting_frame_size_change_ &&
|
||||
!awaiting_frame_size_change_->pixels_increased &&
|
||||
degradation_preference_ == DegradationPreference::MAINTAIN_FRAMERATE &&
|
||||
input_state.frame_size_pixels().value() >=
|
||||
awaiting_frame_size_change_->frame_size_pixels) {
|
||||
return Adaptation::Status::kAwaitingPreviousAdaptation;
|
||||
}
|
||||
// Maybe propose targets based on degradation preference.
|
||||
switch (degradation_preference_) {
|
||||
case DegradationPreference::BALANCED: {
|
||||
// Try scale down framerate, if lower.
|
||||
RestrictionsOrState decrease_frame_rate =
|
||||
DecreaseFramerate(input_state, current_restrictions);
|
||||
if (absl::holds_alternative<RestrictionsWithCounters>(
|
||||
decrease_frame_rate)) {
|
||||
return decrease_frame_rate;
|
||||
}
|
||||
// else, decrease resolution.
|
||||
[[fallthrough]];
|
||||
}
|
||||
case DegradationPreference::MAINTAIN_FRAMERATE: {
|
||||
return DecreaseResolution(input_state, current_restrictions);
|
||||
}
|
||||
case DegradationPreference::MAINTAIN_RESOLUTION: {
|
||||
return DecreaseFramerate(input_state, current_restrictions);
|
||||
}
|
||||
case DegradationPreference::DISABLED:
|
||||
return Adaptation::Status::kAdaptationDisabled;
|
||||
}
|
||||
RTC_CHECK_NOTREACHED();
|
||||
}
|
||||
|
||||
VideoStreamAdapter::RestrictionsOrState VideoStreamAdapter::DecreaseResolution(
|
||||
const VideoStreamInputState& input_state,
|
||||
const RestrictionsWithCounters& current_restrictions) {
|
||||
int target_pixels =
|
||||
GetLowerResolutionThan(input_state.frame_size_pixels().value());
|
||||
// Use single active stream if set, this stream could be lower than the input.
|
||||
int target_pixels_min =
|
||||
GetLowerResolutionThan(input_state.single_active_stream_pixels().value_or(
|
||||
input_state.frame_size_pixels().value()));
|
||||
if (!CanDecreaseResolutionTo(target_pixels, target_pixels_min, input_state,
|
||||
current_restrictions.restrictions)) {
|
||||
return Adaptation::Status::kLimitReached;
|
||||
}
|
||||
RestrictionsWithCounters new_restrictions = current_restrictions;
|
||||
RTC_LOG(LS_INFO) << "Scaling down resolution, max pixels: " << target_pixels;
|
||||
new_restrictions.restrictions.set_max_pixels_per_frame(
|
||||
target_pixels != std::numeric_limits<int>::max()
|
||||
? absl::optional<size_t>(target_pixels)
|
||||
: absl::nullopt);
|
||||
new_restrictions.restrictions.set_target_pixels_per_frame(absl::nullopt);
|
||||
++new_restrictions.counters.resolution_adaptations;
|
||||
return new_restrictions;
|
||||
}
|
||||
|
||||
VideoStreamAdapter::RestrictionsOrState VideoStreamAdapter::DecreaseFramerate(
|
||||
const VideoStreamInputState& input_state,
|
||||
const RestrictionsWithCounters& current_restrictions) const {
|
||||
int max_frame_rate;
|
||||
if (degradation_preference_ == DegradationPreference::MAINTAIN_RESOLUTION) {
|
||||
max_frame_rate = GetLowerFrameRateThan(input_state.frames_per_second());
|
||||
} else if (degradation_preference_ == DegradationPreference::BALANCED) {
|
||||
int frame_size_pixels = input_state.single_active_stream_pixels().value_or(
|
||||
input_state.frame_size_pixels().value());
|
||||
max_frame_rate = balanced_settings_.MinFps(input_state.video_codec_type(),
|
||||
frame_size_pixels);
|
||||
} else {
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
max_frame_rate = GetLowerFrameRateThan(input_state.frames_per_second());
|
||||
}
|
||||
if (!CanDecreaseFrameRateTo(max_frame_rate,
|
||||
current_restrictions.restrictions)) {
|
||||
return Adaptation::Status::kLimitReached;
|
||||
}
|
||||
RestrictionsWithCounters new_restrictions = current_restrictions;
|
||||
max_frame_rate = std::max(kMinFrameRateFps, max_frame_rate);
|
||||
RTC_LOG(LS_INFO) << "Scaling down framerate: " << max_frame_rate;
|
||||
new_restrictions.restrictions.set_max_frame_rate(
|
||||
max_frame_rate != std::numeric_limits<int>::max()
|
||||
? absl::optional<double>(max_frame_rate)
|
||||
: absl::nullopt);
|
||||
++new_restrictions.counters.fps_adaptations;
|
||||
return new_restrictions;
|
||||
}
|
||||
|
||||
VideoStreamAdapter::RestrictionsOrState VideoStreamAdapter::IncreaseResolution(
|
||||
const VideoStreamInputState& input_state,
|
||||
const RestrictionsWithCounters& current_restrictions) {
|
||||
int target_pixels = input_state.frame_size_pixels().value();
|
||||
if (current_restrictions.counters.resolution_adaptations == 1) {
|
||||
RTC_LOG(LS_INFO) << "Removing resolution down-scaling setting.";
|
||||
target_pixels = std::numeric_limits<int>::max();
|
||||
}
|
||||
target_pixels = GetHigherResolutionThan(target_pixels);
|
||||
if (!CanIncreaseResolutionTo(target_pixels,
|
||||
current_restrictions.restrictions)) {
|
||||
return Adaptation::Status::kLimitReached;
|
||||
}
|
||||
int max_pixels_wanted = GetIncreasedMaxPixelsWanted(target_pixels);
|
||||
RestrictionsWithCounters new_restrictions = current_restrictions;
|
||||
RTC_LOG(LS_INFO) << "Scaling up resolution, max pixels: "
|
||||
<< max_pixels_wanted;
|
||||
new_restrictions.restrictions.set_max_pixels_per_frame(
|
||||
max_pixels_wanted != std::numeric_limits<int>::max()
|
||||
? absl::optional<size_t>(max_pixels_wanted)
|
||||
: absl::nullopt);
|
||||
new_restrictions.restrictions.set_target_pixels_per_frame(
|
||||
max_pixels_wanted != std::numeric_limits<int>::max()
|
||||
? absl::optional<size_t>(target_pixels)
|
||||
: absl::nullopt);
|
||||
--new_restrictions.counters.resolution_adaptations;
|
||||
RTC_DCHECK_GE(new_restrictions.counters.resolution_adaptations, 0);
|
||||
return new_restrictions;
|
||||
}
|
||||
|
||||
VideoStreamAdapter::RestrictionsOrState VideoStreamAdapter::IncreaseFramerate(
|
||||
const VideoStreamInputState& input_state,
|
||||
const RestrictionsWithCounters& current_restrictions) const {
|
||||
int max_frame_rate;
|
||||
if (degradation_preference_ == DegradationPreference::MAINTAIN_RESOLUTION) {
|
||||
max_frame_rate = GetHigherFrameRateThan(input_state.frames_per_second());
|
||||
} else if (degradation_preference_ == DegradationPreference::BALANCED) {
|
||||
int frame_size_pixels = input_state.single_active_stream_pixels().value_or(
|
||||
input_state.frame_size_pixels().value());
|
||||
max_frame_rate = balanced_settings_.MaxFps(input_state.video_codec_type(),
|
||||
frame_size_pixels);
|
||||
// Temporary fix for cases when there are fewer framerate adaptation steps
|
||||
// up than down. Make number of down/up steps equal.
|
||||
if (max_frame_rate == std::numeric_limits<int>::max() &&
|
||||
current_restrictions.counters.fps_adaptations > 1) {
|
||||
// Do not unrestrict framerate to allow additional adaptation up steps.
|
||||
RTC_LOG(LS_INFO) << "Modifying framerate due to remaining fps count.";
|
||||
max_frame_rate -= current_restrictions.counters.fps_adaptations;
|
||||
}
|
||||
// In BALANCED, the max_frame_rate must be checked before proceeding. This
|
||||
// is because the MaxFps might be the current Fps and so the balanced
|
||||
// settings may want to scale up the resolution.
|
||||
if (!CanIncreaseFrameRateTo(max_frame_rate,
|
||||
current_restrictions.restrictions)) {
|
||||
return Adaptation::Status::kLimitReached;
|
||||
}
|
||||
} else {
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
max_frame_rate = GetHigherFrameRateThan(input_state.frames_per_second());
|
||||
}
|
||||
if (current_restrictions.counters.fps_adaptations == 1) {
|
||||
RTC_LOG(LS_INFO) << "Removing framerate down-scaling setting.";
|
||||
max_frame_rate = std::numeric_limits<int>::max();
|
||||
}
|
||||
if (!CanIncreaseFrameRateTo(max_frame_rate,
|
||||
current_restrictions.restrictions)) {
|
||||
return Adaptation::Status::kLimitReached;
|
||||
}
|
||||
RTC_LOG(LS_INFO) << "Scaling up framerate: " << max_frame_rate;
|
||||
RestrictionsWithCounters new_restrictions = current_restrictions;
|
||||
new_restrictions.restrictions.set_max_frame_rate(
|
||||
max_frame_rate != std::numeric_limits<int>::max()
|
||||
? absl::optional<double>(max_frame_rate)
|
||||
: absl::nullopt);
|
||||
--new_restrictions.counters.fps_adaptations;
|
||||
RTC_DCHECK_GE(new_restrictions.counters.fps_adaptations, 0);
|
||||
return new_restrictions;
|
||||
}
|
||||
|
||||
Adaptation VideoStreamAdapter::GetAdaptDownResolution() {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
VideoStreamInputState input_state = input_state_provider_->InputState();
|
||||
switch (degradation_preference_) {
|
||||
case DegradationPreference::DISABLED:
|
||||
return RestrictionsOrStateToAdaptation(
|
||||
Adaptation::Status::kAdaptationDisabled, input_state);
|
||||
case DegradationPreference::MAINTAIN_RESOLUTION:
|
||||
return RestrictionsOrStateToAdaptation(Adaptation::Status::kLimitReached,
|
||||
input_state);
|
||||
case DegradationPreference::MAINTAIN_FRAMERATE:
|
||||
return GetAdaptationDown();
|
||||
case DegradationPreference::BALANCED: {
|
||||
return RestrictionsOrStateToAdaptation(
|
||||
GetAdaptDownResolutionStepForBalanced(input_state), input_state);
|
||||
}
|
||||
}
|
||||
RTC_CHECK_NOTREACHED();
|
||||
}
|
||||
|
||||
VideoStreamAdapter::RestrictionsOrState
|
||||
VideoStreamAdapter::GetAdaptDownResolutionStepForBalanced(
|
||||
const VideoStreamInputState& input_state) const {
|
||||
// Adapt twice if the first adaptation did not decrease resolution.
|
||||
auto first_step = GetAdaptationDownStep(input_state, current_restrictions_);
|
||||
if (!absl::holds_alternative<RestrictionsWithCounters>(first_step)) {
|
||||
return first_step;
|
||||
}
|
||||
auto first_restrictions = absl::get<RestrictionsWithCounters>(first_step);
|
||||
if (first_restrictions.counters.resolution_adaptations >
|
||||
current_restrictions_.counters.resolution_adaptations) {
|
||||
return first_step;
|
||||
}
|
||||
// We didn't decrease resolution so force it; amend a resolution resuction
|
||||
// to the existing framerate reduction in `first_restrictions`.
|
||||
auto second_step = DecreaseResolution(input_state, first_restrictions);
|
||||
if (absl::holds_alternative<RestrictionsWithCounters>(second_step)) {
|
||||
return second_step;
|
||||
}
|
||||
// If the second step was not successful then settle for the first one.
|
||||
return first_step;
|
||||
}
|
||||
|
||||
void VideoStreamAdapter::ApplyAdaptation(
|
||||
const Adaptation& adaptation,
|
||||
rtc::scoped_refptr<Resource> resource) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
RTC_DCHECK_EQ(adaptation.validation_id_, adaptation_validation_id_);
|
||||
if (adaptation.status() != Adaptation::Status::kValid)
|
||||
return;
|
||||
// Remember the input pixels and fps of this adaptation. Used to avoid
|
||||
// adapting again before this adaptation has had an effect.
|
||||
if (DidIncreaseResolution(current_restrictions_.restrictions,
|
||||
adaptation.restrictions())) {
|
||||
awaiting_frame_size_change_.emplace(
|
||||
true, adaptation.input_state().frame_size_pixels().value());
|
||||
} else if (DidDecreaseResolution(current_restrictions_.restrictions,
|
||||
adaptation.restrictions())) {
|
||||
awaiting_frame_size_change_.emplace(
|
||||
false, adaptation.input_state().frame_size_pixels().value());
|
||||
} else {
|
||||
awaiting_frame_size_change_ = absl::nullopt;
|
||||
}
|
||||
current_restrictions_ = {adaptation.restrictions(), adaptation.counters()};
|
||||
BroadcastVideoRestrictionsUpdate(adaptation.input_state(), resource);
|
||||
}
|
||||
|
||||
Adaptation VideoStreamAdapter::GetAdaptationTo(
|
||||
const VideoAdaptationCounters& counters,
|
||||
const VideoSourceRestrictions& restrictions) {
|
||||
// Adapts up/down from the current levels so counters are equal.
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
VideoStreamInputState input_state = input_state_provider_->InputState();
|
||||
return Adaptation(adaptation_validation_id_, restrictions, counters,
|
||||
input_state);
|
||||
}
|
||||
|
||||
void VideoStreamAdapter::BroadcastVideoRestrictionsUpdate(
|
||||
const VideoStreamInputState& input_state,
|
||||
const rtc::scoped_refptr<Resource>& resource) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
VideoSourceRestrictions filtered = FilterRestrictionsByDegradationPreference(
|
||||
source_restrictions(), degradation_preference_);
|
||||
if (last_filtered_restrictions_ == filtered) {
|
||||
return;
|
||||
}
|
||||
for (auto* restrictions_listener : restrictions_listeners_) {
|
||||
restrictions_listener->OnVideoSourceRestrictionsUpdated(
|
||||
filtered, current_restrictions_.counters, resource,
|
||||
source_restrictions());
|
||||
}
|
||||
last_video_source_restrictions_ = current_restrictions_.restrictions;
|
||||
last_filtered_restrictions_ = filtered;
|
||||
}
|
||||
|
||||
bool VideoStreamAdapter::HasSufficientInputForAdaptation(
|
||||
const VideoStreamInputState& input_state) const {
|
||||
return input_state.HasInputFrameSizeAndFramesPerSecond() &&
|
||||
(degradation_preference_ !=
|
||||
DegradationPreference::MAINTAIN_RESOLUTION ||
|
||||
input_state.frames_per_second() >= kMinFrameRateFps);
|
||||
}
|
||||
|
||||
VideoStreamAdapter::AwaitingFrameSizeChange::AwaitingFrameSizeChange(
|
||||
bool pixels_increased,
|
||||
int frame_size_pixels)
|
||||
: pixels_increased(pixels_increased),
|
||||
frame_size_pixels(frame_size_pixels) {}
|
||||
|
||||
absl::optional<uint32_t> VideoStreamAdapter::GetSingleActiveLayerPixels(
|
||||
const VideoCodec& codec) {
|
||||
int num_active = 0;
|
||||
absl::optional<uint32_t> pixels;
|
||||
if (codec.codecType == VideoCodecType::kVideoCodecAV1 &&
|
||||
codec.GetScalabilityMode().has_value()) {
|
||||
for (int i = 0;
|
||||
i < ScalabilityModeToNumSpatialLayers(*(codec.GetScalabilityMode()));
|
||||
++i) {
|
||||
if (codec.spatialLayers[i].active) {
|
||||
++num_active;
|
||||
pixels = codec.spatialLayers[i].width * codec.spatialLayers[i].height;
|
||||
}
|
||||
}
|
||||
} else if (codec.codecType == VideoCodecType::kVideoCodecVP9) {
|
||||
for (int i = 0; i < codec.VP9().numberOfSpatialLayers; ++i) {
|
||||
if (codec.spatialLayers[i].active) {
|
||||
++num_active;
|
||||
pixels = codec.spatialLayers[i].width * codec.spatialLayers[i].height;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < codec.numberOfSimulcastStreams; ++i) {
|
||||
if (codec.simulcastStream[i].active) {
|
||||
++num_active;
|
||||
pixels =
|
||||
codec.simulcastStream[i].width * codec.simulcastStream[i].height;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (num_active > 1) ? absl::nullopt : pixels;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef CALL_ADAPTATION_VIDEO_STREAM_ADAPTER_H_
|
||||
#define CALL_ADAPTATION_VIDEO_STREAM_ADAPTER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "absl/types/variant.h"
|
||||
#include "api/adaptation/resource.h"
|
||||
#include "api/field_trials_view.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/video/video_adaptation_counters.h"
|
||||
#include "call/adaptation/adaptation_constraint.h"
|
||||
#include "call/adaptation/degradation_preference_provider.h"
|
||||
#include "call/adaptation/video_source_restrictions.h"
|
||||
#include "call/adaptation/video_stream_input_state.h"
|
||||
#include "call/adaptation/video_stream_input_state_provider.h"
|
||||
#include "modules/video_coding/utility/quality_scaler.h"
|
||||
#include "rtc_base/experiments/balanced_degradation_settings.h"
|
||||
#include "rtc_base/system/no_unique_address.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
#include "video/video_stream_encoder_observer.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// The listener is responsible for carrying out the reconfiguration of the video
|
||||
// source such that the VideoSourceRestrictions are fulfilled.
|
||||
class VideoSourceRestrictionsListener {
|
||||
public:
|
||||
virtual ~VideoSourceRestrictionsListener();
|
||||
|
||||
// The `restrictions` are filtered by degradation preference but not the
|
||||
// `adaptation_counters`, which are currently only reported for legacy stats
|
||||
// calculation purposes.
|
||||
virtual void OnVideoSourceRestrictionsUpdated(
|
||||
VideoSourceRestrictions restrictions,
|
||||
const VideoAdaptationCounters& adaptation_counters,
|
||||
rtc::scoped_refptr<Resource> reason,
|
||||
const VideoSourceRestrictions& unfiltered_restrictions) = 0;
|
||||
};
|
||||
|
||||
class VideoStreamAdapter;
|
||||
|
||||
extern const int kMinFrameRateFps;
|
||||
|
||||
VideoSourceRestrictions FilterRestrictionsByDegradationPreference(
|
||||
VideoSourceRestrictions source_restrictions,
|
||||
DegradationPreference degradation_preference);
|
||||
|
||||
int GetLowerResolutionThan(int pixel_count);
|
||||
int GetHigherResolutionThan(int pixel_count);
|
||||
|
||||
// Either represents the next VideoSourceRestrictions the VideoStreamAdapter
|
||||
// will take, or provides a Status code indicating the reason for not adapting
|
||||
// if the adaptation is not valid.
|
||||
class Adaptation final {
|
||||
public:
|
||||
enum class Status {
|
||||
// Applying this adaptation will have an effect. All other Status codes
|
||||
// indicate that adaptation is not possible and why.
|
||||
kValid,
|
||||
// Cannot adapt. The minimum or maximum adaptation has already been reached.
|
||||
// There are no more steps to take.
|
||||
kLimitReached,
|
||||
// Cannot adapt. The resolution or frame rate requested by a recent
|
||||
// adaptation has not yet been reflected in the input resolution or frame
|
||||
// rate; adaptation is refused to avoid "double-adapting".
|
||||
kAwaitingPreviousAdaptation,
|
||||
// Not enough input.
|
||||
kInsufficientInput,
|
||||
// Adaptation disabled via degradation preference.
|
||||
kAdaptationDisabled,
|
||||
// Adaptation up was rejected by a VideoAdaptationConstraint.
|
||||
kRejectedByConstraint,
|
||||
};
|
||||
|
||||
static const char* StatusToString(Status status);
|
||||
|
||||
Status status() const;
|
||||
const VideoStreamInputState& input_state() const;
|
||||
const VideoSourceRestrictions& restrictions() const;
|
||||
const VideoAdaptationCounters& counters() const;
|
||||
|
||||
private:
|
||||
friend class VideoStreamAdapter;
|
||||
|
||||
// Constructs with a valid adaptation. Status is kValid.
|
||||
Adaptation(int validation_id,
|
||||
VideoSourceRestrictions restrictions,
|
||||
VideoAdaptationCounters counters,
|
||||
VideoStreamInputState input_state);
|
||||
// Constructor when adaptation is not valid. Status MUST NOT be kValid.
|
||||
Adaptation(int validation_id, Status invalid_status);
|
||||
|
||||
// An Adaptation can become invalidated if the state of VideoStreamAdapter is
|
||||
// modified before the Adaptation is applied. To guard against this, this ID
|
||||
// has to match VideoStreamAdapter::adaptation_validation_id_ when applied.
|
||||
// TODO(https://crbug.com/webrtc/11700): Remove the validation_id_.
|
||||
const int validation_id_;
|
||||
const Status status_;
|
||||
// Input state when adaptation was made.
|
||||
const VideoStreamInputState input_state_;
|
||||
const VideoSourceRestrictions restrictions_;
|
||||
const VideoAdaptationCounters counters_;
|
||||
};
|
||||
|
||||
// Owns the VideoSourceRestriction for a single stream and is responsible for
|
||||
// adapting it up or down when told to do so. This class serves the following
|
||||
// purposes:
|
||||
// 1. Keep track of a stream's restrictions.
|
||||
// 2. Provide valid ways to adapt up or down the stream's restrictions.
|
||||
// 3. Modify the stream's restrictions in one of the valid ways.
|
||||
class VideoStreamAdapter {
|
||||
public:
|
||||
VideoStreamAdapter(VideoStreamInputStateProvider* input_state_provider,
|
||||
VideoStreamEncoderObserver* encoder_stats_observer,
|
||||
const FieldTrialsView& field_trials);
|
||||
~VideoStreamAdapter();
|
||||
|
||||
VideoSourceRestrictions source_restrictions() const;
|
||||
const VideoAdaptationCounters& adaptation_counters() const;
|
||||
void ClearRestrictions();
|
||||
|
||||
void AddRestrictionsListener(
|
||||
VideoSourceRestrictionsListener* restrictions_listener);
|
||||
void RemoveRestrictionsListener(
|
||||
VideoSourceRestrictionsListener* restrictions_listener);
|
||||
void AddAdaptationConstraint(AdaptationConstraint* adaptation_constraint);
|
||||
void RemoveAdaptationConstraint(AdaptationConstraint* adaptation_constraint);
|
||||
|
||||
// TODO(hbos): Setting the degradation preference should not clear
|
||||
// restrictions! This is not defined in the spec and is unexpected, there is a
|
||||
// tiny risk that people would discover and rely on this behavior.
|
||||
void SetDegradationPreference(DegradationPreference degradation_preference);
|
||||
|
||||
// Returns an adaptation that we are guaranteed to be able to apply, or a
|
||||
// status code indicating the reason why we cannot adapt.
|
||||
Adaptation GetAdaptationUp();
|
||||
Adaptation GetAdaptationDown();
|
||||
Adaptation GetAdaptationTo(const VideoAdaptationCounters& counters,
|
||||
const VideoSourceRestrictions& restrictions);
|
||||
// Tries to adapt the resolution one step. This is used for initial frame
|
||||
// dropping. Does nothing if the degradation preference is not BALANCED or
|
||||
// MAINTAIN_FRAMERATE. In the case of BALANCED, it will try twice to reduce
|
||||
// the resolution. If it fails twice it gives up.
|
||||
Adaptation GetAdaptDownResolution();
|
||||
|
||||
// Updates source_restrictions() the Adaptation.
|
||||
void ApplyAdaptation(const Adaptation& adaptation,
|
||||
rtc::scoped_refptr<Resource> resource);
|
||||
|
||||
struct RestrictionsWithCounters {
|
||||
VideoSourceRestrictions restrictions;
|
||||
VideoAdaptationCounters counters;
|
||||
};
|
||||
|
||||
static absl::optional<uint32_t> GetSingleActiveLayerPixels(
|
||||
const VideoCodec& codec);
|
||||
|
||||
private:
|
||||
void BroadcastVideoRestrictionsUpdate(
|
||||
const VideoStreamInputState& input_state,
|
||||
const rtc::scoped_refptr<Resource>& resource);
|
||||
|
||||
bool HasSufficientInputForAdaptation(const VideoStreamInputState& input_state)
|
||||
const RTC_RUN_ON(&sequence_checker_);
|
||||
|
||||
using RestrictionsOrState =
|
||||
absl::variant<RestrictionsWithCounters, Adaptation::Status>;
|
||||
RestrictionsOrState GetAdaptationUpStep(
|
||||
const VideoStreamInputState& input_state) const
|
||||
RTC_RUN_ON(&sequence_checker_);
|
||||
RestrictionsOrState GetAdaptationDownStep(
|
||||
const VideoStreamInputState& input_state,
|
||||
const RestrictionsWithCounters& current_restrictions) const
|
||||
RTC_RUN_ON(&sequence_checker_);
|
||||
RestrictionsOrState GetAdaptDownResolutionStepForBalanced(
|
||||
const VideoStreamInputState& input_state) const
|
||||
RTC_RUN_ON(&sequence_checker_);
|
||||
RestrictionsOrState AdaptIfFpsDiffInsufficient(
|
||||
const VideoStreamInputState& input_state,
|
||||
const RestrictionsWithCounters& restrictions) const
|
||||
RTC_RUN_ON(&sequence_checker_);
|
||||
|
||||
Adaptation GetAdaptationUp(const VideoStreamInputState& input_state) const
|
||||
RTC_RUN_ON(&sequence_checker_);
|
||||
Adaptation GetAdaptationDown(const VideoStreamInputState& input_state) const
|
||||
RTC_RUN_ON(&sequence_checker_);
|
||||
|
||||
static RestrictionsOrState DecreaseResolution(
|
||||
const VideoStreamInputState& input_state,
|
||||
const RestrictionsWithCounters& current_restrictions);
|
||||
static RestrictionsOrState IncreaseResolution(
|
||||
const VideoStreamInputState& input_state,
|
||||
const RestrictionsWithCounters& current_restrictions);
|
||||
// Framerate methods are member functions because they need internal state
|
||||
// if the degradation preference is BALANCED.
|
||||
RestrictionsOrState DecreaseFramerate(
|
||||
const VideoStreamInputState& input_state,
|
||||
const RestrictionsWithCounters& current_restrictions) const
|
||||
RTC_RUN_ON(&sequence_checker_);
|
||||
RestrictionsOrState IncreaseFramerate(
|
||||
const VideoStreamInputState& input_state,
|
||||
const RestrictionsWithCounters& current_restrictions) const
|
||||
RTC_RUN_ON(&sequence_checker_);
|
||||
|
||||
struct RestrictionsOrStateVisitor;
|
||||
Adaptation RestrictionsOrStateToAdaptation(
|
||||
RestrictionsOrState step_or_state,
|
||||
const VideoStreamInputState& input_state) const
|
||||
RTC_RUN_ON(&sequence_checker_);
|
||||
|
||||
RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_
|
||||
RTC_GUARDED_BY(&sequence_checker_);
|
||||
// Gets the input state which is the basis of all adaptations.
|
||||
// Thread safe.
|
||||
VideoStreamInputStateProvider* input_state_provider_;
|
||||
// Used to signal when min pixel limit has been reached.
|
||||
VideoStreamEncoderObserver* const encoder_stats_observer_;
|
||||
// Decides the next adaptation target in DegradationPreference::BALANCED.
|
||||
const BalancedDegradationSettings balanced_settings_;
|
||||
// To guard against applying adaptations that have become invalidated, an
|
||||
// Adaptation that is applied has to have a matching validation ID.
|
||||
int adaptation_validation_id_ RTC_GUARDED_BY(&sequence_checker_);
|
||||
// When deciding the next target up or down, different strategies are used
|
||||
// depending on the DegradationPreference.
|
||||
// https://w3c.github.io/mst-content-hint/#dom-rtcdegradationpreference
|
||||
DegradationPreference degradation_preference_
|
||||
RTC_GUARDED_BY(&sequence_checker_);
|
||||
// Used to avoid adapting twice. Stores the resolution at the time of the last
|
||||
// adaptation.
|
||||
// TODO(hbos): Can we implement a more general "cooldown" mechanism of
|
||||
// resources intead? If we already have adapted it seems like we should wait
|
||||
// a while before adapting again, so that we are not acting on usage
|
||||
// measurements that are made obsolete/unreliable by an "ongoing" adaptation.
|
||||
struct AwaitingFrameSizeChange {
|
||||
AwaitingFrameSizeChange(bool pixels_increased, int frame_size);
|
||||
const bool pixels_increased;
|
||||
const int frame_size_pixels;
|
||||
};
|
||||
absl::optional<AwaitingFrameSizeChange> awaiting_frame_size_change_
|
||||
RTC_GUARDED_BY(&sequence_checker_);
|
||||
// The previous restrictions value. Starts as unrestricted.
|
||||
VideoSourceRestrictions last_video_source_restrictions_
|
||||
RTC_GUARDED_BY(&sequence_checker_);
|
||||
VideoSourceRestrictions last_filtered_restrictions_
|
||||
RTC_GUARDED_BY(&sequence_checker_);
|
||||
|
||||
std::vector<VideoSourceRestrictionsListener*> restrictions_listeners_
|
||||
RTC_GUARDED_BY(&sequence_checker_);
|
||||
std::vector<AdaptationConstraint*> adaptation_constraints_
|
||||
RTC_GUARDED_BY(&sequence_checker_);
|
||||
|
||||
RestrictionsWithCounters current_restrictions_
|
||||
RTC_GUARDED_BY(&sequence_checker_);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // CALL_ADAPTATION_VIDEO_STREAM_ADAPTER_H_
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "call/adaptation/video_stream_input_state.h"
|
||||
|
||||
#include "api/video_codecs/video_encoder.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
VideoStreamInputState::VideoStreamInputState()
|
||||
: has_input_(false),
|
||||
frame_size_pixels_(absl::nullopt),
|
||||
frames_per_second_(0),
|
||||
video_codec_type_(VideoCodecType::kVideoCodecGeneric),
|
||||
min_pixels_per_frame_(kDefaultMinPixelsPerFrame),
|
||||
single_active_stream_pixels_(absl::nullopt) {}
|
||||
|
||||
void VideoStreamInputState::set_has_input(bool has_input) {
|
||||
has_input_ = has_input;
|
||||
}
|
||||
|
||||
void VideoStreamInputState::set_frame_size_pixels(
|
||||
absl::optional<int> frame_size_pixels) {
|
||||
frame_size_pixels_ = frame_size_pixels;
|
||||
}
|
||||
|
||||
void VideoStreamInputState::set_frames_per_second(int frames_per_second) {
|
||||
frames_per_second_ = frames_per_second;
|
||||
}
|
||||
|
||||
void VideoStreamInputState::set_video_codec_type(
|
||||
VideoCodecType video_codec_type) {
|
||||
video_codec_type_ = video_codec_type;
|
||||
}
|
||||
|
||||
void VideoStreamInputState::set_min_pixels_per_frame(int min_pixels_per_frame) {
|
||||
min_pixels_per_frame_ = min_pixels_per_frame;
|
||||
}
|
||||
|
||||
void VideoStreamInputState::set_single_active_stream_pixels(
|
||||
absl::optional<int> single_active_stream_pixels) {
|
||||
single_active_stream_pixels_ = single_active_stream_pixels;
|
||||
}
|
||||
|
||||
bool VideoStreamInputState::has_input() const {
|
||||
return has_input_;
|
||||
}
|
||||
|
||||
absl::optional<int> VideoStreamInputState::frame_size_pixels() const {
|
||||
return frame_size_pixels_;
|
||||
}
|
||||
|
||||
int VideoStreamInputState::frames_per_second() const {
|
||||
return frames_per_second_;
|
||||
}
|
||||
|
||||
VideoCodecType VideoStreamInputState::video_codec_type() const {
|
||||
return video_codec_type_;
|
||||
}
|
||||
|
||||
int VideoStreamInputState::min_pixels_per_frame() const {
|
||||
return min_pixels_per_frame_;
|
||||
}
|
||||
|
||||
absl::optional<int> VideoStreamInputState::single_active_stream_pixels() const {
|
||||
return single_active_stream_pixels_;
|
||||
}
|
||||
|
||||
bool VideoStreamInputState::HasInputFrameSizeAndFramesPerSecond() const {
|
||||
return has_input_ && frame_size_pixels_.has_value();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef CALL_ADAPTATION_VIDEO_STREAM_INPUT_STATE_H_
|
||||
#define CALL_ADAPTATION_VIDEO_STREAM_INPUT_STATE_H_
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/video/video_codec_type.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// The source resolution, frame rate and other properties of a
|
||||
// VideoStreamEncoder.
|
||||
class VideoStreamInputState {
|
||||
public:
|
||||
VideoStreamInputState();
|
||||
|
||||
void set_has_input(bool has_input);
|
||||
void set_frame_size_pixels(absl::optional<int> frame_size_pixels);
|
||||
void set_frames_per_second(int frames_per_second);
|
||||
void set_video_codec_type(VideoCodecType video_codec_type);
|
||||
void set_min_pixels_per_frame(int min_pixels_per_frame);
|
||||
void set_single_active_stream_pixels(
|
||||
absl::optional<int> single_active_stream_pixels);
|
||||
|
||||
bool has_input() const;
|
||||
absl::optional<int> frame_size_pixels() const;
|
||||
int frames_per_second() const;
|
||||
VideoCodecType video_codec_type() const;
|
||||
int min_pixels_per_frame() const;
|
||||
absl::optional<int> single_active_stream_pixels() const;
|
||||
|
||||
bool HasInputFrameSizeAndFramesPerSecond() const;
|
||||
|
||||
private:
|
||||
bool has_input_;
|
||||
absl::optional<int> frame_size_pixels_;
|
||||
int frames_per_second_;
|
||||
VideoCodecType video_codec_type_;
|
||||
int min_pixels_per_frame_;
|
||||
absl::optional<int> single_active_stream_pixels_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // CALL_ADAPTATION_VIDEO_STREAM_INPUT_STATE_H_
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "call/adaptation/video_stream_input_state_provider.h"
|
||||
|
||||
#include "call/adaptation/video_stream_adapter.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
VideoStreamInputStateProvider::VideoStreamInputStateProvider(
|
||||
VideoStreamEncoderObserver* frame_rate_provider)
|
||||
: frame_rate_provider_(frame_rate_provider) {}
|
||||
|
||||
VideoStreamInputStateProvider::~VideoStreamInputStateProvider() {}
|
||||
|
||||
void VideoStreamInputStateProvider::OnHasInputChanged(bool has_input) {
|
||||
MutexLock lock(&mutex_);
|
||||
input_state_.set_has_input(has_input);
|
||||
}
|
||||
|
||||
void VideoStreamInputStateProvider::OnFrameSizeObserved(int frame_size_pixels) {
|
||||
RTC_DCHECK_GT(frame_size_pixels, 0);
|
||||
MutexLock lock(&mutex_);
|
||||
input_state_.set_frame_size_pixels(frame_size_pixels);
|
||||
}
|
||||
|
||||
void VideoStreamInputStateProvider::OnEncoderSettingsChanged(
|
||||
EncoderSettings encoder_settings) {
|
||||
MutexLock lock(&mutex_);
|
||||
input_state_.set_video_codec_type(
|
||||
encoder_settings.encoder_config().codec_type);
|
||||
input_state_.set_min_pixels_per_frame(
|
||||
encoder_settings.encoder_info().scaling_settings.min_pixels_per_frame);
|
||||
input_state_.set_single_active_stream_pixels(
|
||||
VideoStreamAdapter::GetSingleActiveLayerPixels(
|
||||
encoder_settings.video_codec()));
|
||||
}
|
||||
|
||||
VideoStreamInputState VideoStreamInputStateProvider::InputState() {
|
||||
// GetInputFrameRate() is thread-safe.
|
||||
int input_fps = frame_rate_provider_->GetInputFrameRate();
|
||||
MutexLock lock(&mutex_);
|
||||
input_state_.set_frames_per_second(input_fps);
|
||||
return input_state_;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef CALL_ADAPTATION_VIDEO_STREAM_INPUT_STATE_PROVIDER_H_
|
||||
#define CALL_ADAPTATION_VIDEO_STREAM_INPUT_STATE_PROVIDER_H_
|
||||
|
||||
#include "call/adaptation/encoder_settings.h"
|
||||
#include "call/adaptation/video_stream_input_state.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "video/video_stream_encoder_observer.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class VideoStreamInputStateProvider {
|
||||
public:
|
||||
VideoStreamInputStateProvider(
|
||||
VideoStreamEncoderObserver* frame_rate_provider);
|
||||
virtual ~VideoStreamInputStateProvider();
|
||||
|
||||
void OnHasInputChanged(bool has_input);
|
||||
void OnFrameSizeObserved(int frame_size_pixels);
|
||||
void OnEncoderSettingsChanged(EncoderSettings encoder_settings);
|
||||
|
||||
virtual VideoStreamInputState InputState();
|
||||
|
||||
private:
|
||||
Mutex mutex_;
|
||||
VideoStreamEncoderObserver* const frame_rate_provider_;
|
||||
VideoStreamInputState input_state_ RTC_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // CALL_ADAPTATION_VIDEO_STREAM_INPUT_STATE_PROVIDER_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue