blob: d028e5049a0dc9f7e6e30bb2692e3671cff3e558 [file] [log] [blame]
/*
* 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 VIDEO_ADAPTATION_VIDEO_STREAM_ENCODER_RESOURCE_MANAGER_H_
#define VIDEO_ADAPTATION_VIDEO_STREAM_ENCODER_RESOURCE_MANAGER_H_
#include <atomic>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "absl/types/optional.h"
#include "api/rtp_parameters.h"
#include "api/scoped_refptr.h"
#include "api/video/video_adaptation_counters.h"
#include "api/video/video_adaptation_reason.h"
#include "api/video/video_frame.h"
#include "api/video/video_source_interface.h"
#include "api/video/video_stream_encoder_observer.h"
#include "api/video_codecs/video_codec.h"
#include "api/video_codecs/video_encoder.h"
#include "api/video_codecs/video_encoder_config.h"
#include "call/adaptation/resource.h"
#include "call/adaptation/resource_adaptation_processor_interface.h"
#include "call/adaptation/video_stream_adapter.h"
#include "call/adaptation/video_stream_input_state_provider.h"
#include "rtc_base/critical_section.h"
#include "rtc_base/experiments/quality_rampup_experiment.h"
#include "rtc_base/experiments/quality_scaler_settings.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/task_queue.h"
#include "system_wrappers/include/clock.h"
#include "video/adaptation/encode_usage_resource.h"
#include "video/adaptation/overuse_frame_detector.h"
#include "video/adaptation/quality_scaler_resource.h"
namespace webrtc {
// The assumed input frame size if we have not yet received a frame.
// TODO(hbos): This is 144p - why are we assuming super low quality? Seems like
// a bad heuristic.
extern const int kDefaultInputPixelsWidth;
extern const int kDefaultInputPixelsHeight;
// Owns adaptation-related Resources pertaining to a single VideoStreamEncoder
// and passes on the relevant input from the encoder to the resources. The
// resources provide resource usage states to the ResourceAdaptationProcessor
// which is responsible for reconfiguring streams in order not to overuse
// resources.
//
// The manager is also involved with various mitigations not part of the
// ResourceAdaptationProcessor code such as the inital frame dropping.
class VideoStreamEncoderResourceManager
: public ResourceAdaptationProcessorListener {
public:
VideoStreamEncoderResourceManager(
VideoStreamInputStateProvider* input_state_provider,
VideoStreamEncoderObserver* encoder_stats_observer,
Clock* clock,
bool experiment_cpu_load_estimator,
std::unique_ptr<OveruseFrameDetector> overuse_detector);
~VideoStreamEncoderResourceManager() override;
void Initialize(rtc::TaskQueue* encoder_queue,
rtc::TaskQueue* resource_adaptation_queue);
void SetAdaptationProcessor(
ResourceAdaptationProcessorInterface* adaptation_processor);
// TODO(https://crbug.com/webrtc/11563): The degradation preference is a
// setting of the Processor, it does not belong to the Manager - can we get
// rid of this?
void SetDegradationPreferences(DegradationPreference degradation_preference);
DegradationPreference degradation_preference() const;
// Starts the encode usage resource. The quality scaler resource is
// automatically started on being configured.
void StartEncodeUsageResource();
// Stops the encode usage and quality scaler resources if not already stopped.
void StopManagedResources();
// Settings that affect the VideoStreamEncoder-specific resources.
void SetEncoderSettings(EncoderSettings encoder_settings);
void SetStartBitrate(DataRate start_bitrate);
void SetTargetBitrate(DataRate target_bitrate);
void SetEncoderRates(
const VideoEncoder::RateControlParameters& encoder_rates);
// TODO(https://crbug.com/webrtc/11338): This can be made private if we
// configure on SetDegredationPreference and SetEncoderSettings.
void ConfigureQualityScaler(const VideoEncoder::EncoderInfo& encoder_info);
// Methods corresponding to different points in the encoding pipeline.
void OnFrameDroppedDueToSize();
void OnMaybeEncodeFrame();
void OnEncodeStarted(const VideoFrame& cropped_frame,
int64_t time_when_first_seen_us);
void OnEncodeCompleted(const EncodedImage& encoded_image,
int64_t time_sent_in_us,
absl::optional<int> encode_duration_us);
void OnFrameDropped(EncodedImageCallback::DropReason reason);
// Resources need to be mapped to an AdaptReason (kCpu or kQuality) in order
// to be able to update |active_counts_|, which is used...
// - Legacy getStats() purposes.
// - Preventing adapting up in some circumstances (which may be questionable).
// TODO(hbos): Can we get rid of this?
void MapResourceToReason(rtc::scoped_refptr<Resource> resource,
VideoAdaptationReason reason);
std::vector<rtc::scoped_refptr<Resource>> MappedResources() const;
rtc::scoped_refptr<QualityScalerResource>
quality_scaler_resource_for_testing();
// If true, the VideoStreamEncoder should eexecute its logic to maybe drop
// frames baseed on size and bitrate.
bool DropInitialFrames() const;
// ResourceAdaptationProcessorListener implementation.
// Updates |video_source_restrictions_| and |active_counts_|.
void OnVideoSourceRestrictionsUpdated(
VideoSourceRestrictions restrictions,
const VideoAdaptationCounters& adaptation_counters,
rtc::scoped_refptr<Resource> reason) override;
// For reasons of adaptation and statistics, we not only count the total
// number of adaptations, but we also count the number of adaptations per
// reason.
// This method takes the new total number of adaptations and allocates that to
// the "active" count - number of adaptations for the current reason.
// The "other" count is the number of adaptations for the other reason.
// This must be called for each adaptation step made.
static void OnAdaptationCountChanged(
const VideoAdaptationCounters& adaptation_count,
VideoAdaptationCounters* active_count,
VideoAdaptationCounters* other_active);
private:
class InitialFrameDropper;
VideoAdaptationReason GetReasonFromResource(
rtc::scoped_refptr<Resource> resource) const;
CpuOveruseOptions GetCpuOveruseOptions() const;
int LastInputFrameSizeOrDefault() const;
// Calculates an up-to-date value of the target frame rate and informs the
// |encode_usage_resource_| of the new value.
void MaybeUpdateTargetFrameRate();
// Use nullopt to disable quality scaling.
void UpdateQualityScalerSettings(
absl::optional<VideoEncoder::QpThresholds> qp_thresholds);
void UpdateAdaptationStats(const VideoAdaptationCounters& total_counts,
VideoAdaptationReason reason);
void UpdateStatsAdaptationSettings() const;
// Checks to see if we should execute the quality rampup experiment. The
// experiment resets all video restrictions at the start of the call in the
// case the bandwidth estimate is high enough.
// TODO(https://crbug.com/webrtc/11222) Move experiment details into an inner
// class.
void MaybePerformQualityRampupExperiment();
void ResetActiveCounts();
std::string ActiveCountsToString() const;
// TODO(hbos): Consider moving all of the manager's resources into separate
// files for testability.
// Does not trigger adaptations, only prevents adapting up based on
// |active_counts_|.
class PreventAdaptUpDueToActiveCounts final
: public rtc::RefCountedObject<Resource> {
public:
explicit PreventAdaptUpDueToActiveCounts(
VideoStreamEncoderResourceManager* manager);
~PreventAdaptUpDueToActiveCounts() override = default;
void SetAdaptationProcessor(
ResourceAdaptationProcessorInterface* adaptation_processor);
// Resource overrides.
std::string name() const override {
return "PreventAdaptUpDueToActiveCounts";
}
bool IsAdaptationUpAllowed(
const VideoStreamInputState& input_state,
const VideoSourceRestrictions& restrictions_before,
const VideoSourceRestrictions& restrictions_after,
rtc::scoped_refptr<Resource> reason_resource) const override;
private:
// The |manager_| must be alive as long as this resource is added to the
// ResourceAdaptationProcessor, i.e. when IsAdaptationUpAllowed() is called.
VideoStreamEncoderResourceManager* const manager_;
ResourceAdaptationProcessorInterface* adaptation_processor_
RTC_GUARDED_BY(resource_adaptation_queue());
};
// Does not trigger adaptations, only prevents adapting up resolution.
class PreventIncreaseResolutionDueToBitrateResource final
: public rtc::RefCountedObject<Resource> {
public:
explicit PreventIncreaseResolutionDueToBitrateResource(
VideoStreamEncoderResourceManager* manager);
~PreventIncreaseResolutionDueToBitrateResource() override = default;
void OnEncoderSettingsUpdated(
absl::optional<EncoderSettings> encoder_settings);
void OnEncoderTargetBitrateUpdated(
absl::optional<uint32_t> encoder_target_bitrate_bps);
// Resource overrides.
std::string name() const override {
return "PreventIncreaseResolutionDueToBitrateResource";
}
bool IsAdaptationUpAllowed(
const VideoStreamInputState& input_state,
const VideoSourceRestrictions& restrictions_before,
const VideoSourceRestrictions& restrictions_after,
rtc::scoped_refptr<Resource> reason_resource) const override;
private:
// The |manager_| must be alive as long as this resource is added to the
// ResourceAdaptationProcessor, i.e. when IsAdaptationUpAllowed() is called.
VideoStreamEncoderResourceManager* const manager_;
absl::optional<EncoderSettings> encoder_settings_
RTC_GUARDED_BY(resource_adaptation_queue());
absl::optional<uint32_t> encoder_target_bitrate_bps_
RTC_GUARDED_BY(resource_adaptation_queue());
};
// Does not trigger adaptations, only prevents adapting up in BALANCED.
class PreventAdaptUpInBalancedResource final
: public rtc::RefCountedObject<Resource> {
public:
explicit PreventAdaptUpInBalancedResource(
VideoStreamEncoderResourceManager* manager);
~PreventAdaptUpInBalancedResource() override = default;
void SetAdaptationProcessor(
ResourceAdaptationProcessorInterface* adaptation_processor);
void OnEncoderTargetBitrateUpdated(
absl::optional<uint32_t> encoder_target_bitrate_bps);
// Resource overrides.
std::string name() const override {
return "PreventAdaptUpInBalancedResource";
}
bool IsAdaptationUpAllowed(
const VideoStreamInputState& input_state,
const VideoSourceRestrictions& restrictions_before,
const VideoSourceRestrictions& restrictions_after,
rtc::scoped_refptr<Resource> reason_resource) const override;
private:
// The |manager_| must be alive as long as this resource is added to the
// ResourceAdaptationProcessor, i.e. when IsAdaptationUpAllowed() is called.
VideoStreamEncoderResourceManager* const manager_;
ResourceAdaptationProcessorInterface* adaptation_processor_
RTC_GUARDED_BY(resource_adaptation_queue());
absl::optional<uint32_t> encoder_target_bitrate_bps_
RTC_GUARDED_BY(resource_adaptation_queue());
};
const rtc::scoped_refptr<PreventAdaptUpDueToActiveCounts>
prevent_adapt_up_due_to_active_counts_;
const rtc::scoped_refptr<PreventIncreaseResolutionDueToBitrateResource>
prevent_increase_resolution_due_to_bitrate_resource_;
const rtc::scoped_refptr<PreventAdaptUpInBalancedResource>
prevent_adapt_up_in_balanced_resource_;
const rtc::scoped_refptr<EncodeUsageResource> encode_usage_resource_;
const rtc::scoped_refptr<QualityScalerResource> quality_scaler_resource_;
rtc::TaskQueue* encoder_queue_;
rtc::TaskQueue* resource_adaptation_queue_;
VideoStreamInputStateProvider* const input_state_provider_
RTC_GUARDED_BY(encoder_queue_);
ResourceAdaptationProcessorInterface* adaptation_processor_
RTC_GUARDED_BY(resource_adaptation_queue_);
// Thread-safe.
VideoStreamEncoderObserver* const encoder_stats_observer_;
DegradationPreference degradation_preference_ RTC_GUARDED_BY(encoder_queue_);
VideoSourceRestrictions video_source_restrictions_
RTC_GUARDED_BY(encoder_queue_);
const BalancedDegradationSettings balanced_settings_;
Clock* clock_ RTC_GUARDED_BY(encoder_queue_);
const bool experiment_cpu_load_estimator_ RTC_GUARDED_BY(encoder_queue_);
const std::unique_ptr<InitialFrameDropper> initial_frame_dropper_
RTC_GUARDED_BY(encoder_queue_);
const bool quality_scaling_experiment_enabled_ RTC_GUARDED_BY(encoder_queue_);
absl::optional<uint32_t> encoder_target_bitrate_bps_
RTC_GUARDED_BY(encoder_queue_);
absl::optional<VideoEncoder::RateControlParameters> encoder_rates_
RTC_GUARDED_BY(encoder_queue_);
// Used on both the encoder queue and resource adaptation queue.
std::atomic<bool> quality_rampup_done_;
QualityRampupExperiment quality_rampup_experiment_
RTC_GUARDED_BY(encoder_queue_);
absl::optional<EncoderSettings> encoder_settings_
RTC_GUARDED_BY(encoder_queue_);
// Ties a resource to a reason for statistical reporting. This AdaptReason is
// also used by this module to make decisions about how to adapt up/down.
struct ResourceAndReason {
ResourceAndReason(rtc::scoped_refptr<Resource> resource,
VideoAdaptationReason reason)
: resource(resource), reason(reason) {}
virtual ~ResourceAndReason() = default;
const rtc::scoped_refptr<Resource> resource;
const VideoAdaptationReason reason;
};
rtc::CriticalSection resource_lock_;
std::vector<ResourceAndReason> resources_ RTC_GUARDED_BY(&resource_lock_);
// One AdaptationCounter for each reason, tracking the number of times we have
// adapted for each reason. The sum of active_counts_ MUST always equal the
// total adaptation provided by the VideoSourceRestrictions.
// TODO(https://crbug.com/webrtc/11542): When we have an adaptation queue,
// guard the activec counts by it instead. The |encoder_stats_observer_| is
// thread-safe anyway, and active counts are used by
// PreventAdaptUpDueToActiveCounts to make decisions.
std::unordered_map<VideoAdaptationReason, VideoAdaptationCounters>
active_counts_ RTC_GUARDED_BY(resource_adaptation_queue_);
};
} // namespace webrtc
#endif // VIDEO_ADAPTATION_VIDEO_STREAM_ENCODER_RESOURCE_MANAGER_H_