From 83bd37cda43249e69de07d40bcba5539bcbe7f9e Mon Sep 17 00:00:00 2001 From: Jakob Ivarsson Date: Mon, 15 Oct 2018 09:54:46 +0200 Subject: [PATCH] Add field trials for configuring Opus encoder packet loss rate. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add options to: 1. Bypass optimization (use reported packet loss). 2. Set a maximum value. 3. Set a coefficient. Bug: webrtc:9866 Change-Id: I3fef43e5186a4f0f50fda3506e445860518cfbd7 Reviewed-on: https://webrtc-review.googlesource.com/c/105304 Commit-Queue: Jakob Ivarsson‎ Reviewed-by: Minyue Li Cr-Commit-Position: refs/heads/master@{#25161} --- .../codecs/opus/audio_encoder_opus.cc | 68 +++++++++++++++++-- .../codecs/opus/audio_encoder_opus.h | 24 +++++++ .../opus/audio_encoder_opus_unittest.cc | 59 ++++++++++++++-- 3 files changed, 141 insertions(+), 10 deletions(-) diff --git a/modules/audio_coding/codecs/opus/audio_encoder_opus.cc b/modules/audio_coding/codecs/opus/audio_encoder_opus.cc index a35ada80c8..c07abbe03f 100644 --- a/modules/audio_coding/codecs/opus/audio_encoder_opus.cc +++ b/modules/audio_coding/codecs/opus/audio_encoder_opus.cc @@ -214,6 +214,14 @@ int GetBitrateBps(const AudioEncoderOpusConfig& config) { return *config.bitrate_bps; } +bool IsValidPacketLossRate(int value) { + return value >= 0 && value <= 100; +} + +float ToFraction(int percent) { + return static_cast(percent) / 100; +} + float GetMinPacketLossRate() { constexpr char kPacketLossFieldTrial[] = "WebRTC-Audio-OpusMinPacketLossRate"; const bool use_opus_min_packet_loss_rate = @@ -224,19 +232,62 @@ float GetMinPacketLossRate() { constexpr int kDefaultMinPacketLossRate = 1; int value = kDefaultMinPacketLossRate; if (sscanf(field_trial_string.c_str(), "Enabled-%d", &value) == 1 && - (value < 0 || value > 100)) { + !IsValidPacketLossRate(value)) { RTC_LOG(LS_WARNING) << "Invalid parameter for " << kPacketLossFieldTrial << ", using default value: " << kDefaultMinPacketLossRate; value = kDefaultMinPacketLossRate; } - return static_cast(value) / 100; + return ToFraction(value); } return 0.0; } +std::unique_ptr +GetNewPacketLossRateOptimizer() { + constexpr char kPacketLossOptimizationName[] = + "WebRTC-Audio-NewOpusPacketLossRateOptimization"; + const bool use_new_packet_loss_optimization = + webrtc::field_trial::IsEnabled(kPacketLossOptimizationName); + if (use_new_packet_loss_optimization) { + const std::string field_trial_string = + webrtc::field_trial::FindFullName(kPacketLossOptimizationName); + int min_rate; + int max_rate; + float slope; + if (sscanf(field_trial_string.c_str(), "Enabled-%d-%d-%f", &min_rate, + &max_rate, &slope) == 3 && + IsValidPacketLossRate(min_rate) && IsValidPacketLossRate(max_rate)) { + return absl::make_unique< + AudioEncoderOpusImpl::NewPacketLossRateOptimizer>( + ToFraction(min_rate), ToFraction(max_rate), slope); + } + RTC_LOG(LS_WARNING) << "Invalid parameters for " + << kPacketLossOptimizationName + << ", using default values."; + return absl::make_unique< + AudioEncoderOpusImpl::NewPacketLossRateOptimizer>(); + } + return nullptr; +} + } // namespace +AudioEncoderOpusImpl::NewPacketLossRateOptimizer::NewPacketLossRateOptimizer( + float min_packet_loss_rate, + float max_packet_loss_rate, + float slope) + : min_packet_loss_rate_(min_packet_loss_rate), + max_packet_loss_rate_(max_packet_loss_rate), + slope_(slope) {} + +float AudioEncoderOpusImpl::NewPacketLossRateOptimizer::OptimizePacketLossRate( + float packet_loss_rate) const { + packet_loss_rate = slope_ * packet_loss_rate; + return std::min(std::max(packet_loss_rate, min_packet_loss_rate_), + max_packet_loss_rate_); +} + void AudioEncoderOpusImpl::AppendSupportedEncoders( std::vector* specs) { const SdpAudioFormat fmt = { @@ -424,6 +475,7 @@ AudioEncoderOpusImpl::AudioEncoderOpusImpl( bitrate_changed_(true), packet_loss_rate_(0.0), min_packet_loss_rate_(GetMinPacketLossRate()), + new_packet_loss_optimizer_(GetNewPacketLossRateOptimizer()), inst_(nullptr), packet_loss_fraction_smoother_(new PacketLossFractionSmoother()), audio_network_adaptor_creator_(audio_network_adaptor_creator), @@ -761,10 +813,14 @@ void AudioEncoderOpusImpl::SetNumChannelsToEncode( } void AudioEncoderOpusImpl::SetProjectedPacketLossRate(float fraction) { - float opt_loss_rate = OptimizePacketLossRate(fraction, packet_loss_rate_); - opt_loss_rate = std::max(opt_loss_rate, min_packet_loss_rate_); - if (packet_loss_rate_ != opt_loss_rate) { - packet_loss_rate_ = opt_loss_rate; + if (new_packet_loss_optimizer_) { + fraction = new_packet_loss_optimizer_->OptimizePacketLossRate(fraction); + } else { + fraction = OptimizePacketLossRate(fraction, packet_loss_rate_); + fraction = std::max(fraction, min_packet_loss_rate_); + } + if (packet_loss_rate_ != fraction) { + packet_loss_rate_ = fraction; RTC_CHECK_EQ( 0, WebRtcOpus_SetPacketLossRate( inst_, static_cast(packet_loss_rate_ * 100 + .5))); diff --git a/modules/audio_coding/codecs/opus/audio_encoder_opus.h b/modules/audio_coding/codecs/opus/audio_encoder_opus.h index d34e22069d..c26c6daaf1 100644 --- a/modules/audio_coding/codecs/opus/audio_encoder_opus.h +++ b/modules/audio_coding/codecs/opus/audio_encoder_opus.h @@ -34,6 +34,26 @@ struct CodecInst; class AudioEncoderOpusImpl final : public AudioEncoder { public: + class NewPacketLossRateOptimizer { + public: + NewPacketLossRateOptimizer(float min_packet_loss_rate = 0.01, + float max_packet_loss_rate = 0.2, + float slope = 1.0); + + float OptimizePacketLossRate(float packet_loss_rate) const; + + // Getters for testing. + float min_packet_loss_rate() const { return min_packet_loss_rate_; }; + float max_packet_loss_rate() const { return max_packet_loss_rate_; }; + float slope() const { return slope_; }; + + private: + const float min_packet_loss_rate_; + const float max_packet_loss_rate_; + const float slope_; + RTC_DISALLOW_COPY_AND_ASSIGN(NewPacketLossRateOptimizer); + }; + static AudioEncoderOpusConfig CreateConfig(const CodecInst& codec_inst); // Returns empty if the current bitrate falls within the hysteresis window, @@ -110,6 +130,9 @@ class AudioEncoderOpusImpl final : public AudioEncoder { // Getters for testing. float packet_loss_rate() const { return packet_loss_rate_; } + NewPacketLossRateOptimizer* new_packet_loss_optimizer() const { + return new_packet_loss_optimizer_.get(); + } AudioEncoderOpusConfig::ApplicationMode application() const { return config_.application; } @@ -159,6 +182,7 @@ class AudioEncoderOpusImpl final : public AudioEncoder { bool bitrate_changed_; float packet_loss_rate_; const float min_packet_loss_rate_; + const std::unique_ptr new_packet_loss_optimizer_; std::vector input_buffer_; OpusEncInst* inst_; uint32_t first_timestamp_in_buffer_; diff --git a/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc b/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc index 0420c63739..d5c2c84f1f 100644 --- a/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc +++ b/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc @@ -273,7 +273,7 @@ TEST(AudioEncoderOpusTest, PacketLossRateOptimized) { } TEST(AudioEncoderOpusTest, PacketLossRateLowerBounded) { - test::ScopedFieldTrials override_field_trails( + test::ScopedFieldTrials override_field_trials( "WebRTC-Audio-OpusMinPacketLossRate/Enabled-5/"); auto states = CreateCodec(1); auto I = [](float a, float b) { return IntervalSteps(a, b, 10); }; @@ -294,6 +294,29 @@ TEST(AudioEncoderOpusTest, PacketLossRateLowerBounded) { // clang-format on } +TEST(AudioEncoderOpusTest, NewPacketLossRateOptimization) { + { + test::ScopedFieldTrials override_field_trials( + "WebRTC-Audio-NewOpusPacketLossRateOptimization/Enabled-5-15-0.5/"); + auto states = CreateCodec(1); + + TestSetPacketLossRate(states.get(), {0.00f}, 0.05f); + TestSetPacketLossRate(states.get(), {0.12f}, 0.06f); + TestSetPacketLossRate(states.get(), {0.22f}, 0.11f); + TestSetPacketLossRate(states.get(), {0.50f}, 0.15f); + } + { + test::ScopedFieldTrials override_field_trials( + "WebRTC-Audio-NewOpusPacketLossRateOptimization/Enabled/"); + auto states = CreateCodec(1); + + TestSetPacketLossRate(states.get(), {0.00f}, 0.01f); + TestSetPacketLossRate(states.get(), {0.12f}, 0.12f); + TestSetPacketLossRate(states.get(), {0.22f}, 0.20f); + TestSetPacketLossRate(states.get(), {0.50f}, 0.20f); + } +} + TEST(AudioEncoderOpusTest, SetReceiverFrameLengthRange) { auto states = CreateCodec(2); // Before calling to |SetReceiverFrameLengthRange|, @@ -471,19 +494,19 @@ TEST(AudioEncoderOpusTest, BitrateBounded) { TEST(AudioEncoderOpusTest, MinPacketLossRate) { constexpr float kDefaultMinPacketLossRate = 0.01; { - test::ScopedFieldTrials override_field_trails( + test::ScopedFieldTrials override_field_trials( "WebRTC-Audio-OpusMinPacketLossRate/Enabled/"); auto states = CreateCodec(1); EXPECT_EQ(kDefaultMinPacketLossRate, states->encoder->packet_loss_rate()); } { - test::ScopedFieldTrials override_field_trails( + test::ScopedFieldTrials override_field_trials( "WebRTC-Audio-OpusMinPacketLossRate/Enabled-200/"); auto states = CreateCodec(1); EXPECT_EQ(kDefaultMinPacketLossRate, states->encoder->packet_loss_rate()); } { - test::ScopedFieldTrials override_field_trails( + test::ScopedFieldTrials override_field_trials( "WebRTC-Audio-OpusMinPacketLossRate/Enabled-50/"); constexpr float kMinPacketLossRate = 0.5; auto states = CreateCodec(1); @@ -491,6 +514,34 @@ TEST(AudioEncoderOpusTest, MinPacketLossRate) { } } +TEST(AudioEncoderOpusTest, NewPacketLossRateOptimizer) { + { + auto states = CreateCodec(1); + auto optimizer = states->encoder->new_packet_loss_optimizer(); + EXPECT_EQ(nullptr, optimizer); + } + { + test::ScopedFieldTrials override_field_trials( + "WebRTC-Audio-NewOpusPacketLossRateOptimization/Enabled/"); + auto states = CreateCodec(1); + auto optimizer = states->encoder->new_packet_loss_optimizer(); + ASSERT_NE(nullptr, optimizer); + EXPECT_FLOAT_EQ(0.01, optimizer->min_packet_loss_rate()); + EXPECT_FLOAT_EQ(0.20, optimizer->max_packet_loss_rate()); + EXPECT_FLOAT_EQ(1.00, optimizer->slope()); + } + { + test::ScopedFieldTrials override_field_trials( + "WebRTC-Audio-NewOpusPacketLossRateOptimization/Enabled-2-50-0.7/"); + auto states = CreateCodec(1); + auto optimizer = states->encoder->new_packet_loss_optimizer(); + ASSERT_NE(nullptr, optimizer); + EXPECT_FLOAT_EQ(0.02, optimizer->min_packet_loss_rate()); + EXPECT_FLOAT_EQ(0.50, optimizer->max_packet_loss_rate()); + EXPECT_FLOAT_EQ(0.70, optimizer->slope()); + } +} + // Verifies that the complexity adaptation in the config works as intended. TEST(AudioEncoderOpusTest, ConfigComplexityAdaptation) { AudioEncoderOpusConfig config;