| /* | |
| * Copyright 2021 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 "rtc_base/experiments/encoder_info_settings.h" | |
| #include <algorithm> | |
| #include <cstdint> | |
| #include <cstdio> | |
| #include <optional> | |
| #include <string> | |
| #include <vector> | |
| #include "absl/strings/string_view.h" | |
| #include "api/field_trials_view.h" | |
| #include "api/video/video_codec_type.h" | |
| #include "api/video_codecs/video_encoder.h" | |
| #include "rtc_base/experiments/field_trial_list.h" | |
| #include "rtc_base/experiments/field_trial_parser.h" | |
| #include "rtc_base/logging.h" | |
| namespace webrtc { | |
| namespace { | |
| std::vector<VideoEncoder::ResolutionBitrateLimits> ToResolutionBitrateLimits( | |
| const std::vector<EncoderInfoSettings::BitrateLimit>& limits) { | |
| std::vector<VideoEncoder::ResolutionBitrateLimits> result; | |
| for (const auto& limit : limits) { | |
| result.push_back(VideoEncoder::ResolutionBitrateLimits( | |
| limit.frame_size_pixels, limit.min_start_bitrate_bps, | |
| limit.min_bitrate_bps, limit.max_bitrate_bps)); | |
| } | |
| return result; | |
| } | |
| constexpr float kDefaultMinBitratebps = 30000; | |
| } // namespace | |
| // Default bitrate limits for simulcast with one active stream: | |
| // {frame_size_pixels, min_start_bitrate_bps, min_bitrate_bps, max_bitrate_bps}. | |
| std::vector<VideoEncoder::ResolutionBitrateLimits> | |
| EncoderInfoSettings::GetDefaultSinglecastBitrateLimits( | |
| VideoCodecType codec_type) { | |
| if (codec_type == kVideoCodecAV1) { | |
| // AV1 singlecast max bitrate limits are higher than AV1 SVC max limits. | |
| // This is because in singlecast we normally have just one receiver, BWE is | |
| // known end-to-end and the encode target bitrate guarantees delivery of | |
| // video. | |
| // The min bitrate limits are not used in singlecast (used in SVC/simulcast | |
| // to de-/activate spatial layers) and are set to zero. Send resolution in | |
| // singlecast is assumed to be regulated by QP-based quality scaler. | |
| return { | |
| {320 * 180, 0, 0, 256000}, {480 * 270, 176000, 0, 384000}, | |
| {640 * 360, 256000, 0, 512000}, {960 * 540, 384000, 0, 1024000}, | |
| {1280 * 720, 576000, 0, 1536000}, {1920 * 1080, 1000000, 0, 3700000}}; | |
| } | |
| if (codec_type == kVideoCodecVP9 || codec_type == kVideoCodecH265) { | |
| // VP9 singlecast bitrate limits are derived ~directly from VP9 SVC bitrate | |
| // limits. The current max limits are unnecessarily too strict for | |
| // singlecast, where BWE is known end-to-end, especially for low | |
| // resolutions. | |
| // TODO(crbugs.com/39206082): Consider fine-tuning H.265 to have its own | |
| // bitrate settings separate from VP9. | |
| return {{320 * 180, 0, 30000, 150000}, | |
| {480 * 270, 120000, 30000, 300000}, | |
| {640 * 360, 190000, 30000, 420000}, | |
| {960 * 540, 350000, 30000, 1000000}, | |
| {1280 * 720, 480000, 30000, 1500000}, | |
| {1920 * 1080, 1000000, 30000, 3700000}}; | |
| } | |
| // VP8 and other codecs. | |
| return {{320 * 180, 0, 30000, 300000}, | |
| {480 * 270, 200000, 30000, 500000}, | |
| {640 * 360, 300000, 30000, 800000}, | |
| {960 * 540, 500000, 30000, 1500000}, | |
| {1280 * 720, 900000, 30000, 2500000}, | |
| {1920 * 1080, 2000000, 30000, 5000000}}; | |
| } | |
| std::optional<VideoEncoder::ResolutionBitrateLimits> | |
| EncoderInfoSettings::GetDefaultSinglecastBitrateLimitsForResolution( | |
| VideoCodecType codec_type, | |
| int frame_size_pixels) { | |
| VideoEncoder::EncoderInfo info; | |
| info.resolution_bitrate_limits = | |
| GetDefaultSinglecastBitrateLimits(codec_type); | |
| return info.GetEncoderBitrateLimitsForResolution(frame_size_pixels); | |
| } | |
| // Return the suitable bitrate limits for specified resolution when qp is | |
| // untrusted, they are experimental values. | |
| std::vector<VideoEncoder::ResolutionBitrateLimits> | |
| EncoderInfoSettings::GetDefaultSinglecastBitrateLimitsWhenQpIsUntrusted( | |
| VideoCodecType codec_type) { | |
| if (codec_type == kVideoCodecH265) { | |
| // Similar settings from the simulcast bitate limits for H.265. | |
| return {{0 * 0, 0, 0, 0}, | |
| {320 * 180, 0, 30000, 150000}, | |
| {480 * 270, 150000, 30000, 300000}, | |
| {640 * 360, 300000, 30000, 420000}, | |
| {960 * 540, 420000, 30000, 1000000}, | |
| {1280 * 720, 1000000, 30000, 1500000}, | |
| {1920 * 1080, 1500000, 30000, 3300000}}; | |
| } else { | |
| // Settings for H.264. Other codecs will not work in QP-untrusted mode. | |
| return {{0 * 0, 0, 0, 0}, | |
| {320 * 180, 0, 30000, 300000}, | |
| {480 * 270, 300000, 30000, 500000}, | |
| {640 * 360, 500000, 30000, 800000}, | |
| {960 * 540, 800000, 30000, 1500000}, | |
| {1280 * 720, 1500000, 30000, 2500000}, | |
| {1920 * 1080, 2500000, 30000, 4000000}}; | |
| } | |
| } | |
| // Through linear interpolation, return the bitrate limit corresponding to the | |
| // specified |frame_size_pixels|. | |
| std::optional<VideoEncoder::ResolutionBitrateLimits> | |
| EncoderInfoSettings::GetSinglecastBitrateLimitForResolutionWhenQpIsUntrusted( | |
| std::optional<int> frame_size_pixels, | |
| const std::vector<VideoEncoder::ResolutionBitrateLimits>& | |
| resolution_bitrate_limits) { | |
| if (!frame_size_pixels.has_value() || frame_size_pixels.value() <= 0) { | |
| return std::nullopt; | |
| } | |
| std::vector<VideoEncoder::ResolutionBitrateLimits> bitrate_limits = | |
| resolution_bitrate_limits; | |
| // Sort the list of bitrate limits by resolution. | |
| sort(bitrate_limits.begin(), bitrate_limits.end(), | |
| [](const VideoEncoder::ResolutionBitrateLimits& lhs, | |
| const VideoEncoder::ResolutionBitrateLimits& rhs) { | |
| return lhs.frame_size_pixels < rhs.frame_size_pixels; | |
| }); | |
| if (bitrate_limits.empty()) { | |
| return std::nullopt; | |
| } | |
| int interpolation_index = -1; | |
| for (size_t i = 0; i < bitrate_limits.size(); ++i) { | |
| if (bitrate_limits[i].frame_size_pixels >= frame_size_pixels.value()) { | |
| interpolation_index = i; | |
| break; | |
| } | |
| } | |
| // -1 means that the maximum resolution is exceeded, we will select the | |
| // largest data as the return result. | |
| if (interpolation_index == -1) { | |
| return *bitrate_limits.rbegin(); | |
| } | |
| // If we have a matching resolution, return directly without interpolation. | |
| if (bitrate_limits[interpolation_index].frame_size_pixels == | |
| frame_size_pixels.value()) { | |
| return bitrate_limits[interpolation_index]; | |
| } | |
| // 0 means our resolution is smaller than the smallest resolution in the list, | |
| // we will select smallest data as the return result. | |
| if (interpolation_index == 0) { | |
| return *bitrate_limits.begin(); | |
| } | |
| // No matching resolution, do a linear interpolate. | |
| int lower_pixel_count = | |
| bitrate_limits[interpolation_index - 1].frame_size_pixels; | |
| int upper_pixel_count = bitrate_limits[interpolation_index].frame_size_pixels; | |
| float alpha = (frame_size_pixels.value() - lower_pixel_count) * 1.0 / | |
| (upper_pixel_count - lower_pixel_count); | |
| int min_start_bitrate_bps = static_cast<int>( | |
| bitrate_limits[interpolation_index].min_start_bitrate_bps * alpha + | |
| bitrate_limits[interpolation_index - 1].min_start_bitrate_bps * | |
| (1.0 - alpha)); | |
| int max_bitrate_bps = static_cast<int>( | |
| bitrate_limits[interpolation_index].max_bitrate_bps * alpha + | |
| bitrate_limits[interpolation_index - 1].max_bitrate_bps * (1.0 - alpha)); | |
| if (max_bitrate_bps >= min_start_bitrate_bps) { | |
| return VideoEncoder::ResolutionBitrateLimits( | |
| frame_size_pixels.value(), min_start_bitrate_bps, kDefaultMinBitratebps, | |
| max_bitrate_bps); | |
| } else { | |
| RTC_LOG(LS_WARNING) | |
| << "BitRate interpolation calculating result is abnormal. " | |
| << " lower_pixel_count = " << lower_pixel_count | |
| << " upper_pixel_count = " << upper_pixel_count | |
| << " frame_size_pixels = " << frame_size_pixels.value() | |
| << " min_start_bitrate_bps = " << min_start_bitrate_bps | |
| << " min_bitrate_bps = " << kDefaultMinBitratebps | |
| << " max_bitrate_bps = " << max_bitrate_bps; | |
| return std::nullopt; | |
| } | |
| } | |
| EncoderInfoSettings::EncoderInfoSettings(const FieldTrialsView& field_trials, | |
| absl::string_view name) | |
| : requested_resolution_alignment_("requested_resolution_alignment"), | |
| apply_alignment_to_all_simulcast_layers_( | |
| "apply_alignment_to_all_simulcast_layers") { | |
| FieldTrialStructList<BitrateLimit> bitrate_limits( | |
| {FieldTrialStructMember( | |
| "frame_size_pixels", | |
| [](BitrateLimit* b) { return &b->frame_size_pixels; }), | |
| FieldTrialStructMember( | |
| "min_start_bitrate_bps", | |
| [](BitrateLimit* b) { return &b->min_start_bitrate_bps; }), | |
| FieldTrialStructMember( | |
| "min_bitrate_bps", | |
| [](BitrateLimit* b) { return &b->min_bitrate_bps; }), | |
| FieldTrialStructMember( | |
| "max_bitrate_bps", | |
| [](BitrateLimit* b) { return &b->max_bitrate_bps; })}, | |
| {}); | |
| std::string experiment_string = field_trials.Lookup(name); | |
| if (experiment_string.empty()) { | |
| // Encoder name not found, use common string applying to all encoders. | |
| experiment_string = field_trials.Lookup("WebRTC-GetEncoderInfoOverride"); | |
| } | |
| ParseFieldTrial({&bitrate_limits, &requested_resolution_alignment_, | |
| &apply_alignment_to_all_simulcast_layers_}, | |
| experiment_string); | |
| resolution_bitrate_limits_ = ToResolutionBitrateLimits(bitrate_limits.Get()); | |
| } | |
| std::optional<uint32_t> EncoderInfoSettings::requested_resolution_alignment() | |
| const { | |
| if (requested_resolution_alignment_ && | |
| requested_resolution_alignment_.Value() < 1) { | |
| RTC_LOG(LS_WARNING) << "Unsupported alignment value, ignored."; | |
| return std::nullopt; | |
| } | |
| return requested_resolution_alignment_.GetOptional(); | |
| } | |
| EncoderInfoSettings::~EncoderInfoSettings() {} | |
| SimulcastEncoderAdapterEncoderInfoSettings:: | |
| SimulcastEncoderAdapterEncoderInfoSettings( | |
| const FieldTrialsView& field_trials) | |
| : EncoderInfoSettings( | |
| field_trials, | |
| "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride") {} | |
| LibvpxVp8EncoderInfoSettings::LibvpxVp8EncoderInfoSettings( | |
| const FieldTrialsView& field_trials) | |
| : EncoderInfoSettings(field_trials, "WebRTC-VP8-GetEncoderInfoOverride") {} | |
| LibvpxVp9EncoderInfoSettings::LibvpxVp9EncoderInfoSettings( | |
| const FieldTrialsView& field_trials) | |
| : EncoderInfoSettings(field_trials, "WebRTC-VP9-GetEncoderInfoOverride") {} | |
| LibaomAv1EncoderInfoSettings::LibaomAv1EncoderInfoSettings( | |
| const FieldTrialsView& field_trials) | |
| : EncoderInfoSettings(field_trials, "WebRTC-Av1-GetEncoderInfoOverride") {} | |
| } // namespace webrtc |