Ensure that we always set values for min and max audio bitrate.
Use (in order from lowest to highest precedence):
-- fixed 32000bps
-- fixed target bitrate from codec
-- explicit values from the rtp encoding parameters
-- Final precedence is given to field trial values from
WebRTC-Audio-Allocation
Bug: webrtc:10487
Change-Id: I7e289f209a927785572058b6fbfdf60fa14edf05
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/126229
Reviewed-by: Minyue Li <minyue@google.com>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Commit-Queue: Daniel Lee <dklee@google.com>
Cr-Commit-Position: refs/heads/master@{#27667}
diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc
index 283fd9a..c8730d7 100644
--- a/audio/audio_send_stream.cc
+++ b/audio/audio_send_stream.cc
@@ -446,18 +446,12 @@
}
uint32_t AudioSendStream::OnBitrateUpdated(BitrateAllocationUpdate update) {
- // A send stream may be allocated a bitrate of zero if the allocator decides
- // to disable it. For now we ignore this decision and keep sending on min
- // bitrate.
- if (update.target_bitrate.IsZero()) {
- update.target_bitrate = DataRate::bps(config_.min_bitrate_bps);
- }
- RTC_DCHECK_GE(update.target_bitrate.bps<int>(), config_.min_bitrate_bps);
- // The bitrate allocator might allocate an higher than max configured bitrate
- // if there is room, to allow for, as example, extra FEC. Ignore that for now.
- const DataRate max_bitrate = DataRate::bps(config_.max_bitrate_bps);
- if (update.target_bitrate > max_bitrate)
- update.target_bitrate = max_bitrate;
+ // Pick a target bitrate between the constraints. Overrules the allocator if
+ // it 1) allocated a bitrate of zero to disable the stream or 2) allocated a
+ // higher than max to allow for e.g. extra FEC. For now we ignore these the
+ // exceptions.
+ auto constraints = GetMinMaxBitrateConstraints();
+ update.target_bitrate.Clamp(constraints.min, constraints.max);
channel_send_->OnBitrateAllocation(update);
@@ -799,12 +793,14 @@
void AudioSendStream::ConfigureBitrateObserver() {
// This either updates the current observer or adds a new observer.
// TODO(srte): Add overhead compensation here.
+ auto constraints = GetMinMaxBitrateConstraints();
+
bitrate_allocator_->AddObserver(
- this, MediaStreamAllocationConfig{
- static_cast<uint32_t>(config_.min_bitrate_bps),
- static_cast<uint32_t>(config_.max_bitrate_bps), 0,
- allocation_settings_.DefaultPriorityBitrate().bps(), true,
- config_.track_id, config_.bitrate_priority});
+ this,
+ MediaStreamAllocationConfig{
+ constraints.min.bps<uint32_t>(), constraints.max.bps<uint32_t>(), 0,
+ allocation_settings_.DefaultPriorityBitrate().bps(), true,
+ config_.track_id, config_.bitrate_priority});
}
void AudioSendStream::RemoveBitrateObserver() {
@@ -819,6 +815,36 @@
thread_sync_event.Wait(rtc::Event::kForever);
}
+AudioSendStream::TargetAudioBitrateConstraints
+AudioSendStream::GetMinMaxBitrateConstraints() const {
+ TargetAudioBitrateConstraints constraints{
+ DataRate::bps(config_.min_bitrate_bps),
+ DataRate::bps(config_.max_bitrate_bps)};
+
+ // If bitrates were explicitly overriden via field trial, use those values.
+ if (allocation_settings_.MinBitrate())
+ constraints.min = *allocation_settings_.MinBitrate();
+ if (allocation_settings_.MaxBitrate())
+ constraints.max = *allocation_settings_.MaxBitrate();
+
+ RTC_DCHECK_GE(constraints.min.bps(), 0);
+ RTC_DCHECK_GE(constraints.max.bps(), 0);
+ RTC_DCHECK_GE(constraints.max.bps(), constraints.min.bps());
+
+ // TODO(srte,dklee): Replace these with proper overhead calculations.
+ if (allocation_settings_.IncludeOverheadInAudioAllocation()) {
+ // OverheadPerPacket = Ipv4(20B) + UDP(8B) + SRTP(10B) + RTP(12)
+ const DataSize kOverheadPerPacket = DataSize::bytes(20 + 8 + 10 + 12);
+ const TimeDelta kMaxFrameLength = TimeDelta::ms(60); // Based on Opus spec
+ const DataRate kMinOverhead = kOverheadPerPacket / kMaxFrameLength;
+ constraints.min += kMinOverhead;
+ // TODO(dklee): This is obviously overly conservative to avoid exceeding max
+ // bitrate. Carefully reconsider the logic when addressing todo above.
+ constraints.max += kMinOverhead;
+ }
+ return constraints;
+}
+
void AudioSendStream::RegisterCngPayloadType(int payload_type,
int clockrate_hz) {
channel_send_->RegisterCngPayloadType(payload_type, clockrate_hz);
diff --git a/audio/audio_send_stream.h b/audio/audio_send_stream.h
index 8bee352..9796e80 100644
--- a/audio/audio_send_stream.h
+++ b/audio/audio_send_stream.h
@@ -104,6 +104,11 @@
private:
class TimedTransport;
+ // Constraints including overhead.
+ struct TargetAudioBitrateConstraints {
+ DataRate min;
+ DataRate max;
+ };
internal::AudioState* audio_state();
const internal::AudioState* audio_state() const;
@@ -126,6 +131,10 @@
void ConfigureBitrateObserver() RTC_RUN_ON(worker_queue_);
void RemoveBitrateObserver();
+ // Returns bitrate constraints, maybe including overhead when enabled by
+ // field trial.
+ TargetAudioBitrateConstraints GetMinMaxBitrateConstraints() const;
+
// Sets per-packet overhead on encoded (for ANA) based on current known values
// of transport and packetization overheads.
void UpdateOverheadForEncoder()
diff --git a/audio/audio_send_stream_unittest.cc b/audio/audio_send_stream_unittest.cc
index 701df1c..61acc96 100644
--- a/audio/audio_send_stream_unittest.cc
+++ b/audio/audio_send_stream_unittest.cc
@@ -73,6 +73,13 @@
{kOpusFormat, {48000, 1, 32000, 6000, 510000}},
{kG722Format, {16000, 1, 64000}}};
+// TODO(dklee): This mirrors calculation in audio_send_stream.cc, which
+// should be made more precise in the future. This can be changed when that
+// logic is more accurate.
+const DataSize kOverheadPerPacket = DataSize::bytes(20 + 8 + 10 + 12);
+const TimeDelta kMaxFrameLength = TimeDelta::ms(60);
+const DataRate kOverheadRate = kOverheadPerPacket / kMaxFrameLength;
+
class MockLimitObserver : public BitrateAllocator::LimitObserver {
public:
MOCK_METHOD3(OnAllocationLimitsChanged,
@@ -483,6 +490,97 @@
send_stream->OnBitrateUpdated(update);
}
+TEST(AudioSendStreamTest, SSBweTargetInRangeRespected) {
+ ScopedFieldTrials field_trials("WebRTC-Audio-SendSideBwe/Enabled/");
+ ConfigHelper helper(true, true);
+ auto send_stream = helper.CreateAudioSendStream();
+ EXPECT_CALL(*helper.channel_send(),
+ OnBitrateAllocation(Field(
+ &BitrateAllocationUpdate::target_bitrate,
+ Eq(DataRate::bps(helper.config().max_bitrate_bps - 5000)))));
+ BitrateAllocationUpdate update;
+ update.target_bitrate = DataRate::bps(helper.config().max_bitrate_bps - 5000);
+ send_stream->OnBitrateUpdated(update);
+}
+
+TEST(AudioSendStreamTest, SSBweFieldTrialMinRespected) {
+ ScopedFieldTrials field_trials(
+ "WebRTC-Audio-SendSideBwe/Enabled/"
+ "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/");
+ ConfigHelper helper(true, true);
+ auto send_stream = helper.CreateAudioSendStream();
+ EXPECT_CALL(
+ *helper.channel_send(),
+ OnBitrateAllocation(Field(&BitrateAllocationUpdate::target_bitrate,
+ Eq(DataRate::kbps(6)))));
+ BitrateAllocationUpdate update;
+ update.target_bitrate = DataRate::kbps(1);
+ send_stream->OnBitrateUpdated(update);
+}
+
+TEST(AudioSendStreamTest, SSBweFieldTrialMaxRespected) {
+ ScopedFieldTrials field_trials(
+ "WebRTC-Audio-SendSideBwe/Enabled/"
+ "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/");
+ ConfigHelper helper(true, true);
+ auto send_stream = helper.CreateAudioSendStream();
+ EXPECT_CALL(
+ *helper.channel_send(),
+ OnBitrateAllocation(Field(&BitrateAllocationUpdate::target_bitrate,
+ Eq(DataRate::kbps(64)))));
+ BitrateAllocationUpdate update;
+ update.target_bitrate = DataRate::kbps(128);
+ send_stream->OnBitrateUpdated(update);
+}
+
+TEST(AudioSendStreamTest, SSBweWithOverhead) {
+ ScopedFieldTrials field_trials(
+ "WebRTC-Audio-SendSideBwe/Enabled/"
+ "WebRTC-SendSideBwe-WithOverhead/Enabled/");
+ ConfigHelper helper(true, true);
+ auto send_stream = helper.CreateAudioSendStream();
+ const DataRate bitrate =
+ DataRate::bps(helper.config().max_bitrate_bps) + kOverheadRate;
+ EXPECT_CALL(*helper.channel_send(),
+ OnBitrateAllocation(Field(
+ &BitrateAllocationUpdate::target_bitrate, Eq(bitrate))));
+ BitrateAllocationUpdate update;
+ update.target_bitrate = bitrate;
+ send_stream->OnBitrateUpdated(update);
+}
+
+TEST(AudioSendStreamTest, SSBweWithOverheadMinRespected) {
+ ScopedFieldTrials field_trials(
+ "WebRTC-Audio-SendSideBwe/Enabled/"
+ "WebRTC-SendSideBwe-WithOverhead/Enabled/"
+ "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/");
+ ConfigHelper helper(true, true);
+ auto send_stream = helper.CreateAudioSendStream();
+ const DataRate bitrate = DataRate::kbps(6) + kOverheadRate;
+ EXPECT_CALL(*helper.channel_send(),
+ OnBitrateAllocation(Field(
+ &BitrateAllocationUpdate::target_bitrate, Eq(bitrate))));
+ BitrateAllocationUpdate update;
+ update.target_bitrate = DataRate::kbps(1);
+ send_stream->OnBitrateUpdated(update);
+}
+
+TEST(AudioSendStreamTest, SSBweWithOverheadMaxRespected) {
+ ScopedFieldTrials field_trials(
+ "WebRTC-Audio-SendSideBwe/Enabled/"
+ "WebRTC-SendSideBwe-WithOverhead/Enabled/"
+ "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/");
+ ConfigHelper helper(true, true);
+ auto send_stream = helper.CreateAudioSendStream();
+ const DataRate bitrate = DataRate::kbps(64) + kOverheadRate;
+ EXPECT_CALL(*helper.channel_send(),
+ OnBitrateAllocation(Field(
+ &BitrateAllocationUpdate::target_bitrate, Eq(bitrate))));
+ BitrateAllocationUpdate update;
+ update.target_bitrate = DataRate::kbps(128);
+ send_stream->OnBitrateUpdated(update);
+}
+
TEST(AudioSendStreamTest, ProbingIntervalOnBitrateUpdated) {
ConfigHelper helper(false, true);
auto send_stream = helper.CreateAudioSendStream();
diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc
index 9110d55..b0c5b7b 100644
--- a/media/engine/webrtc_voice_engine.cc
+++ b/media/engine/webrtc_voice_engine.cc
@@ -977,10 +977,27 @@
config_.send_codec_spec &&
absl::EqualsIgnoreCase(config_.send_codec_spec->format.name,
kOpusCodecName);
- if (is_opus && allocation_settings_.ConfigureRateAllocationRange()) {
- config_.min_bitrate_bps = allocation_settings_.MinBitrateBps();
- config_.max_bitrate_bps = allocation_settings_.MaxBitrateBps(
- rtp_parameters_.encodings[0].max_bitrate_bps);
+ if (is_opus) {
+ // The order of precedence (from lowest to highest is)
+ // - a reasonable default of 32kbps min/max
+ // - fixed target bitrate from codec spec
+ // - bitrate configured in the rtp_parameter encodings settings
+ const int kDefaultBitrateBps = 32000;
+ config_.min_bitrate_bps = kDefaultBitrateBps;
+ config_.max_bitrate_bps = kDefaultBitrateBps;
+
+ if (config_.send_codec_spec &&
+ config_.send_codec_spec->target_bitrate_bps) {
+ config_.min_bitrate_bps = *config_.send_codec_spec->target_bitrate_bps;
+ config_.max_bitrate_bps = *config_.send_codec_spec->target_bitrate_bps;
+ }
+
+ if (rtp_parameters_.encodings[0].min_bitrate_bps) {
+ config_.min_bitrate_bps = *rtp_parameters_.encodings[0].min_bitrate_bps;
+ }
+ if (rtp_parameters_.encodings[0].max_bitrate_bps) {
+ config_.max_bitrate_bps = *rtp_parameters_.encodings[0].max_bitrate_bps;
+ }
}
}
diff --git a/media/engine/webrtc_voice_engine_unittest.cc b/media/engine/webrtc_voice_engine_unittest.cc
index 4ddcd43..a76186d 100644
--- a/media/engine/webrtc_voice_engine_unittest.cc
+++ b/media/engine/webrtc_voice_engine_unittest.cc
@@ -2405,72 +2405,11 @@
public:
WebRtcVoiceEngineWithSendSideBweWithOverheadTest()
: WebRtcVoiceEngineTestFake(
- "WebRTC-Audio-SendSideBwe/Enabled/WebRTC-SendSideBwe-WithOverhead/"
+ "WebRTC-Audio-SendSideBwe/Enabled/WebRTC-Audio-Allocation/"
+ "min:6000bps,max:32000bps/WebRTC-SendSideBwe-WithOverhead/"
"Enabled/") {}
};
-TEST_F(WebRtcVoiceEngineWithSendSideBweWithOverheadTest, MinAndMaxBitrate) {
- EXPECT_TRUE(SetupSendStream());
- cricket::AudioSendParameters parameters;
- parameters.codecs.push_back(kOpusCodec);
- SetSendParameters(parameters);
- const int initial_num = call_.GetNumCreatedSendStreams();
- EXPECT_EQ(initial_num, call_.GetNumCreatedSendStreams());
-
- // OverheadPerPacket = Ipv4(20B) + UDP(8B) + SRTP(10B) + RTP(12)
- constexpr int kOverheadPerPacket = 20 + 8 + 10 + 12;
- constexpr int kOpusMaxPtimeMs = WEBRTC_OPUS_SUPPORT_120MS_PTIME ? 120 : 60;
- constexpr int kMinOverheadBps =
- kOverheadPerPacket * 8 * 1000 / kOpusMaxPtimeMs;
-
- constexpr int kOpusMinBitrateBps = 6000;
- EXPECT_EQ(kOpusMinBitrateBps + kMinOverheadBps,
- GetSendStreamConfig(kSsrcX).min_bitrate_bps);
- constexpr int kOpusBitrateFbBps = 32000;
- EXPECT_EQ(kOpusBitrateFbBps + kMinOverheadBps,
- GetSendStreamConfig(kSsrcX).max_bitrate_bps);
-
- parameters.options.audio_network_adaptor = true;
- parameters.options.audio_network_adaptor_config = {"1234"};
- SetSendParameters(parameters);
-
- constexpr int kMinOverheadWithAnaBps =
- kOverheadPerPacket * 8 * 1000 / kOpusMaxPtimeMs;
-
- EXPECT_EQ(kOpusMinBitrateBps + kMinOverheadWithAnaBps,
- GetSendStreamConfig(kSsrcX).min_bitrate_bps);
-
- EXPECT_EQ(kOpusBitrateFbBps + kMinOverheadWithAnaBps,
- GetSendStreamConfig(kSsrcX).max_bitrate_bps);
-}
-
-// This test is similar to
-// WebRtcVoiceEngineTestFake.SetRtpSendParameterUpdatesMaxBitrate but with an
-// additional field trial.
-TEST_F(WebRtcVoiceEngineWithSendSideBweWithOverheadTest,
- SetRtpSendParameterUpdatesMaxBitrate) {
- EXPECT_TRUE(SetupSendStream());
- cricket::AudioSendParameters send_parameters;
- send_parameters.codecs.push_back(kOpusCodec);
- SetSendParameters(send_parameters);
-
- webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrcX);
- // Expect empty on parameters.encodings[0].max_bitrate_bps;
- EXPECT_FALSE(rtp_parameters.encodings[0].max_bitrate_bps);
-
- constexpr int kMaxBitrateBps = 6000;
- rtp_parameters.encodings[0].max_bitrate_bps = kMaxBitrateBps;
- EXPECT_TRUE(channel_->SetRtpSendParameters(kSsrcX, rtp_parameters).ok());
-
- const int max_bitrate = GetSendStreamConfig(kSsrcX).max_bitrate_bps;
-#if WEBRTC_OPUS_SUPPORT_120MS_PTIME
- constexpr int kMinOverhead = 3333;
-#else
- constexpr int kMinOverhead = 6666;
-#endif
- EXPECT_EQ(max_bitrate, kMaxBitrateBps + kMinOverhead);
-}
-
// Test that we can set the outgoing SSRC properly.
// SSRC is set in SetupSendStream() by calling AddSendStream.
TEST_F(WebRtcVoiceEngineTestFake, SetSendSsrc) {
diff --git a/rtc_base/experiments/audio_allocation_settings.cc b/rtc_base/experiments/audio_allocation_settings.cc
index a601cce..6579531 100644
--- a/rtc_base/experiments/audio_allocation_settings.cc
+++ b/rtc_base/experiments/audio_allocation_settings.cc
@@ -12,9 +12,6 @@
namespace webrtc {
namespace {
-// For SendSideBwe, Opus bitrate should be in the range between 6000 and 32000.
-const int kOpusMinBitrateBps = 6000;
-const int kOpusBitrateFbBps = 32000;
// OverheadPerPacket = Ipv4(20B) + UDP(8B) + SRTP(10B) + RTP(12)
constexpr int kOverheadPerPacket = 20 + 8 + 10 + 12;
} // namespace
@@ -23,8 +20,8 @@
allocate_audio_without_feedback_("Enabled"),
force_no_audio_feedback_("Enabled"),
send_side_bwe_with_overhead_("Enabled"),
- default_min_bitrate_("min", DataRate::bps(kOpusMinBitrateBps)),
- default_max_bitrate_("max", DataRate::bps(kOpusBitrateFbBps)),
+ min_bitrate_("min"),
+ max_bitrate_("max"),
priority_bitrate_("prio", DataRate::Zero()) {
ParseFieldTrial({&audio_send_side_bwe_},
field_trial::FindFullName("WebRTC-Audio-SendSideBwe"));
@@ -35,9 +32,8 @@
ParseFieldTrial({&send_side_bwe_with_overhead_},
field_trial::FindFullName("WebRTC-SendSideBwe-WithOverhead"));
- ParseFieldTrial(
- {&default_min_bitrate_, &default_max_bitrate_, &priority_bitrate_},
- field_trial::FindFullName("WebRTC-Audio-Allocation"));
+ ParseFieldTrial({&min_bitrate_, &max_bitrate_, &priority_bitrate_},
+ field_trial::FindFullName("WebRTC-Audio-Allocation"));
// TODO(mflodman): Keep testing this and set proper values.
// Note: This is an early experiment currently only supported by Opus.
@@ -100,25 +96,15 @@
return true;
}
-int AudioAllocationSettings::MinBitrateBps() const {
- return default_min_bitrate_->bps() + min_overhead_bps_;
+bool AudioAllocationSettings::IncludeOverheadInAudioAllocation() const {
+ return send_side_bwe_with_overhead_;
}
-int AudioAllocationSettings::MaxBitrateBps(
- absl::optional<int> rtp_parameter_max_bitrate_bps) const {
- // We assume that the max is a hard limit on the payload bitrate, so we add
- // min_overhead_bps to it to ensure that, when overhead is deducted, the
- // payload rate never goes beyond the limit. Note: this also means that if a
- // higher overhead is forced, we cannot reach the limit.
- // TODO(minyue): Reconsider this when the signaling to BWE is done
- // through a dedicated API.
-
- // This means that when RtpParameters is reset, we may change the
- // encoder's bit rate immediately (through ReconfigureAudioSendStream()),
- // meanwhile change the cap to the output of BWE.
- if (rtp_parameter_max_bitrate_bps)
- return *rtp_parameter_max_bitrate_bps + min_overhead_bps_;
- return default_max_bitrate_->bps() + min_overhead_bps_;
+absl::optional<DataRate> AudioAllocationSettings::MinBitrate() const {
+ return min_bitrate_.GetOptional();
+}
+absl::optional<DataRate> AudioAllocationSettings::MaxBitrate() const {
+ return max_bitrate_.GetOptional();
}
DataRate AudioAllocationSettings::DefaultPriorityBitrate() const {
DataRate max_overhead = DataRate::Zero();
diff --git a/rtc_base/experiments/audio_allocation_settings.h b/rtc_base/experiments/audio_allocation_settings.h
index 32e11df..c099973 100644
--- a/rtc_base/experiments/audio_allocation_settings.h
+++ b/rtc_base/experiments/audio_allocation_settings.h
@@ -60,14 +60,13 @@
int max_bitrate_bps,
bool has_dscp,
int transport_seq_num_extension_header_id) const;
+ // Returns true if we should include packet overhead in audio allocation.
+ bool IncludeOverheadInAudioAllocation() const;
- // Returns the min bitrate for audio rate allocation, potentially including
- // overhead.
- int MinBitrateBps() const;
- // Returns the max bitrate for audio rate allocation, potentially including
- // overhead. |rtp_parameter_max_bitrate_bps| max bitrate as configured in rtp
- // parameters, excluding overhead.
- int MaxBitrateBps(absl::optional<int> rtp_parameter_max_bitrate_bps) const;
+ // Returns the min bitrate for audio rate allocation.
+ absl::optional<DataRate> MinBitrate() const;
+ // Returns the max bitrate for audio rate allocation.
+ absl::optional<DataRate> MaxBitrate() const;
// Indicates the default priority bitrate for audio streams. The bitrate
// allocator will prioritize audio until it reaches this bitrate and will
// divide bitrate evently between audio and video above this bitrate.
@@ -79,10 +78,10 @@
FieldTrialFlag force_no_audio_feedback_;
FieldTrialFlag send_side_bwe_with_overhead_;
int min_overhead_bps_ = 0;
- // Default bitrates to use as range if there's no user configured
- // bitrate range but audio bitrate allocation is enabled.
- FieldTrialParameter<DataRate> default_min_bitrate_;
- FieldTrialParameter<DataRate> default_max_bitrate_;
+ // Field Trial configured bitrates to use as overrides over default/user
+ // configured bitrate range when audio bitrate allocation is enabled.
+ FieldTrialOptional<DataRate> min_bitrate_;
+ FieldTrialOptional<DataRate> max_bitrate_;
FieldTrialParameter<DataRate> priority_bitrate_;
};
} // namespace webrtc