Cleanup NetEq decision logic field trial.

Bug: webrtc:42223518
Change-Id: I2e5064109b9f0358e8e590e4aae6ebeb9d16bc3e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/358861
Reviewed-by: Tomas Lundqvist <tomasl@google.com>
Commit-Queue: Jakob Ivarsson‎ <jakobi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42746}
This commit is contained in:
Jakob Ivarsson 2024-08-08 09:21:33 +00:00 committed by WebRTC LUCI CQ
parent e864dec2e9
commit 723ea45075
2 changed files with 38 additions and 154 deletions

View File

@ -21,10 +21,8 @@
#include "modules/audio_coding/neteq/packet_arrival_history.h"
#include "modules/audio_coding/neteq/packet_buffer.h"
#include "rtc_base/checks.h"
#include "rtc_base/experiments/struct_parameters_parser.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "system_wrappers/include/field_trial.h"
namespace webrtc {
@ -32,11 +30,11 @@ namespace {
constexpr int kPostponeDecodingLevel = 50;
constexpr int kTargetLevelWindowMs = 100;
constexpr int kMaxWaitForPacketMs = 100;
// The granularity of delay adjustments (accelerate/preemptive expand) is 15ms,
// but round up since the clock has a granularity of 10ms.
constexpr int kDelayAdjustmentGranularityMs = 20;
constexpr int kReinitAfterExpandsMs = 1000;
constexpr int kPacketHistorySizeMs = 2000;
constexpr size_t kCngTimeoutMs = 1000;
std::unique_ptr<DelayManager> CreateDelayManager(
const NetEqController::Config& neteq_config) {
@ -65,26 +63,6 @@ bool IsExpand(NetEq::Mode mode) {
} // namespace
DecisionLogic::Config::Config() {
StructParametersParser::Create(
"enable_stable_delay_mode", &enable_stable_delay_mode, //
"combine_concealment_decision", &combine_concealment_decision, //
"packet_history_size_ms", &packet_history_size_ms, //
"cng_timeout_ms", &cng_timeout_ms, //
"deceleration_target_level_offset_ms",
&deceleration_target_level_offset_ms)
->Parse(webrtc::field_trial::FindFullName(
"WebRTC-Audio-NetEqDecisionLogicConfig"));
RTC_LOG(LS_INFO) << "NetEq decision logic config:"
<< " enable_stable_delay_mode=" << enable_stable_delay_mode
<< " combine_concealment_decision="
<< combine_concealment_decision
<< " packet_history_size_ms=" << packet_history_size_ms
<< " cng_timeout_ms=" << cng_timeout_ms.value_or(-1)
<< " deceleration_target_level_offset_ms="
<< deceleration_target_level_offset_ms;
}
DecisionLogic::DecisionLogic(NetEqController::Config config)
: DecisionLogic(config,
CreateDelayManager(config),
@ -97,11 +75,11 @@ DecisionLogic::DecisionLogic(
std::unique_ptr<PacketArrivalHistory> packet_arrival_history)
: delay_manager_(std::move(delay_manager)),
buffer_level_filter_(std::move(buffer_level_filter)),
packet_arrival_history_(packet_arrival_history
? std::move(packet_arrival_history)
: std::make_unique<PacketArrivalHistory>(
config.tick_timer,
config_.packet_history_size_ms)),
packet_arrival_history_(
packet_arrival_history
? std::move(packet_arrival_history)
: std::make_unique<PacketArrivalHistory>(config.tick_timer,
kPacketHistorySizeMs)),
tick_timer_(config.tick_timer),
disallow_time_stretching_(!config.allow_time_stretching),
timescale_countdown_(
@ -136,8 +114,7 @@ NetEq::Operation DecisionLogic::GetDecision(const NetEqStatus& status,
if (prev_time_scale_) {
timescale_countdown_ = tick_timer_->GetNewCountdown(kMinTimescaleInterval);
}
if (!IsCng(status.last_mode) &&
!(config_.combine_concealment_decision && IsExpand(status.last_mode))) {
if (!IsCng(status.last_mode) && !IsExpand(status.last_mode)) {
FilterBufferLevel(status.packet_buffer_info.span_samples);
}
@ -160,15 +137,6 @@ NetEq::Operation DecisionLogic::GetDecision(const NetEqStatus& status,
return NoPacket(status);
}
// If the expand period was very long, reset NetEQ since it is likely that the
// sender was restarted.
if (!config_.combine_concealment_decision && IsExpand(status.last_mode) &&
status.generated_noise_samples >
static_cast<size_t>(kReinitAfterExpandsMs * sample_rate_khz_)) {
*reset_decoder = true;
return NetEq::Operation::kNormal;
}
if (PostponeDecode(status)) {
return NoPacket(status);
}
@ -190,13 +158,7 @@ NetEq::Operation DecisionLogic::GetDecision(const NetEqStatus& status,
}
int DecisionLogic::TargetLevelMs() const {
int target_delay_ms = delay_manager_->TargetDelayMs();
if (!config_.enable_stable_delay_mode) {
target_delay_ms =
std::max(target_delay_ms,
static_cast<int>(packet_length_samples_ / sample_rate_khz_));
}
return target_delay_ms;
return delay_manager_->TargetDelayMs();
}
int DecisionLogic::UnlimitedTargetLevelMs() const {
@ -290,9 +252,7 @@ NetEq::Operation DecisionLogic::NoPacket(NetEqController::NetEqStatus status) {
return NetEq::Operation::kRfc3389CngNoPacket;
case NetEq::Mode::kCodecInternalCng: {
// Stop CNG after a timeout.
if (config_.cng_timeout_ms &&
status.generated_noise_samples >
static_cast<size_t>(*config_.cng_timeout_ms * sample_rate_khz_)) {
if (status.generated_noise_samples > kCngTimeoutMs * sample_rate_khz_) {
return NetEq::Operation::kExpand;
}
return NetEq::Operation::kCodecInternalCng;
@ -307,42 +267,20 @@ NetEq::Operation DecisionLogic::ExpectedPacketAvailable(
NetEqController::NetEqStatus status) {
if (!disallow_time_stretching_ && status.last_mode != NetEq::Mode::kExpand &&
!status.play_dtmf) {
if (config_.enable_stable_delay_mode) {
const int playout_delay_ms = GetPlayoutDelayMs(status);
const int64_t low_limit = TargetLevelMs();
const int64_t high_limit = low_limit +
packet_arrival_history_->GetMaxDelayMs() +
kDelayAdjustmentGranularityMs;
if (playout_delay_ms >= high_limit * 4) {
return NetEq::Operation::kFastAccelerate;
const int playout_delay_ms = GetPlayoutDelayMs(status);
const int64_t low_limit = TargetLevelMs();
const int64_t high_limit = low_limit +
packet_arrival_history_->GetMaxDelayMs() +
kDelayAdjustmentGranularityMs;
if (playout_delay_ms >= high_limit * 4) {
return NetEq::Operation::kFastAccelerate;
}
if (TimescaleAllowed()) {
if (playout_delay_ms >= high_limit) {
return NetEq::Operation::kAccelerate;
}
if (TimescaleAllowed()) {
if (playout_delay_ms >= high_limit) {
return NetEq::Operation::kAccelerate;
}
if (playout_delay_ms < low_limit) {
return NetEq::Operation::kPreemptiveExpand;
}
}
} else {
const int target_level_samples = TargetLevelMs() * sample_rate_khz_;
const int low_limit = std::max(
target_level_samples * 3 / 4,
target_level_samples -
config_.deceleration_target_level_offset_ms * sample_rate_khz_);
const int high_limit = std::max(
target_level_samples,
low_limit + kDelayAdjustmentGranularityMs * sample_rate_khz_);
const int buffer_level_samples =
buffer_level_filter_->filtered_current_level();
if (buffer_level_samples >= high_limit * 4)
return NetEq::Operation::kFastAccelerate;
if (TimescaleAllowed()) {
if (buffer_level_samples >= high_limit)
return NetEq::Operation::kAccelerate;
if (buffer_level_samples < low_limit)
return NetEq::Operation::kPreemptiveExpand;
if (playout_delay_ms < low_limit) {
return NetEq::Operation::kPreemptiveExpand;
}
}
}
@ -354,35 +292,20 @@ NetEq::Operation DecisionLogic::FuturePacketAvailable(
// Required packet is not available, but a future packet is.
// Check if we should continue with an ongoing concealment because the new
// packet is too far into the future.
if (config_.combine_concealment_decision || IsCng(status.last_mode)) {
const int buffer_delay_samples =
config_.combine_concealment_decision
? status.packet_buffer_info.span_samples_wait_time
: status.packet_buffer_info.span_samples;
const int buffer_delay_ms = buffer_delay_samples / sample_rate_khz_;
const int high_limit = TargetLevelMs() + kTargetLevelWindowMs / 2;
const int low_limit =
std::max(0, TargetLevelMs() - kTargetLevelWindowMs / 2);
const bool above_target_delay = buffer_delay_ms > high_limit;
const bool below_target_delay = buffer_delay_ms < low_limit;
if ((PacketTooEarly(status) && !above_target_delay) ||
(below_target_delay && !config_.combine_concealment_decision)) {
return NoPacket(status);
}
uint32_t timestamp_leap =
status.next_packet->timestamp - status.target_timestamp;
if (config_.combine_concealment_decision) {
if (timestamp_leap != status.generated_noise_samples) {
// The delay was adjusted, reinitialize the buffer level filter.
buffer_level_filter_->SetFilteredBufferLevel(buffer_delay_samples);
}
} else {
time_stretched_cn_samples_ =
timestamp_leap - status.generated_noise_samples;
}
} else if (IsExpand(status.last_mode) && ShouldContinueExpand(status)) {
const int buffer_delay_samples =
status.packet_buffer_info.span_samples_wait_time;
const int buffer_delay_ms = buffer_delay_samples / sample_rate_khz_;
const int high_limit = TargetLevelMs() + kTargetLevelWindowMs / 2;
const bool above_target_delay = buffer_delay_ms > high_limit;
if ((PacketTooEarly(status) && !above_target_delay)) {
return NoPacket(status);
}
uint32_t timestamp_leap =
status.next_packet->timestamp - status.target_timestamp;
if (timestamp_leap != status.generated_noise_samples) {
// The delay was adjusted, reinitialize the buffer level filter.
buffer_level_filter_->SetFilteredBufferLevel(buffer_delay_samples);
}
// Time to play the next packet.
switch (status.last_mode) {
@ -409,9 +332,7 @@ bool DecisionLogic::PostponeDecode(NetEqController::NetEqStatus status) const {
const size_t min_buffer_level_samples =
TargetLevelMs() * sample_rate_khz_ * kPostponeDecodingLevel / 100;
const size_t buffer_level_samples =
config_.combine_concealment_decision
? status.packet_buffer_info.span_samples_wait_time
: status.packet_buffer_info.span_samples;
status.packet_buffer_info.span_samples_wait_time;
if (buffer_level_samples >= min_buffer_level_samples) {
return false;
}
@ -421,7 +342,7 @@ bool DecisionLogic::PostponeDecode(NetEqController::NetEqStatus status) const {
return false;
}
// Continue CNG until the buffer is at least at the minimum level.
if (config_.combine_concealment_decision && IsCng(status.last_mode)) {
if (IsCng(status.last_mode)) {
return true;
}
// Only continue expand if the mute factor is low enough (otherwise the
@ -433,32 +354,12 @@ bool DecisionLogic::PostponeDecode(NetEqController::NetEqStatus status) const {
return false;
}
bool DecisionLogic::ReinitAfterExpands(
NetEqController::NetEqStatus status) const {
const uint32_t timestamp_leap =
status.next_packet->timestamp - status.target_timestamp;
return timestamp_leap >=
static_cast<uint32_t>(kReinitAfterExpandsMs * sample_rate_khz_);
}
bool DecisionLogic::PacketTooEarly(NetEqController::NetEqStatus status) const {
const uint32_t timestamp_leap =
status.next_packet->timestamp - status.target_timestamp;
return timestamp_leap > status.generated_noise_samples;
}
bool DecisionLogic::MaxWaitForPacket(
NetEqController::NetEqStatus status) const {
return status.generated_noise_samples >=
static_cast<size_t>(kMaxWaitForPacketMs * sample_rate_khz_);
}
bool DecisionLogic::ShouldContinueExpand(
NetEqController::NetEqStatus status) const {
return !ReinitAfterExpands(status) && !MaxWaitForPacket(status) &&
PacketTooEarly(status) && UnderTargetLevel();
}
int DecisionLogic::GetPlayoutDelayMs(
NetEqController::NetEqStatus status) const {
uint32_t playout_timestamp =

View File

@ -140,29 +140,12 @@ class DecisionLogic : public NetEqController {
// level, even though the next packet is available.
bool PostponeDecode(NetEqController::NetEqStatus status) const;
// Checks if the timestamp leap is so long into the future that a reset due
// to exceeding the expand limit will be done.
bool ReinitAfterExpands(NetEqController::NetEqStatus status) const;
// Checks if we still have not done enough expands to cover the distance from
// the last decoded packet to the next available packet.
bool PacketTooEarly(NetEqController::NetEqStatus status) const;
bool MaxWaitForPacket(NetEqController::NetEqStatus status) const;
bool ShouldContinueExpand(NetEqController::NetEqStatus status) const;
int GetPlayoutDelayMs(NetEqController::NetEqStatus status) const;
// Runtime configurable options through field trial
// WebRTC-Audio-NetEqDecisionLogicConfig.
struct Config {
Config();
bool enable_stable_delay_mode = true;
bool combine_concealment_decision = true;
int deceleration_target_level_offset_ms = 85;
int packet_history_size_ms = 2000;
absl::optional<int> cng_timeout_ms = 1000;
};
Config config_;
std::unique_ptr<DelayManager> delay_manager_;
std::unique_ptr<BufferLevelFilter> buffer_level_filter_;
std::unique_ptr<PacketArrivalHistory> packet_arrival_history_;