webrtc_m130/call/adaptation/resource_adaptation_processor.cc
Henrik Boström 87eece9421 [Adaptation] Introducing call/adaptation/ResourceAdaptationProcessor.
This CL is part of the Call-Level Adaptation Processing design doc:
https://docs.google.com/document/d/1ZyC26yOCknrrcYa839ZWLxD6o6Gig5A3lVTh4E41074/edit?usp=sharing

This gets to the heart of unblocking call-level adaptation, largely
made possible due to the previous CLs in the chain.

The parts of the code that are responsible for responding to resource
usage signals, obtaining adaptations and applying them are moved to
ResourceAdaptationProcessor in call/adaptation/.

The parts of the code that are responsible for managing
VideoStreamEncoder-specific resources stay inside the
VideoStreamEncoderResourceManager class in video/adaptation/.

After this CL lands it should soon be possible to move the Processor
over to a separate task queue and let the Manager stay on the encoder
queue if PostTasks are added for communication between the two objects.

Bug: webrtc:11172
Change-Id: Ifa212467b4afd16e7ebfb9adfe17d2dca1cb7d67
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/173021
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Evan Shrubsole <eshr@google.com>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31105}
2020-04-17 16:39:21 +00:00

210 lines
8.0 KiB
C++

/*
* 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 <utility>
#include "absl/algorithm/container.h"
namespace webrtc {
ResourceAdaptationProcessor::ResourceAdaptationProcessor(
VideoStreamInputStateProvider* input_state_provider,
VideoStreamEncoderObserver* encoder_stats_observer)
: input_state_provider_(input_state_provider),
encoder_stats_observer_(encoder_stats_observer),
resources_(),
degradation_preference_(DegradationPreference::DISABLED),
effective_degradation_preference_(DegradationPreference::DISABLED),
is_screenshare_(false),
stream_adapter_(std::make_unique<VideoStreamAdapter>()),
last_reported_source_restrictions_() {}
ResourceAdaptationProcessor::~ResourceAdaptationProcessor() {}
DegradationPreference ResourceAdaptationProcessor::degradation_preference()
const {
return degradation_preference_;
}
DegradationPreference
ResourceAdaptationProcessor::effective_degradation_preference() const {
return effective_degradation_preference_;
}
void ResourceAdaptationProcessor::StartResourceAdaptation() {
for (auto* resource : resources_) {
resource->RegisterListener(this);
}
}
void ResourceAdaptationProcessor::StopResourceAdaptation() {
for (auto* resource : resources_) {
resource->UnregisterListener(this);
}
}
void ResourceAdaptationProcessor::AddAdaptationListener(
ResourceAdaptationProcessorListener* adaptation_listener) {
adaptation_listeners_.push_back(adaptation_listener);
}
void ResourceAdaptationProcessor::AddResource(Resource* resource) {
resources_.push_back(resource);
}
void ResourceAdaptationProcessor::SetDegradationPreference(
DegradationPreference degradation_preference) {
degradation_preference_ = degradation_preference;
MaybeUpdateEffectiveDegradationPreference();
}
void ResourceAdaptationProcessor::SetIsScreenshare(bool is_screenshare) {
is_screenshare_ = is_screenshare;
MaybeUpdateEffectiveDegradationPreference();
}
void ResourceAdaptationProcessor::MaybeUpdateEffectiveDegradationPreference() {
effective_degradation_preference_ =
(is_screenshare_ &&
degradation_preference_ == DegradationPreference::BALANCED)
? DegradationPreference::MAINTAIN_RESOLUTION
: degradation_preference_;
stream_adapter_->SetDegradationPreference(effective_degradation_preference_);
MaybeUpdateVideoSourceRestrictions(nullptr);
}
void ResourceAdaptationProcessor::ResetVideoSourceRestrictions() {
stream_adapter_->ClearRestrictions();
MaybeUpdateVideoSourceRestrictions(nullptr);
}
void ResourceAdaptationProcessor::MaybeUpdateVideoSourceRestrictions(
const Resource* reason) {
VideoSourceRestrictions new_soure_restrictions =
FilterRestrictionsByDegradationPreference(
stream_adapter_->source_restrictions(),
effective_degradation_preference_);
if (last_reported_source_restrictions_ != new_soure_restrictions) {
last_reported_source_restrictions_ = std::move(new_soure_restrictions);
for (auto* adaptation_listener : adaptation_listeners_) {
adaptation_listener->OnVideoSourceRestrictionsUpdated(
last_reported_source_restrictions_,
stream_adapter_->adaptation_counters(), reason);
}
}
}
ResourceListenerResponse
ResourceAdaptationProcessor::OnResourceUsageStateMeasured(
const Resource& resource) {
switch (resource.usage_state()) {
case ResourceUsageState::kOveruse:
return OnResourceOveruse(resource);
case ResourceUsageState::kStable:
// TODO(https://crbug.com/webrtc/11172): Delete kStable in favor of null.
RTC_NOTREACHED();
return ResourceListenerResponse::kNothing;
case ResourceUsageState::kUnderuse:
OnResourceUnderuse(resource);
return ResourceListenerResponse::kNothing;
}
}
bool ResourceAdaptationProcessor::HasSufficientInputForAdaptation(
const VideoStreamInputState& input_state) const {
return input_state.HasInputFrameSizeAndFramesPerSecond() &&
(effective_degradation_preference_ !=
DegradationPreference::MAINTAIN_RESOLUTION ||
input_state.frames_per_second() >= kMinFrameRateFps);
}
void ResourceAdaptationProcessor::OnResourceUnderuse(
const Resource& reason_resource) {
VideoStreamInputState input_state = input_state_provider_->InputState();
if (effective_degradation_preference_ == DegradationPreference::DISABLED ||
!HasSufficientInputForAdaptation(input_state)) {
return;
}
// Update video input states and encoder settings for accurate adaptation.
stream_adapter_->SetInput(input_state);
// How can this stream be adapted up?
Adaptation adaptation = stream_adapter_->GetAdaptationUp();
if (adaptation.status() != Adaptation::Status::kValid)
return;
// Are all resources OK with this adaptation being applied?
VideoSourceRestrictions restrictions_before =
stream_adapter_->source_restrictions();
VideoSourceRestrictions restrictions_after =
stream_adapter_->PeekNextRestrictions(adaptation);
if (!absl::c_all_of(resources_, [&input_state, &restrictions_before,
&restrictions_after,
&reason_resource](const Resource* resource) {
return resource->IsAdaptationUpAllowed(input_state, restrictions_before,
restrictions_after,
reason_resource);
})) {
return;
}
// Apply adaptation.
stream_adapter_->ApplyAdaptation(adaptation);
// Update VideoSourceRestrictions based on adaptation. This also informs the
// |adaptation_listeners_|.
MaybeUpdateVideoSourceRestrictions(&reason_resource);
}
ResourceListenerResponse ResourceAdaptationProcessor::OnResourceOveruse(
const Resource& reason_resource) {
VideoStreamInputState input_state = input_state_provider_->InputState();
if (!input_state.has_input()) {
return ResourceListenerResponse::kQualityScalerShouldIncreaseFrequency;
}
if (effective_degradation_preference_ == DegradationPreference::DISABLED ||
!HasSufficientInputForAdaptation(input_state)) {
return ResourceListenerResponse::kNothing;
}
// Update video input states and encoder settings for accurate adaptation.
stream_adapter_->SetInput(input_state);
// How can this stream be adapted up?
Adaptation adaptation = stream_adapter_->GetAdaptationDown();
if (adaptation.min_pixel_limit_reached())
encoder_stats_observer_->OnMinPixelLimitReached();
if (adaptation.status() != Adaptation::Status::kValid)
return ResourceListenerResponse::kNothing;
// Apply adaptation.
ResourceListenerResponse response =
stream_adapter_->ApplyAdaptation(adaptation);
// Update VideoSourceRestrictions based on adaptation. This also informs the
// |adaptation_listeners_|.
MaybeUpdateVideoSourceRestrictions(&reason_resource);
return response;
}
void ResourceAdaptationProcessor::TriggerAdaptationDueToFrameDroppedDueToSize(
const Resource& reason_resource) {
VideoAdaptationCounters counters_before =
stream_adapter_->adaptation_counters();
OnResourceOveruse(reason_resource);
if (degradation_preference_ == DegradationPreference::BALANCED &&
stream_adapter_->adaptation_counters().fps_adaptations >
counters_before.fps_adaptations) {
// Oops, we adapted frame rate. Adapt again, maybe it will adapt resolution!
// Though this is not guaranteed...
OnResourceOveruse(reason_resource);
}
if (stream_adapter_->adaptation_counters().resolution_adaptations >
counters_before.resolution_adaptations) {
encoder_stats_observer_->OnInitialQualityResolutionAdaptDown();
}
}
} // namespace webrtc