From 74aaf29a0ff1b211dbfdbb6309791111a7871779 Mon Sep 17 00:00:00 2001 From: "minyue@webrtc.org" Date: Wed, 16 Jul 2014 21:28:26 +0000 Subject: [PATCH] Raw packet loss rate reported by RTP_RTCP module may vary too drastically over time. This CL is to add a filter to the value in VoE before lending it to audio coding module. The filter is an exponential filter borrowed from video coding module. The method is written in a new class called PacketLossProtector (not sure if the name is nice), which can be used in the future for more sophisticated logic. BUG= R=henrika@webrtc.org, stefan@webrtc.org Review URL: https://webrtc-codereview.appspot.com/20809004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6709 4adac7df-926f-26a2-2b94-8c16560cd09d --- webrtc/base/base.gyp | 2 + webrtc/base/base_tests.gyp | 1 + webrtc/base/exp_filter.cc | 43 +++++++++++ webrtc/base/exp_filter.h | 49 +++++++++++++ webrtc/base/exp_filter_unittest.cc | 71 +++++++++++++++++++ .../main/source/media_opt_util.cc | 6 +- .../video_coding/main/source/media_opt_util.h | 44 ++++++------ .../video_coding/utility/exp_filter.cc | 60 ---------------- .../video_coding/utility/frame_dropper.cc | 32 +++++---- .../video_coding/utility/include/exp_filter.h | 58 --------------- .../utility/include/frame_dropper.h | 36 +++++----- .../utility/video_coding_utility.gyp | 2 - webrtc/video_engine/overuse_frame_detector.cc | 44 ++++++------ webrtc/video_engine/overuse_frame_detector.h | 6 +- webrtc/voice_engine/channel.cc | 10 ++- webrtc/voice_engine/channel.h | 2 + webrtc/voice_engine/network_predictor.cc | 37 ++++++++++ webrtc/voice_engine/network_predictor.h | 46 ++++++++++++ .../network_predictor_unittest.cc | 43 +++++++++++ webrtc/voice_engine/voice_engine.gyp | 3 + 20 files changed, 391 insertions(+), 204 deletions(-) create mode 100644 webrtc/base/exp_filter.cc create mode 100644 webrtc/base/exp_filter.h create mode 100644 webrtc/base/exp_filter_unittest.cc delete mode 100644 webrtc/modules/video_coding/utility/exp_filter.cc delete mode 100644 webrtc/modules/video_coding/utility/include/exp_filter.h create mode 100644 webrtc/voice_engine/network_predictor.cc create mode 100644 webrtc/voice_engine/network_predictor.h create mode 100644 webrtc/voice_engine/network_predictor_unittest.cc diff --git a/webrtc/base/base.gyp b/webrtc/base/base.gyp index f9ba4041d..6a0c359ff 100644 --- a/webrtc/base/base.gyp +++ b/webrtc/base/base.gyp @@ -85,6 +85,8 @@ 'diskcache_win32.h', 'event.cc', 'event.h', + 'exp_filter.cc', + 'exp_filter.h', 'filelock.cc', 'filelock.h', 'fileutils.cc', diff --git a/webrtc/base/base_tests.gyp b/webrtc/base/base_tests.gyp index ca0d72a04..3cef10285 100644 --- a/webrtc/base/base_tests.gyp +++ b/webrtc/base/base_tests.gyp @@ -57,6 +57,7 @@ 'crc32_unittest.cc', 'criticalsection_unittest.cc', 'event_unittest.cc', + 'exp_filter_unittest.cc', 'filelock_unittest.cc', 'fileutils_unittest.cc', 'helpers_unittest.cc', diff --git a/webrtc/base/exp_filter.cc b/webrtc/base/exp_filter.cc new file mode 100644 index 000000000..952948006 --- /dev/null +++ b/webrtc/base/exp_filter.cc @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011 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/base/exp_filter.h" + +#include + +namespace rtc { + +const float ExpFilter::kValueUndefined = -1.0f; + +void ExpFilter::Reset(float alpha) { + alpha_ = alpha; + filtered_ = kValueUndefined; +} + +float ExpFilter::Apply(float exp, float sample) { + if (filtered_ == kValueUndefined) { + // Initialize filtered value. + filtered_ = sample; + } else if (exp == 1.0) { + filtered_ = alpha_ * filtered_ + (1 - alpha_) * sample; + } else { + float alpha = pow(alpha_, exp); + filtered_ = alpha * filtered_ + (1 - alpha) * sample; + } + if (max_ != kValueUndefined && filtered_ > max_) { + filtered_ = max_; + } + return filtered_; +} + +void ExpFilter::UpdateBase(float alpha) { + alpha_ = alpha; +} +} // namespace rtc diff --git a/webrtc/base/exp_filter.h b/webrtc/base/exp_filter.h new file mode 100644 index 000000000..174159b45 --- /dev/null +++ b/webrtc/base/exp_filter.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 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 WEBRTC_BASE_EXP_FILTER_H_ +#define WEBRTC_BASE_EXP_FILTER_H_ + +namespace rtc { + +// This class can be used, for example, for smoothing the result of bandwidth +// estimation and packet loss estimation. + +class ExpFilter { + public: + static const float kValueUndefined; + + explicit ExpFilter(float alpha, float max = kValueUndefined) + : max_(max) { + Reset(alpha); + } + + // Resets the filter to its initial state, and resets filter factor base to + // the given value |alpha|. + void Reset(float alpha); + + // Applies the filter with a given exponent on the provided sample: + // y(k) = min(alpha_^ exp * y(k-1) + (1 - alpha_^ exp) * sample, max_). + float Apply(float exp, float sample); + + // Returns current filtered value. + float filtered() const { return filtered_; } + + // Changes the filter factor base to the given value |alpha|. + void UpdateBase(float alpha); + + private: + float alpha_; // Filter factor base. + float filtered_; // Current filter output. + const float max_; +}; +} // namespace rtc + +#endif // WEBRTC_BASE_EXP_FILTER_H_ diff --git a/webrtc/base/exp_filter_unittest.cc b/webrtc/base/exp_filter_unittest.cc new file mode 100644 index 000000000..f02780811 --- /dev/null +++ b/webrtc/base/exp_filter_unittest.cc @@ -0,0 +1,71 @@ +/* + * Copyright 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 + +#include "webrtc/base/gunit.h" +#include "webrtc/base/exp_filter.h" + +namespace rtc { + +TEST(ExpFilterTest, FirstTimeOutputEqualInput) { + // No max value defined. + ExpFilter filter = ExpFilter(0.9f); + filter.Apply(100.0f, 10.0f); + + // First time, first argument no effect. + double value = 10.0f; + EXPECT_FLOAT_EQ(value, filter.filtered()); +} + +TEST(ExpFilterTest, SecondTime) { + double value; + + ExpFilter filter = ExpFilter(0.9f); + filter.Apply(100.0f, 10.0f); + + // First time, first argument no effect. + value = 10.0f; + + filter.Apply(10.0f, 20.0f); + double alpha = pow(0.9f, 10.0f); + value = alpha * value + (1.0f - alpha) * 20.0f; + EXPECT_FLOAT_EQ(value, filter.filtered()); +} + +TEST(ExpFilterTest, Reset) { + ExpFilter filter = ExpFilter(0.9f); + filter.Apply(100.0f, 10.0f); + + filter.Reset(0.8f); + filter.Apply(100.0f, 1.0f); + + // Become first time after a reset. + double value = 1.0f; + EXPECT_FLOAT_EQ(value, filter.filtered()); +} + +TEST(ExpfilterTest, OutputLimitedByMax) { + double value; + + // Max value defined. + ExpFilter filter = ExpFilter(0.9f, 1.0f); + filter.Apply(100.0f, 10.0f); + + // Limited to max value. + value = 1.0f; + EXPECT_EQ(value, filter.filtered()); + + filter.Apply(1.0f, 0.0f); + value = 0.9f * value; + EXPECT_FLOAT_EQ(value, filter.filtered()); +} + +} // namespace rtc diff --git a/webrtc/modules/video_coding/main/source/media_opt_util.cc b/webrtc/modules/video_coding/main/source/media_opt_util.cc index ba86575fa..b506a5b5c 100644 --- a/webrtc/modules/video_coding/main/source/media_opt_util.cc +++ b/webrtc/modules/video_coding/main/source/media_opt_util.cc @@ -837,7 +837,7 @@ uint8_t VCMLossProtectionLogic::FilteredLoss( case kNoFilter: break; case kAvgFilter: - filtered_loss = static_cast (_lossPr255.Value() + 0.5); + filtered_loss = static_cast(_lossPr255.filtered() + 0.5); break; case kMaxFilter: filtered_loss = MaxFilteredLossPr(nowMs); @@ -907,8 +907,8 @@ VCMLossProtectionLogic::UpdateMethod() _currentParameters.keyFrameSize = _keyFrameSize; _currentParameters.fecRateDelta = _fecRateDelta; _currentParameters.fecRateKey = _fecRateKey; - _currentParameters.packetsPerFrame = _packetsPerFrame.Value(); - _currentParameters.packetsPerFrameKey = _packetsPerFrameKey.Value(); + _currentParameters.packetsPerFrame = _packetsPerFrame.filtered(); + _currentParameters.packetsPerFrameKey = _packetsPerFrameKey.filtered(); _currentParameters.residualPacketLossFec = _residualPacketLossFec; _currentParameters.codecWidth = _codecWidth; _currentParameters.codecHeight = _codecHeight; diff --git a/webrtc/modules/video_coding/main/source/media_opt_util.h b/webrtc/modules/video_coding/main/source/media_opt_util.h index f39a5780e..d421d9e7c 100644 --- a/webrtc/modules/video_coding/main/source/media_opt_util.h +++ b/webrtc/modules/video_coding/main/source/media_opt_util.h @@ -14,9 +14,9 @@ #include #include +#include "webrtc/base/exp_filter.h" #include "webrtc/modules/video_coding/main/source/internal_defines.h" #include "webrtc/modules/video_coding/main/source/qm_select.h" -#include "webrtc/modules/video_coding/utility/include/exp_filter.h" #include "webrtc/system_wrappers/interface/trace.h" #include "webrtc/typedefs.h" @@ -367,27 +367,27 @@ private: // Sets the available loss protection methods. void UpdateMaxLossHistory(uint8_t lossPr255, int64_t now); uint8_t MaxFilteredLossPr(int64_t nowMs) const; - VCMProtectionMethod* _selectedMethod; - VCMProtectionParameters _currentParameters; - uint32_t _rtt; - float _lossPr; - float _bitRate; - float _frameRate; - float _keyFrameSize; - uint8_t _fecRateKey; - uint8_t _fecRateDelta; - int64_t _lastPrUpdateT; - int64_t _lastPacketPerFrameUpdateT; - int64_t _lastPacketPerFrameUpdateTKey; - VCMExpFilter _lossPr255; - VCMLossProbabilitySample _lossPrHistory[kLossPrHistorySize]; - uint8_t _shortMaxLossPr255; - VCMExpFilter _packetsPerFrame; - VCMExpFilter _packetsPerFrameKey; - float _residualPacketLossFec; - uint16_t _codecWidth; - uint16_t _codecHeight; - int _numLayers; + VCMProtectionMethod* _selectedMethod; + VCMProtectionParameters _currentParameters; + uint32_t _rtt; + float _lossPr; + float _bitRate; + float _frameRate; + float _keyFrameSize; + uint8_t _fecRateKey; + uint8_t _fecRateDelta; + int64_t _lastPrUpdateT; + int64_t _lastPacketPerFrameUpdateT; + int64_t _lastPacketPerFrameUpdateTKey; + rtc::ExpFilter _lossPr255; + VCMLossProbabilitySample _lossPrHistory[kLossPrHistorySize]; + uint8_t _shortMaxLossPr255; + rtc::ExpFilter _packetsPerFrame; + rtc::ExpFilter _packetsPerFrameKey; + float _residualPacketLossFec; + uint16_t _codecWidth; + uint16_t _codecHeight; + int _numLayers; }; } // namespace media_optimization diff --git a/webrtc/modules/video_coding/utility/exp_filter.cc b/webrtc/modules/video_coding/utility/exp_filter.cc deleted file mode 100644 index 44f280bc2..000000000 --- a/webrtc/modules/video_coding/utility/exp_filter.cc +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2011 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/video_coding/utility/include/exp_filter.h" - -#include - -namespace webrtc { - -void -VCMExpFilter::Reset(float alpha) -{ - _alpha = alpha; - _filtered = -1.0; -} - -float -VCMExpFilter::Apply(float exp, float sample) -{ - if (_filtered == -1.0) - { - // Initialize filtered bit rates - _filtered = sample; - } - else if (exp == 1.0) - { - _filtered = _alpha * _filtered + (1 - _alpha) * sample; - } - else - { - float alpha = pow(_alpha, exp); - _filtered = alpha * _filtered + (1 - alpha) * sample; - } - if (_max != -1 && _filtered > _max) - { - _filtered = _max; - } - return _filtered; -} - -void -VCMExpFilter::UpdateBase(float alpha) -{ - _alpha = alpha; -} - -float -VCMExpFilter::Value() const -{ - return _filtered; -} - -} diff --git a/webrtc/modules/video_coding/utility/frame_dropper.cc b/webrtc/modules/video_coding/utility/frame_dropper.cc index d3c25fb94..54c8cb8a3 100644 --- a/webrtc/modules/video_coding/utility/frame_dropper.cc +++ b/webrtc/modules/video_coding/utility/frame_dropper.cc @@ -86,25 +86,27 @@ FrameDropper::Fill(uint32_t frameSizeBytes, bool deltaFrame) { _keyFrameSizeAvgKbits.Apply(1, frameSizeKbits); _keyFrameRatio.Apply(1.0, 1.0); - if (frameSizeKbits > _keyFrameSizeAvgKbits.Value()) + if (frameSizeKbits > _keyFrameSizeAvgKbits.filtered()) { // Remove the average key frame size since we // compensate for key frames when adding delta // frames. - frameSizeKbits -= _keyFrameSizeAvgKbits.Value(); + frameSizeKbits -= _keyFrameSizeAvgKbits.filtered(); } else { // Shouldn't be negative, so zero is the lower bound. frameSizeKbits = 0; } - if (_keyFrameRatio.Value() > 1e-5 && 1 / _keyFrameRatio.Value() < _keyFrameSpreadFrames) + if (_keyFrameRatio.filtered() > 1e-5 && + 1 / _keyFrameRatio.filtered() < _keyFrameSpreadFrames) { // We are sending key frames more often than our upper bound for // how much we allow the key frame compensation to be spread // out in time. Therefor we must use the key frame ratio rather // than keyFrameSpreadFrames. - _keyFrameCount = static_cast(1 / _keyFrameRatio.Value() + 0.5); + _keyFrameCount = + static_cast(1 / _keyFrameRatio.filtered() + 0.5); } else { @@ -145,13 +147,14 @@ FrameDropper::Leak(uint32_t inputFrameRate) if (_keyFrameCount > 0) { // Perform the key frame compensation - if (_keyFrameRatio.Value() > 0 && 1 / _keyFrameRatio.Value() < _keyFrameSpreadFrames) + if (_keyFrameRatio.filtered() > 0 && + 1 / _keyFrameRatio.filtered() < _keyFrameSpreadFrames) { - T -= _keyFrameSizeAvgKbits.Value() * _keyFrameRatio.Value(); + T -= _keyFrameSizeAvgKbits.filtered() * _keyFrameRatio.filtered(); } else { - T -= _keyFrameSizeAvgKbits.Value() / _keyFrameSpreadFrames; + T -= _keyFrameSizeAvgKbits.filtered() / _keyFrameSpreadFrames; } _keyFrameCount--; } @@ -232,11 +235,11 @@ FrameDropper::DropFrame() _dropCount = 0; } - if (_dropRatio.Value() >= 0.5f) // Drops per keep + if (_dropRatio.filtered() >= 0.5f) // Drops per keep { // limit is the number of frames we should drop between each kept frame // to keep our drop ratio. limit is positive in this case. - float denom = 1.0f - _dropRatio.Value(); + float denom = 1.0f - _dropRatio.filtered(); if (denom < 1e-5) { denom = (float)1e-5; @@ -252,7 +255,7 @@ FrameDropper::DropFrame() if (_dropCount < 0) { // Reset the _dropCount since it was negative and should be positive. - if (_dropRatio.Value() > 0.4f) + if (_dropRatio.filtered() > 0.4f) { _dropCount = -_dropCount; } @@ -274,12 +277,13 @@ FrameDropper::DropFrame() return false; } } - else if (_dropRatio.Value() > 0.0f && _dropRatio.Value() < 0.5f) // Keeps per drop + else if (_dropRatio.filtered() > 0.0f && + _dropRatio.filtered() < 0.5f) // Keeps per drop { // limit is the number of frames we should keep between each drop // in order to keep the drop ratio. limit is negative in this case, // and the _dropCount is also negative. - float denom = _dropRatio.Value(); + float denom = _dropRatio.filtered(); if (denom < 1e-5) { denom = (float)1e-5; @@ -289,7 +293,7 @@ FrameDropper::DropFrame() { // Reset the _dropCount since we have a positive // _dropCount, and it should be negative. - if (_dropRatio.Value() < 0.6f) + if (_dropRatio.filtered() < 0.6f) { _dropCount = -_dropCount; } @@ -350,7 +354,7 @@ FrameDropper::ActualFrameRate(uint32_t inputFrameRate) const { return static_cast(inputFrameRate); } - return inputFrameRate * (1.0f - _dropRatio.Value()); + return inputFrameRate * (1.0f - _dropRatio.filtered()); } // Put a cap on the accumulator, i.e., don't let it grow beyond some level. diff --git a/webrtc/modules/video_coding/utility/include/exp_filter.h b/webrtc/modules/video_coding/utility/include/exp_filter.h deleted file mode 100644 index d8c37a30b..000000000 --- a/webrtc/modules/video_coding/utility/include/exp_filter.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2011 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 WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_EXP_FILTER_H_ -#define WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_EXP_FILTER_H_ - -namespace webrtc -{ - -/**********************/ -/* ExpFilter class */ -/**********************/ - -class VCMExpFilter -{ -public: - VCMExpFilter(float alpha, float max = -1.0) : _alpha(alpha), _filtered(-1.0), _max(max) {} - - // Resets the filter to its initial state, and resets alpha to the given value - // - // Input: - // - alpha : the new value of the filter factor base. - void Reset(float alpha); - - // Applies the filter with the given exponent on the provided sample - // - // Input: - // - exp : Exponent T in y(k) = alpha^T * y(k-1) + (1 - alpha^T) * x(k) - // - sample : x(k) in the above filter equation - float Apply(float exp, float sample); - - // Return current filtered value: y(k) - // - // Return value : The current filter output - float Value() const; - - // Change the filter factor base - // - // Input: - // - alpha : The new filter factor base. - void UpdateBase(float alpha); - -private: - float _alpha; // Filter factor base - float _filtered; // Current filter output - const float _max; -}; // end of ExpFilter class - -} // namespace webrtc - -#endif // WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_EXP_FILTER_H_ diff --git a/webrtc/modules/video_coding/utility/include/frame_dropper.h b/webrtc/modules/video_coding/utility/include/frame_dropper.h index 4c1c168c2..8eebd7842 100644 --- a/webrtc/modules/video_coding/utility/include/frame_dropper.h +++ b/webrtc/modules/video_coding/utility/include/frame_dropper.h @@ -11,7 +11,7 @@ #ifndef WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_FRAME_DROPPER_H_ #define WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_FRAME_DROPPER_H_ -#include "webrtc/modules/video_coding/utility/include/exp_filter.h" +#include "webrtc/base/exp_filter.h" #include "webrtc/typedefs.h" namespace webrtc @@ -72,23 +72,23 @@ private: void UpdateRatio(); void CapAccumulator(); - VCMExpFilter _keyFrameSizeAvgKbits; - VCMExpFilter _keyFrameRatio; - float _keyFrameSpreadFrames; - int32_t _keyFrameCount; - float _accumulator; - float _accumulatorMax; - float _targetBitRate; - bool _dropNext; - VCMExpFilter _dropRatio; - int32_t _dropCount; - float _windowSize; - float _incoming_frame_rate; - bool _wasBelowMax; - bool _enabled; - bool _fastMode; - float _cap_buffer_size; - float _max_time_drops; + rtc::ExpFilter _keyFrameSizeAvgKbits; + rtc::ExpFilter _keyFrameRatio; + float _keyFrameSpreadFrames; + int32_t _keyFrameCount; + float _accumulator; + float _accumulatorMax; + float _targetBitRate; + bool _dropNext; + rtc::ExpFilter _dropRatio; + int32_t _dropCount; + float _windowSize; + float _incoming_frame_rate; + bool _wasBelowMax; + bool _enabled; + bool _fastMode; + float _cap_buffer_size; + float _max_time_drops; }; // end of VCMFrameDropper class } // namespace webrtc diff --git a/webrtc/modules/video_coding/utility/video_coding_utility.gyp b/webrtc/modules/video_coding/utility/video_coding_utility.gyp index 24f88800e..2f0202b18 100644 --- a/webrtc/modules/video_coding/utility/video_coding_utility.gyp +++ b/webrtc/modules/video_coding/utility/video_coding_utility.gyp @@ -18,9 +18,7 @@ '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', ], 'sources': [ - 'include/exp_filter.h', 'include/frame_dropper.h', - 'exp_filter.cc', 'frame_dropper.cc', ], }, diff --git a/webrtc/video_engine/overuse_frame_detector.cc b/webrtc/video_engine/overuse_frame_detector.cc index 764c2584c..6efb4be9c 100644 --- a/webrtc/video_engine/overuse_frame_detector.cc +++ b/webrtc/video_engine/overuse_frame_detector.cc @@ -17,7 +17,7 @@ #include #include -#include "webrtc/modules/video_coding/utility/include/exp_filter.h" +#include "webrtc/base/exp_filter.h" #include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" #include "webrtc/system_wrappers/interface/logging.h" @@ -54,8 +54,8 @@ const float kMaxExp = 7.0f; Statistics::Statistics() : sum_(0.0), count_(0), - filtered_samples_(new VCMExpFilter(kWeightFactorMean)), - filtered_variance_(new VCMExpFilter(kWeightFactor)) { + filtered_samples_(new rtc::ExpFilter(kWeightFactorMean)), + filtered_variance_(new rtc::ExpFilter(kWeightFactor)) { Reset(); } @@ -84,8 +84,8 @@ void Statistics::AddSample(float sample_ms) { float exp = sample_ms / kSampleDiffMs; exp = std::min(exp, kMaxExp); filtered_samples_->Apply(exp, sample_ms); - filtered_variance_->Apply(exp, (sample_ms - filtered_samples_->Value()) * - (sample_ms - filtered_samples_->Value())); + filtered_variance_->Apply(exp, (sample_ms - filtered_samples_->filtered()) * + (sample_ms - filtered_samples_->filtered())); } float Statistics::InitialMean() const { @@ -101,10 +101,10 @@ float Statistics::InitialVariance() const { return average_stddev * average_stddev; } -float Statistics::Mean() const { return filtered_samples_->Value(); } +float Statistics::Mean() const { return filtered_samples_->filtered(); } float Statistics::StdDev() const { - return sqrt(std::max(filtered_variance_->Value(), 0.0f)); + return sqrt(std::max(filtered_variance_->filtered(), 0.0f)); } uint64_t Statistics::Count() const { return count_; } @@ -116,7 +116,7 @@ class OveruseFrameDetector::EncodeTimeAvg { EncodeTimeAvg() : kWeightFactor(0.5f), kInitialAvgEncodeTimeMs(5.0f), - filtered_encode_time_ms_(new VCMExpFilter(kWeightFactor)) { + filtered_encode_time_ms_(new rtc::ExpFilter(kWeightFactor)) { filtered_encode_time_ms_->Apply(1.0f, kInitialAvgEncodeTimeMs); } ~EncodeTimeAvg() {} @@ -128,13 +128,13 @@ class OveruseFrameDetector::EncodeTimeAvg { } int Value() const { - return static_cast(filtered_encode_time_ms_->Value() + 0.5); + return static_cast(filtered_encode_time_ms_->filtered() + 0.5); } private: const float kWeightFactor; const float kInitialAvgEncodeTimeMs; - scoped_ptr filtered_encode_time_ms_; + scoped_ptr filtered_encode_time_ms_; }; // Class for calculating the encode usage. @@ -146,8 +146,8 @@ class OveruseFrameDetector::EncodeUsage { kInitialSampleDiffMs(40.0f), kMaxSampleDiffMs(45.0f), count_(0), - filtered_encode_time_ms_(new VCMExpFilter(kWeightFactorEncodeTime)), - filtered_frame_diff_ms_(new VCMExpFilter(kWeightFactorFrameDiff)) { + filtered_encode_time_ms_(new rtc::ExpFilter(kWeightFactorEncodeTime)), + filtered_frame_diff_ms_(new rtc::ExpFilter(kWeightFactorFrameDiff)) { Reset(); } ~EncodeUsage() {} @@ -181,10 +181,10 @@ class OveruseFrameDetector::EncodeUsage { if (count_ < static_cast(options_.min_frame_samples)) { return static_cast(InitialUsageInPercent() + 0.5f); } - float frame_diff_ms = std::max(filtered_frame_diff_ms_->Value(), 1.0f); + float frame_diff_ms = std::max(filtered_frame_diff_ms_->filtered(), 1.0f); frame_diff_ms = std::min(frame_diff_ms, kMaxSampleDiffMs); float encode_usage_percent = - 100.0f * filtered_encode_time_ms_->Value() / frame_diff_ms; + 100.0f * filtered_encode_time_ms_->filtered() / frame_diff_ms; return static_cast(encode_usage_percent + 0.5); } @@ -205,8 +205,8 @@ class OveruseFrameDetector::EncodeUsage { const float kMaxSampleDiffMs; uint64_t count_; CpuOveruseOptions options_; - scoped_ptr filtered_encode_time_ms_; - scoped_ptr filtered_frame_diff_ms_; + scoped_ptr filtered_encode_time_ms_; + scoped_ptr filtered_frame_diff_ms_; }; // Class for calculating the relative standard deviation of encode times. @@ -215,7 +215,7 @@ class OveruseFrameDetector::EncodeTimeRsd { EncodeTimeRsd(Clock* clock) : kWeightFactor(0.6f), count_(0), - filtered_rsd_(new VCMExpFilter(kWeightFactor)), + filtered_rsd_(new rtc::ExpFilter(kWeightFactor)), hist_samples_(0), hist_sum_(0.0f), last_process_time_ms_(clock->TimeInMilliseconds()) { @@ -294,7 +294,7 @@ class OveruseFrameDetector::EncodeTimeRsd { } int Value() const { - return static_cast(filtered_rsd_->Value() + 0.5); + return static_cast(filtered_rsd_->filtered() + 0.5); } private: @@ -307,7 +307,7 @@ class OveruseFrameDetector::EncodeTimeRsd { const float kWeightFactor; uint32_t count_; // Number of encode samples since last reset. CpuOveruseOptions options_; - scoped_ptr filtered_rsd_; + scoped_ptr filtered_rsd_; int hist_samples_; float hist_sum_; std::map hist_; // Histogram of encode time of frames. @@ -320,7 +320,7 @@ class OveruseFrameDetector::CaptureQueueDelay { CaptureQueueDelay() : kWeightFactor(0.5f), delay_ms_(0), - filtered_delay_ms_per_s_(new VCMExpFilter(kWeightFactor)) { + filtered_delay_ms_per_s_(new rtc::ExpFilter(kWeightFactor)) { filtered_delay_ms_per_s_->Apply(1.0f, 0.0f); } ~CaptureQueueDelay() {} @@ -361,14 +361,14 @@ class OveruseFrameDetector::CaptureQueueDelay { } int Value() const { - return static_cast(filtered_delay_ms_per_s_->Value() + 0.5); + return static_cast(filtered_delay_ms_per_s_->filtered() + 0.5); } private: const float kWeightFactor; std::list frames_; int delay_ms_; - scoped_ptr filtered_delay_ms_per_s_; + scoped_ptr filtered_delay_ms_per_s_; }; OveruseFrameDetector::OveruseFrameDetector(Clock* clock) diff --git a/webrtc/video_engine/overuse_frame_detector.h b/webrtc/video_engine/overuse_frame_detector.h index efd23dc4a..df3c1a04c 100644 --- a/webrtc/video_engine/overuse_frame_detector.h +++ b/webrtc/video_engine/overuse_frame_detector.h @@ -12,6 +12,7 @@ #define WEBRTC_VIDEO_ENGINE_OVERUSE_FRAME_DETECTOR_H_ #include "webrtc/base/constructormagic.h" +#include "webrtc/base/exp_filter.h" #include "webrtc/modules/interface/module.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/video_engine/include/vie_base.h" @@ -21,7 +22,6 @@ namespace webrtc { class Clock; class CpuOveruseObserver; class CriticalSectionWrapper; -class VCMExpFilter; // TODO(pbos): Move this somewhere appropriate. class Statistics { @@ -43,8 +43,8 @@ class Statistics { float sum_; uint64_t count_; CpuOveruseOptions options_; - scoped_ptr filtered_samples_; - scoped_ptr filtered_variance_; + scoped_ptr filtered_samples_; + scoped_ptr filtered_variance_; }; // Use to detect system overuse based on jitter in incoming frames. diff --git a/webrtc/voice_engine/channel.cc b/webrtc/voice_engine/channel.cc index 2d4ba5714..af773e770 100644 --- a/webrtc/voice_engine/channel.cc +++ b/webrtc/voice_engine/channel.cc @@ -934,7 +934,8 @@ Channel::Channel(int32_t channelId, true)), rtcp_bandwidth_observer_( bitrate_controller_->CreateRtcpBandwidthObserver()), - send_bitrate_observer_(new VoEBitrateObserver(this)) + send_bitrate_observer_(new VoEBitrateObserver(this)), + network_predictor_(new NetworkPredictor(Clock::GetRealTimeClock())) { WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::Channel() - ctor"); @@ -1537,8 +1538,13 @@ Channel::OnNetworkChanged(const uint32_t bitrate_bps, WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId), "Channel::OnNetworkChanged(bitrate_bps=%d, fration_lost=%d, rtt=%d)", bitrate_bps, fraction_lost, rtt); + // |fraction_lost| from BitrateObserver is short time observation of packet + // loss rate from past. We use network predictor to make a more reasonable + // loss rate estimation. + network_predictor_->UpdatePacketLossRate(fraction_lost); + uint8_t loss_rate = network_predictor_->GetLossRate(); // Normalizes rate to 0 - 100. - if (audio_coding_->SetPacketLossRate(100 * fraction_lost / 255) != 0) { + if (audio_coding_->SetPacketLossRate(100 * loss_rate / 255) != 0) { _engineStatisticsPtr->SetLastError(VE_AUDIO_CODING_MODULE_ERROR, kTraceError, "OnNetworkChanged() failed to set packet loss rate"); assert(false); // This should not happen. diff --git a/webrtc/voice_engine/channel.h b/webrtc/voice_engine/channel.h index 5cb2b9c33..8385ccc81 100644 --- a/webrtc/voice_engine/channel.h +++ b/webrtc/voice_engine/channel.h @@ -27,6 +27,7 @@ #include "webrtc/voice_engine/include/voe_audio_processing.h" #include "webrtc/voice_engine/include/voe_network.h" #include "webrtc/voice_engine/level_indicator.h" +#include "webrtc/voice_engine/network_predictor.h" #include "webrtc/voice_engine/shared_data.h" #include "webrtc/voice_engine/voice_engine_defines.h" @@ -621,6 +622,7 @@ private: scoped_ptr bitrate_controller_; scoped_ptr rtcp_bandwidth_observer_; scoped_ptr send_bitrate_observer_; + scoped_ptr network_predictor_; }; } // namespace voe diff --git a/webrtc/voice_engine/network_predictor.cc b/webrtc/voice_engine/network_predictor.cc new file mode 100644 index 000000000..409387734 --- /dev/null +++ b/webrtc/voice_engine/network_predictor.cc @@ -0,0 +1,37 @@ +/* + * 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/voice_engine/network_predictor.h" + +namespace webrtc { +namespace voe { + +NetworkPredictor::NetworkPredictor(Clock* clock) + : clock_(clock), + last_loss_rate_update_time_ms_(clock_->TimeInMilliseconds()), + loss_rate_filter_(new rtc::ExpFilter(0.9999f)) { +} + +void NetworkPredictor::UpdatePacketLossRate(uint8_t loss_rate) { + int64_t now_ms = clock_->TimeInMilliseconds(); + // Update the recursive average filter. + loss_rate_filter_->Apply( + static_cast(now_ms - last_loss_rate_update_time_ms_), + static_cast(loss_rate)); + last_loss_rate_update_time_ms_ = now_ms; +} + +uint8_t NetworkPredictor::GetLossRate() { + float value = loss_rate_filter_->filtered(); + return (value == rtc::ExpFilter::kValueUndefined) ? 0 : + static_cast(value + 0.5); +} +} // namespace voe +} // namespace webrtc diff --git a/webrtc/voice_engine/network_predictor.h b/webrtc/voice_engine/network_predictor.h new file mode 100644 index 000000000..d9f0b7b14 --- /dev/null +++ b/webrtc/voice_engine/network_predictor.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef WEBRTC_VOICE_ENGINE_NETWORK_PREDICTOR_H_ +#define WEBRTC_VOICE_ENGINE_NETWORK_PREDICTOR_H_ + +#include "webrtc/base/exp_filter.h" +#include "webrtc/system_wrappers/interface/clock.h" + +namespace webrtc { + +namespace voe { + +// NetworkPredictor is to predict network conditions e.g., packet loss rate, for +// sender and/or receiver to cope with changes in the network condition. +class NetworkPredictor { + public: + explicit NetworkPredictor(Clock* clock); + ~NetworkPredictor() {} + + // Gets the predicted packet loss rate. + uint8_t GetLossRate(); + + // Updates the packet loss rate predictor, on receiving a new observation of + // packet loss rate from past. Input packet loss rate should be in the + // interval [0, 255]. + void UpdatePacketLossRate(uint8_t loss_rate); + + private: + Clock* clock_; + int64_t last_loss_rate_update_time_ms_; + + // An exponential filter is used to predict packet loss rate. + scoped_ptr loss_rate_filter_; +}; + +} // namespace voe +} // namespace webrtc +#endif // WEBRTC_VOICE_ENGINE_NETWORK_PREDICTOR_H_ diff --git a/webrtc/voice_engine/network_predictor_unittest.cc b/webrtc/voice_engine/network_predictor_unittest.cc new file mode 100644 index 000000000..e399f6820 --- /dev/null +++ b/webrtc/voice_engine/network_predictor_unittest.cc @@ -0,0 +1,43 @@ +/* + * 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 + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/voice_engine/network_predictor.h" +#include "webrtc/system_wrappers/interface/clock.h" + +namespace webrtc { +namespace voe { + +class TestNetworkPredictor : public ::testing::Test { + protected: + TestNetworkPredictor() + : clock_(0), + network_predictor_(new NetworkPredictor(&clock_)) {} + SimulatedClock clock_; + scoped_ptr network_predictor_; +}; + +TEST_F(TestNetworkPredictor, TestPacketLossRateFilter) { + // Test initial packet loss rate estimate is 0. + EXPECT_EQ(0, network_predictor_->GetLossRate()); + network_predictor_->UpdatePacketLossRate(32); + // First time, no filtering. + EXPECT_EQ(32, network_predictor_->GetLossRate()); + clock_.AdvanceTimeMilliseconds(1000); + network_predictor_->UpdatePacketLossRate(40); + float exp = pow(0.9999f, 1000); + float value = 32.0f * exp + (1 - exp) * 40.0f; + EXPECT_EQ(static_cast(value + 0.5f), + network_predictor_->GetLossRate()); +} +} // namespace voe +} // namespace webrtc diff --git a/webrtc/voice_engine/voice_engine.gyp b/webrtc/voice_engine/voice_engine.gyp index 19342c3a9..43296ff35 100644 --- a/webrtc/voice_engine/voice_engine.gyp +++ b/webrtc/voice_engine/voice_engine.gyp @@ -55,6 +55,8 @@ 'level_indicator.h', 'monitor_module.cc', 'monitor_module.h', + 'network_predictor.cc', + 'network_predictor.h', 'output_mixer.cc', 'output_mixer.h', 'shared_data.cc', @@ -122,6 +124,7 @@ ], 'sources': [ 'channel_unittest.cc', + 'network_predictor_unittest.cc', 'transmit_mixer_unittest.cc', 'utility_unittest.cc', 'voe_audio_processing_unittest.cc',