diff --git a/modules/audio_coding/neteq/decision_logic.cc b/modules/audio_coding/neteq/decision_logic.cc index 144b4e7e9e..32b9f1c95a 100644 --- a/modules/audio_coding/neteq/decision_logic.cc +++ b/modules/audio_coding/neteq/decision_logic.cc @@ -38,6 +38,7 @@ 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; std::unique_ptr CreateDelayManager( const NetEqController::Config& neteq_config) { @@ -68,10 +69,10 @@ bool IsExpand(NetEq::Mode mode) { DecisionLogic::Config::Config() { StructParametersParser::Create( - "enable_stable_playout_delay", &enable_stable_playout_delay, // - "reinit_after_expand_ms", &reinit_after_expand_ms, // - "packet_history_size_ms", &packet_history_size_ms, // - "cng_timeout_ms", &cng_timeout_ms, // + "enable_stable_playout_delay", &enable_stable_playout_delay, // + "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( @@ -79,7 +80,8 @@ DecisionLogic::Config::Config() { RTC_LOG(LS_INFO) << "NetEq decision logic config:" << " enable_stable_playout_delay=" << enable_stable_playout_delay - << " reinit_after_expand_ms=" << reinit_after_expand_ms + << " 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=" @@ -137,7 +139,8 @@ NetEq::Operation DecisionLogic::GetDecision(const NetEqStatus& status, if (prev_time_scale_) { timescale_countdown_ = tick_timer_->GetNewCountdown(kMinTimescaleInterval); } - if (!IsCng(status.last_mode)) { + if (!IsCng(status.last_mode) && + !(config_.combine_concealment_decision && IsExpand(status.last_mode))) { FilterBufferLevel(status.packet_buffer_info.span_samples); } @@ -162,29 +165,15 @@ NetEq::Operation DecisionLogic::GetDecision(const NetEqStatus& status, // If the expand period was very long, reset NetEQ since it is likely that the // sender was restarted. - if (IsExpand(status.last_mode) && + if (!config_.combine_concealment_decision && IsExpand(status.last_mode) && status.generated_noise_samples > - static_cast(config_.reinit_after_expand_ms * - sample_rate_khz_)) { + static_cast(kReinitAfterExpandsMs * sample_rate_khz_)) { *reset_decoder = true; return NetEq::Operation::kNormal; } - // Make sure we don't restart audio too soon after an expansion to avoid - // running out of data right away again. We should only wait if there are no - // DTX or CNG packets in the buffer (otherwise we should just play out what we - // have, since we cannot know the exact duration of DTX or CNG packets), and - // if the mute factor is low enough (otherwise the expansion was short enough - // to not be noticable). - // Note that the MuteFactor is in Q14, so a value of 16384 corresponds to 1. - const int target_level_samples = TargetLevelMs() * sample_rate_khz_; - if (!config_.enable_stable_playout_delay && IsExpand(status.last_mode) && - status.expand_mutefactor < 16384 / 2 && - status.packet_buffer_info.span_samples < - static_cast(target_level_samples * kPostponeDecodingLevel / - 100) && - !status.packet_buffer_info.dtx_or_cng) { - return NetEq::Operation::kExpand; + if (PostponeDecode(status)) { + return NoPacket(status); } const uint32_t five_seconds_samples = @@ -365,23 +354,31 @@ NetEq::Operation DecisionLogic::ExpectedPacketAvailable( NetEq::Operation DecisionLogic::FuturePacketAvailable( NetEqController::NetEqStatus status) { // Required packet is not available, but a future packet is. - // Check if we should continue with an ongoing expand because the new packet - // is too far into the future. - if (IsExpand(status.last_mode) && ShouldContinueExpand(status)) { - return NoPacket(status); - } - - if (IsCng(status.last_mode)) { - int playout_delay_ms = GetNextPacketDelayMs(status); - const bool above_target_delay = playout_delay_ms > HighThresholdCng(); - const bool below_target_delay = playout_delay_ms < LowThresholdCng(); - if ((PacketTooEarly(status) && !above_target_delay) || below_target_delay) { + // 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_ms = + status.packet_buffer_info.span_samples / sample_rate_khz_; + const bool above_target_delay = buffer_delay_ms > HighThresholdCng(); + const bool below_target_delay = buffer_delay_ms < LowThresholdCng(); + 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; - time_stretched_cn_samples_ = - timestamp_leap - status.generated_noise_samples; + 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( + status.packet_buffer_info.span_samples); + } + } else { + time_stretched_cn_samples_ = + timestamp_leap - status.generated_noise_samples; + } + } else if (IsExpand(status.last_mode) && ShouldContinueExpand(status)) { + return NoPacket(status); } // Time to play the next packet. @@ -403,13 +400,38 @@ bool DecisionLogic::UnderTargetLevel() const { TargetLevelMs() * sample_rate_khz_; } +bool DecisionLogic::PostponeDecode(NetEqController::NetEqStatus status) const { + // Make sure we don't restart audio too soon after CNG or expand to avoid + // running out of data right away again. + const size_t min_buffer_level_samples = + TargetLevelMs() * sample_rate_khz_ * kPostponeDecodingLevel / 100; + if (status.packet_buffer_info.span_samples >= min_buffer_level_samples) { + return false; + } + // Don't postpone decoding if there is a future DTX packet in the packet + // buffer. + if (status.packet_buffer_info.dtx_or_cng) { + return false; + } + // Continue CNG until the buffer is at least at the minimum level. + if (config_.combine_concealment_decision && IsCng(status.last_mode)) { + return true; + } + // Only continue expand if the mute factor is low enough (otherwise the + // expansion was short enough to not be noticable). Note that the MuteFactor + // is in Q14, so a value of 16384 corresponds to 1. + if (IsExpand(status.last_mode) && status.expand_mutefactor < 16384 / 2) { + return true; + } + 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(config_.reinit_after_expand_ms * - sample_rate_khz_); + static_cast(kReinitAfterExpandsMs * sample_rate_khz_); } bool DecisionLogic::PacketTooEarly(NetEqController::NetEqStatus status) const { diff --git a/modules/audio_coding/neteq/decision_logic.h b/modules/audio_coding/neteq/decision_logic.h index 008679d07f..2f885d96ea 100644 --- a/modules/audio_coding/neteq/decision_logic.h +++ b/modules/audio_coding/neteq/decision_logic.h @@ -134,8 +134,12 @@ class DecisionLogic : public NetEqController { // Checks if the current (filtered) buffer level is under the target level. bool UnderTargetLevel() const; + // Checks if an ongoing concealment should be continued due to low buffer + // 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 `reinit_after_expand_ms` will be done. + // 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 @@ -157,7 +161,7 @@ class DecisionLogic : public NetEqController { Config(); bool enable_stable_playout_delay = false; - int reinit_after_expand_ms = 1000; + bool combine_concealment_decision = false; int deceleration_target_level_offset_ms = 85; int packet_history_size_ms = 2000; absl::optional cng_timeout_ms;