From 1d956dd1a797169b68929d8f4bb063c335dfcf81 Mon Sep 17 00:00:00 2001 From: "minyue@webrtc.org" Date: Thu, 7 Aug 2014 12:31:36 +0000 Subject: [PATCH] Since the packet loss rate cannot be estimated accurately, there is always a mismatch between the estimated packet loss rate and the true one. Such a mismatch will make Opus FEC suboptimal. It is advisable to set the packet loss rate of FEC conservatively. Say, if the estimated loss rate is 5%, we can set it to 1%. The risk of degradation in quality is small and the overall performance is good. BUG= R=henrik.lundin@webrtc.org, turaj@webrtc.org Review URL: https://webrtc-codereview.appspot.com/16979004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6844 4adac7df-926f-26a2-2b94-8c16560cd09d --- .../audio_coding/main/acm2/acm_opus.cc | 46 ++++++++-- .../main/acm2/acm_opus_unittest.cc | 86 +++++++++++++++++++ webrtc/modules/modules.gyp | 1 + 3 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 webrtc/modules/audio_coding/main/acm2/acm_opus_unittest.cc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_opus.cc b/webrtc/modules/audio_coding/main/acm2/acm_opus.cc index 638c72a91..f75a34881 100644 --- a/webrtc/modules/audio_coding/main/acm2/acm_opus.cc +++ b/webrtc/modules/audio_coding/main/acm2/acm_opus.cc @@ -15,6 +15,8 @@ #include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h" #include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h" #include "webrtc/system_wrappers/interface/trace.h" + +#define SIGN(x) (x > 0 ? 1 : -1) #endif namespace webrtc { @@ -27,7 +29,9 @@ ACMOpus::ACMOpus(int16_t /* codec_id */) : encoder_inst_ptr_(NULL), sample_freq_(0), bitrate_(0), - channels_(1) { + channels_(1), + fec_enabled_(false), + packet_loss_rate_(0) { return; } @@ -70,7 +74,9 @@ ACMOpus::ACMOpus(int16_t codec_id) : encoder_inst_ptr_(NULL), sample_freq_(32000), // Default sampling frequency. bitrate_(20000), // Default bit-rate. - channels_(1) { // Default mono + channels_(1), // Default mono. + fec_enabled_(false), // Default FEC is off. + packet_loss_rate_(0) { codec_id_ = codec_id; // Opus has internal DTX, but we dont use it for now. has_internal_dtx_ = false; @@ -188,6 +194,7 @@ int16_t ACMOpus::SetBitRateSafe(const int32_t rate) { "SetBitRateSafe: Invalid rate Opus"); return -1; } + // Initial packet loss rate. bitrate_ = rate; @@ -217,11 +224,40 @@ int ACMOpus::SetFEC(bool enable_fec) { } int ACMOpus::SetPacketLossRate(int loss_rate) { - // Ask the encoder to change the target packet loss rate. - if (WebRtcOpus_SetPacketLossRate(encoder_inst_ptr_, loss_rate) == 0) { - packet_loss_rate_ = loss_rate; + // Optimize the loss rate to configure Opus. Basically, optimized loss rate is + // the input loss rate rounded down to various levels, because a robustly good + // audio quality is achieved by lowering the packet loss lower down. + // Additionally, to prevent toggling, margins are used, i.e., when jumping to + // a loss rate from below, a higher threshold is used than jumping to the same + // level from above. + const int kPacketLossRateHigh = 20; + const int kPacketLossRateMedium = 10; + const int kPacketLossRateLow = 1; + const int kLossRateHighMargin = 2; + const int kLossRateMediumMargin = 1; + int opt_loss_rate; + if (loss_rate >= kPacketLossRateHigh + kLossRateHighMargin * + SIGN(kPacketLossRateHigh - packet_loss_rate_)) { + opt_loss_rate = kPacketLossRateHigh; + } else if (loss_rate >= kPacketLossRateMedium + kLossRateMediumMargin * + SIGN(kPacketLossRateMedium - packet_loss_rate_)) { + opt_loss_rate = kPacketLossRateMedium; + } else if (loss_rate >= kPacketLossRateLow) { + opt_loss_rate = kPacketLossRateLow; + } else { + opt_loss_rate = 0; + } + + if (packet_loss_rate_ == opt_loss_rate) { return 0; } + + // Ask the encoder to change the target packet loss rate. + if (WebRtcOpus_SetPacketLossRate(encoder_inst_ptr_, opt_loss_rate) == 0) { + packet_loss_rate_ = opt_loss_rate; + return 0; + } + return -1; } diff --git a/webrtc/modules/audio_coding/main/acm2/acm_opus_unittest.cc b/webrtc/modules/audio_coding/main/acm2/acm_opus_unittest.cc new file mode 100644 index 000000000..ecc96674e --- /dev/null +++ b/webrtc/modules/audio_coding/main/acm2/acm_opus_unittest.cc @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014 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 "webrtc/modules/audio_coding/main/acm2/acm_opus.h" + +#include "gtest/gtest.h" +#include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h" + +namespace webrtc { + +namespace acm2 { + +namespace { + const CodecInst kOpusCodecInst = {105, "opus", 48000, 960, 1, 32000}; + // These constants correspond to those used in ACMOpus::SetPacketLossRate(). + const int kPacketLossRateHigh = 20; + const int kPacketLossRateMedium = 10; + const int kPacketLossRateLow = 1; + const int kLossRateHighMargin = 2; + const int kLossRateMediumMargin = 1; +} // namespace + +class AcmOpusTest : public ACMOpus { + public: + explicit AcmOpusTest(int16_t codec_id) + : ACMOpus(codec_id) {} + ~AcmOpusTest() {} + int packet_loss_rate() { return packet_loss_rate_; } + + void TestSetPacketLossRate(int from, int to, int expected_return); +}; + +#ifdef WEBRTC_CODEC_OPUS +void AcmOpusTest::TestSetPacketLossRate(int from, int to, int expected_return) { + for (int loss = from; loss <= to; (to >= from) ? ++loss : --loss) { + EXPECT_EQ(0, SetPacketLossRate(loss)); + EXPECT_EQ(expected_return, packet_loss_rate()); + } +} + +TEST(AcmOpusTest, PacketLossRateOptimized) { + AcmOpusTest opus(ACMCodecDB::kOpus); + WebRtcACMCodecParams params; + memcpy(&(params.codec_inst), &kOpusCodecInst, sizeof(CodecInst)); + EXPECT_EQ(0, opus.InitEncoder(¶ms, true)); + EXPECT_EQ(0, opus.SetFEC(true)); + + // Note that the order of the following calls is critical. + opus.TestSetPacketLossRate(0, 0, 0); + opus.TestSetPacketLossRate(kPacketLossRateLow, + kPacketLossRateMedium + kLossRateMediumMargin - 1, + kPacketLossRateLow); + opus.TestSetPacketLossRate(kPacketLossRateMedium + kLossRateMediumMargin, + kPacketLossRateHigh + kLossRateHighMargin - 1, + kPacketLossRateMedium); + opus.TestSetPacketLossRate(kPacketLossRateHigh + kLossRateHighMargin, + 100, + kPacketLossRateHigh); + opus.TestSetPacketLossRate(kPacketLossRateHigh + kLossRateHighMargin, + kPacketLossRateHigh - kLossRateHighMargin, + kPacketLossRateHigh); + opus.TestSetPacketLossRate(kPacketLossRateHigh - kLossRateHighMargin - 1, + kPacketLossRateMedium - kLossRateMediumMargin, + kPacketLossRateMedium); + opus.TestSetPacketLossRate(kPacketLossRateMedium - kLossRateMediumMargin - 1, + kPacketLossRateLow, + kPacketLossRateLow); + opus.TestSetPacketLossRate(0, 0, 0); +} +#else +void AcmOpusTest:TestSetPacketLossRate(int /* from */, int /* to */, + int /* expected_return */) { + return; +} +#endif // WEBRTC_CODEC_OPUS + +} // namespace acm2 + +} // namespace webrtc diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index b8aa55b18..e6919f1aa 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -101,6 +101,7 @@ '<(webrtc_root)/test/test.gyp:rtcp_packet_parser', ], 'sources': [ + 'audio_coding/main/acm2/acm_opus_unittest.cc', 'audio_coding/main/acm2/acm_receiver_unittest.cc', 'audio_coding/main/acm2/audio_coding_module_unittest.cc', 'audio_coding/main/acm2/call_statistics_unittest.cc',