Add resolution_bitrate_limits to EncoderInfo field trial.
Added class EncoderInfoSettings for parsing settings.
Added use of class to SimulcastEncoderAdapter.
Bug: none
Change-Id: I8182b2ab43f0c330ebdf077e9f7cbc79247da90e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/202246
Commit-Queue: Åsa Persson <asapersson@webrtc.org>
Reviewed-by: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33050}
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 84f938b..4843b89 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -193,7 +193,7 @@
"../modules/video_coding:video_coding_utility",
"../rtc_base:checks",
"../rtc_base:rtc_base_approved",
- "../rtc_base/experiments:field_trial_parser",
+ "../rtc_base/experiments:encoder_info_settings",
"../rtc_base/experiments:rate_control_settings",
"../rtc_base/synchronization:sequence_checker",
"../rtc_base/system:no_unique_address",
diff --git a/media/engine/simulcast_encoder_adapter.cc b/media/engine/simulcast_encoder_adapter.cc
index 10cf686..52790f9 100644
--- a/media/engine/simulcast_encoder_adapter.cc
+++ b/media/engine/simulcast_encoder_adapter.cc
@@ -228,11 +228,6 @@
"WebRTC-Video-PreferTemporalSupportOnBaseLayer")) {
RTC_DCHECK(primary_factory);
- ParseFieldTrial({&requested_resolution_alignment_override_,
- &apply_alignment_to_all_simulcast_layers_override_},
- field_trial::FindFullName(
- "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride"));
-
// The adapter is typically created on the worker thread, but operated on
// the encoder task queue.
encoder_queue_.Detach();
@@ -430,8 +425,9 @@
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
}
- if (requested_resolution_alignment_override_) {
- const int alignment = *requested_resolution_alignment_override_;
+ if (encoder_info_override_.requested_resolution_alignment()) {
+ const int alignment =
+ *encoder_info_override_.requested_resolution_alignment();
if (input_image.width() % alignment != 0 ||
input_image.height() % alignment != 0) {
RTC_LOG(LS_WARNING) << "Frame " << input_image.width() << "x"
@@ -439,7 +435,7 @@
<< alignment;
return WEBRTC_VIDEO_CODEC_ERROR;
}
- if (apply_alignment_to_all_simulcast_layers_override_.Get()) {
+ if (encoder_info_override_.apply_alignment_to_all_simulcast_layers()) {
for (const auto& layer : encoder_contexts_) {
if (layer.width() % alignment != 0 || layer.height() % alignment != 0) {
RTC_LOG(LS_WARNING)
@@ -741,11 +737,15 @@
void SimulcastEncoderAdapter::OverrideFromFieldTrial(
VideoEncoder::EncoderInfo* info) const {
- if (requested_resolution_alignment_override_) {
+ if (encoder_info_override_.requested_resolution_alignment()) {
info->requested_resolution_alignment =
- *requested_resolution_alignment_override_;
+ *encoder_info_override_.requested_resolution_alignment();
info->apply_alignment_to_all_simulcast_layers =
- apply_alignment_to_all_simulcast_layers_override_.Get();
+ encoder_info_override_.apply_alignment_to_all_simulcast_layers();
+ }
+ if (!encoder_info_override_.resolution_bitrate_limits().empty()) {
+ info->resolution_bitrate_limits =
+ encoder_info_override_.resolution_bitrate_limits();
}
}
diff --git a/media/engine/simulcast_encoder_adapter.h b/media/engine/simulcast_encoder_adapter.h
index d3d5d17..c127fee 100644
--- a/media/engine/simulcast_encoder_adapter.h
+++ b/media/engine/simulcast_encoder_adapter.h
@@ -26,7 +26,7 @@
#include "modules/video_coding/include/video_codec_interface.h"
#include "modules/video_coding/utility/framerate_controller.h"
#include "rtc_base/atomic_ops.h"
-#include "rtc_base/experiments/field_trial_parser.h"
+#include "rtc_base/experiments/encoder_info_settings.h"
#include "rtc_base/synchronization/sequence_checker.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/system/rtc_export.h"
@@ -162,13 +162,7 @@
const bool boost_base_layer_quality_;
const bool prefer_temporal_support_on_base_layer_;
- // Overrides from field trial.
- // EncoderInfo::requested_resolution_alignment.
- FieldTrialOptional<int> requested_resolution_alignment_override_{
- "requested_resolution_alignment"};
- // EncoderInfo::apply_alignment_to_all_simulcast_layers.
- FieldTrialFlag apply_alignment_to_all_simulcast_layers_override_{
- "apply_alignment_to_all_simulcast_layers"};
+ const SimulcastEncoderAdapterEncoderInfoSettings encoder_info_override_;
};
} // namespace webrtc
diff --git a/media/engine/simulcast_encoder_adapter_unittest.cc b/media/engine/simulcast_encoder_adapter_unittest.cc
index 510db6f..8a64ba4 100644
--- a/media/engine/simulcast_encoder_adapter_unittest.cc
+++ b/media/engine/simulcast_encoder_adapter_unittest.cc
@@ -1292,7 +1292,7 @@
adapter_->GetEncoderInfo().apply_alignment_to_all_simulcast_layers);
}
-TEST_F(TestSimulcastEncoderAdapterFake, AlignmentFromFieldTrial) {
+TEST_F(TestSimulcastEncoderAdapterFake, EncoderInfoFromFieldTrial) {
test::ScopedFieldTrials field_trials(
"WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/"
"requested_resolution_alignment:8,"
@@ -1308,13 +1308,18 @@
EXPECT_EQ(8, adapter_->GetEncoderInfo().requested_resolution_alignment);
EXPECT_TRUE(
adapter_->GetEncoderInfo().apply_alignment_to_all_simulcast_layers);
+ EXPECT_TRUE(adapter_->GetEncoderInfo().resolution_bitrate_limits.empty());
}
TEST_F(TestSimulcastEncoderAdapterFake,
- AlignmentFromFieldTrialForSingleStream) {
+ EncoderInfoFromFieldTrialForSingleStream) {
test::ScopedFieldTrials field_trials(
"WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/"
- "requested_resolution_alignment:9/");
+ "requested_resolution_alignment:9,"
+ "frame_size_pixels:123|456|789,"
+ "min_start_bitrate_bps:11000|22000|33000,"
+ "min_bitrate_bps:44000|55000|66000,"
+ "max_bitrate_bps:77000|88000|99000/");
SetUp();
SimulcastTestFixtureImpl::DefaultSettings(
&codec_, static_cast<const int*>(kTestTemporalLayerProfile),
@@ -1326,6 +1331,12 @@
EXPECT_EQ(9, adapter_->GetEncoderInfo().requested_resolution_alignment);
EXPECT_FALSE(
adapter_->GetEncoderInfo().apply_alignment_to_all_simulcast_layers);
+ EXPECT_THAT(
+ adapter_->GetEncoderInfo().resolution_bitrate_limits,
+ ::testing::ElementsAre(
+ VideoEncoder::ResolutionBitrateLimits{123, 11000, 44000, 77000},
+ VideoEncoder::ResolutionBitrateLimits{456, 22000, 55000, 88000},
+ VideoEncoder::ResolutionBitrateLimits{789, 33000, 66000, 99000}));
}
TEST_F(TestSimulcastEncoderAdapterFake, ReportsInternalSource) {
diff --git a/rtc_base/experiments/BUILD.gn b/rtc_base/experiments/BUILD.gn
index a40c9e0..3ea815d 100644
--- a/rtc_base/experiments/BUILD.gn
+++ b/rtc_base/experiments/BUILD.gn
@@ -130,6 +130,20 @@
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
+rtc_library("encoder_info_settings") {
+ sources = [
+ "encoder_info_settings.cc",
+ "encoder_info_settings.h",
+ ]
+ deps = [
+ ":field_trial_parser",
+ "../:rtc_base_approved",
+ "../../api/video_codecs:video_codecs_api",
+ "../../system_wrappers:field_trial",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
+}
+
rtc_library("rtt_mult_experiment") {
sources = [
"rtt_mult_experiment.cc",
@@ -224,6 +238,7 @@
sources = [
"balanced_degradation_settings_unittest.cc",
"cpu_speed_experiment_unittest.cc",
+ "encoder_info_settings_unittest.cc",
"field_trial_list_unittest.cc",
"field_trial_parser_unittest.cc",
"field_trial_units_unittest.cc",
@@ -241,6 +256,7 @@
deps = [
":balanced_degradation_settings",
":cpu_speed_experiment",
+ ":encoder_info_settings",
":field_trial_parser",
":keyframe_interval_settings_experiment",
":min_video_bitrate_experiment",
diff --git a/rtc_base/experiments/encoder_info_settings.cc b/rtc_base/experiments/encoder_info_settings.cc
new file mode 100644
index 0000000..fa6b843
--- /dev/null
+++ b/rtc_base/experiments/encoder_info_settings.cc
@@ -0,0 +1,78 @@
+/*
+ * 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 <stdio.h>
+
+#include "rtc_base/experiments/field_trial_list.h"
+#include "rtc_base/logging.h"
+#include "system_wrappers/include/field_trial.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;
+}
+
+} // namespace
+
+EncoderInfoSettings::EncoderInfoSettings(std::string 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; })},
+ {});
+
+ ParseFieldTrial({&bitrate_limits, &requested_resolution_alignment_,
+ &apply_alignment_to_all_simulcast_layers_},
+ field_trial::FindFullName(name));
+
+ resolution_bitrate_limits_ = ToResolutionBitrateLimits(bitrate_limits.Get());
+}
+
+absl::optional<int> EncoderInfoSettings::requested_resolution_alignment()
+ const {
+ if (requested_resolution_alignment_ &&
+ requested_resolution_alignment_.Value() < 1) {
+ RTC_LOG(LS_WARNING) << "Unsupported alignment value, ignored.";
+ return absl::nullopt;
+ }
+ return requested_resolution_alignment_.GetOptional();
+}
+
+EncoderInfoSettings::~EncoderInfoSettings() {}
+
+SimulcastEncoderAdapterEncoderInfoSettings::
+ SimulcastEncoderAdapterEncoderInfoSettings()
+ : EncoderInfoSettings(
+ "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride") {}
+
+} // namespace webrtc
diff --git a/rtc_base/experiments/encoder_info_settings.h b/rtc_base/experiments/encoder_info_settings.h
new file mode 100644
index 0000000..9cacdbd
--- /dev/null
+++ b/rtc_base/experiments/encoder_info_settings.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#ifndef RTC_BASE_EXPERIMENTS_ENCODER_INFO_SETTINGS_H_
+#define RTC_BASE_EXPERIMENTS_ENCODER_INFO_SETTINGS_H_
+
+#include <string>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "api/video_codecs/video_encoder.h"
+#include "rtc_base/experiments/field_trial_parser.h"
+
+namespace webrtc {
+
+class EncoderInfoSettings {
+ public:
+ virtual ~EncoderInfoSettings();
+
+ // Bitrate limits per resolution.
+ struct BitrateLimit {
+ int frame_size_pixels = 0; // The video frame size.
+ int min_start_bitrate_bps = 0; // The minimum bitrate to start encoding.
+ int min_bitrate_bps = 0; // The minimum bitrate.
+ int max_bitrate_bps = 0; // The maximum bitrate.
+ };
+
+ absl::optional<int> requested_resolution_alignment() const;
+ bool apply_alignment_to_all_simulcast_layers() const {
+ return apply_alignment_to_all_simulcast_layers_.Get();
+ }
+ std::vector<VideoEncoder::ResolutionBitrateLimits> resolution_bitrate_limits()
+ const {
+ return resolution_bitrate_limits_;
+ }
+
+ protected:
+ explicit EncoderInfoSettings(std::string name);
+
+ private:
+ FieldTrialOptional<int> requested_resolution_alignment_;
+ FieldTrialFlag apply_alignment_to_all_simulcast_layers_;
+ std::vector<VideoEncoder::ResolutionBitrateLimits> resolution_bitrate_limits_;
+};
+
+// EncoderInfo settings for SimulcastEncoderAdapter.
+class SimulcastEncoderAdapterEncoderInfoSettings : public EncoderInfoSettings {
+ public:
+ SimulcastEncoderAdapterEncoderInfoSettings();
+ ~SimulcastEncoderAdapterEncoderInfoSettings() override {}
+};
+
+} // namespace webrtc
+
+#endif // RTC_BASE_EXPERIMENTS_ENCODER_INFO_SETTINGS_H_
diff --git a/rtc_base/experiments/encoder_info_settings_unittest.cc b/rtc_base/experiments/encoder_info_settings_unittest.cc
new file mode 100644
index 0000000..0208c0d
--- /dev/null
+++ b/rtc_base/experiments/encoder_info_settings_unittest.cc
@@ -0,0 +1,91 @@
+/*
+ * 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 "rtc_base/gunit.h"
+#include "test/field_trial.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+TEST(SimulcastEncoderAdapterSettingsTest, NoValuesWithoutFieldTrial) {
+ SimulcastEncoderAdapterEncoderInfoSettings settings;
+ EXPECT_EQ(absl::nullopt, settings.requested_resolution_alignment());
+ EXPECT_FALSE(settings.apply_alignment_to_all_simulcast_layers());
+ EXPECT_TRUE(settings.resolution_bitrate_limits().empty());
+}
+
+TEST(SimulcastEncoderAdapterSettingsTest, NoValueForInvalidAlignment) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/"
+ "requested_resolution_alignment:0/");
+
+ SimulcastEncoderAdapterEncoderInfoSettings settings;
+ EXPECT_EQ(absl::nullopt, settings.requested_resolution_alignment());
+}
+
+TEST(SimulcastEncoderAdapterSettingsTest, GetResolutionAlignment) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/"
+ "requested_resolution_alignment:2/");
+
+ SimulcastEncoderAdapterEncoderInfoSettings settings;
+ EXPECT_EQ(2, settings.requested_resolution_alignment());
+ EXPECT_FALSE(settings.apply_alignment_to_all_simulcast_layers());
+ EXPECT_TRUE(settings.resolution_bitrate_limits().empty());
+}
+
+TEST(SimulcastEncoderAdapterSettingsTest, GetApplyAlignment) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/"
+ "requested_resolution_alignment:3,"
+ "apply_alignment_to_all_simulcast_layers/");
+
+ SimulcastEncoderAdapterEncoderInfoSettings settings;
+ EXPECT_EQ(3, settings.requested_resolution_alignment());
+ EXPECT_TRUE(settings.apply_alignment_to_all_simulcast_layers());
+ EXPECT_TRUE(settings.resolution_bitrate_limits().empty());
+}
+
+TEST(SimulcastEncoderAdapterSettingsTest, GetResolutionBitrateLimits) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/"
+ "frame_size_pixels:123,"
+ "min_start_bitrate_bps:11000,"
+ "min_bitrate_bps:44000,"
+ "max_bitrate_bps:77000/");
+
+ SimulcastEncoderAdapterEncoderInfoSettings settings;
+ EXPECT_EQ(absl::nullopt, settings.requested_resolution_alignment());
+ EXPECT_FALSE(settings.apply_alignment_to_all_simulcast_layers());
+ EXPECT_THAT(settings.resolution_bitrate_limits(),
+ ::testing::ElementsAre(VideoEncoder::ResolutionBitrateLimits{
+ 123, 11000, 44000, 77000}));
+}
+
+TEST(SimulcastEncoderAdapterSettingsTest, GetResolutionBitrateLimitsWithList) {
+ webrtc::test::ScopedFieldTrials field_trials(
+ "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/"
+ "frame_size_pixels:123|456|789,"
+ "min_start_bitrate_bps:11000|22000|33000,"
+ "min_bitrate_bps:44000|55000|66000,"
+ "max_bitrate_bps:77000|88000|99000/");
+
+ SimulcastEncoderAdapterEncoderInfoSettings settings;
+ EXPECT_THAT(
+ settings.resolution_bitrate_limits(),
+ ::testing::ElementsAre(
+ VideoEncoder::ResolutionBitrateLimits{123, 11000, 44000, 77000},
+ VideoEncoder::ResolutionBitrateLimits{456, 22000, 55000, 88000},
+ VideoEncoder::ResolutionBitrateLimits{789, 33000, 66000, 99000}));
+}
+
+} // namespace webrtc
diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
index 3d87379..9a05e47 100644
--- a/video/video_stream_encoder.cc
+++ b/video/video_stream_encoder.cc
@@ -337,6 +337,78 @@
return layers_allocation;
}
+int NumActiveStreams(const std::vector<VideoStream>& streams) {
+ int num_active = 0;
+ for (const auto& stream : streams) {
+ if (stream.active)
+ ++num_active;
+ }
+ return num_active;
+}
+
+void ApplyEncoderBitrateLimitsIfSingleActiveStream(
+ const VideoEncoder::EncoderInfo& encoder_info,
+ const std::vector<VideoStream>& encoder_config_layers,
+ std::vector<VideoStream>* streams) {
+ // Apply limits if simulcast with one active stream (expect lowest).
+ bool single_active_stream =
+ streams->size() > 1 && NumActiveStreams(*streams) == 1 &&
+ !streams->front().active && NumActiveStreams(encoder_config_layers) == 1;
+ if (!single_active_stream) {
+ return;
+ }
+
+ // Index for the active stream.
+ size_t index = 0;
+ for (size_t i = 0; i < encoder_config_layers.size(); ++i) {
+ if (encoder_config_layers[i].active)
+ index = i;
+ }
+ if (streams->size() < (index + 1) || !(*streams)[index].active) {
+ return;
+ }
+
+ // Get bitrate limits for active stream.
+ absl::optional<VideoEncoder::ResolutionBitrateLimits> encoder_bitrate_limits =
+ encoder_info.GetEncoderBitrateLimitsForResolution(
+ (*streams)[index].width * (*streams)[index].height);
+ if (!encoder_bitrate_limits) {
+ return;
+ }
+
+ // If bitrate limits are set by RtpEncodingParameters, use intersection.
+ int min_bitrate_bps;
+ if (encoder_config_layers[index].min_bitrate_bps <= 0) {
+ min_bitrate_bps = encoder_bitrate_limits->min_bitrate_bps;
+ } else {
+ min_bitrate_bps = std::max(encoder_bitrate_limits->min_bitrate_bps,
+ (*streams)[index].min_bitrate_bps);
+ }
+ int max_bitrate_bps;
+ if (encoder_config_layers[index].max_bitrate_bps <= 0) {
+ max_bitrate_bps = encoder_bitrate_limits->max_bitrate_bps;
+ } else {
+ max_bitrate_bps = std::min(encoder_bitrate_limits->max_bitrate_bps,
+ (*streams)[index].max_bitrate_bps);
+ }
+ if (min_bitrate_bps >= max_bitrate_bps) {
+ RTC_LOG(LS_WARNING) << "Encoder bitrate limits"
+ << " (min=" << encoder_bitrate_limits->min_bitrate_bps
+ << ", max=" << encoder_bitrate_limits->max_bitrate_bps
+ << ") do not intersect with stream limits"
+ << " (min=" << (*streams)[index].min_bitrate_bps
+ << ", max=" << (*streams)[index].max_bitrate_bps
+ << "). Encoder bitrate limits not used.";
+ return;
+ }
+
+ (*streams)[index].min_bitrate_bps = min_bitrate_bps;
+ (*streams)[index].max_bitrate_bps = max_bitrate_bps;
+ (*streams)[index].target_bitrate_bps =
+ std::min((*streams)[index].target_bitrate_bps,
+ encoder_bitrate_limits->max_bitrate_bps);
+}
+
} // namespace
VideoStreamEncoder::EncoderRateSettings::EncoderRateSettings()
@@ -769,46 +841,51 @@
encoder_->GetEncoderInfo().GetEncoderBitrateLimitsForResolution(
last_frame_info_->width * last_frame_info_->height);
- if (streams.size() == 1 && encoder_bitrate_limits_) {
- // Bitrate limits can be set by app (in SDP or RtpEncodingParameters) or/and
- // can be provided by encoder. In presence of both set of limits, the final
- // set is derived as their intersection.
- int min_bitrate_bps;
- if (encoder_config_.simulcast_layers.empty() ||
- encoder_config_.simulcast_layers[0].min_bitrate_bps <= 0) {
- min_bitrate_bps = encoder_bitrate_limits_->min_bitrate_bps;
- } else {
- min_bitrate_bps = std::max(encoder_bitrate_limits_->min_bitrate_bps,
- streams.back().min_bitrate_bps);
- }
+ if (encoder_bitrate_limits_) {
+ if (streams.size() == 1 && encoder_config_.simulcast_layers.size() == 1) {
+ // Bitrate limits can be set by app (in SDP or RtpEncodingParameters)
+ // or/and can be provided by encoder. In presence of both set of limits,
+ // the final set is derived as their intersection.
+ int min_bitrate_bps;
+ if (encoder_config_.simulcast_layers.empty() ||
+ encoder_config_.simulcast_layers[0].min_bitrate_bps <= 0) {
+ min_bitrate_bps = encoder_bitrate_limits_->min_bitrate_bps;
+ } else {
+ min_bitrate_bps = std::max(encoder_bitrate_limits_->min_bitrate_bps,
+ streams.back().min_bitrate_bps);
+ }
- int max_bitrate_bps;
- // We don't check encoder_config_.simulcast_layers[0].max_bitrate_bps
- // here since encoder_config_.max_bitrate_bps is derived from it (as
- // well as from other inputs).
- if (encoder_config_.max_bitrate_bps <= 0) {
- max_bitrate_bps = encoder_bitrate_limits_->max_bitrate_bps;
- } else {
- max_bitrate_bps = std::min(encoder_bitrate_limits_->max_bitrate_bps,
- streams.back().max_bitrate_bps);
- }
+ int max_bitrate_bps;
+ // We don't check encoder_config_.simulcast_layers[0].max_bitrate_bps
+ // here since encoder_config_.max_bitrate_bps is derived from it (as
+ // well as from other inputs).
+ if (encoder_config_.max_bitrate_bps <= 0) {
+ max_bitrate_bps = encoder_bitrate_limits_->max_bitrate_bps;
+ } else {
+ max_bitrate_bps = std::min(encoder_bitrate_limits_->max_bitrate_bps,
+ streams.back().max_bitrate_bps);
+ }
- if (min_bitrate_bps < max_bitrate_bps) {
- streams.back().min_bitrate_bps = min_bitrate_bps;
- streams.back().max_bitrate_bps = max_bitrate_bps;
- streams.back().target_bitrate_bps =
- std::min(streams.back().target_bitrate_bps,
- encoder_bitrate_limits_->max_bitrate_bps);
+ if (min_bitrate_bps < max_bitrate_bps) {
+ streams.back().min_bitrate_bps = min_bitrate_bps;
+ streams.back().max_bitrate_bps = max_bitrate_bps;
+ streams.back().target_bitrate_bps =
+ std::min(streams.back().target_bitrate_bps,
+ encoder_bitrate_limits_->max_bitrate_bps);
+ } else {
+ RTC_LOG(LS_WARNING)
+ << "Bitrate limits provided by encoder"
+ << " (min=" << encoder_bitrate_limits_->min_bitrate_bps
+ << ", max=" << encoder_bitrate_limits_->min_bitrate_bps
+ << ") do not intersect with limits set by app"
+ << " (min=" << streams.back().min_bitrate_bps
+ << ", max=" << encoder_config_.max_bitrate_bps
+ << "). The app bitrate limits will be used.";
+ }
} else {
- RTC_LOG(LS_WARNING) << "Bitrate limits provided by encoder"
- << " (min="
- << encoder_bitrate_limits_->min_bitrate_bps
- << ", max="
- << encoder_bitrate_limits_->min_bitrate_bps
- << ") do not intersect with limits set by app"
- << " (min=" << streams.back().min_bitrate_bps
- << ", max=" << encoder_config_.max_bitrate_bps
- << "). The app bitrate limits will be used.";
+ ApplyEncoderBitrateLimitsIfSingleActiveStream(
+ encoder_->GetEncoderInfo(), encoder_config_.simulcast_layers,
+ &streams);
}
}
diff --git a/video/video_stream_encoder_unittest.cc b/video/video_stream_encoder_unittest.cc
index 85be695..dea22de 100644
--- a/video/video_stream_encoder_unittest.cc
+++ b/video/video_stream_encoder_unittest.cc
@@ -2009,6 +2009,201 @@
video_stream_encoder_->Stop();
}
+TEST_F(VideoStreamEncoderTest,
+ EncoderMaxAndMinBitratesUsedForTwoStreamsHighestActive) {
+ const VideoEncoder::ResolutionBitrateLimits kEncoderLimits270p(
+ 480 * 270, 34 * 1000, 12 * 1000, 1234 * 1000);
+ const VideoEncoder::ResolutionBitrateLimits kEncoderLimits360p(
+ 640 * 360, 43 * 1000, 21 * 1000, 2345 * 1000);
+ fake_encoder_.SetResolutionBitrateLimits(
+ {kEncoderLimits270p, kEncoderLimits360p});
+
+ // Two streams, highest stream active.
+ VideoEncoderConfig config;
+ const int kNumStreams = 2;
+ test::FillEncoderConfiguration(kVideoCodecVP8, kNumStreams, &config);
+ config.max_bitrate_bps = 0;
+ config.simulcast_layers[0].active = false;
+ config.simulcast_layers[1].active = true;
+ config.video_stream_factory =
+ new rtc::RefCountedObject<cricket::EncoderStreamFactory>(
+ "VP8", /*max qp*/ 56, /*screencast*/ false,
+ /*screenshare enabled*/ false);
+ video_stream_encoder_->ConfigureEncoder(config.Copy(), kMaxPayloadLength);
+
+ // The encoder bitrate limits for 270p should be used.
+ video_source_.IncomingCapturedFrame(CreateFrame(1, 480, 270));
+ EXPECT_FALSE(WaitForFrame(1000));
+ EXPECT_EQ(fake_encoder_.video_codec().numberOfSimulcastStreams, kNumStreams);
+ EXPECT_EQ(static_cast<uint32_t>(kEncoderLimits270p.min_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].minBitrate * 1000);
+ EXPECT_EQ(static_cast<uint32_t>(kEncoderLimits270p.max_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].maxBitrate * 1000);
+
+ // The encoder bitrate limits for 360p should be used.
+ video_source_.IncomingCapturedFrame(CreateFrame(2, 640, 360));
+ EXPECT_FALSE(WaitForFrame(1000));
+ EXPECT_EQ(static_cast<uint32_t>(kEncoderLimits360p.min_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].minBitrate * 1000);
+ EXPECT_EQ(static_cast<uint32_t>(kEncoderLimits360p.max_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].maxBitrate * 1000);
+
+ // Resolution b/w 270p and 360p. The encoder limits for 360p should be used.
+ video_source_.IncomingCapturedFrame(
+ CreateFrame(3, (640 + 480) / 2, (360 + 270) / 2));
+ EXPECT_FALSE(WaitForFrame(1000));
+ EXPECT_EQ(static_cast<uint32_t>(kEncoderLimits360p.min_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].minBitrate * 1000);
+ EXPECT_EQ(static_cast<uint32_t>(kEncoderLimits360p.max_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].maxBitrate * 1000);
+
+ // Resolution higher than 360p. Encoder limits should be ignored.
+ video_source_.IncomingCapturedFrame(CreateFrame(4, 960, 540));
+ EXPECT_FALSE(WaitForFrame(1000));
+ EXPECT_NE(static_cast<uint32_t>(kEncoderLimits270p.min_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].minBitrate * 1000);
+ EXPECT_NE(static_cast<uint32_t>(kEncoderLimits270p.max_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].maxBitrate * 1000);
+ EXPECT_NE(static_cast<uint32_t>(kEncoderLimits360p.min_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].minBitrate * 1000);
+ EXPECT_NE(static_cast<uint32_t>(kEncoderLimits360p.max_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].maxBitrate * 1000);
+
+ // Resolution lower than 270p. The encoder limits for 270p should be used.
+ video_source_.IncomingCapturedFrame(CreateFrame(5, 320, 180));
+ EXPECT_FALSE(WaitForFrame(1000));
+ EXPECT_EQ(static_cast<uint32_t>(kEncoderLimits270p.min_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].minBitrate * 1000);
+ EXPECT_EQ(static_cast<uint32_t>(kEncoderLimits270p.max_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].maxBitrate * 1000);
+
+ video_stream_encoder_->Stop();
+}
+
+TEST_F(VideoStreamEncoderTest,
+ EncoderMaxAndMinBitratesUsedForThreeStreamsMiddleActive) {
+ const VideoEncoder::ResolutionBitrateLimits kEncoderLimits270p(
+ 480 * 270, 34 * 1000, 12 * 1000, 1234 * 1000);
+ const VideoEncoder::ResolutionBitrateLimits kEncoderLimits360p(
+ 640 * 360, 43 * 1000, 21 * 1000, 2345 * 1000);
+ const VideoEncoder::ResolutionBitrateLimits kEncoderLimits720p(
+ 1280 * 720, 54 * 1000, 31 * 1000, 3456 * 1000);
+ fake_encoder_.SetResolutionBitrateLimits(
+ {kEncoderLimits270p, kEncoderLimits360p, kEncoderLimits720p});
+
+ // Three streams, middle stream active.
+ VideoEncoderConfig config;
+ const int kNumStreams = 3;
+ test::FillEncoderConfiguration(kVideoCodecVP8, kNumStreams, &config);
+ config.simulcast_layers[0].active = false;
+ config.simulcast_layers[1].active = true;
+ config.simulcast_layers[2].active = false;
+ config.video_stream_factory =
+ new rtc::RefCountedObject<cricket::EncoderStreamFactory>(
+ "VP8", /*max qp*/ 56, /*screencast*/ false,
+ /*screenshare enabled*/ false);
+ video_stream_encoder_->ConfigureEncoder(config.Copy(), kMaxPayloadLength);
+
+ // The encoder bitrate limits for 360p should be used.
+ video_source_.IncomingCapturedFrame(CreateFrame(1, 1280, 720));
+ EXPECT_FALSE(WaitForFrame(1000));
+ EXPECT_EQ(fake_encoder_.video_codec().numberOfSimulcastStreams, kNumStreams);
+ EXPECT_EQ(static_cast<uint32_t>(kEncoderLimits360p.min_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].minBitrate * 1000);
+ EXPECT_EQ(static_cast<uint32_t>(kEncoderLimits360p.max_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].maxBitrate * 1000);
+
+ // The encoder bitrate limits for 270p should be used.
+ video_source_.IncomingCapturedFrame(CreateFrame(2, 960, 540));
+ EXPECT_FALSE(WaitForFrame(1000));
+ EXPECT_EQ(static_cast<uint32_t>(kEncoderLimits270p.min_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].minBitrate * 1000);
+ EXPECT_EQ(static_cast<uint32_t>(kEncoderLimits270p.max_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].maxBitrate * 1000);
+
+ video_stream_encoder_->Stop();
+}
+
+TEST_F(VideoStreamEncoderTest,
+ EncoderMaxAndMinBitratesNotUsedForThreeStreamsLowestActive) {
+ const VideoEncoder::ResolutionBitrateLimits kEncoderLimits270p(
+ 480 * 270, 34 * 1000, 12 * 1000, 1234 * 1000);
+ const VideoEncoder::ResolutionBitrateLimits kEncoderLimits360p(
+ 640 * 360, 43 * 1000, 21 * 1000, 2345 * 1000);
+ const VideoEncoder::ResolutionBitrateLimits kEncoderLimits720p(
+ 1280 * 720, 54 * 1000, 31 * 1000, 3456 * 1000);
+ fake_encoder_.SetResolutionBitrateLimits(
+ {kEncoderLimits270p, kEncoderLimits360p, kEncoderLimits720p});
+
+ // Three streams, lowest stream active.
+ VideoEncoderConfig config;
+ const int kNumStreams = 3;
+ test::FillEncoderConfiguration(kVideoCodecVP8, kNumStreams, &config);
+ config.simulcast_layers[0].active = true;
+ config.simulcast_layers[1].active = false;
+ config.simulcast_layers[2].active = false;
+ config.video_stream_factory =
+ new rtc::RefCountedObject<cricket::EncoderStreamFactory>(
+ "VP8", /*max qp*/ 56, /*screencast*/ false,
+ /*screenshare enabled*/ false);
+ video_stream_encoder_->ConfigureEncoder(config.Copy(), kMaxPayloadLength);
+
+ // Resolution on lowest stream lower than 270p. The encoder limits not applied
+ // on lowest stream, limits for 270p should not be used
+ video_source_.IncomingCapturedFrame(CreateFrame(1, 1280, 720));
+ EXPECT_FALSE(WaitForFrame(1000));
+ EXPECT_EQ(fake_encoder_.video_codec().numberOfSimulcastStreams, kNumStreams);
+ EXPECT_NE(static_cast<uint32_t>(kEncoderLimits270p.min_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].minBitrate * 1000);
+ EXPECT_NE(static_cast<uint32_t>(kEncoderLimits270p.max_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].maxBitrate * 1000);
+
+ video_stream_encoder_->Stop();
+}
+
+TEST_F(VideoStreamEncoderTest,
+ EncoderMaxBitrateCappedByConfigForTwoStreamsHighestActive) {
+ const VideoEncoder::ResolutionBitrateLimits kEncoderLimits270p(
+ 480 * 270, 34 * 1000, 12 * 1000, 1234 * 1000);
+ const VideoEncoder::ResolutionBitrateLimits kEncoderLimits360p(
+ 640 * 360, 43 * 1000, 21 * 1000, 2345 * 1000);
+ fake_encoder_.SetResolutionBitrateLimits(
+ {kEncoderLimits270p, kEncoderLimits360p});
+ const int kMaxBitrateBps = kEncoderLimits360p.max_bitrate_bps - 100 * 1000;
+
+ // Two streams, highest stream active.
+ VideoEncoderConfig config;
+ const int kNumStreams = 2;
+ test::FillEncoderConfiguration(kVideoCodecVP8, kNumStreams, &config);
+ config.simulcast_layers[0].active = false;
+ config.simulcast_layers[1].active = true;
+ config.simulcast_layers[1].max_bitrate_bps = kMaxBitrateBps;
+ config.video_stream_factory =
+ new rtc::RefCountedObject<cricket::EncoderStreamFactory>(
+ "VP8", /*max qp*/ 56, /*screencast*/ false,
+ /*screenshare enabled*/ false);
+ video_stream_encoder_->ConfigureEncoder(config.Copy(), kMaxPayloadLength);
+
+ // The encoder bitrate limits for 270p should be used.
+ video_source_.IncomingCapturedFrame(CreateFrame(1, 480, 270));
+ EXPECT_FALSE(WaitForFrame(1000));
+ EXPECT_EQ(fake_encoder_.video_codec().numberOfSimulcastStreams, kNumStreams);
+ EXPECT_EQ(static_cast<uint32_t>(kEncoderLimits270p.min_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].minBitrate * 1000);
+ EXPECT_EQ(static_cast<uint32_t>(kEncoderLimits270p.max_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].maxBitrate * 1000);
+
+ // The max configured bitrate is less than the encoder limit for 360p.
+ video_source_.IncomingCapturedFrame(CreateFrame(2, 640, 360));
+ EXPECT_FALSE(WaitForFrame(1000));
+ EXPECT_EQ(static_cast<uint32_t>(kEncoderLimits360p.min_bitrate_bps),
+ fake_encoder_.video_codec().simulcastStream[1].minBitrate * 1000);
+ EXPECT_EQ(static_cast<uint32_t>(kMaxBitrateBps),
+ fake_encoder_.video_codec().simulcastStream[1].maxBitrate * 1000);
+
+ video_stream_encoder_->Stop();
+}
+
TEST_F(VideoStreamEncoderTest, SwitchSourceDeregisterEncoderAsSink) {
EXPECT_TRUE(video_source_.has_sinks());
test::FrameForwarder new_video_source;
@@ -4091,7 +4286,7 @@
}
TEST_F(VideoStreamEncoderTest,
- ReportsVideoLayersAllocationForVP8WithMidleLayerDisabled) {
+ ReportsVideoLayersAllocationForVP8WithMiddleLayerDisabled) {
fake_encoder_.SetTemporalLayersSupported(/*spatial_idx=*/0, true);
fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 1, true);
fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 2, true);
@@ -4136,7 +4331,7 @@
}
TEST_F(VideoStreamEncoderTest,
- ReportsVideoLayersAllocationForVP8WithMidleAndHighestLayerDisabled) {
+ ReportsVideoLayersAllocationForVP8WithMiddleAndHighestLayerDisabled) {
fake_encoder_.SetTemporalLayersSupported(/*spatial_idx=*/0, true);
fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 1, true);
fake_encoder_.SetTemporalLayersSupported(/*spatial_idx*/ 2, true);