Revert "NetEq: Deprecate playout modes Fax, Off and Streaming"

This reverts commit 80c4cca4915dbc6094a5bfae749f85f7371eadd1.

Reason for revert: Breaks downstream tests.

Original change's description:
> NetEq: Deprecate playout modes Fax, Off and Streaming
> 
> The playout modes other than Normal have not been reachable for a long
> time, other than through tests. It is time to deprecate them.
> 
> The only meaningful use was that Fax mode was sometimes set from
> tests, in order to avoid time-stretching operations (accelerate and
> pre-emptive expand) from messing with the test results. With this CL,
> a new config is added instead, which lets the user specify exactly
> this: don't do time-stretching.
> 
> As a result of Fax and Off modes being removed, the following code
> clean-up was done:
> - Fold DecisionLogicNormal into DecisionLogic.
> - Remove AudioRepetition and AlternativePlc operations, since they can
>   no longer be reached.
> 
> Bug: webrtc:9421
> Change-Id: I651458e9c1931a99f3b07e242817d303bac119df
> Reviewed-on: https://webrtc-review.googlesource.com/84123
> Commit-Queue: Henrik Lundin <henrik.lundin@webrtc.org>
> Reviewed-by: Ivo Creusen <ivoc@webrtc.org>
> Reviewed-by: Minyue Li <minyue@webrtc.org>
> Cr-Commit-Position: refs/heads/master@{#23704}

TBR=henrik.lundin@webrtc.org,ivoc@webrtc.org,minyue@webrtc.org

Change-Id: I555aae8850fc4ac1ea919bfa72c11b5218066f30
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: webrtc:9421
Reviewed-on: https://webrtc-review.googlesource.com/84680
Reviewed-by: Henrik Lundin <henrik.lundin@webrtc.org>
Commit-Queue: Henrik Lundin <henrik.lundin@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23706}
diff --git a/modules/audio_coding/BUILD.gn b/modules/audio_coding/BUILD.gn
index 38cce73..fb587c5 100644
--- a/modules/audio_coding/BUILD.gn
+++ b/modules/audio_coding/BUILD.gn
@@ -1057,6 +1057,10 @@
     "neteq/cross_correlation.h",
     "neteq/decision_logic.cc",
     "neteq/decision_logic.h",
+    "neteq/decision_logic_fax.cc",
+    "neteq/decision_logic_fax.h",
+    "neteq/decision_logic_normal.cc",
+    "neteq/decision_logic_normal.h",
     "neteq/decoder_database.cc",
     "neteq/decoder_database.h",
     "neteq/defines.h",
diff --git a/modules/audio_coding/acm2/acm_receiver_unittest.cc b/modules/audio_coding/acm2/acm_receiver_unittest.cc
index 457ea1d..d1cff23 100644
--- a/modules/audio_coding/acm2/acm_receiver_unittest.cc
+++ b/modules/audio_coding/acm2/acm_receiver_unittest.cc
@@ -292,7 +292,7 @@
 class AcmReceiverTestFaxModeOldApi : public AcmReceiverTestOldApi {
  protected:
   AcmReceiverTestFaxModeOldApi() {
-    config_.neteq_config.for_test_no_time_stretching = true;
+    config_.neteq_config.playout_mode = kPlayoutFax;
   }
 
   void RunVerifyAudioFrame(RentACodec::CodecId codec_id) {
@@ -301,7 +301,7 @@
     // timestamp increments predictable; in normal mode, NetEq may decide to do
     // accelerate or pre-emptive expand operations after some time, offsetting
     // the timestamp.
-    EXPECT_TRUE(config_.neteq_config.for_test_no_time_stretching);
+    EXPECT_EQ(kPlayoutFax, config_.neteq_config.playout_mode);
 
     const RentACodec::CodecId kCodecId[] = {codec_id};
     AddSetOfCodecs(kCodecId);
diff --git a/modules/audio_coding/neteq/decision_logic.cc b/modules/audio_coding/neteq/decision_logic.cc
index afe25e1..cc58f04 100644
--- a/modules/audio_coding/neteq/decision_logic.cc
+++ b/modules/audio_coding/neteq/decision_logic.cc
@@ -10,37 +10,47 @@
 
 #include "modules/audio_coding/neteq/decision_logic.h"
 
-#include <assert.h>
 #include <algorithm>
-#include <limits>
 
 #include "modules/audio_coding/neteq/buffer_level_filter.h"
-#include "modules/audio_coding/neteq/decoder_database.h"
+#include "modules/audio_coding/neteq/decision_logic_fax.h"
+#include "modules/audio_coding/neteq/decision_logic_normal.h"
 #include "modules/audio_coding/neteq/delay_manager.h"
 #include "modules/audio_coding/neteq/expand.h"
 #include "modules/audio_coding/neteq/packet_buffer.h"
 #include "modules/audio_coding/neteq/sync_buffer.h"
 #include "modules/include/module_common_types.h"
-#include "system_wrappers/include/field_trial.h"
 
 namespace webrtc {
 
 DecisionLogic* DecisionLogic::Create(int fs_hz,
                                      size_t output_size_samples,
-                                     bool disallow_time_stretching,
+                                     NetEqPlayoutMode playout_mode,
                                      DecoderDatabase* decoder_database,
                                      const PacketBuffer& packet_buffer,
                                      DelayManager* delay_manager,
                                      BufferLevelFilter* buffer_level_filter,
                                      const TickTimer* tick_timer) {
-  return new DecisionLogic(fs_hz, output_size_samples, disallow_time_stretching,
-                           decoder_database, packet_buffer, delay_manager,
-                           buffer_level_filter, tick_timer);
+  switch (playout_mode) {
+    case kPlayoutOn:
+    case kPlayoutStreaming:
+      return new DecisionLogicNormal(
+          fs_hz, output_size_samples, playout_mode, decoder_database,
+          packet_buffer, delay_manager, buffer_level_filter, tick_timer);
+    case kPlayoutFax:
+    case kPlayoutOff:
+      return new DecisionLogicFax(
+          fs_hz, output_size_samples, playout_mode, decoder_database,
+          packet_buffer, delay_manager, buffer_level_filter, tick_timer);
+  }
+  // This line cannot be reached, but must be here to avoid compiler errors.
+  assert(false);
+  return NULL;
 }
 
 DecisionLogic::DecisionLogic(int fs_hz,
                              size_t output_size_samples,
-                             bool disallow_time_stretching,
+                             NetEqPlayoutMode playout_mode,
                              DecoderDatabase* decoder_database,
                              const PacketBuffer& packet_buffer,
                              DelayManager* delay_manager,
@@ -55,13 +65,11 @@
       packet_length_samples_(0),
       sample_memory_(0),
       prev_time_scale_(false),
-      disallow_time_stretching_(disallow_time_stretching),
       timescale_countdown_(
           tick_timer_->GetNewCountdown(kMinTimescaleInterval + 1)),
       num_consecutive_expands_(0),
-      postpone_decoding_after_expand_(field_trial::IsEnabled(
-          "WebRTC-Audio-NetEqPostponeDecodingAfterExpand")) {
-  delay_manager_->set_streaming_mode(false);
+      playout_mode_(playout_mode) {
+  delay_manager_->set_streaming_mode(playout_mode_ == kPlayoutStreaming);
   SetSampleRate(fs_hz, output_size_samples);
 }
 
@@ -160,228 +168,4 @@
   }
 }
 
-Operations DecisionLogic::GetDecisionSpecialized(const SyncBuffer& sync_buffer,
-                                                 const Expand& expand,
-                                                 size_t decoder_frame_length,
-                                                 const Packet* next_packet,
-                                                 Modes prev_mode,
-                                                 bool play_dtmf,
-                                                 bool* reset_decoder,
-                                                 size_t generated_noise_samples,
-                                                 size_t cur_size_samples) {
-  // Guard for errors, to avoid getting stuck in error mode.
-  if (prev_mode == kModeError) {
-    if (!next_packet) {
-      return kExpand;
-    } else {
-      return kUndefined;  // Use kUndefined to flag for a reset.
-    }
-  }
-
-  uint32_t target_timestamp = sync_buffer.end_timestamp();
-  uint32_t available_timestamp = 0;
-  bool is_cng_packet = false;
-  if (next_packet) {
-    available_timestamp = next_packet->timestamp;
-    is_cng_packet =
-        decoder_database_->IsComfortNoise(next_packet->payload_type);
-  }
-
-  if (is_cng_packet) {
-    return CngOperation(prev_mode, target_timestamp, available_timestamp,
-                        generated_noise_samples);
-  }
-
-  // Handle the case with no packet at all available (except maybe DTMF).
-  if (!next_packet) {
-    return NoPacket(play_dtmf);
-  }
-
-  // If the expand period was very long, reset NetEQ since it is likely that the
-  // sender was restarted.
-  if (num_consecutive_expands_ > kReinitAfterExpands) {
-    *reset_decoder = true;
-    return 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.
-  if (postpone_decoding_after_expand_ && prev_mode == kModeExpand &&
-      !packet_buffer_.ContainsDtxOrCngPacket(decoder_database_) &&
-      cur_size_samples<static_cast<size_t>(delay_manager_->TargetLevel() *
-                                           packet_length_samples_)>> 8 &&
-      expand.MuteFactor(0) < 16384 / 2) {
-    return kExpand;
-  }
-
-  const uint32_t five_seconds_samples =
-      static_cast<uint32_t>(5 * 8000 * fs_mult_);
-  // Check if the required packet is available.
-  if (target_timestamp == available_timestamp) {
-    return ExpectedPacketAvailable(prev_mode, play_dtmf);
-  } else if (!PacketBuffer::IsObsoleteTimestamp(
-                 available_timestamp, target_timestamp, five_seconds_samples)) {
-    return FuturePacketAvailable(
-        sync_buffer, expand, decoder_frame_length, prev_mode, target_timestamp,
-        available_timestamp, play_dtmf, generated_noise_samples);
-  } else {
-    // This implies that available_timestamp < target_timestamp, which can
-    // happen when a new stream or codec is received. Signal for a reset.
-    return kUndefined;
-  }
-}
-
-Operations DecisionLogic::CngOperation(Modes prev_mode,
-                                       uint32_t target_timestamp,
-                                       uint32_t available_timestamp,
-                                       size_t generated_noise_samples) {
-  // Signed difference between target and available timestamp.
-  int32_t timestamp_diff = static_cast<int32_t>(
-      static_cast<uint32_t>(generated_noise_samples + target_timestamp) -
-      available_timestamp);
-  int32_t optimal_level_samp = static_cast<int32_t>(
-      (delay_manager_->TargetLevel() * packet_length_samples_) >> 8);
-  const int64_t excess_waiting_time_samp =
-      -static_cast<int64_t>(timestamp_diff) - optimal_level_samp;
-
-  if (excess_waiting_time_samp > optimal_level_samp / 2) {
-    // The waiting time for this packet will be longer than 1.5
-    // times the wanted buffer delay. Apply fast-forward to cut the
-    // waiting time down to the optimal.
-    noise_fast_forward_ = rtc::dchecked_cast<size_t>(noise_fast_forward_ +
-                                                     excess_waiting_time_samp);
-    timestamp_diff =
-        rtc::saturated_cast<int32_t>(timestamp_diff + excess_waiting_time_samp);
-  }
-
-  if (timestamp_diff < 0 && prev_mode == kModeRfc3389Cng) {
-    // Not time to play this packet yet. Wait another round before using this
-    // packet. Keep on playing CNG from previous CNG parameters.
-    return kRfc3389CngNoPacket;
-  } else {
-    // Otherwise, go for the CNG packet now.
-    noise_fast_forward_ = 0;
-    return kRfc3389Cng;
-  }
-}
-
-Operations DecisionLogic::NoPacket(bool play_dtmf) {
-  if (cng_state_ == kCngRfc3389On) {
-    // Keep on playing comfort noise.
-    return kRfc3389CngNoPacket;
-  } else if (cng_state_ == kCngInternalOn) {
-    // Keep on playing codec internal comfort noise.
-    return kCodecInternalCng;
-  } else if (play_dtmf) {
-    return kDtmf;
-  } else {
-    // Nothing to play, do expand.
-    return kExpand;
-  }
-}
-
-Operations DecisionLogic::ExpectedPacketAvailable(Modes prev_mode,
-                                                  bool play_dtmf) {
-  if (!disallow_time_stretching_ && prev_mode != kModeExpand && !play_dtmf) {
-    // Check criterion for time-stretching.
-    int low_limit, high_limit;
-    delay_manager_->BufferLimits(&low_limit, &high_limit);
-    if (buffer_level_filter_->filtered_current_level() >= high_limit << 2)
-      return kFastAccelerate;
-    if (TimescaleAllowed()) {
-      if (buffer_level_filter_->filtered_current_level() >= high_limit)
-        return kAccelerate;
-      if (buffer_level_filter_->filtered_current_level() < low_limit)
-        return kPreemptiveExpand;
-    }
-  }
-  return kNormal;
-}
-
-Operations DecisionLogic::FuturePacketAvailable(
-    const SyncBuffer& sync_buffer,
-    const Expand& expand,
-    size_t decoder_frame_length,
-    Modes prev_mode,
-    uint32_t target_timestamp,
-    uint32_t available_timestamp,
-    bool play_dtmf,
-    size_t generated_noise_samples) {
-  // 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.
-  uint32_t timestamp_leap = available_timestamp - target_timestamp;
-  if ((prev_mode == kModeExpand) && !ReinitAfterExpands(timestamp_leap) &&
-      !MaxWaitForPacket() && PacketTooEarly(timestamp_leap) &&
-      UnderTargetLevel()) {
-    if (play_dtmf) {
-      // Still have DTMF to play, so do not do expand.
-      return kDtmf;
-    } else {
-      // Nothing to play.
-      return kExpand;
-    }
-  }
-
-  const size_t samples_left =
-      sync_buffer.FutureLength() - expand.overlap_length();
-  const size_t cur_size_samples =
-      samples_left + packet_buffer_.NumPacketsInBuffer() * decoder_frame_length;
-
-  // If previous was comfort noise, then no merge is needed.
-  if (prev_mode == kModeRfc3389Cng || prev_mode == kModeCodecInternalCng) {
-    // Keep the same delay as before the CNG, but make sure that the number of
-    // samples in buffer is no higher than 4 times the optimal level. (Note that
-    // TargetLevel() is in Q8.)
-    if (static_cast<uint32_t>(generated_noise_samples + target_timestamp) >=
-            available_timestamp ||
-        cur_size_samples >
-            ((delay_manager_->TargetLevel() * packet_length_samples_) >> 8) *
-                4) {
-      // Time to play this new packet.
-      return kNormal;
-    } else {
-      // Too early to play this new packet; keep on playing comfort noise.
-      if (prev_mode == kModeRfc3389Cng) {
-        return kRfc3389CngNoPacket;
-      } else {  // prevPlayMode == kModeCodecInternalCng.
-        return kCodecInternalCng;
-      }
-    }
-  }
-  // Do not merge unless we have done an expand before.
-  if (prev_mode == kModeExpand) {
-    return kMerge;
-  } else if (play_dtmf) {
-    // Play DTMF instead of expand.
-    return kDtmf;
-  } else {
-    return kExpand;
-  }
-}
-
-bool DecisionLogic::UnderTargetLevel() const {
-  return buffer_level_filter_->filtered_current_level() <=
-         delay_manager_->TargetLevel();
-}
-
-bool DecisionLogic::ReinitAfterExpands(uint32_t timestamp_leap) const {
-  return timestamp_leap >=
-         static_cast<uint32_t>(output_size_samples_ * kReinitAfterExpands);
-}
-
-bool DecisionLogic::PacketTooEarly(uint32_t timestamp_leap) const {
-  return timestamp_leap >
-         static_cast<uint32_t>(output_size_samples_ * num_consecutive_expands_);
-}
-
-bool DecisionLogic::MaxWaitForPacket() const {
-  return num_consecutive_expands_ >= kMaxWaitForPacket;
-}
-
 }  // namespace webrtc
diff --git a/modules/audio_coding/neteq/decision_logic.h b/modules/audio_coding/neteq/decision_logic.h
index 4914123..d23aa74 100644
--- a/modules/audio_coding/neteq/decision_logic.h
+++ b/modules/audio_coding/neteq/decision_logic.h
@@ -28,34 +28,32 @@
 class SyncBuffer;
 struct Packet;
 
-// This is the class for the decision tree implementation.
+// This is the base class for the decision tree implementations. Derived classes
+// must implement the method GetDecisionSpecialized().
 class DecisionLogic {
  public:
   // Static factory function which creates different types of objects depending
   // on the |playout_mode|.
   static DecisionLogic* Create(int fs_hz,
                                size_t output_size_samples,
-                               bool disallow_time_stretching,
+                               NetEqPlayoutMode playout_mode,
                                DecoderDatabase* decoder_database,
                                const PacketBuffer& packet_buffer,
                                DelayManager* delay_manager,
                                BufferLevelFilter* buffer_level_filter,
                                const TickTimer* tick_timer);
 
-  static const int kReinitAfterExpands = 100;
-  static const int kMaxWaitForPacket = 10;
-
   // Constructor.
   DecisionLogic(int fs_hz,
                 size_t output_size_samples,
-                bool disallow_time_stretching,
+                NetEqPlayoutMode playout_mode,
                 DecoderDatabase* decoder_database,
                 const PacketBuffer& packet_buffer,
                 DelayManager* delay_manager,
                 BufferLevelFilter* buffer_level_filter,
                 const TickTimer* tick_timer);
 
-  ~DecisionLogic();
+  virtual ~DecisionLogic();
 
   // Resets object to a clean state.
   void Reset();
@@ -96,7 +94,7 @@
   // not. Note that this is necessary, since an expand decision can be changed
   // to kNormal in NetEqImpl::GetDecision if there is still enough data in the
   // sync buffer.
-  void ExpandDecision(Operations operation);
+  virtual void ExpandDecision(Operations operation);
 
   // Adds |value| to |sample_memory_|.
   void AddSampleMemory(int32_t value) { sample_memory_ += value; }
@@ -109,17 +107,14 @@
     packet_length_samples_ = value;
   }
   void set_prev_time_scale(bool value) { prev_time_scale_ = value; }
+  NetEqPlayoutMode playout_mode() const { return playout_mode_; }
 
- private:
+ protected:
   // The value 5 sets maximum time-stretch rate to about 100 ms/s.
   static const int kMinTimescaleInterval = 5;
 
   enum CngState { kCngOff, kCngRfc3389On, kCngInternalOn };
 
-  // Updates the |buffer_level_filter_| with the current buffer level
-  // |buffer_size_packets|.
-  void FilterBufferLevel(size_t buffer_size_packets, Modes prev_mode);
-
   // Returns the operation that should be done next. |sync_buffer| and |expand|
   // are provided for reference. |decoder_frame_length| is the number of samples
   // obtained from the last decoded frame. If there is a packet available, it
@@ -128,63 +123,20 @@
   // |prev_mode|. If there is a DTMF event to play, |play_dtmf| should be set to
   // true. The output variable |reset_decoder| will be set to true if a reset is
   // required; otherwise it is left unchanged (i.e., it can remain true if it
-  // was true before the call).
-  // TODO(henrik.lundin) Fold this method into GetDecision.
-  Operations GetDecisionSpecialized(const SyncBuffer& sync_buffer,
-                                    const Expand& expand,
-                                    size_t decoder_frame_length,
-                                    const Packet* next_packet,
-                                    Modes prev_mode,
-                                    bool play_dtmf,
-                                    bool* reset_decoder,
-                                    size_t generated_noise_samples,
-                                    size_t cur_size_samples);
+  // was true before the call).  Should be implemented by derived classes.
+  virtual Operations GetDecisionSpecialized(const SyncBuffer& sync_buffer,
+                                            const Expand& expand,
+                                            size_t decoder_frame_length,
+                                            const Packet* next_packet,
+                                            Modes prev_mode,
+                                            bool play_dtmf,
+                                            bool* reset_decoder,
+                                            size_t generated_noise_samples,
+                                            size_t cur_size_samples) = 0;
 
-  // Returns the operation given that the next available packet is a comfort
-  // noise payload (RFC 3389 only, not codec-internal).
-  Operations CngOperation(Modes prev_mode,
-                          uint32_t target_timestamp,
-                          uint32_t available_timestamp,
-                          size_t generated_noise_samples);
-
-  // Returns the operation given that no packets are available (except maybe
-  // a DTMF event, flagged by setting |play_dtmf| true).
-  Operations NoPacket(bool play_dtmf);
-
-  // Returns the operation to do given that the expected packet is available.
-  Operations ExpectedPacketAvailable(Modes prev_mode, bool play_dtmf);
-
-  // Returns the operation to do given that the expected packet is not
-  // available, but a packet further into the future is at hand.
-  Operations FuturePacketAvailable(const SyncBuffer& sync_buffer,
-                                   const Expand& expand,
-                                   size_t decoder_frame_length,
-                                   Modes prev_mode,
-                                   uint32_t target_timestamp,
-                                   uint32_t available_timestamp,
-                                   bool play_dtmf,
-                                   size_t generated_noise_samples);
-
-  // Checks if enough time has elapsed since the last successful timescale
-  // operation was done (i.e., accelerate or preemptive expand).
-  bool TimescaleAllowed() const {
-    return !timescale_countdown_ || timescale_countdown_->Finished();
-  }
-
-  // Checks if the current (filtered) buffer level is under the target level.
-  bool UnderTargetLevel() const;
-
-  // Checks if |timestamp_leap| is so long into the future that a reset due
-  // to exceeding kReinitAfterExpands will be done.
-  bool ReinitAfterExpands(uint32_t timestamp_leap) const;
-
-  // Checks if we still have not done enough expands to cover the distance from
-  // the last decoded packet to the next available packet, the distance beeing
-  // conveyed in |timestamp_leap|.
-  bool PacketTooEarly(uint32_t timestamp_leap) const;
-
-  // Checks if num_consecutive_expands_ >= kMaxWaitForPacket.
-  bool MaxWaitForPacket() const;
+  // Updates the |buffer_level_filter_| with the current buffer level
+  // |buffer_size_packets|.
+  void FilterBufferLevel(size_t buffer_size_packets, Modes prev_mode);
 
   DecoderDatabase* decoder_database_;
   const PacketBuffer& packet_buffer_;
@@ -199,11 +151,11 @@
   size_t packet_length_samples_;
   int sample_memory_;
   bool prev_time_scale_;
-  bool disallow_time_stretching_;
   std::unique_ptr<TickTimer::Countdown> timescale_countdown_;
   int num_consecutive_expands_;
-  const bool postpone_decoding_after_expand_;
+  const NetEqPlayoutMode playout_mode_;
 
+ private:
   RTC_DISALLOW_COPY_AND_ASSIGN(DecisionLogic);
 };
 
diff --git a/modules/audio_coding/neteq/decision_logic_fax.cc b/modules/audio_coding/neteq/decision_logic_fax.cc
new file mode 100644
index 0000000..0f904bb
--- /dev/null
+++ b/modules/audio_coding/neteq/decision_logic_fax.cc
@@ -0,0 +1,103 @@
+/*
+ *  Copyright (c) 2013 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/decision_logic_fax.h"
+
+#include <assert.h>
+
+#include <algorithm>
+
+#include "modules/audio_coding/neteq/decoder_database.h"
+#include "modules/audio_coding/neteq/sync_buffer.h"
+
+namespace webrtc {
+
+Operations DecisionLogicFax::GetDecisionSpecialized(
+    const SyncBuffer& sync_buffer,
+    const Expand& expand,
+    size_t decoder_frame_length,
+    const Packet* next_packet,
+    Modes prev_mode,
+    bool play_dtmf,
+    bool* reset_decoder,
+    size_t generated_noise_samples,
+    size_t /*cur_size_samples*/) {
+  assert(playout_mode_ == kPlayoutFax || playout_mode_ == kPlayoutOff);
+  uint32_t target_timestamp = sync_buffer.end_timestamp();
+  uint32_t available_timestamp = 0;
+  int is_cng_packet = 0;
+  if (next_packet) {
+    available_timestamp = next_packet->timestamp;
+    is_cng_packet =
+        decoder_database_->IsComfortNoise(next_packet->payload_type);
+  }
+  if (is_cng_packet) {
+    if (static_cast<int32_t>((generated_noise_samples + target_timestamp) -
+                             available_timestamp) >= 0) {
+      // Time to play this packet now.
+      return kRfc3389Cng;
+    } else {
+      // Wait before playing this packet.
+      return kRfc3389CngNoPacket;
+    }
+  }
+  if (!next_packet) {
+    // No packet. If in CNG mode, play as usual. Otherwise, use other method to
+    // generate data.
+    if (cng_state_ == kCngRfc3389On) {
+      // Continue playing comfort noise.
+      return kRfc3389CngNoPacket;
+    } else if (cng_state_ == kCngInternalOn) {
+      // Continue playing codec-internal comfort noise.
+      return kCodecInternalCng;
+    } else {
+      // Nothing to play. Generate some data to play out.
+      switch (playout_mode_) {
+        case kPlayoutOff:
+          return kAlternativePlc;
+        case kPlayoutFax:
+          return kAudioRepetition;
+        default:
+          assert(false);
+          return kUndefined;
+      }
+    }
+  } else if (target_timestamp == available_timestamp) {
+    return kNormal;
+  } else {
+    if (static_cast<int32_t>((generated_noise_samples + target_timestamp) -
+                             available_timestamp) >= 0) {
+      return kNormal;
+    } else {
+      // If currently playing comfort noise, continue with that. Do not
+      // increase the timestamp counter since generated_noise_stopwatch_ in
+      // NetEqImpl will take care of the time-keeping.
+      if (cng_state_ == kCngRfc3389On) {
+        return kRfc3389CngNoPacket;
+      } else if (cng_state_ == kCngInternalOn) {
+        return kCodecInternalCng;
+      } else {
+        // Otherwise, do packet-loss concealment and increase the
+        // timestamp while waiting for the time to play this packet.
+        switch (playout_mode_) {
+          case kPlayoutOff:
+            return kAlternativePlcIncreaseTimestamp;
+          case kPlayoutFax:
+            return kAudioRepetitionIncreaseTimestamp;
+          default:
+            assert(0);
+            return kUndefined;
+        }
+      }
+    }
+  }
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_coding/neteq/decision_logic_fax.h b/modules/audio_coding/neteq/decision_logic_fax.h
new file mode 100644
index 0000000..1436f99
--- /dev/null
+++ b/modules/audio_coding/neteq/decision_logic_fax.h
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (c) 2013 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.
+ */
+
+#ifndef MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_FAX_H_
+#define MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_FAX_H_
+
+#include "modules/audio_coding/neteq/decision_logic.h"
+#include "rtc_base/constructormagic.h"
+#include "typedefs.h"  // NOLINT(build/include)
+
+namespace webrtc {
+
+// Implementation of the DecisionLogic class for playout modes kPlayoutFax and
+// kPlayoutOff.
+class DecisionLogicFax : public DecisionLogic {
+ public:
+  // Constructor.
+  DecisionLogicFax(int fs_hz,
+                   size_t output_size_samples,
+                   NetEqPlayoutMode playout_mode,
+                   DecoderDatabase* decoder_database,
+                   const PacketBuffer& packet_buffer,
+                   DelayManager* delay_manager,
+                   BufferLevelFilter* buffer_level_filter,
+                   const TickTimer* tick_timer)
+      : DecisionLogic(fs_hz,
+                      output_size_samples,
+                      playout_mode,
+                      decoder_database,
+                      packet_buffer,
+                      delay_manager,
+                      buffer_level_filter,
+                      tick_timer) {}
+
+ protected:
+  Operations GetDecisionSpecialized(const SyncBuffer& sync_buffer,
+                                    const Expand& expand,
+                                    size_t decoder_frame_length,
+                                    const Packet* next_packet,
+                                    Modes prev_mode,
+                                    bool play_dtmf,
+                                    bool* reset_decoder,
+                                    size_t generated_noise_samples,
+                                    size_t cur_size_samples) override;
+
+ private:
+  RTC_DISALLOW_COPY_AND_ASSIGN(DecisionLogicFax);
+};
+
+}  // namespace webrtc
+#endif  // MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_FAX_H_
diff --git a/modules/audio_coding/neteq/decision_logic_normal.cc b/modules/audio_coding/neteq/decision_logic_normal.cc
new file mode 100644
index 0000000..a683b8c
--- /dev/null
+++ b/modules/audio_coding/neteq/decision_logic_normal.cc
@@ -0,0 +1,253 @@
+/*
+ *  Copyright (c) 2013 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/decision_logic_normal.h"
+
+#include <assert.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "modules/audio_coding/neteq/buffer_level_filter.h"
+#include "modules/audio_coding/neteq/decoder_database.h"
+#include "modules/audio_coding/neteq/delay_manager.h"
+#include "modules/audio_coding/neteq/expand.h"
+#include "modules/audio_coding/neteq/packet_buffer.h"
+#include "modules/audio_coding/neteq/sync_buffer.h"
+
+namespace webrtc {
+
+Operations DecisionLogicNormal::GetDecisionSpecialized(
+    const SyncBuffer& sync_buffer,
+    const Expand& expand,
+    size_t decoder_frame_length,
+    const Packet* next_packet,
+    Modes prev_mode,
+    bool play_dtmf,
+    bool* reset_decoder,
+    size_t generated_noise_samples,
+    size_t cur_size_samples) {
+  assert(playout_mode_ == kPlayoutOn || playout_mode_ == kPlayoutStreaming);
+  // Guard for errors, to avoid getting stuck in error mode.
+  if (prev_mode == kModeError) {
+    if (!next_packet) {
+      return kExpand;
+    } else {
+      return kUndefined;  // Use kUndefined to flag for a reset.
+    }
+  }
+
+  uint32_t target_timestamp = sync_buffer.end_timestamp();
+  uint32_t available_timestamp = 0;
+  bool is_cng_packet = false;
+  if (next_packet) {
+    available_timestamp = next_packet->timestamp;
+    is_cng_packet =
+        decoder_database_->IsComfortNoise(next_packet->payload_type);
+  }
+
+  if (is_cng_packet) {
+    return CngOperation(prev_mode, target_timestamp, available_timestamp,
+                        generated_noise_samples);
+  }
+
+  // Handle the case with no packet at all available (except maybe DTMF).
+  if (!next_packet) {
+    return NoPacket(play_dtmf);
+  }
+
+  // If the expand period was very long, reset NetEQ since it is likely that the
+  // sender was restarted.
+  if (num_consecutive_expands_ > kReinitAfterExpands) {
+    *reset_decoder = true;
+    return 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.
+  if (postpone_decoding_after_expand_ && prev_mode == kModeExpand &&
+      !packet_buffer_.ContainsDtxOrCngPacket(decoder_database_) &&
+      cur_size_samples<static_cast<size_t>(delay_manager_->TargetLevel() *
+                                           packet_length_samples_)>> 8 &&
+      expand.MuteFactor(0) < 16384 / 2) {
+    return kExpand;
+  }
+
+  const uint32_t five_seconds_samples =
+      static_cast<uint32_t>(5 * 8000 * fs_mult_);
+  // Check if the required packet is available.
+  if (target_timestamp == available_timestamp) {
+    return ExpectedPacketAvailable(prev_mode, play_dtmf);
+  } else if (!PacketBuffer::IsObsoleteTimestamp(
+                 available_timestamp, target_timestamp, five_seconds_samples)) {
+    return FuturePacketAvailable(
+        sync_buffer, expand, decoder_frame_length, prev_mode, target_timestamp,
+        available_timestamp, play_dtmf, generated_noise_samples);
+  } else {
+    // This implies that available_timestamp < target_timestamp, which can
+    // happen when a new stream or codec is received. Signal for a reset.
+    return kUndefined;
+  }
+}
+
+Operations DecisionLogicNormal::CngOperation(Modes prev_mode,
+                                             uint32_t target_timestamp,
+                                             uint32_t available_timestamp,
+                                             size_t generated_noise_samples) {
+  // Signed difference between target and available timestamp.
+  int32_t timestamp_diff = static_cast<int32_t>(
+      static_cast<uint32_t>(generated_noise_samples + target_timestamp) -
+      available_timestamp);
+  int32_t optimal_level_samp = static_cast<int32_t>(
+      (delay_manager_->TargetLevel() * packet_length_samples_) >> 8);
+  const int64_t excess_waiting_time_samp =
+      -static_cast<int64_t>(timestamp_diff) - optimal_level_samp;
+
+  if (excess_waiting_time_samp > optimal_level_samp / 2) {
+    // The waiting time for this packet will be longer than 1.5
+    // times the wanted buffer delay. Apply fast-forward to cut the
+    // waiting time down to the optimal.
+    noise_fast_forward_ = rtc::dchecked_cast<size_t>(noise_fast_forward_ +
+                                                     excess_waiting_time_samp);
+    timestamp_diff =
+        rtc::saturated_cast<int32_t>(timestamp_diff + excess_waiting_time_samp);
+  }
+
+  if (timestamp_diff < 0 && prev_mode == kModeRfc3389Cng) {
+    // Not time to play this packet yet. Wait another round before using this
+    // packet. Keep on playing CNG from previous CNG parameters.
+    return kRfc3389CngNoPacket;
+  } else {
+    // Otherwise, go for the CNG packet now.
+    noise_fast_forward_ = 0;
+    return kRfc3389Cng;
+  }
+}
+
+Operations DecisionLogicNormal::NoPacket(bool play_dtmf) {
+  if (cng_state_ == kCngRfc3389On) {
+    // Keep on playing comfort noise.
+    return kRfc3389CngNoPacket;
+  } else if (cng_state_ == kCngInternalOn) {
+    // Keep on playing codec internal comfort noise.
+    return kCodecInternalCng;
+  } else if (play_dtmf) {
+    return kDtmf;
+  } else {
+    // Nothing to play, do expand.
+    return kExpand;
+  }
+}
+
+Operations DecisionLogicNormal::ExpectedPacketAvailable(Modes prev_mode,
+                                                        bool play_dtmf) {
+  if (prev_mode != kModeExpand && !play_dtmf) {
+    // Check criterion for time-stretching.
+    int low_limit, high_limit;
+    delay_manager_->BufferLimits(&low_limit, &high_limit);
+    if (buffer_level_filter_->filtered_current_level() >= high_limit << 2)
+      return kFastAccelerate;
+    if (TimescaleAllowed()) {
+      if (buffer_level_filter_->filtered_current_level() >= high_limit)
+        return kAccelerate;
+      if (buffer_level_filter_->filtered_current_level() < low_limit)
+        return kPreemptiveExpand;
+    }
+  }
+  return kNormal;
+}
+
+Operations DecisionLogicNormal::FuturePacketAvailable(
+    const SyncBuffer& sync_buffer,
+    const Expand& expand,
+    size_t decoder_frame_length,
+    Modes prev_mode,
+    uint32_t target_timestamp,
+    uint32_t available_timestamp,
+    bool play_dtmf,
+    size_t generated_noise_samples) {
+  // 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.
+  uint32_t timestamp_leap = available_timestamp - target_timestamp;
+  if ((prev_mode == kModeExpand) && !ReinitAfterExpands(timestamp_leap) &&
+      !MaxWaitForPacket() && PacketTooEarly(timestamp_leap) &&
+      UnderTargetLevel()) {
+    if (play_dtmf) {
+      // Still have DTMF to play, so do not do expand.
+      return kDtmf;
+    } else {
+      // Nothing to play.
+      return kExpand;
+    }
+  }
+
+  const size_t samples_left =
+      sync_buffer.FutureLength() - expand.overlap_length();
+  const size_t cur_size_samples =
+      samples_left + packet_buffer_.NumPacketsInBuffer() * decoder_frame_length;
+
+  // If previous was comfort noise, then no merge is needed.
+  if (prev_mode == kModeRfc3389Cng || prev_mode == kModeCodecInternalCng) {
+    // Keep the same delay as before the CNG, but make sure that the number of
+    // samples in buffer is no higher than 4 times the optimal level. (Note that
+    // TargetLevel() is in Q8.)
+    if (static_cast<uint32_t>(generated_noise_samples + target_timestamp) >=
+            available_timestamp ||
+        cur_size_samples >
+            ((delay_manager_->TargetLevel() * packet_length_samples_) >> 8) *
+                4) {
+      // Time to play this new packet.
+      return kNormal;
+    } else {
+      // Too early to play this new packet; keep on playing comfort noise.
+      if (prev_mode == kModeRfc3389Cng) {
+        return kRfc3389CngNoPacket;
+      } else {  // prevPlayMode == kModeCodecInternalCng.
+        return kCodecInternalCng;
+      }
+    }
+  }
+  // Do not merge unless we have done an expand before.
+  if (prev_mode == kModeExpand) {
+    return kMerge;
+  } else if (play_dtmf) {
+    // Play DTMF instead of expand.
+    return kDtmf;
+  } else {
+    return kExpand;
+  }
+}
+
+bool DecisionLogicNormal::UnderTargetLevel() const {
+  return buffer_level_filter_->filtered_current_level() <=
+         delay_manager_->TargetLevel();
+}
+
+bool DecisionLogicNormal::ReinitAfterExpands(uint32_t timestamp_leap) const {
+  return timestamp_leap >=
+         static_cast<uint32_t>(output_size_samples_ * kReinitAfterExpands);
+}
+
+bool DecisionLogicNormal::PacketTooEarly(uint32_t timestamp_leap) const {
+  return timestamp_leap >
+         static_cast<uint32_t>(output_size_samples_ * num_consecutive_expands_);
+}
+
+bool DecisionLogicNormal::MaxWaitForPacket() const {
+  return num_consecutive_expands_ >= kMaxWaitForPacket;
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_coding/neteq/decision_logic_normal.h b/modules/audio_coding/neteq/decision_logic_normal.h
new file mode 100644
index 0000000..ed2ea39
--- /dev/null
+++ b/modules/audio_coding/neteq/decision_logic_normal.h
@@ -0,0 +1,112 @@
+/*
+ *  Copyright (c) 2013 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.
+ */
+
+#ifndef MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_NORMAL_H_
+#define MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_NORMAL_H_
+
+#include "modules/audio_coding/neteq/decision_logic.h"
+#include "rtc_base/constructormagic.h"
+#include "system_wrappers/include/field_trial.h"
+#include "typedefs.h"  // NOLINT(build/include)
+
+namespace webrtc {
+
+// Implementation of the DecisionLogic class for playout modes kPlayoutOn and
+// kPlayoutStreaming.
+class DecisionLogicNormal : public DecisionLogic {
+ public:
+  // Constructor.
+  DecisionLogicNormal(int fs_hz,
+                      size_t output_size_samples,
+                      NetEqPlayoutMode playout_mode,
+                      DecoderDatabase* decoder_database,
+                      const PacketBuffer& packet_buffer,
+                      DelayManager* delay_manager,
+                      BufferLevelFilter* buffer_level_filter,
+                      const TickTimer* tick_timer)
+      : DecisionLogic(fs_hz,
+                      output_size_samples,
+                      playout_mode,
+                      decoder_database,
+                      packet_buffer,
+                      delay_manager,
+                      buffer_level_filter,
+                      tick_timer),
+        postpone_decoding_after_expand_(field_trial::IsEnabled(
+            "WebRTC-Audio-NetEqPostponeDecodingAfterExpand")) {}
+
+ protected:
+  static const int kReinitAfterExpands = 100;
+  static const int kMaxWaitForPacket = 10;
+
+  Operations GetDecisionSpecialized(const SyncBuffer& sync_buffer,
+                                    const Expand& expand,
+                                    size_t decoder_frame_length,
+                                    const Packet* next_packet,
+                                    Modes prev_mode,
+                                    bool play_dtmf,
+                                    bool* reset_decoder,
+                                    size_t generated_noise_samples,
+                                    size_t cur_size_samples) override;
+
+  // Returns the operation to do given that the expected packet is not
+  // available, but a packet further into the future is at hand.
+  virtual Operations FuturePacketAvailable(const SyncBuffer& sync_buffer,
+                                           const Expand& expand,
+                                           size_t decoder_frame_length,
+                                           Modes prev_mode,
+                                           uint32_t target_timestamp,
+                                           uint32_t available_timestamp,
+                                           bool play_dtmf,
+                                           size_t generated_noise_samples);
+
+  // Returns the operation to do given that the expected packet is available.
+  virtual Operations ExpectedPacketAvailable(Modes prev_mode, bool play_dtmf);
+
+  // Returns the operation given that no packets are available (except maybe
+  // a DTMF event, flagged by setting |play_dtmf| true).
+  virtual Operations NoPacket(bool play_dtmf);
+
+ private:
+  // Returns the operation given that the next available packet is a comfort
+  // noise payload (RFC 3389 only, not codec-internal).
+  Operations CngOperation(Modes prev_mode,
+                          uint32_t target_timestamp,
+                          uint32_t available_timestamp,
+                          size_t generated_noise_samples);
+
+  // Checks if enough time has elapsed since the last successful timescale
+  // operation was done (i.e., accelerate or preemptive expand).
+  bool TimescaleAllowed() const {
+    return !timescale_countdown_ || timescale_countdown_->Finished();
+  }
+
+  // Checks if the current (filtered) buffer level is under the target level.
+  bool UnderTargetLevel() const;
+
+  // Checks if |timestamp_leap| is so long into the future that a reset due
+  // to exceeding kReinitAfterExpands will be done.
+  bool ReinitAfterExpands(uint32_t timestamp_leap) const;
+
+  // Checks if we still have not done enough expands to cover the distance from
+  // the last decoded packet to the next available packet, the distance beeing
+  // conveyed in |timestamp_leap|.
+  bool PacketTooEarly(uint32_t timestamp_leap) const;
+
+  // Checks if num_consecutive_expands_ >= kMaxWaitForPacket.
+  bool MaxWaitForPacket() const;
+
+  const bool postpone_decoding_after_expand_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(DecisionLogicNormal);
+};
+
+}  // namespace webrtc
+#endif  // MODULES_AUDIO_CODING_NETEQ_DECISION_LOGIC_NORMAL_H_
diff --git a/modules/audio_coding/neteq/decision_logic_unittest.cc b/modules/audio_coding/neteq/decision_logic_unittest.cc
index 6929daa..5f0e5c2 100644
--- a/modules/audio_coding/neteq/decision_logic_unittest.cc
+++ b/modules/audio_coding/neteq/decision_logic_unittest.cc
@@ -33,7 +33,19 @@
   DelayManager delay_manager(240, &delay_peak_detector, &tick_timer);
   BufferLevelFilter buffer_level_filter;
   DecisionLogic* logic = DecisionLogic::Create(
-      fs_hz, output_size_samples, false, &decoder_database, packet_buffer,
+      fs_hz, output_size_samples, kPlayoutOn, &decoder_database, packet_buffer,
+      &delay_manager, &buffer_level_filter, &tick_timer);
+  delete logic;
+  logic = DecisionLogic::Create(
+      fs_hz, output_size_samples, kPlayoutStreaming, &decoder_database,
+      packet_buffer, &delay_manager, &buffer_level_filter, &tick_timer);
+  delete logic;
+  logic = DecisionLogic::Create(
+      fs_hz, output_size_samples, kPlayoutFax, &decoder_database, packet_buffer,
+      &delay_manager, &buffer_level_filter, &tick_timer);
+  delete logic;
+  logic = DecisionLogic::Create(
+      fs_hz, output_size_samples, kPlayoutOff, &decoder_database, packet_buffer,
       &delay_manager, &buffer_level_filter, &tick_timer);
   delete logic;
 }
diff --git a/modules/audio_coding/neteq/defines.h b/modules/audio_coding/neteq/defines.h
index 768f0b9..496a36d 100644
--- a/modules/audio_coding/neteq/defines.h
+++ b/modules/audio_coding/neteq/defines.h
@@ -24,6 +24,10 @@
   kRfc3389CngNoPacket,
   kCodecInternalCng,
   kDtmf,
+  kAlternativePlc,
+  kAlternativePlcIncreaseTimestamp,
+  kAudioRepetition,
+  kAudioRepetitionIncreaseTimestamp,
   kUndefined = -1
 };
 
diff --git a/modules/audio_coding/neteq/include/neteq.h b/modules/audio_coding/neteq/include/neteq.h
index ce1448a..273979b 100644
--- a/modules/audio_coding/neteq/include/neteq.h
+++ b/modules/audio_coding/neteq/include/neteq.h
@@ -74,6 +74,13 @@
   uint64_t voice_concealed_samples = 0;
 };
 
+enum NetEqPlayoutMode {
+  kPlayoutOn,
+  kPlayoutOff,
+  kPlayoutFax,
+  kPlayoutStreaming
+};
+
 // This is the interface class for NetEq.
 class NetEq {
  public:
@@ -91,10 +98,10 @@
     bool enable_post_decode_vad = false;
     size_t max_packets_in_buffer = 50;
     int max_delay_ms = 2000;
+    NetEqPlayoutMode playout_mode = kPlayoutOn;
     bool enable_fast_accelerate = false;
     bool enable_muted_state = false;
     absl::optional<AudioCodecPairId> codec_pair_id;
-    bool for_test_no_time_stretching = false;  // Use only for testing.
   };
 
   enum ReturnCodes { kOK = 0, kFail = -1, kNotImplemented = -2 };
@@ -202,6 +209,16 @@
   // The packet buffer part of the delay is not updated during DTX/CNG periods.
   virtual int FilteredCurrentDelayMs() const = 0;
 
+  // Sets the playout mode to |mode|.
+  // Deprecated. Set the mode in the Config struct passed to the constructor.
+  // TODO(henrik.lundin) Delete.
+  virtual void SetPlayoutMode(NetEqPlayoutMode mode) = 0;
+
+  // Returns the current playout mode.
+  // Deprecated.
+  // TODO(henrik.lundin) Delete.
+  virtual NetEqPlayoutMode PlayoutMode() const = 0;
+
   // Writes the current network statistics to |stats|. The statistics are reset
   // after the call.
   virtual int NetworkStatistics(NetEqNetworkStatistics* stats) = 0;
diff --git a/modules/audio_coding/neteq/neteq.cc b/modules/audio_coding/neteq/neteq.cc
index cf1c6aa..55af23e 100644
--- a/modules/audio_coding/neteq/neteq.cc
+++ b/modules/audio_coding/neteq/neteq.cc
@@ -30,7 +30,7 @@
   ss << "sample_rate_hz=" << sample_rate_hz << ", enable_post_decode_vad="
      << (enable_post_decode_vad ? "true" : "false")
      << ", max_packets_in_buffer=" << max_packets_in_buffer
-     << ", enable_fast_accelerate="
+     << ", playout_mode=" << playout_mode << ", enable_fast_accelerate="
      << (enable_fast_accelerate ? " true" : "false")
      << ", enable_muted_state=" << (enable_muted_state ? " true" : "false");
   return ss.str();
diff --git a/modules/audio_coding/neteq/neteq_impl.cc b/modules/audio_coding/neteq/neteq_impl.cc
index 4630448..afc15bf 100644
--- a/modules/audio_coding/neteq/neteq_impl.cc
+++ b/modules/audio_coding/neteq/neteq_impl.cc
@@ -101,6 +101,7 @@
       reset_decoder_(false),
       ssrc_(0),
       first_packet_(true),
+      playout_mode_(config.playout_mode),
       enable_fast_accelerate_(config.enable_fast_accelerate),
       nack_enabled_(false),
       enable_muted_state_(config.enable_muted_state),
@@ -109,8 +110,7 @@
                          tick_timer_.get()),
       speech_expand_uma_logger_("WebRTC.Audio.SpeechExpandRatePercent",
                                 10,  // Report once every 10 s.
-                                tick_timer_.get()),
-      no_time_stretching_(config.for_test_no_time_stretching) {
+                                tick_timer_.get()) {
   RTC_LOG(LS_INFO) << "NetEq config: " << config.ToString();
   int fs = config.sample_rate_hz;
   if (fs != 8000 && fs != 16000 && fs != 32000 && fs != 48000) {
@@ -358,6 +358,23 @@
   return static_cast<int>(delay_samples) / rtc::CheckedDivExact(fs_hz_, 1000);
 }
 
+// Deprecated.
+// TODO(henrik.lundin) Delete.
+void NetEqImpl::SetPlayoutMode(NetEqPlayoutMode mode) {
+  rtc::CritScope lock(&crit_sect_);
+  if (mode != playout_mode_) {
+    playout_mode_ = mode;
+    CreateDecisionLogic();
+  }
+}
+
+// Deprecated.
+// TODO(henrik.lundin) Delete.
+NetEqPlayoutMode NetEqImpl::PlayoutMode() const {
+  rtc::CritScope lock(&crit_sect_);
+  return playout_mode_;
+}
+
 int NetEqImpl::NetworkStatistics(NetEqNetworkStatistics* stats) {
   rtc::CritScope lock(&crit_sect_);
   assert(decoder_database_.get());
@@ -920,6 +937,33 @@
       return_value = DoDtmf(dtmf_event, &play_dtmf);
       break;
     }
+    case kAlternativePlc: {
+      // TODO(hlundin): Write test for this.
+      DoAlternativePlc(false);
+      break;
+    }
+    case kAlternativePlcIncreaseTimestamp: {
+      // TODO(hlundin): Write test for this.
+      DoAlternativePlc(true);
+      break;
+    }
+    case kAudioRepetitionIncreaseTimestamp: {
+      // TODO(hlundin): Write test for this.
+      sync_buffer_->IncreaseEndTimestamp(
+          static_cast<uint32_t>(output_size_samples_));
+      // Skipping break on purpose. Execution should move on into the
+      // next case.
+      RTC_FALLTHROUGH();
+    }
+    case kAudioRepetition: {
+      // TODO(hlundin): Write test for this.
+      // Copy last |output_size_samples_| from |sync_buffer_| to
+      // |algorithm_buffer|.
+      algorithm_buffer_->PushBackFromIndex(
+          *sync_buffer_, sync_buffer_->Size() - output_size_samples_);
+      expand_->Reset();
+      break;
+    }
     case kUndefined: {
       RTC_LOG(LS_ERROR) << "Invalid operation kUndefined.";
       assert(false);  // This should not happen.
@@ -1247,7 +1291,10 @@
 
   // Get packets from buffer.
   int extracted_samples = 0;
-  if (packet) {
+  if (packet && *operation != kAlternativePlc &&
+      *operation != kAlternativePlcIncreaseTimestamp &&
+      *operation != kAudioRepetition &&
+      *operation != kAudioRepetitionIncreaseTimestamp) {
     sync_buffer_->IncreaseEndTimestamp(packet->timestamp - end_timestamp);
     if (decision_logic_->CngOff()) {
       // Adjustment of timestamp only corresponds to an actual packet loss
@@ -1836,6 +1883,29 @@
   return 0;
 }
 
+void NetEqImpl::DoAlternativePlc(bool increase_timestamp) {
+  AudioDecoder* decoder = decoder_database_->GetActiveDecoder();
+  size_t length;
+  if (decoder && decoder->HasDecodePlc()) {
+    // Use the decoder's packet-loss concealment.
+    // TODO(hlundin): Will probably need a longer buffer for multi-channel.
+    int16_t decoded_buffer[kMaxFrameSize];
+    length = decoder->DecodePlc(1, decoded_buffer);
+    if (length > 0)
+      algorithm_buffer_->PushBackInterleaved(decoded_buffer, length);
+  } else {
+    // Do simple zero-stuffing.
+    length = output_size_samples_;
+    algorithm_buffer_->Zeros(length);
+    // By not advancing the timestamp, NetEq inserts samples.
+    stats_.AddZeros(length);
+  }
+  if (increase_timestamp) {
+    sync_buffer_->IncreaseEndTimestamp(static_cast<uint32_t>(length));
+  }
+  expand_->Reset();
+}
+
 int NetEqImpl::DtmfOverdub(const DtmfEvent& dtmf_event,
                            size_t num_channels,
                            int16_t* output) const {
@@ -2060,8 +2130,8 @@
 
 void NetEqImpl::CreateDecisionLogic() {
   decision_logic_.reset(DecisionLogic::Create(
-      fs_hz_, output_size_samples_, no_time_stretching_,
-      decoder_database_.get(), *packet_buffer_.get(), delay_manager_.get(),
-      buffer_level_filter_.get(), tick_timer_.get()));
+      fs_hz_, output_size_samples_, playout_mode_, decoder_database_.get(),
+      *packet_buffer_.get(), delay_manager_.get(), buffer_level_filter_.get(),
+      tick_timer_.get()));
 }
 }  // namespace webrtc
diff --git a/modules/audio_coding/neteq/neteq_impl.h b/modules/audio_coding/neteq/neteq_impl.h
index 6b8764d..6f69680 100644
--- a/modules/audio_coding/neteq/neteq_impl.h
+++ b/modules/audio_coding/neteq/neteq_impl.h
@@ -168,6 +168,16 @@
 
   int FilteredCurrentDelayMs() const override;
 
+  // Sets the playout mode to |mode|.
+  // Deprecated.
+  // TODO(henrik.lundin) Delete.
+  void SetPlayoutMode(NetEqPlayoutMode mode) override;
+
+  // Returns the current playout mode.
+  // Deprecated.
+  // TODO(henrik.lundin) Delete.
+  NetEqPlayoutMode PlayoutMode() const override;
+
   // Writes the current network statistics to |stats|. The statistics are reset
   // after the call.
   int NetworkStatistics(NetEqNetworkStatistics* stats) override;
@@ -326,6 +336,12 @@
   int DoDtmf(const DtmfEvent& dtmf_event, bool* play_dtmf)
       RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
 
+  // Produces packet-loss concealment using alternative methods. If the codec
+  // has an internal PLC, it is called to generate samples. Otherwise, the
+  // method performs zero-stuffing.
+  void DoAlternativePlc(bool increase_timestamp)
+      RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
+
   // Overdub DTMF on top of |output|.
   int DtmfOverdub(const DtmfEvent& dtmf_event,
                   size_t num_channels,
@@ -413,6 +429,7 @@
       RTC_GUARDED_BY(crit_sect_);
   uint32_t ssrc_ RTC_GUARDED_BY(crit_sect_);
   bool first_packet_ RTC_GUARDED_BY(crit_sect_);
+  NetEqPlayoutMode playout_mode_ RTC_GUARDED_BY(crit_sect_);
   bool enable_fast_accelerate_ RTC_GUARDED_BY(crit_sect_);
   std::unique_ptr<NackTracker> nack_ RTC_GUARDED_BY(crit_sect_);
   bool nack_enabled_ RTC_GUARDED_BY(crit_sect_);
@@ -424,7 +441,6 @@
   std::vector<uint32_t> last_decoded_timestamps_ RTC_GUARDED_BY(crit_sect_);
   ExpandUmaLogger expand_uma_logger_ RTC_GUARDED_BY(crit_sect_);
   ExpandUmaLogger speech_expand_uma_logger_ RTC_GUARDED_BY(crit_sect_);
-  bool no_time_stretching_ RTC_GUARDED_BY(crit_sect_);  // Only used for test.
 
  private:
   RTC_DISALLOW_COPY_AND_ASSIGN(NetEqImpl);
diff --git a/modules/audio_coding/neteq/neteq_impl_unittest.cc b/modules/audio_coding/neteq/neteq_impl_unittest.cc
index b772dfa..c8fd91a 100644
--- a/modules/audio_coding/neteq/neteq_impl_unittest.cc
+++ b/modules/audio_coding/neteq/neteq_impl_unittest.cc
@@ -1377,6 +1377,32 @@
   uint16_t sequence_number_ = 1;
 };
 
+TEST_F(NetEqImplTest120ms, AudioRepetition) {
+  config_.playout_mode = kPlayoutFax;
+  CreateInstanceNoMocks();
+  Register120msCodec(AudioDecoder::kSpeech);
+
+  InsertPacket(first_timestamp());
+  GetFirstPacket();
+
+  bool muted;
+  EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output_, &muted));
+  EXPECT_EQ(kAudioRepetition, neteq_->last_operation_for_test());
+}
+
+TEST_F(NetEqImplTest120ms, AlternativePlc) {
+  config_.playout_mode = kPlayoutOff;
+  CreateInstanceNoMocks();
+  Register120msCodec(AudioDecoder::kSpeech);
+
+  InsertPacket(first_timestamp());
+  GetFirstPacket();
+
+  bool muted;
+  EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output_, &muted));
+  EXPECT_EQ(kAlternativePlc, neteq_->last_operation_for_test());
+}
+
 TEST_F(NetEqImplTest120ms, CodecInternalCng) {
   CreateInstanceNoMocks();
   Register120msCodec(AudioDecoder::kComfortNoise);
diff --git a/modules/audio_coding/neteq/neteq_unittest.cc b/modules/audio_coding/neteq/neteq_unittest.cc
index 27e9535..4ed7a6b 100644
--- a/modules/audio_coding/neteq/neteq_unittest.cc
+++ b/modules/audio_coding/neteq/neteq_unittest.cc
@@ -25,8 +25,6 @@
 #include "common_types.h"  // NOLINT(build/include)
 #include "modules/audio_coding/codecs/pcm16b/pcm16b.h"
 #include "modules/audio_coding/neteq/tools/audio_loop.h"
-#include "modules/audio_coding/neteq/tools/neteq_packet_source_input.h"
-#include "modules/audio_coding/neteq/tools/neteq_test.h"
 #include "modules/audio_coding/neteq/tools/rtp_file_source.h"
 #include "rtc_base/ignore_wundef.h"
 #include "rtc_base/messagedigest.h"
@@ -557,7 +555,7 @@
 class NetEqDecodingTestFaxMode : public NetEqDecodingTest {
  protected:
   NetEqDecodingTestFaxMode() : NetEqDecodingTest() {
-    config_.for_test_no_time_stretching = true;
+    config_.playout_mode = kPlayoutFax;
   }
   void TestJitterBufferDelay(bool apply_packet_loss);
 };
@@ -1725,37 +1723,4 @@
   TestJitterBufferDelay(true);
 }
 
-namespace test {
-// TODO(henrik.lundin) NetEqRtpDumpInput requires protobuf support. It shouldn't
-// need it, but because it is bundled with NetEqEventLogInput, it is neded.
-// This should be refactored.
-#if WEBRTC_ENABLE_PROTOBUF
-TEST(NetEqNoTimeStretchingMode, RunTest) {
-  NetEq::Config config;
-  config.for_test_no_time_stretching = true;
-  auto codecs = NetEqTest::StandardDecoderMap();
-  NetEqTest::ExtDecoderMap ext_codecs;
-  NetEqPacketSourceInput::RtpHeaderExtensionMap rtp_ext_map = {
-      {1, kRtpExtensionAudioLevel},
-      {3, kRtpExtensionAbsoluteSendTime},
-      {5, kRtpExtensionTransportSequenceNumber},
-      {7, kRtpExtensionVideoContentType},
-      {8, kRtpExtensionVideoTiming}};
-  std::unique_ptr<NetEqInput> input(new NetEqRtpDumpInput(
-      webrtc::test::ResourcePath("audio_coding/neteq_universal_new", "rtp"),
-      rtp_ext_map));
-  std::unique_ptr<TimeLimitedNetEqInput> input_time_limit(
-      new TimeLimitedNetEqInput(std::move(input), 20000));
-  std::unique_ptr<AudioSink> output(new VoidAudioSink);
-  NetEqTest::Callbacks callbacks;
-  NetEqTest test(config, codecs, ext_codecs, std::move(input_time_limit),
-                 std::move(output), callbacks);
-  test.Run();
-  const auto stats = test.SimulationStats();
-  EXPECT_EQ(0, stats.accelerate_rate);
-  EXPECT_EQ(0, stats.preemptive_rate);
-}
-#endif
-
-}  // namespace test
 }  // namespace webrtc
diff --git a/modules/audio_coding/neteq/tools/neteq_input.cc b/modules/audio_coding/neteq/tools/neteq_input.cc
index 12a75fc..44513ab 100644
--- a/modules/audio_coding/neteq/tools/neteq_input.cc
+++ b/modules/audio_coding/neteq/tools/neteq_input.cc
@@ -28,50 +28,5 @@
   return ss.str();
 }
 
-TimeLimitedNetEqInput::TimeLimitedNetEqInput(std::unique_ptr<NetEqInput> input,
-                                             int64_t duration_ms)
-    : input_(std::move(input)),
-      start_time_ms_(input_->NextEventTime()),
-      duration_ms_(duration_ms) {}
-
-rtc::Optional<int64_t> TimeLimitedNetEqInput::NextPacketTime() const {
-  return ended_ ? rtc::Optional<int64_t>() : input_->NextPacketTime();
-}
-
-rtc::Optional<int64_t> TimeLimitedNetEqInput::NextOutputEventTime() const {
-  return ended_ ? rtc::Optional<int64_t>() : input_->NextOutputEventTime();
-}
-
-std::unique_ptr<NetEqInput::PacketData> TimeLimitedNetEqInput::PopPacket() {
-  if (ended_) {
-    return std::unique_ptr<PacketData>();
-  }
-  auto packet = input_->PopPacket();
-  MaybeSetEnded();
-  return packet;
-}
-
-void TimeLimitedNetEqInput::AdvanceOutputEvent() {
-  if (!ended_) {
-    input_->AdvanceOutputEvent();
-    MaybeSetEnded();
-  }
-}
-
-bool TimeLimitedNetEqInput::ended() const {
-  return ended_ || input_->ended();
-}
-
-rtc::Optional<RTPHeader> TimeLimitedNetEqInput::NextHeader() const {
-  return ended_ ? rtc::Optional<RTPHeader>() : input_->NextHeader();
-}
-
-void TimeLimitedNetEqInput::MaybeSetEnded() {
-  if (NextEventTime() && start_time_ms_ &&
-      *NextEventTime() - *start_time_ms_ > duration_ms_) {
-    ended_ = true;
-  }
-}
-
 }  // namespace test
 }  // namespace webrtc
diff --git a/modules/audio_coding/neteq/tools/neteq_input.h b/modules/audio_coding/neteq/tools/neteq_input.h
index a13a86e..5e2cbd2 100644
--- a/modules/audio_coding/neteq/tools/neteq_input.h
+++ b/modules/audio_coding/neteq/tools/neteq_input.h
@@ -78,28 +78,6 @@
   virtual absl::optional<RTPHeader> NextHeader() const = 0;
 };
 
-// Wrapper class to impose a time limit on a NetEqInput object, typically
-// another time limit than what the object itself provides. For example, an
-// input taken from a file can be cut shorter by wrapping it in this class.
-class TimeLimitedNetEqInput : public NetEqInput {
- public:
-  TimeLimitedNetEqInput(std::unique_ptr<NetEqInput> input, int64_t duration_ms);
-  rtc::Optional<int64_t> NextPacketTime() const override;
-  rtc::Optional<int64_t> NextOutputEventTime() const override;
-  std::unique_ptr<PacketData> PopPacket() override;
-  void AdvanceOutputEvent() override;
-  bool ended() const override;
-  rtc::Optional<RTPHeader> NextHeader() const override;
-
- private:
-  void MaybeSetEnded();
-
-  std::unique_ptr<NetEqInput> input_;
-  const rtc::Optional<int64_t> start_time_ms_;
-  const int64_t duration_ms_;
-  bool ended_ = false;
-};
-
 }  // namespace test
 }  // namespace webrtc
 #endif  // MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_INPUT_H_
diff --git a/modules/audio_coding/neteq/tools/neteq_test.cc b/modules/audio_coding/neteq/tools/neteq_test.cc
index 6d8e7ff..e6dd114 100644
--- a/modules/audio_coding/neteq/tools/neteq_test.cc
+++ b/modules/audio_coding/neteq/tools/neteq_test.cc
@@ -115,34 +115,6 @@
   return neteq_->GetLifetimeStatistics();
 }
 
-NetEqTest::DecoderMap NetEqTest::StandardDecoderMap() {
-  DecoderMap codecs = {
-    {0, std::make_pair(NetEqDecoder::kDecoderPCMu, "pcmu")},
-    {8, std::make_pair(NetEqDecoder::kDecoderPCMa, "pcma")},
-    {102, std::make_pair(NetEqDecoder::kDecoderILBC, "ilbc")},
-    {103, std::make_pair(NetEqDecoder::kDecoderISAC, "isac")},
-#if !defined(WEBRTC_ANDROID)
-    {104, std::make_pair(NetEqDecoder::kDecoderISACswb, "isac-swb")},
-#endif
-    {111, std::make_pair(NetEqDecoder::kDecoderOpus, "opus")},
-    {93, std::make_pair(NetEqDecoder::kDecoderPCM16B, "pcm16-nb")},
-    {94, std::make_pair(NetEqDecoder::kDecoderPCM16Bwb, "pcm16-wb")},
-    {95, std::make_pair(NetEqDecoder::kDecoderPCM16Bswb32kHz, "pcm16-swb32")},
-    {96, std::make_pair(NetEqDecoder::kDecoderPCM16Bswb48kHz, "pcm16-swb48")},
-    {9, std::make_pair(NetEqDecoder::kDecoderG722, "g722")},
-    {106, std::make_pair(NetEqDecoder::kDecoderAVT, "avt")},
-    {114, std::make_pair(NetEqDecoder::kDecoderAVT16kHz, "avt-16")},
-    {115, std::make_pair(NetEqDecoder::kDecoderAVT32kHz, "avt-32")},
-    {116, std::make_pair(NetEqDecoder::kDecoderAVT48kHz, "avt-48")},
-    {117, std::make_pair(NetEqDecoder::kDecoderRED, "red")},
-    {13, std::make_pair(NetEqDecoder::kDecoderCNGnb, "cng-nb")},
-    {98, std::make_pair(NetEqDecoder::kDecoderCNGwb, "cng-wb")},
-    {99, std::make_pair(NetEqDecoder::kDecoderCNGswb32kHz, "cng-swb32")},
-    {100, std::make_pair(NetEqDecoder::kDecoderCNGswb48kHz, "cng-swb48")}
-  };
-  return codecs;
-}
-
 void NetEqTest::RegisterDecoders(const DecoderMap& codecs) {
   for (const auto& c : codecs) {
     RTC_CHECK_EQ(
diff --git a/modules/audio_coding/neteq/tools/neteq_test.h b/modules/audio_coding/neteq/tools/neteq_test.h
index 9c05fc4..e645e42 100644
--- a/modules/audio_coding/neteq/tools/neteq_test.h
+++ b/modules/audio_coding/neteq/tools/neteq_test.h
@@ -91,8 +91,6 @@
   NetEqNetworkStatistics SimulationStats();
   NetEqLifetimeStatistics LifetimeStats() const;
 
-  static DecoderMap StandardDecoderMap();
-
  private:
   void RegisterDecoders(const DecoderMap& codecs);
   void RegisterExternalDecoders(const ExtDecoderMap& codecs);
diff --git a/modules/audio_coding/test/TwoWayCommunication.cc b/modules/audio_coding/test/TwoWayCommunication.cc
index 4367faf..5a78c11 100644
--- a/modules/audio_coding/test/TwoWayCommunication.cc
+++ b/modules/audio_coding/test/TwoWayCommunication.cc
@@ -40,9 +40,8 @@
           AudioCodingModule::Config(CreateBuiltinAudioDecoderFactory()))),
       _testMode(testMode) {
   AudioCodingModule::Config config;
-  // The clicks will be more obvious if time-stretching is not allowed.
-  // TODO(henrik.lundin) Really?
-  config.neteq_config.for_test_no_time_stretching = true;
+  // The clicks will be more obvious in FAX mode. TODO(henrik.lundin) Really?
+  config.neteq_config.playout_mode = kPlayoutFax;
   config.decoder_factory = CreateBuiltinAudioDecoderFactory();
   _acmB.reset(AudioCodingModule::Create(config));
   _acmRefB.reset(AudioCodingModule::Create(config));
diff --git a/test/fuzzers/neteq_rtp_fuzzer.cc b/test/fuzzers/neteq_rtp_fuzzer.cc
index 2b150a7..e28af90 100644
--- a/test/fuzzers/neteq_rtp_fuzzer.cc
+++ b/test/fuzzers/neteq_rtp_fuzzer.cc
@@ -133,13 +133,29 @@
   std::unique_ptr<AudioChecksum> output(new AudioChecksum);
   NetEqTest::Callbacks callbacks;
   NetEq::Config config;
-  auto codecs = NetEqTest::StandardDecoderMap();
-  // kPayloadType is the payload type that will be used for encoding. Verify
-  // that it is included in the standard decoder map, and that it points to the
-  // expected decoder type.
-  RTC_CHECK_EQ(codecs.count(kPayloadType), 1);
-  RTC_CHECK(codecs[kPayloadType].first == NetEqDecoder::kDecoderPCM16Bswb32kHz);
-
+  NetEqTest::DecoderMap codecs;
+  codecs[0] = std::make_pair(NetEqDecoder::kDecoderPCMu, "pcmu");
+  codecs[8] = std::make_pair(NetEqDecoder::kDecoderPCMa, "pcma");
+  codecs[103] = std::make_pair(NetEqDecoder::kDecoderISAC, "isac");
+  codecs[104] = std::make_pair(NetEqDecoder::kDecoderISACswb, "isac-swb");
+  codecs[111] = std::make_pair(NetEqDecoder::kDecoderOpus, "opus");
+  codecs[93] = std::make_pair(NetEqDecoder::kDecoderPCM16B, "pcm16-nb");
+  codecs[94] = std::make_pair(NetEqDecoder::kDecoderPCM16Bwb, "pcm16-wb");
+  codecs[96] =
+      std::make_pair(NetEqDecoder::kDecoderPCM16Bswb48kHz, "pcm16-swb48");
+  codecs[9] = std::make_pair(NetEqDecoder::kDecoderG722, "g722");
+  codecs[106] = std::make_pair(NetEqDecoder::kDecoderAVT, "avt");
+  codecs[114] = std::make_pair(NetEqDecoder::kDecoderAVT16kHz, "avt-16");
+  codecs[115] = std::make_pair(NetEqDecoder::kDecoderAVT32kHz, "avt-32");
+  codecs[116] = std::make_pair(NetEqDecoder::kDecoderAVT48kHz, "avt-48");
+  codecs[117] = std::make_pair(NetEqDecoder::kDecoderRED, "red");
+  codecs[13] = std::make_pair(NetEqDecoder::kDecoderCNGnb, "cng-nb");
+  codecs[98] = std::make_pair(NetEqDecoder::kDecoderCNGwb, "cng-wb");
+  codecs[99] = std::make_pair(NetEqDecoder::kDecoderCNGswb32kHz, "cng-swb32");
+  codecs[100] = std::make_pair(NetEqDecoder::kDecoderCNGswb48kHz, "cng-swb48");
+  // This is the payload type that will be used for encoding.
+  codecs[kPayloadType] =
+      std::make_pair(NetEqDecoder::kDecoderPCM16Bswb32kHz, "pcm16-swb32");
   NetEqTest::ExtDecoderMap ext_codecs;
 
   NetEqTest test(config, codecs, ext_codecs, std::move(input),
diff --git a/test/fuzzers/neteq_signal_fuzzer.cc b/test/fuzzers/neteq_signal_fuzzer.cc
index 0ba25d5..611964d 100644
--- a/test/fuzzers/neteq_signal_fuzzer.cc
+++ b/test/fuzzers/neteq_signal_fuzzer.cc
@@ -167,22 +167,31 @@
   NetEq::Config config;
   config.enable_post_decode_vad = true;
   config.enable_fast_accelerate = true;
-  auto codecs = NetEqTest::StandardDecoderMap();
-  // rate_types contains the payload types that will be used for encoding.
-  // Verify that they all are included in the standard decoder map, and that
-  // they point to the expected decoder types.
-  RTC_CHECK_EQ(codecs.count(rate_types[0].second), 1);
-  RTC_CHECK(codecs[rate_types[0].second].first == NetEqDecoder::kDecoderPCM16B);
-  RTC_CHECK_EQ(codecs.count(rate_types[1].second), 1);
-  RTC_CHECK(codecs[rate_types[1].second].first ==
-            NetEqDecoder::kDecoderPCM16Bwb);
-  RTC_CHECK_EQ(codecs.count(rate_types[2].second), 1);
-  RTC_CHECK(codecs[rate_types[2].second].first ==
-            NetEqDecoder::kDecoderPCM16Bswb32kHz);
-  RTC_CHECK_EQ(codecs.count(rate_types[3].second), 1);
-  RTC_CHECK(codecs[rate_types[3].second].first ==
-            NetEqDecoder::kDecoderPCM16Bswb48kHz);
-
+  NetEqTest::DecoderMap codecs;
+  codecs[0] = std::make_pair(NetEqDecoder::kDecoderPCMu, "pcmu");
+  codecs[8] = std::make_pair(NetEqDecoder::kDecoderPCMa, "pcma");
+  codecs[103] = std::make_pair(NetEqDecoder::kDecoderISAC, "isac");
+  codecs[104] = std::make_pair(NetEqDecoder::kDecoderISACswb, "isac-swb");
+  codecs[111] = std::make_pair(NetEqDecoder::kDecoderOpus, "opus");
+  codecs[9] = std::make_pair(NetEqDecoder::kDecoderG722, "g722");
+  codecs[106] = std::make_pair(NetEqDecoder::kDecoderAVT, "avt");
+  codecs[114] = std::make_pair(NetEqDecoder::kDecoderAVT16kHz, "avt-16");
+  codecs[115] = std::make_pair(NetEqDecoder::kDecoderAVT32kHz, "avt-32");
+  codecs[116] = std::make_pair(NetEqDecoder::kDecoderAVT48kHz, "avt-48");
+  codecs[117] = std::make_pair(NetEqDecoder::kDecoderRED, "red");
+  codecs[13] = std::make_pair(NetEqDecoder::kDecoderCNGnb, "cng-nb");
+  codecs[98] = std::make_pair(NetEqDecoder::kDecoderCNGwb, "cng-wb");
+  codecs[99] = std::make_pair(NetEqDecoder::kDecoderCNGswb32kHz, "cng-swb32");
+  codecs[100] = std::make_pair(NetEqDecoder::kDecoderCNGswb48kHz, "cng-swb48");
+  // One of these payload types will be used for encoding.
+  codecs[rate_types[0].second] =
+      std::make_pair(NetEqDecoder::kDecoderPCM16B, "pcm16-nb");
+  codecs[rate_types[1].second] =
+      std::make_pair(NetEqDecoder::kDecoderPCM16Bwb, "pcm16-wb");
+  codecs[rate_types[2].second] =
+      std::make_pair(NetEqDecoder::kDecoderPCM16Bswb32kHz, "pcm16-swb32");
+  codecs[rate_types[3].second] =
+      std::make_pair(NetEqDecoder::kDecoderPCM16Bswb48kHz, "pcm16-swb48");
   NetEqTest::ExtDecoderMap ext_codecs;
 
   NetEqTest test(config, codecs, ext_codecs, std::move(input),