webrtc/modules/audio_coding/neteq/delay_peak_detector.cc
Ivo Creusen 385b10bbaa Added experiment to improve handling of frame length changes in NetEq.
The field trial effects two things: after a frame length change the IAT
histogram is scaled to prevent an immediate change in target buffer
level. Also, the peak history in the delay peak detector is cleared, 
because the size of the peaks is stored in number of packets (which
will be incorrect after a frame length change).

Bug: webrtc:8381
Change-Id: I214b990f6e5959b655b6542884a7f75da181a0d8
Reviewed-on: https://webrtc-review.googlesource.com/8101
Reviewed-by: Henrik Lundin <henrik.lundin@webrtc.org>
Commit-Queue: Ivo Creusen <ivoc@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20284}
2017-10-13 13:26:57 +00:00

128 lines
4.5 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_peak_detector.h"
#include <algorithm> // max
#include "rtc_base/checks.h"
#include "rtc_base/safe_conversions.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
// The DelayPeakDetector keeps track of severe inter-arrival times, called
// delay peaks. When a peak is observed, the "height" (the time elapsed since
// the previous packet arrival) and the peak "period" (the time since the last
// observed peak) is recorded in a vector. When enough peaks have been observed,
// peak-mode is engaged and the DelayManager asks the DelayPeakDetector for
// the worst peak height.
DelayPeakDetector::~DelayPeakDetector() = default;
DelayPeakDetector::DelayPeakDetector(const TickTimer* tick_timer)
: peak_found_(false),
peak_detection_threshold_(0),
tick_timer_(tick_timer),
frame_length_change_experiment_(
field_trial::IsEnabled("WebRTC-Audio-NetEqFramelengthExperiment")) {
RTC_DCHECK(!peak_period_stopwatch_);
}
void DelayPeakDetector::Reset() {
peak_period_stopwatch_.reset();
peak_found_ = false;
peak_history_.clear();
}
// Calculates the threshold in number of packets.
void DelayPeakDetector::SetPacketAudioLength(int length_ms) {
if (length_ms > 0) {
if (frame_length_change_experiment_) {
peak_detection_threshold_ = std::max(2, kPeakHeightMs / length_ms);
} else {
peak_detection_threshold_ = kPeakHeightMs / length_ms;
}
}
if (frame_length_change_experiment_) {
peak_history_.clear();
}
}
bool DelayPeakDetector::peak_found() {
return peak_found_;
}
int DelayPeakDetector::MaxPeakHeight() const {
int max_height = -1; // Returns -1 for an empty history.
std::list<Peak>::const_iterator it;
for (it = peak_history_.begin(); it != peak_history_.end(); ++it) {
max_height = std::max(max_height, it->peak_height_packets);
}
return max_height;
}
uint64_t DelayPeakDetector::MaxPeakPeriod() const {
auto max_period_element = std::max_element(
peak_history_.begin(), peak_history_.end(),
[](Peak a, Peak b) { return a.period_ms < b.period_ms; });
if (max_period_element == peak_history_.end()) {
return 0; // |peak_history_| is empty.
}
RTC_DCHECK_GT(max_period_element->period_ms, 0);
return max_period_element->period_ms;
}
bool DelayPeakDetector::Update(int inter_arrival_time, int target_level) {
if (inter_arrival_time > target_level + peak_detection_threshold_ ||
inter_arrival_time > 2 * target_level) {
// A delay peak is observed.
if (!peak_period_stopwatch_) {
// This is the first peak. Reset the period counter.
peak_period_stopwatch_ = tick_timer_->GetNewStopwatch();
} else if (peak_period_stopwatch_->ElapsedMs() > 0) {
if (peak_period_stopwatch_->ElapsedMs() <= kMaxPeakPeriodMs) {
// This is not the first peak, and the period is valid.
// Store peak data in the vector.
Peak peak_data;
peak_data.period_ms = peak_period_stopwatch_->ElapsedMs();
peak_data.peak_height_packets = inter_arrival_time;
peak_history_.push_back(peak_data);
while (peak_history_.size() > kMaxNumPeaks) {
// Delete the oldest data point.
peak_history_.pop_front();
}
peak_period_stopwatch_ = tick_timer_->GetNewStopwatch();
} else if (peak_period_stopwatch_->ElapsedMs() <= 2 * kMaxPeakPeriodMs) {
// Invalid peak due to too long period. Reset period counter and start
// looking for next peak.
peak_period_stopwatch_ = tick_timer_->GetNewStopwatch();
} else {
// More than 2 times the maximum period has elapsed since the last peak
// was registered. It seams that the network conditions have changed.
// Reset the peak statistics.
Reset();
}
}
}
return CheckPeakConditions();
}
bool DelayPeakDetector::CheckPeakConditions() {
size_t s = peak_history_.size();
if (s >= kMinPeaksToTrigger &&
peak_period_stopwatch_->ElapsedMs() <= 2 * MaxPeakPeriod()) {
peak_found_ = true;
} else {
peak_found_ = false;
}
return peak_found_;
}
} // namespace webrtc