webrtc/modules/audio_coding/neteq/delay_manager.cc
Ruslan Burakov 76a74e6dc8 Delay bug during audio receiver stream recreation.
For e.g. when audio receiver is recreated during SetRtpExtensionsAndRecreateStream in webrtc_voice_engine.h,
the audio minimum delay can't go down.

Imagine we set base minimum playout delay when audio receiver stream is created, then its value will be cached, to be applied during recreation. Then SetRtpExtensionsAndRecreateStream is fired, and audio receiver stream is recreated with the cached value, but currently it in the constructor it is used to initialize both base minimum playout delay and minimum playout delay. Which leads to the bug that effective minimum playout delay can't go down anymore as if you set base minimum playout delay to the low value then effective delay use the biggest value which minimum playout delay.

This didn't come up during previous trials because of
https://webrtc-review.googlesource.com/c/src/+/122280
It was reseting minimum playout delay to 0 asynchronously, that is why you couldn't see this bug.


Bug: webrtc:10287
Change-Id: I924446bfcb33ac94f7e5bf987a1868acaf1b0346
Reviewed-on: https://webrtc-review.googlesource.com/c/124000
Reviewed-by: Henrik Lundin <henrik.lundin@webrtc.org>
Commit-Queue: Ruslan Burakov <kuddai@google.com>
Cr-Commit-Position: refs/heads/master@{#26832}
2019-02-25 09:23:56 +00:00

455 lines
16 KiB
C++

/*
* Copyright (c) 2012 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/audio_coding/neteq/delay_manager.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <numeric>
#include <string>
#include "absl/memory/memory.h"
#include "modules/audio_coding/neteq/delay_peak_detector.h"
#include "modules/include/module_common_types_public.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/numerics/safe_minmax.h"
#include "system_wrappers/include/field_trial.h"
namespace {
constexpr int kLimitProbability = 1020054733; // 19/20 in Q30.
constexpr int kLimitProbabilityStreaming = 1073204953; // 1999/2000 in Q30.
constexpr int kMaxStreamingPeakPeriodMs = 600000; // 10 minutes in ms.
constexpr int kCumulativeSumDrift = 2; // Drift term for cumulative sum
// |iat_cumulative_sum_|.
constexpr int kMinBaseMinimumDelayMs = 0;
constexpr int kMaxBaseMinimumDelayMs = 10000;
constexpr int kIatFactor = 32745; // 0.9993 in Q15.
constexpr int kMaxIat = 64; // Max inter-arrival time to register.
constexpr int kMaxReorderedPackets =
10; // Max number of consecutive reordered packets.
absl::optional<int> GetForcedLimitProbability() {
constexpr char kForceTargetDelayPercentileFieldTrial[] =
"WebRTC-Audio-NetEqForceTargetDelayPercentile";
const bool use_forced_target_delay_percentile =
webrtc::field_trial::IsEnabled(kForceTargetDelayPercentileFieldTrial);
if (use_forced_target_delay_percentile) {
const std::string field_trial_string = webrtc::field_trial::FindFullName(
kForceTargetDelayPercentileFieldTrial);
double percentile = -1.0;
if (sscanf(field_trial_string.c_str(), "Enabled-%lf", &percentile) == 1 &&
percentile >= 0.0 && percentile <= 100.0) {
return absl::make_optional<int>(
static_cast<int>((1 << 30) * percentile / 100.0 + 0.5)); // in Q30.
} else {
RTC_LOG(LS_WARNING) << "Invalid parameter for "
<< kForceTargetDelayPercentileFieldTrial
<< ", ignored.";
}
}
return absl::nullopt;
}
} // namespace
namespace webrtc {
DelayManager::DelayManager(size_t max_packets_in_buffer,
int base_minimum_delay_ms,
bool enable_rtx_handling,
DelayPeakDetector* peak_detector,
const TickTimer* tick_timer,
std::unique_ptr<Histogram> iat_histogram)
: first_packet_received_(false),
max_packets_in_buffer_(max_packets_in_buffer),
iat_histogram_(std::move(iat_histogram)),
tick_timer_(tick_timer),
base_minimum_delay_ms_(base_minimum_delay_ms),
effective_minimum_delay_ms_(base_minimum_delay_ms),
base_target_level_(4), // In Q0 domain.
target_level_(base_target_level_ << 8), // In Q8 domain.
packet_len_ms_(0),
streaming_mode_(false),
last_seq_no_(0),
last_timestamp_(0),
minimum_delay_ms_(0),
maximum_delay_ms_(0),
iat_cumulative_sum_(0),
max_iat_cumulative_sum_(0),
peak_detector_(*peak_detector),
last_pack_cng_or_dtmf_(1),
frame_length_change_experiment_(
field_trial::IsEnabled("WebRTC-Audio-NetEqFramelengthExperiment")),
forced_limit_probability_(GetForcedLimitProbability()),
enable_rtx_handling_(enable_rtx_handling) {
assert(peak_detector); // Should never be NULL.
RTC_CHECK(iat_histogram_);
RTC_DCHECK_GE(base_minimum_delay_ms_, 0);
Reset();
}
std::unique_ptr<DelayManager> DelayManager::Create(
size_t max_packets_in_buffer,
int base_minimum_delay_ms,
bool enable_rtx_handling,
DelayPeakDetector* peak_detector,
const TickTimer* tick_timer) {
return absl::make_unique<DelayManager>(
max_packets_in_buffer, base_minimum_delay_ms, enable_rtx_handling,
peak_detector, tick_timer,
absl::make_unique<Histogram>(kMaxIat + 1, kIatFactor));
}
DelayManager::~DelayManager() {}
int DelayManager::Update(uint16_t sequence_number,
uint32_t timestamp,
int sample_rate_hz) {
if (sample_rate_hz <= 0) {
return -1;
}
if (!first_packet_received_) {
// Prepare for next packet arrival.
packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
last_seq_no_ = sequence_number;
last_timestamp_ = timestamp;
first_packet_received_ = true;
return 0;
}
// Try calculating packet length from current and previous timestamps.
int packet_len_ms;
if (!IsNewerTimestamp(timestamp, last_timestamp_) ||
!IsNewerSequenceNumber(sequence_number, last_seq_no_)) {
// Wrong timestamp or sequence order; use stored value.
packet_len_ms = packet_len_ms_;
} else {
// Calculate timestamps per packet and derive packet length in ms.
int64_t packet_len_samp =
static_cast<uint32_t>(timestamp - last_timestamp_) /
static_cast<uint16_t>(sequence_number - last_seq_no_);
packet_len_ms =
rtc::saturated_cast<int>(1000 * packet_len_samp / sample_rate_hz);
}
bool reordered = false;
if (packet_len_ms > 0) {
// Cannot update statistics unless |packet_len_ms| is valid.
// Calculate inter-arrival time (IAT) in integer "packet times"
// (rounding down). This is the value added to the inter-arrival time
// histogram.
int iat_packets = packet_iat_stopwatch_->ElapsedMs() / packet_len_ms;
if (streaming_mode_) {
UpdateCumulativeSums(packet_len_ms, sequence_number);
}
// Check for discontinuous packet sequence and re-ordering.
if (IsNewerSequenceNumber(sequence_number, last_seq_no_ + 1)) {
// Compensate for gap in the sequence numbers. Reduce IAT with the
// expected extra time due to lost packets, but ensure that the IAT is
// not negative.
iat_packets -= static_cast<uint16_t>(sequence_number - last_seq_no_ - 1);
iat_packets = std::max(iat_packets, 0);
} else if (!IsNewerSequenceNumber(sequence_number, last_seq_no_)) {
iat_packets += static_cast<uint16_t>(last_seq_no_ + 1 - sequence_number);
reordered = true;
}
// Saturate IAT at maximum value.
iat_packets = std::min(iat_packets, iat_histogram_->NumBuckets() - 1);
iat_histogram_->Add(iat_packets);
// Calculate new |target_level_| based on updated statistics.
target_level_ = CalculateTargetLevel(iat_packets, reordered);
if (streaming_mode_) {
target_level_ = std::max(target_level_, max_iat_cumulative_sum_);
}
LimitTargetLevel();
} // End if (packet_len_ms > 0).
if (enable_rtx_handling_ && reordered &&
num_reordered_packets_ < kMaxReorderedPackets) {
++num_reordered_packets_;
return 0;
}
num_reordered_packets_ = 0;
// Prepare for next packet arrival.
packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
last_seq_no_ = sequence_number;
last_timestamp_ = timestamp;
return 0;
}
void DelayManager::UpdateCumulativeSums(int packet_len_ms,
uint16_t sequence_number) {
// Calculate IAT in Q8, including fractions of a packet (i.e., more
// accurate than |iat_packets|.
int iat_packets_q8 =
(packet_iat_stopwatch_->ElapsedMs() << 8) / packet_len_ms;
// Calculate cumulative sum IAT with sequence number compensation. The sum
// is zero if there is no clock-drift.
iat_cumulative_sum_ +=
(iat_packets_q8 -
(static_cast<int>(sequence_number - last_seq_no_) << 8));
// Subtract drift term.
iat_cumulative_sum_ -= kCumulativeSumDrift;
// Ensure not negative.
iat_cumulative_sum_ = std::max(iat_cumulative_sum_, 0);
if (iat_cumulative_sum_ > max_iat_cumulative_sum_) {
// Found a new maximum.
max_iat_cumulative_sum_ = iat_cumulative_sum_;
max_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
}
if (max_iat_stopwatch_->ElapsedMs() > kMaxStreamingPeakPeriodMs) {
// Too long since the last maximum was observed; decrease max value.
max_iat_cumulative_sum_ -= kCumulativeSumDrift;
}
}
// Enforces upper and lower limits for |target_level_|. The upper limit is
// chosen to be minimum of i) 75% of |max_packets_in_buffer_|, to leave some
// headroom for natural fluctuations around the target, and ii) equivalent of
// |maximum_delay_ms_| in packets. Note that in practice, if no
// |maximum_delay_ms_| is specified, this does not have any impact, since the
// target level is far below the buffer capacity in all reasonable cases.
// The lower limit is equivalent of |effective_minimum_delay_ms_| in packets.
// We update |least_required_level_| while the above limits are applied.
// TODO(hlundin): Move this check to the buffer logistics class.
void DelayManager::LimitTargetLevel() {
if (packet_len_ms_ > 0 && effective_minimum_delay_ms_ > 0) {
int minimum_delay_packet_q8 =
(effective_minimum_delay_ms_ << 8) / packet_len_ms_;
target_level_ = std::max(target_level_, minimum_delay_packet_q8);
}
if (maximum_delay_ms_ > 0 && packet_len_ms_ > 0) {
int maximum_delay_packet_q8 = (maximum_delay_ms_ << 8) / packet_len_ms_;
target_level_ = std::min(target_level_, maximum_delay_packet_q8);
}
// Shift to Q8, then 75%.;
int max_buffer_packets_q8 =
static_cast<int>((3 * (max_packets_in_buffer_ << 8)) / 4);
target_level_ = std::min(target_level_, max_buffer_packets_q8);
// Sanity check, at least 1 packet (in Q8).
target_level_ = std::max(target_level_, 1 << 8);
}
int DelayManager::CalculateTargetLevel(int iat_packets, bool reordered) {
int limit_probability = forced_limit_probability_.value_or(kLimitProbability);
if (streaming_mode_) {
limit_probability = kLimitProbabilityStreaming;
}
// Calculate target buffer level from inter-arrival time histogram.
// This is the base value for the target buffer level.
int target_level = iat_histogram_->Quantile(limit_probability);
base_target_level_ = target_level;
// Update detector for delay peaks.
bool delay_peak_found =
peak_detector_.Update(iat_packets, reordered, target_level);
if (delay_peak_found) {
target_level = std::max(target_level, peak_detector_.MaxPeakHeight());
}
// Sanity check. |target_level| must be strictly positive.
target_level = std::max(target_level, 1);
// Scale to Q8 and assign to member variable.
target_level_ = target_level << 8;
return target_level_;
}
int DelayManager::SetPacketAudioLength(int length_ms) {
if (length_ms <= 0) {
RTC_LOG_F(LS_ERROR) << "length_ms = " << length_ms;
return -1;
}
if (frame_length_change_experiment_ && packet_len_ms_ != length_ms &&
packet_len_ms_ > 0) {
iat_histogram_->Scale(packet_len_ms_, length_ms);
}
packet_len_ms_ = length_ms;
peak_detector_.SetPacketAudioLength(packet_len_ms_);
packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
last_pack_cng_or_dtmf_ = 1; // TODO(hlundin): Legacy. Remove?
return 0;
}
void DelayManager::Reset() {
packet_len_ms_ = 0; // Packet size unknown.
streaming_mode_ = false;
peak_detector_.Reset();
iat_histogram_->Reset();
base_target_level_ = 4;
target_level_ = base_target_level_ << 8;
packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
max_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
iat_cumulative_sum_ = 0;
max_iat_cumulative_sum_ = 0;
last_pack_cng_or_dtmf_ = 1;
}
double DelayManager::EstimatedClockDriftPpm() const {
double sum = 0.0;
// Calculate the expected value based on the probabilities in
// |iat_histogram_|.
auto buckets = iat_histogram_->buckets();
for (size_t i = 0; i < buckets.size(); ++i) {
sum += static_cast<double>(buckets[i]) * i;
}
// The probabilities in |iat_histogram_| are in Q30. Divide by 1 << 30 to
// convert to Q0; subtract the nominal inter-arrival time (1) to make a zero
// clockdrift represent as 0; mulitply by 1000000 to produce parts-per-million
// (ppm).
return (sum / (1 << 30) - 1) * 1e6;
}
bool DelayManager::PeakFound() const {
return peak_detector_.peak_found();
}
void DelayManager::ResetPacketIatCount() {
packet_iat_stopwatch_ = tick_timer_->GetNewStopwatch();
}
// Note that |low_limit| and |higher_limit| are not assigned to
// |minimum_delay_ms_| and |maximum_delay_ms_| defined by the client of this
// class. They are computed from |target_level_| and used for decision making.
void DelayManager::BufferLimits(int* lower_limit, int* higher_limit) const {
if (!lower_limit || !higher_limit) {
RTC_LOG_F(LS_ERROR) << "NULL pointers supplied as input";
assert(false);
return;
}
int window_20ms = 0x7FFF; // Default large value for legacy bit-exactness.
if (packet_len_ms_ > 0) {
window_20ms = (20 << 8) / packet_len_ms_;
}
// |target_level_| is in Q8 already.
*lower_limit = (target_level_ * 3) / 4;
// |higher_limit| is equal to |target_level_|, but should at
// least be 20 ms higher than |lower_limit_|.
*higher_limit = std::max(target_level_, *lower_limit + window_20ms);
}
int DelayManager::TargetLevel() const {
return target_level_;
}
void DelayManager::LastDecodedWasCngOrDtmf(bool it_was) {
if (it_was) {
last_pack_cng_or_dtmf_ = 1;
} else if (last_pack_cng_or_dtmf_ != 0) {
last_pack_cng_or_dtmf_ = -1;
}
}
void DelayManager::RegisterEmptyPacket() {
++last_seq_no_;
}
bool DelayManager::IsValidMinimumDelay(int delay_ms) const {
return 0 <= delay_ms && delay_ms <= MinimumDelayUpperBound();
}
bool DelayManager::IsValidBaseMinimumDelay(int delay_ms) const {
return kMinBaseMinimumDelayMs <= delay_ms &&
delay_ms <= kMaxBaseMinimumDelayMs;
}
bool DelayManager::SetMinimumDelay(int delay_ms) {
if (!IsValidMinimumDelay(delay_ms)) {
return false;
}
minimum_delay_ms_ = delay_ms;
UpdateEffectiveMinimumDelay();
return true;
}
bool DelayManager::SetMaximumDelay(int delay_ms) {
// If |delay_ms| is zero then it unsets the maximum delay and target level is
// unconstrained by maximum delay.
if (delay_ms != 0 &&
(delay_ms < minimum_delay_ms_ || delay_ms < packet_len_ms_)) {
// Maximum delay shouldn't be less than minimum delay or less than a packet.
return false;
}
maximum_delay_ms_ = delay_ms;
UpdateEffectiveMinimumDelay();
return true;
}
bool DelayManager::SetBaseMinimumDelay(int delay_ms) {
if (!IsValidBaseMinimumDelay(delay_ms)) {
return false;
}
base_minimum_delay_ms_ = delay_ms;
UpdateEffectiveMinimumDelay();
return true;
}
int DelayManager::GetBaseMinimumDelay() const {
return base_minimum_delay_ms_;
}
int DelayManager::base_target_level() const {
return base_target_level_;
}
void DelayManager::set_streaming_mode(bool value) {
streaming_mode_ = value;
}
int DelayManager::last_pack_cng_or_dtmf() const {
return last_pack_cng_or_dtmf_;
}
void DelayManager::set_last_pack_cng_or_dtmf(int value) {
last_pack_cng_or_dtmf_ = value;
}
void DelayManager::UpdateEffectiveMinimumDelay() {
// Clamp |base_minimum_delay_ms_| into the range which can be effectively
// used.
const int base_minimum_delay_ms =
rtc::SafeClamp(base_minimum_delay_ms_, 0, MinimumDelayUpperBound());
effective_minimum_delay_ms_ =
std::max(minimum_delay_ms_, base_minimum_delay_ms);
}
int DelayManager::MinimumDelayUpperBound() const {
// Choose the lowest possible bound discarding 0 cases which mean the value
// is not set and unconstrained.
int q75 = MaxBufferTimeQ75();
q75 = q75 > 0 ? q75 : kMaxBaseMinimumDelayMs;
const int maximum_delay_ms =
maximum_delay_ms_ > 0 ? maximum_delay_ms_ : kMaxBaseMinimumDelayMs;
return std::min(maximum_delay_ms, q75);
}
int DelayManager::MaxBufferTimeQ75() const {
const int max_buffer_time = max_packets_in_buffer_ * packet_len_ms_;
return rtc::dchecked_cast<int>(3 * max_buffer_time / 4);
}
} // namespace webrtc