Switch VCMRttFilter to use TimeDelta
* Moved into its own GN target * Switched the internal buffer types to absl::InlinedVector as arrays are tricky to use with types that do not have default constructors. * Update fields arnd variables to use style guide. * Use constexpr for formerly const fields. * Adds unit tests. Change-Id: I476ae8491f0f9878c176e7b87a5133942c3d79f7 Bug: webrtc:13756 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/253120 Reviewed-by: Erik Språng <sprang@webrtc.org> Reviewed-by: Stefan Holmer <stefan@webrtc.org> Commit-Queue: Evan Shrubsole <eshr@webrtc.org> Cr-Commit-Position: refs/heads/main@{#36133}
This commit is contained in:
parent
a2ee9234b4
commit
80260c226d
@ -202,6 +202,18 @@ rtc_library("timing") {
|
||||
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
|
||||
}
|
||||
|
||||
rtc_library("rtt_filter") {
|
||||
sources = [
|
||||
"rtt_filter.cc",
|
||||
"rtt_filter.h",
|
||||
]
|
||||
deps = [ "../../api/units:time_delta" ]
|
||||
absl_deps = [
|
||||
"//third_party/abseil-cpp/absl/algorithm:container",
|
||||
"//third_party/abseil-cpp/absl/container:inlined_vector",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_library("video_coding") {
|
||||
visibility = [ "*" ]
|
||||
sources = [
|
||||
@ -242,8 +254,6 @@ rtc_library("video_coding") {
|
||||
"rtp_vp8_ref_finder.h",
|
||||
"rtp_vp9_ref_finder.cc",
|
||||
"rtp_vp9_ref_finder.h",
|
||||
"rtt_filter.cc",
|
||||
"rtt_filter.h",
|
||||
"timestamp_map.cc",
|
||||
"timestamp_map.h",
|
||||
"unique_timestamp_counter.cc",
|
||||
@ -259,6 +269,7 @@ rtc_library("video_coding") {
|
||||
":frame_buffer",
|
||||
":frame_helpers",
|
||||
":packet_buffer",
|
||||
":rtt_filter",
|
||||
":timing",
|
||||
":video_codec_interface",
|
||||
":video_coding_utility",
|
||||
@ -1084,6 +1095,7 @@ if (rtc_include_tests) {
|
||||
"rtp_frame_reference_finder_unittest.cc",
|
||||
"rtp_vp8_ref_finder_unittest.cc",
|
||||
"rtp_vp9_ref_finder_unittest.cc",
|
||||
"rtt_filter_unittest.cc",
|
||||
"session_info_unittest.cc",
|
||||
"test/stream_generator.cc",
|
||||
"test/stream_generator.h",
|
||||
@ -1119,6 +1131,7 @@ if (rtc_include_tests) {
|
||||
":h264_packet_buffer",
|
||||
":nack_requester",
|
||||
":packet_buffer",
|
||||
":rtt_filter",
|
||||
":simulcast_test_fixture_impl",
|
||||
":timing",
|
||||
":video_codec_interface",
|
||||
|
||||
@ -346,7 +346,7 @@ void VCMJitterEstimator::PostProcessEstimate() {
|
||||
}
|
||||
|
||||
void VCMJitterEstimator::UpdateRtt(int64_t rttMs) {
|
||||
_rttFilter.Update(rttMs);
|
||||
_rttFilter.Update(TimeDelta::Millis(rttMs));
|
||||
}
|
||||
|
||||
// Returns the current filtered estimate if available,
|
||||
@ -364,10 +364,10 @@ int VCMJitterEstimator::GetJitterEstimate(
|
||||
jitterMS = _filterJitterEstimate;
|
||||
if (_nackCount >= _nackLimit) {
|
||||
if (rttMultAddCapMs.has_value()) {
|
||||
jitterMS +=
|
||||
std::min(_rttFilter.RttMs() * rttMultiplier, rttMultAddCapMs.value());
|
||||
jitterMS += std::min(_rttFilter.Rtt().ms() * rttMultiplier,
|
||||
rttMultAddCapMs.value());
|
||||
} else {
|
||||
jitterMS += _rttFilter.RttMs() * rttMultiplier;
|
||||
jitterMS += _rttFilter.Rtt().ms() * rttMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -14,137 +14,148 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "modules/video_coding/internal_defines.h"
|
||||
#include <algorithm>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "absl/container/inlined_vector.h"
|
||||
#include "api/units/time_delta.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr TimeDelta kMaxRtt = TimeDelta::Seconds(3);
|
||||
constexpr uint32_t kFilterFactorMax = 35;
|
||||
constexpr double kJumpStddev = 2.5;
|
||||
constexpr double kDriftStdDev = 3.5;
|
||||
|
||||
} // namespace
|
||||
|
||||
VCMRttFilter::VCMRttFilter()
|
||||
: _filtFactMax(35),
|
||||
_jumpStdDevs(2.5),
|
||||
_driftStdDevs(3.5),
|
||||
_detectThreshold(kMaxDriftJumpCount) {
|
||||
: avg_rtt_(TimeDelta::Zero()),
|
||||
var_rtt_(0),
|
||||
max_rtt_(TimeDelta::Zero()),
|
||||
jump_buf_(kMaxDriftJumpCount, TimeDelta::Zero()),
|
||||
drift_buf_(kMaxDriftJumpCount, TimeDelta::Zero()) {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void VCMRttFilter::Reset() {
|
||||
_gotNonZeroUpdate = false;
|
||||
_avgRtt = 0;
|
||||
_varRtt = 0;
|
||||
_maxRtt = 0;
|
||||
_filtFactCount = 1;
|
||||
_jumpCount = 0;
|
||||
_driftCount = 0;
|
||||
memset(_jumpBuf, 0, sizeof(_jumpBuf));
|
||||
memset(_driftBuf, 0, sizeof(_driftBuf));
|
||||
got_non_zero_update_ = false;
|
||||
avg_rtt_ = TimeDelta::Zero();
|
||||
var_rtt_ = 0;
|
||||
max_rtt_ = TimeDelta::Zero();
|
||||
filt_fact_count_ = 1;
|
||||
absl::c_fill(jump_buf_, TimeDelta::Zero());
|
||||
absl::c_fill(drift_buf_, TimeDelta::Zero());
|
||||
}
|
||||
|
||||
void VCMRttFilter::Update(int64_t rttMs) {
|
||||
if (!_gotNonZeroUpdate) {
|
||||
if (rttMs == 0) {
|
||||
void VCMRttFilter::Update(TimeDelta rtt) {
|
||||
if (!got_non_zero_update_) {
|
||||
if (rtt.IsZero()) {
|
||||
return;
|
||||
}
|
||||
_gotNonZeroUpdate = true;
|
||||
got_non_zero_update_ = true;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (rttMs > 3000) {
|
||||
rttMs = 3000;
|
||||
if (rtt > kMaxRtt) {
|
||||
rtt = kMaxRtt;
|
||||
}
|
||||
|
||||
double filtFactor = 0;
|
||||
if (_filtFactCount > 1) {
|
||||
filtFactor = static_cast<double>(_filtFactCount - 1) / _filtFactCount;
|
||||
double filt_factor = 0;
|
||||
if (filt_fact_count_ > 1) {
|
||||
filt_factor = static_cast<double>(filt_fact_count_ - 1) / filt_fact_count_;
|
||||
}
|
||||
_filtFactCount++;
|
||||
if (_filtFactCount > _filtFactMax) {
|
||||
// This prevents filtFactor from going above
|
||||
// (_filtFactMax - 1) / _filtFactMax,
|
||||
// e.g., _filtFactMax = 50 => filtFactor = 49/50 = 0.98
|
||||
_filtFactCount = _filtFactMax;
|
||||
filt_fact_count_++;
|
||||
if (filt_fact_count_ > kFilterFactorMax) {
|
||||
// This prevents filt_factor from going above
|
||||
// (_filt_fact_max - 1) / filt_fact_max_,
|
||||
// e.g., filt_fact_max_ = 50 => filt_factor = 49/50 = 0.98
|
||||
filt_fact_count_ = kFilterFactorMax;
|
||||
}
|
||||
double oldAvg = _avgRtt;
|
||||
double oldVar = _varRtt;
|
||||
_avgRtt = filtFactor * _avgRtt + (1 - filtFactor) * rttMs;
|
||||
_varRtt = filtFactor * _varRtt +
|
||||
(1 - filtFactor) * (rttMs - _avgRtt) * (rttMs - _avgRtt);
|
||||
_maxRtt = VCM_MAX(rttMs, _maxRtt);
|
||||
if (!JumpDetection(rttMs) || !DriftDetection(rttMs)) {
|
||||
TimeDelta old_avg = avg_rtt_;
|
||||
int64_t old_var = var_rtt_;
|
||||
avg_rtt_ = filt_factor * avg_rtt_ + (1 - filt_factor) * rtt;
|
||||
int64_t delta_ms = (rtt - avg_rtt_).ms();
|
||||
var_rtt_ = filt_factor * var_rtt_ + (1 - filt_factor) * (delta_ms * delta_ms);
|
||||
max_rtt_ = std::max(rtt, max_rtt_);
|
||||
if (!JumpDetection(rtt) || !DriftDetection(rtt)) {
|
||||
// In some cases we don't want to update the statistics
|
||||
_avgRtt = oldAvg;
|
||||
_varRtt = oldVar;
|
||||
avg_rtt_ = old_avg;
|
||||
var_rtt_ = old_var;
|
||||
}
|
||||
}
|
||||
|
||||
bool VCMRttFilter::JumpDetection(int64_t rttMs) {
|
||||
double diffFromAvg = _avgRtt - rttMs;
|
||||
if (fabs(diffFromAvg) > _jumpStdDevs * sqrt(_varRtt)) {
|
||||
int diffSign = (diffFromAvg >= 0) ? 1 : -1;
|
||||
int jumpCountSign = (_jumpCount >= 0) ? 1 : -1;
|
||||
if (diffSign != jumpCountSign) {
|
||||
bool VCMRttFilter::JumpDetection(TimeDelta rtt) {
|
||||
TimeDelta diff_from_avg = avg_rtt_ - rtt;
|
||||
// Unit of var_rtt_ is ms^2.
|
||||
TimeDelta jump_threshold = TimeDelta::Millis(kJumpStddev * sqrt(var_rtt_));
|
||||
if (diff_from_avg.Abs() > jump_threshold) {
|
||||
bool positive_diff = diff_from_avg >= TimeDelta::Zero();
|
||||
if (!jump_buf_.empty() && positive_diff != last_jump_positive_) {
|
||||
// Since the signs differ the samples currently
|
||||
// in the buffer is useless as they represent a
|
||||
// jump in a different direction.
|
||||
_jumpCount = 0;
|
||||
jump_buf_.clear();
|
||||
}
|
||||
if (abs(_jumpCount) < kMaxDriftJumpCount) {
|
||||
// Update the buffer used for the short time
|
||||
// statistics.
|
||||
if (jump_buf_.size() < kMaxDriftJumpCount) {
|
||||
// Update the buffer used for the short time statistics.
|
||||
// The sign of the diff is used for updating the counter since
|
||||
// we want to use the same buffer for keeping track of when
|
||||
// the RTT jumps down and up.
|
||||
_jumpBuf[abs(_jumpCount)] = rttMs;
|
||||
_jumpCount += diffSign;
|
||||
jump_buf_.push_back(rtt);
|
||||
last_jump_positive_ = positive_diff;
|
||||
}
|
||||
if (abs(_jumpCount) >= _detectThreshold) {
|
||||
if (jump_buf_.size() >= kMaxDriftJumpCount) {
|
||||
// Detected an RTT jump
|
||||
ShortRttFilter(_jumpBuf, abs(_jumpCount));
|
||||
_filtFactCount = _detectThreshold + 1;
|
||||
_jumpCount = 0;
|
||||
ShortRttFilter(jump_buf_);
|
||||
filt_fact_count_ = kMaxDriftJumpCount + 1;
|
||||
jump_buf_.clear();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
_jumpCount = 0;
|
||||
jump_buf_.clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VCMRttFilter::DriftDetection(int64_t rttMs) {
|
||||
if (_maxRtt - _avgRtt > _driftStdDevs * sqrt(_varRtt)) {
|
||||
if (_driftCount < kMaxDriftJumpCount) {
|
||||
// Update the buffer used for the short time
|
||||
// statistics.
|
||||
_driftBuf[_driftCount] = rttMs;
|
||||
_driftCount++;
|
||||
bool VCMRttFilter::DriftDetection(TimeDelta rtt) {
|
||||
// Unit of sqrt of var_rtt_ is ms.
|
||||
TimeDelta drift_threshold = TimeDelta::Millis(kDriftStdDev * sqrt(var_rtt_));
|
||||
if (max_rtt_ - avg_rtt_ > drift_threshold) {
|
||||
if (drift_buf_.size() < kMaxDriftJumpCount) {
|
||||
// Update the buffer used for the short time statistics.
|
||||
drift_buf_.push_back(rtt);
|
||||
}
|
||||
if (_driftCount >= _detectThreshold) {
|
||||
if (drift_buf_.size() >= kMaxDriftJumpCount) {
|
||||
// Detected an RTT drift
|
||||
ShortRttFilter(_driftBuf, _driftCount);
|
||||
_filtFactCount = _detectThreshold + 1;
|
||||
_driftCount = 0;
|
||||
ShortRttFilter(drift_buf_);
|
||||
filt_fact_count_ = kMaxDriftJumpCount + 1;
|
||||
drift_buf_.clear();
|
||||
}
|
||||
} else {
|
||||
_driftCount = 0;
|
||||
drift_buf_.clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VCMRttFilter::ShortRttFilter(int64_t* buf, uint32_t length) {
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
_maxRtt = 0;
|
||||
_avgRtt = 0;
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
if (buf[i] > _maxRtt) {
|
||||
_maxRtt = buf[i];
|
||||
void VCMRttFilter::ShortRttFilter(const BufferList& buf) {
|
||||
RTC_DCHECK_EQ(buf.size(), kMaxDriftJumpCount);
|
||||
max_rtt_ = TimeDelta::Zero();
|
||||
avg_rtt_ = TimeDelta::Zero();
|
||||
for (const TimeDelta& rtt : buf) {
|
||||
if (rtt > max_rtt_) {
|
||||
max_rtt_ = rtt;
|
||||
}
|
||||
_avgRtt += buf[i];
|
||||
avg_rtt_ += rtt;
|
||||
}
|
||||
_avgRtt = _avgRtt / static_cast<double>(length);
|
||||
avg_rtt_ = avg_rtt_ / static_cast<double>(buf.size());
|
||||
}
|
||||
|
||||
int64_t VCMRttFilter::RttMs() const {
|
||||
return static_cast<int64_t>(_maxRtt + 0.5);
|
||||
TimeDelta VCMRttFilter::Rtt() const {
|
||||
return max_rtt_;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -13,6 +13,9 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "absl/container/inlined_vector.h"
|
||||
#include "api/units/time_delta.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class VCMRttFilter {
|
||||
@ -24,41 +27,41 @@ class VCMRttFilter {
|
||||
// Resets the filter.
|
||||
void Reset();
|
||||
// Updates the filter with a new sample.
|
||||
void Update(int64_t rttMs);
|
||||
// A getter function for the current RTT level in ms.
|
||||
int64_t RttMs() const;
|
||||
void Update(TimeDelta rtt);
|
||||
// A getter function for the current RTT level.
|
||||
TimeDelta Rtt() const;
|
||||
|
||||
private:
|
||||
// The size of the drift and jump memory buffers
|
||||
// and thus also the detection threshold for these
|
||||
// detectors in number of samples.
|
||||
enum { kMaxDriftJumpCount = 5 };
|
||||
static constexpr int kMaxDriftJumpCount = 5;
|
||||
using BufferList = absl::InlinedVector<TimeDelta, kMaxDriftJumpCount>;
|
||||
|
||||
// Detects RTT jumps by comparing the difference between
|
||||
// samples and average to the standard deviation.
|
||||
// Returns true if the long time statistics should be updated
|
||||
// and false otherwise
|
||||
bool JumpDetection(int64_t rttMs);
|
||||
bool JumpDetection(TimeDelta rtt);
|
||||
|
||||
// Detects RTT drifts by comparing the difference between
|
||||
// max and average to the standard deviation.
|
||||
// Returns true if the long time statistics should be updated
|
||||
// and false otherwise
|
||||
bool DriftDetection(int64_t rttMs);
|
||||
// Computes the short time average and maximum of the vector buf.
|
||||
void ShortRttFilter(int64_t* buf, uint32_t length);
|
||||
bool DriftDetection(TimeDelta rtt);
|
||||
|
||||
bool _gotNonZeroUpdate;
|
||||
double _avgRtt;
|
||||
double _varRtt;
|
||||
int64_t _maxRtt;
|
||||
uint32_t _filtFactCount;
|
||||
const uint32_t _filtFactMax;
|
||||
const double _jumpStdDevs;
|
||||
const double _driftStdDevs;
|
||||
int32_t _jumpCount;
|
||||
int32_t _driftCount;
|
||||
const int32_t _detectThreshold;
|
||||
int64_t _jumpBuf[kMaxDriftJumpCount];
|
||||
int64_t _driftBuf[kMaxDriftJumpCount];
|
||||
// Computes the short time average and maximum of the vector buf.
|
||||
void ShortRttFilter(const BufferList& buf);
|
||||
|
||||
bool got_non_zero_update_;
|
||||
TimeDelta avg_rtt_;
|
||||
// Variance units are TimeDelta^2. Store as ms^2.
|
||||
int64_t var_rtt_;
|
||||
TimeDelta max_rtt_;
|
||||
uint32_t filt_fact_count_;
|
||||
bool last_jump_positive_ = false;
|
||||
BufferList jump_buf_;
|
||||
BufferList drift_buf_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
105
modules/video_coding/rtt_filter_unittest.cc
Normal file
105
modules/video_coding/rtt_filter_unittest.cc
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2022 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 "modules/video_coding/rtt_filter.h"
|
||||
|
||||
#include "api/units/time_delta.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
TEST(RttFilterTest, RttIsCapped) {
|
||||
VCMRttFilter rtt_filter;
|
||||
rtt_filter.Update(TimeDelta::Seconds(500));
|
||||
|
||||
EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Seconds(3));
|
||||
}
|
||||
|
||||
// If the difference between samples is more than away 2.5 stddev from the mean
|
||||
// then this is considered a jump. After more than 5 data points at the new
|
||||
// level, the RTT is reset to the new level.
|
||||
TEST(RttFilterTest, PositiveJumpDetection) {
|
||||
VCMRttFilter rtt_filter;
|
||||
|
||||
rtt_filter.Update(TimeDelta::Millis(200));
|
||||
rtt_filter.Update(TimeDelta::Millis(200));
|
||||
rtt_filter.Update(TimeDelta::Millis(200));
|
||||
|
||||
// Trigger 5 jumps.
|
||||
rtt_filter.Update(TimeDelta::Millis(1400));
|
||||
rtt_filter.Update(TimeDelta::Millis(1500));
|
||||
rtt_filter.Update(TimeDelta::Millis(1600));
|
||||
rtt_filter.Update(TimeDelta::Millis(1600));
|
||||
|
||||
EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(1600));
|
||||
|
||||
rtt_filter.Update(TimeDelta::Millis(1600));
|
||||
EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(1600));
|
||||
}
|
||||
|
||||
TEST(RttFilterTest, NegativeJumpDetection) {
|
||||
VCMRttFilter rtt_filter;
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
rtt_filter.Update(TimeDelta::Millis(1500));
|
||||
|
||||
// Trigger 5 negative data points that jump rtt down.
|
||||
rtt_filter.Update(TimeDelta::Millis(200));
|
||||
rtt_filter.Update(TimeDelta::Millis(200));
|
||||
rtt_filter.Update(TimeDelta::Millis(200));
|
||||
rtt_filter.Update(TimeDelta::Millis(200));
|
||||
// Before 5 data points at the new level, max RTT is still 1500.
|
||||
EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(1500));
|
||||
|
||||
rtt_filter.Update(TimeDelta::Millis(300));
|
||||
EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(300));
|
||||
}
|
||||
|
||||
TEST(RttFilterTest, JumpsResetByDirectionShift) {
|
||||
VCMRttFilter rtt_filter;
|
||||
for (int i = 0; i < 10; ++i)
|
||||
rtt_filter.Update(TimeDelta::Millis(1500));
|
||||
|
||||
// Trigger 4 negative jumps, then a positive one. This resets the jump
|
||||
// detection.
|
||||
rtt_filter.Update(TimeDelta::Millis(200));
|
||||
rtt_filter.Update(TimeDelta::Millis(200));
|
||||
rtt_filter.Update(TimeDelta::Millis(200));
|
||||
rtt_filter.Update(TimeDelta::Millis(200));
|
||||
rtt_filter.Update(TimeDelta::Millis(2000));
|
||||
EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(2000));
|
||||
|
||||
rtt_filter.Update(TimeDelta::Millis(300));
|
||||
EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(2000));
|
||||
}
|
||||
|
||||
// If the difference between the max and average is more than 3.5 stddevs away
|
||||
// then a drift is detected, and a short filter is applied to find a new max
|
||||
// rtt.
|
||||
TEST(RttFilterTest, DriftDetection) {
|
||||
VCMRttFilter rtt_filter;
|
||||
|
||||
// Descend RTT by 30ms and settle at 700ms RTT. A drift is detected after rtt
|
||||
// of 700ms is reported around 50 times for these targets.
|
||||
constexpr TimeDelta kStartRtt = TimeDelta::Millis(1000);
|
||||
constexpr TimeDelta kDriftTarget = TimeDelta::Millis(700);
|
||||
constexpr TimeDelta kDelta = TimeDelta::Millis(30);
|
||||
for (TimeDelta rtt = kStartRtt; rtt >= kDriftTarget; rtt -= kDelta)
|
||||
rtt_filter.Update(rtt);
|
||||
|
||||
EXPECT_EQ(rtt_filter.Rtt(), kStartRtt);
|
||||
|
||||
for (int i = 0; i < 50; ++i)
|
||||
rtt_filter.Update(kDriftTarget);
|
||||
EXPECT_EQ(rtt_filter.Rtt(), kDriftTarget);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
Loading…
x
Reference in New Issue
Block a user