Sync to upstream webrtc checkout

This change updates webrtc source to commit 62c0c2d1e
and modifies Makefile so everything can build.

BUG=1041306
TEST=emerge-eve webrtc-apm
emerge-veyron_jerry webrtc-apm

Change-Id: Idae39b1ad32ea7826999985461271a2a71e21f83
Reviewed-on: https://chromium-review.googlesource.com/1046725
Commit-Ready: Hsinyu Chao <hychao@chromium.org>
Tested-by: Hsinyu Chao <hychao@chromium.org>
Reviewed-by: Cheng-Yi Chiang <cychiang@chromium.org>
diff --git a/Makefile b/Makefile
index 70040e9..0a23258 100644
--- a/Makefile
+++ b/Makefile
@@ -36,10 +36,10 @@
 
 CXX_LIBRARY(libwebrtc_apm.so): LDLIBS += \
 	libaudio_processing.pic.a \
-	rtc_base/librtc_base.pic.a \
 	common_audio/libcommon_audio.pic.a \
 	system_wrappers/source/libsystem_wrappers.pic.a \
 	modules/audio_coding/libaudio_coding.pic.a \
+	rtc_base/librtc_base.pic.a \
 	libaudioproc_debug_proto.pic.a \
 	$(call get_pc_libs,$(webrtc_apm_PC_DEPS))
 
diff --git a/api/audio/audio_frame.cc b/api/audio/audio_frame.cc
index 108a523..b477a17 100644
--- a/api/audio/audio_frame.cc
+++ b/api/audio/audio_frame.cc
@@ -8,12 +8,11 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include <string.h>
-
 #include "api/audio/audio_frame.h"
 
+#include <string.h>
+
 #include "rtc_base/checks.h"
-#include "rtc_base/numerics/safe_conversions.h"
 #include "rtc_base/timeutils.h"
 
 namespace webrtc {
@@ -43,12 +42,12 @@
 }
 
 void AudioFrame::UpdateFrame(uint32_t timestamp,
-                                    const int16_t* data,
-                                    size_t samples_per_channel,
-                                    int sample_rate_hz,
-                                    SpeechType speech_type,
-                                    VADActivity vad_activity,
-                                    size_t num_channels) {
+                             const int16_t* data,
+                             size_t samples_per_channel,
+                             int sample_rate_hz,
+                             SpeechType speech_type,
+                             VADActivity vad_activity,
+                             size_t num_channels) {
   timestamp_ = timestamp;
   samples_per_channel_ = samples_per_channel;
   sample_rate_hz_ = sample_rate_hz;
@@ -119,67 +118,10 @@
 
 bool AudioFrame::muted() const { return muted_; }
 
-AudioFrame& AudioFrame::operator>>=(const int rhs) {
-  RTC_CHECK_GT(num_channels_, 0);
-  RTC_CHECK_LT(num_channels_, 3);
-  if ((num_channels_ > 2) || (num_channels_ < 1)) return *this;
-  if (muted_) return *this;
-
-  for (size_t i = 0; i < samples_per_channel_ * num_channels_; i++) {
-    data_[i] = static_cast<int16_t>(data_[i] >> rhs);
-  }
-  return *this;
-}
-
-AudioFrame& AudioFrame::operator+=(const AudioFrame& rhs) {
-  // Sanity check
-  RTC_CHECK_GT(num_channels_, 0);
-  RTC_CHECK_LT(num_channels_, 3);
-  if ((num_channels_ > 2) || (num_channels_ < 1)) return *this;
-  if (num_channels_ != rhs.num_channels_) return *this;
-
-  bool noPrevData = muted_;
-  if (samples_per_channel_ != rhs.samples_per_channel_) {
-    if (samples_per_channel_ == 0) {
-      // special case we have no data to start with
-      samples_per_channel_ = rhs.samples_per_channel_;
-      noPrevData = true;
-    } else {
-      return *this;
-    }
-  }
-
-  if ((vad_activity_ == kVadActive) || rhs.vad_activity_ == kVadActive) {
-    vad_activity_ = kVadActive;
-  } else if (vad_activity_ == kVadUnknown || rhs.vad_activity_ == kVadUnknown) {
-    vad_activity_ = kVadUnknown;
-  }
-
-  if (speech_type_ != rhs.speech_type_) speech_type_ = kUndefined;
-
-  if (!rhs.muted()) {
-    muted_ = false;
-    if (noPrevData) {
-      memcpy(data_, rhs.data(),
-             sizeof(int16_t) * rhs.samples_per_channel_ * num_channels_);
-    } else {
-      // IMPROVEMENT this can be done very fast in assembly
-      for (size_t i = 0; i < samples_per_channel_ * num_channels_; i++) {
-        int32_t wrap_guard =
-            static_cast<int32_t>(data_[i]) + static_cast<int32_t>(rhs.data_[i]);
-        data_[i] = rtc::saturated_cast<int16_t>(wrap_guard);
-      }
-    }
-  }
-
-  return *this;
-}
-
 // static
 const int16_t* AudioFrame::empty_data() {
-  static const int16_t kEmptyData[kMaxDataSizeSamples] = {0};
-  static_assert(sizeof(kEmptyData) == kMaxDataSizeBytes, "kMaxDataSizeBytes");
-  return kEmptyData;
+  static int16_t* null_data = new int16_t[kMaxDataSizeSamples]();
+  return &null_data[0];
 }
 
 }  // namespace webrtc
diff --git a/api/audio/audio_frame.h b/api/audio/audio_frame.h
index 5cb2019..39840e5 100644
--- a/api/audio/audio_frame.h
+++ b/api/audio/audio_frame.h
@@ -11,11 +11,9 @@
 #ifndef API_AUDIO_AUDIO_FRAME_H_
 #define API_AUDIO_AUDIO_FRAME_H_
 
-#include <stdint.h>
-#include <stdlib.h>
+#include <stddef.h>
 
 #include "rtc_base/constructormagic.h"
-#include "rtc_base/deprecation.h"
 #include "typedefs.h"  // NOLINT(build/include)
 
 namespace webrtc {
@@ -68,17 +66,6 @@
   // ResetWithoutMuting() to skip this wasteful zeroing.
   void ResetWithoutMuting();
 
-  // TODO(solenberg): Remove once downstream users of AudioFrame have updated.
-  RTC_DEPRECATED
-      void UpdateFrame(int id, uint32_t timestamp, const int16_t* data,
-                       size_t samples_per_channel, int sample_rate_hz,
-                       SpeechType speech_type, VADActivity vad_activity,
-                       size_t num_channels = 1) {
-    RTC_UNUSED(id);
-    UpdateFrame(timestamp, data, samples_per_channel, sample_rate_hz,
-                speech_type, vad_activity, num_channels);
-  }
-
   void UpdateFrame(uint32_t timestamp, const int16_t* data,
                    size_t samples_per_channel, int sample_rate_hz,
                    SpeechType speech_type, VADActivity vad_activity,
@@ -108,13 +95,6 @@
   // Frame is muted by default.
   bool muted() const;
 
-  // These methods are deprecated. Use the functions in
-  // webrtc/audio/utility instead. These methods will exists for a
-  // short period of time until webrtc clients have updated. See
-  // webrtc:6548 for details.
-  RTC_DEPRECATED AudioFrame& operator>>=(const int rhs);
-  RTC_DEPRECATED AudioFrame& operator+=(const AudioFrame& rhs);
-
   // RTP timestamp of the first sample in the AudioFrame.
   uint32_t timestamp_ = 0;
   // Time since the first frame in milliseconds.
diff --git a/api/audio/echo_canceller3_config.cc b/api/audio/echo_canceller3_config.cc
index d74d7a8..c17b0d5 100644
--- a/api/audio/echo_canceller3_config.cc
+++ b/api/audio/echo_canceller3_config.cc
@@ -12,5 +12,9 @@
 namespace webrtc {
 
 EchoCanceller3Config::EchoCanceller3Config() = default;
+EchoCanceller3Config::EchoCanceller3Config(const EchoCanceller3Config& e) =
+    default;
+EchoCanceller3Config::Mask::Mask() = default;
+EchoCanceller3Config::Mask::Mask(const EchoCanceller3Config::Mask& m) = default;
 
 }  // namespace webrtc
diff --git a/api/audio/echo_canceller3_config.h b/api/audio/echo_canceller3_config.h
index fd5bf09..174ef6c 100644
--- a/api/audio/echo_canceller3_config.h
+++ b/api/audio/echo_canceller3_config.h
@@ -18,7 +18,7 @@
 // Configuration struct for EchoCanceller3
 struct EchoCanceller3Config {
   EchoCanceller3Config();
-
+  EchoCanceller3Config(const EchoCanceller3Config& e);
   struct Delay {
     size_t default_delay = 5;
     size_t down_sampling_factor = 4;
@@ -57,7 +57,7 @@
 
   struct Erle {
     float min = 1.f;
-    float max_l = 8.f;
+    float max_l = 4.f;
     float max_h = 1.5f;
   } erle;
 
@@ -71,20 +71,32 @@
   } ep_strength;
 
   struct Mask {
+    Mask();
+    Mask(const Mask& m);
     float m1 = 0.01f;
     float m2 = 0.0001f;
     float m3 = 0.01f;
-    float m4 = 0.1f;
-    float m5 = 0.1f;
+    float m5 = 0.01f;
     float m6 = 0.0001f;
     float m7 = 0.01f;
     float m8 = 0.0001f;
     float m9 = 0.1f;
+
+    float gain_curve_offset = 1.45f;
+    float gain_curve_slope = 5.f;
+    float temporal_masking_lf = 0.9f;
+    float temporal_masking_hf = 0.6f;
+    size_t temporal_masking_lf_bands = 3;
   } gain_mask;
 
   struct EchoAudibility {
     float low_render_limit = 4 * 64.f;
     float normal_render_limit = 64.f;
+    float floor_power = 2 * 64.f;
+    float audibility_threshold_lf = 10;
+    float audibility_threshold_mf = 10;
+    float audibility_threshold_hf = 10;
+    bool use_stationary_properties = false;
   } echo_audibility;
 
   struct RenderLevels {
@@ -120,6 +132,22 @@
 
     bool has_clock_drift = false;
   } echo_removal_control;
+
+  struct EchoModel {
+    size_t noise_floor_hold = 50;
+    float min_noise_floor_power = 1638400.f;
+    float stationary_gate_slope = 10.f;
+    float noise_gate_power = 27509.42f;
+    float noise_gate_slope = 0.3f;
+    size_t render_pre_window_size = 1;
+    size_t render_post_window_size = 1;
+    float nonlinear_hold = 1;
+    float nonlinear_release = 0.001f;
+  } echo_model;
+
+  struct Suppressor {
+    size_t bands_with_reliable_coherence = 5;
+  } suppressor;
 };
 }  // namespace webrtc
 
diff --git a/api/audio/echo_control.h b/api/audio/echo_control.h
index 021bbf8..f549f40 100644
--- a/api/audio/echo_control.h
+++ b/api/audio/echo_control.h
@@ -38,6 +38,9 @@
   // Collect current metrics from the echo controller.
   virtual Metrics GetMetrics() const = 0;
 
+  // Provides an optional external estimate of the audio buffer delay.
+  virtual void SetAudioBufferDelay(size_t delay_ms) = 0;
+
   virtual ~EchoControl() {}
 };
 
diff --git a/api/audio_options.cc b/api/audio_options.cc
new file mode 100644
index 0000000..c196d7d
--- /dev/null
+++ b/api/audio_options.cc
@@ -0,0 +1,18 @@
+/*
+ *  Copyright (c) 2018 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 "api/audio_options.h"
+
+namespace cricket {
+
+AudioOptions::AudioOptions() = default;
+AudioOptions::~AudioOptions() = default;
+
+}  // namespace cricket
diff --git a/api/audio_options.h b/api/audio_options.h
index 8d2880b..5d69842 100644
--- a/api/audio_options.h
+++ b/api/audio_options.h
@@ -23,6 +23,8 @@
 // We are moving all of the setting of options to structs like this,
 // but some things currently still use flags.
 struct AudioOptions {
+  AudioOptions();
+  ~AudioOptions();
   void SetAll(const AudioOptions& change) {
     SetFrom(&echo_cancellation, change.echo_cancellation);
 #if defined(WEBRTC_IOS)
diff --git a/api/candidate.cc b/api/candidate.cc
index 62cd1bd..d51fb84 100644
--- a/api/candidate.cc
+++ b/api/candidate.cc
@@ -73,8 +73,9 @@
       sensitive ? address_.ToSensitiveString() : address_.ToString();
   ost << "Cand[" << transport_name_ << ":" << foundation_ << ":" << component_
       << ":" << protocol_ << ":" << priority_ << ":" << address << ":" << type_
-      << ":" << related_address_ << ":" << username_ << ":" << password_ << ":"
-      << network_id_ << ":" << network_cost_ << ":" << generation_ << "]";
+      << ":" << related_address_.ToString() << ":" << username_ << ":"
+      << password_ << ":" << network_id_ << ":" << network_cost_ << ":"
+      << generation_ << "]";
   return ost.str();
 }
 
diff --git a/api/optional.cc b/api/optional.cc
index 9412617..0f74bd2 100644
--- a/api/optional.cc
+++ b/api/optional.cc
@@ -15,7 +15,7 @@
 
 #if RTC_HAS_ASAN
 
-void* FunctionThatDoesNothingImpl(void* x) {
+const void* FunctionThatDoesNothingImpl(const void* x) {
   return x;
 }
 
diff --git a/api/optional.h b/api/optional.h
index 7a62335..ba06831 100644
--- a/api/optional.h
+++ b/api/optional.h
@@ -33,18 +33,18 @@
 // This is a non-inlined function. The optimizer can't see inside it.  It
 // prevents the compiler from generating optimized code that reads value_ even
 // if it is unset. Although safe, this causes memory sanitizers to complain.
-void* FunctionThatDoesNothingImpl(void*);
+const void* FunctionThatDoesNothingImpl(const void*);
 
 template <typename T>
-inline T* FunctionThatDoesNothing(T* x) {
-  return reinterpret_cast<T*>(
-      FunctionThatDoesNothingImpl(reinterpret_cast<void*>(x)));
+inline const T* FunctionThatDoesNothing(T* x) {
+  return reinterpret_cast<const T*>(
+      FunctionThatDoesNothingImpl(reinterpret_cast<const void*>(x)));
 }
 
 #else
 
 template <typename T>
-inline T* FunctionThatDoesNothing(T* x) {
+inline const T* FunctionThatDoesNothing(T* x) {
   return x;
 }
 
@@ -281,12 +281,6 @@
                       : default_val;
   }
 
-  // Dereference and move value.
-  T MoveValue() {
-    RTC_DCHECK(has_value_);
-    return std::move(value_);
-  }
-
   // Equality tests. Two Optionals are equal if they contain equivalent values,
   // or if they're both empty.
   friend bool operator==(const Optional& m1, const Optional& m2) {
diff --git a/api/optional_unittest.cc b/api/optional_unittest.cc
index 2149033..ad700dc 100644
--- a/api/optional_unittest.cc
+++ b/api/optional_unittest.cc
@@ -861,7 +861,7 @@
   {
     Optional<Logger> x(Logger(42));
     log->push_back("---");
-    Logger moved = x.MoveValue();
+    Logger moved = std::move(x.value());
     log->push_back("---");
   }
   EXPECT_EQ(
diff --git a/api/peerconnectionfactoryproxy.h b/api/peerconnectionfactoryproxy.h
index 7601ed1..7777809 100644
--- a/api/peerconnectionfactoryproxy.h
+++ b/api/peerconnectionfactoryproxy.h
@@ -44,6 +44,10 @@
                 std::unique_ptr<cricket::PortAllocator>,
                 std::unique_ptr<rtc::RTCCertificateGeneratorInterface>,
                 PeerConnectionObserver*);
+  PROXY_METHOD2(rtc::scoped_refptr<PeerConnectionInterface>,
+                CreatePeerConnection,
+                const PeerConnectionInterface::RTCConfiguration&,
+                PeerConnectionDependencies);
   PROXY_METHOD1(rtc::scoped_refptr<MediaStreamInterface>,
                 CreateLocalMediaStream, const std::string&)
   PROXY_METHOD1(rtc::scoped_refptr<AudioSourceInterface>,
diff --git a/api/peerconnectioninterface.h b/api/peerconnectioninterface.h
index 4cb3d65..6e39b4f 100644
--- a/api/peerconnectioninterface.h
+++ b/api/peerconnectioninterface.h
@@ -67,10 +67,6 @@
 #ifndef API_PEERCONNECTIONINTERFACE_H_
 #define API_PEERCONNECTIONINTERFACE_H_
 
-// TODO(sakal): Remove this define after migration to virtual PeerConnection
-// observer is complete.
-#define VIRTUAL_PEERCONNECTION_OBSERVER_DESTRUCTOR
-
 #include <memory>
 #include <string>
 #include <utility>
@@ -158,9 +154,7 @@
   virtual ~StatsObserver() {}
 };
 
-// For now, kDefault is interpreted as kPlanB.
-// TODO(bugs.webrtc.org/8530): Switch default to kUnifiedPlan.
-enum class SdpSemantics { kDefault, kPlanB, kUnifiedPlan };
+enum class SdpSemantics { kPlanB, kUnifiedPlan };
 
 class PeerConnectionInterface : public rtc::RefCountInterface {
  public:
@@ -563,15 +557,12 @@
     // will also cause PeerConnection to ignore all but the first a=ssrc lines
     // that form a Plan B stream.
     //
-    // For users who only send at most one audio and one video track, this
-    // choice does not matter and should be left as kDefault.
-    //
     // For users who wish to send multiple audio/video streams and need to stay
-    // interoperable with legacy WebRTC implementations, specify kPlanB.
+    // interoperable with legacy WebRTC implementations or use legacy APIs,
+    // specify kPlanB.
     //
-    // For users who wish to send multiple audio/video streams and/or wish to
-    // use the new RtpTransceiver API, specify kUnifiedPlan.
-    SdpSemantics sdp_semantics = SdpSemantics::kDefault;
+    // For all other users, specify kUnifiedPlan.
+    SdpSemantics sdp_semantics = SdpSemantics::kPlanB;
 
     //
     // Don't forget to update operator== if adding something.
@@ -809,15 +800,46 @@
     return {};
   }
 
+  // The legacy non-compliant GetStats() API. This correspond to the
+  // callback-based version of getStats() in JavaScript. The returned metrics
+  // are UNDOCUMENTED and many of them rely on implementation-specific details.
+  // The goal is to DELETE THIS VERSION but we can't today because it is heavily
+  // relied upon by third parties. See https://crbug.com/822696.
+  //
+  // This version is wired up into Chrome. Any stats implemented are
+  // automatically exposed to the Web Platform. This has BYPASSED the Chrome
+  // release processes for years and lead to cross-browser incompatibility
+  // issues and web application reliance on Chrome-only behavior.
+  //
+  // This API is in "maintenance mode", serious regressions should be fixed but
+  // adding new stats is highly discouraged.
+  //
+  // TODO(hbos): Deprecate and remove this when third parties have migrated to
+  // the spec-compliant GetStats() API. https://crbug.com/822696
   virtual bool GetStats(StatsObserver* observer,
-                        MediaStreamTrackInterface* track,
+                        MediaStreamTrackInterface* track,  // Optional
                         StatsOutputLevel level) = 0;
-  // Gets stats using the new stats collection API, see webrtc/api/stats/. These
-  // will replace old stats collection API when the new API has matured enough.
-  // TODO(hbos): Default implementation that does nothing only exists as to not
-  // break third party projects. As soon as they have been updated this should
-  // be changed to "= 0;".
+  // The spec-compliant GetStats() API. This correspond to the promise-based
+  // version of getStats() in JavaScript. Implementation status is described in
+  // api/stats/rtcstats_objects.h. For more details on stats, see spec:
+  // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-getstats
+  // TODO(hbos): Takes shared ownership, use rtc::scoped_refptr<> instead. This
+  // requires stop overriding the current version in third party or making third
+  // party calls explicit to avoid ambiguity during switch. Make the future
+  // version abstract as soon as third party projects implement it.
   virtual void GetStats(RTCStatsCollectorCallback* callback) {}
+  // Spec-compliant getStats() performing the stats selection algorithm with the
+  // sender. https://w3c.github.io/webrtc-pc/#dom-rtcrtpsender-getstats
+  // TODO(hbos): Make abstract as soon as third party projects implement it.
+  virtual void GetStats(
+      rtc::scoped_refptr<RtpSenderInterface> selector,
+      rtc::scoped_refptr<RTCStatsCollectorCallback> callback) {}
+  // Spec-compliant getStats() performing the stats selection algorithm with the
+  // receiver. https://w3c.github.io/webrtc-pc/#dom-rtcrtpreceiver-getstats
+  // TODO(hbos): Make abstract as soon as third party projects implement it.
+  virtual void GetStats(
+      rtc::scoped_refptr<RtpReceiverInterface> selector,
+      rtc::scoped_refptr<RTCStatsCollectorCallback> callback) {}
   // Clear cached stats in the RTCStatsCollector.
   // Exposed for testing while waiting for automatic cache clear to work.
   // https://bugs.webrtc.org/8693
@@ -897,13 +919,6 @@
   virtual void SetRemoteDescription(
       std::unique_ptr<SessionDescriptionInterface> desc,
       rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer) {}
-  // Deprecated; Replaced by SetConfiguration.
-  // TODO(deadbeef): Remove once Chrome is moved over to SetConfiguration.
-  virtual bool UpdateIce(const IceServers& configuration,
-                         const MediaConstraintsInterface* constraints) {
-    return false;
-  }
-  virtual bool UpdateIce(const IceServers& configuration) { return false; }
 
   // TODO(deadbeef): Make this pure virtual once all Chrome subclasses of
   // PeerConnectionInterface implement it.
@@ -1125,13 +1140,6 @@
   virtual void OnTrack(
       rtc::scoped_refptr<RtpTransceiverInterface> transceiver) {}
 
-  // TODO(hbos,deadbeef): Add |OnAssociatedStreamsUpdated| with |receiver| and
-  // |streams| as arguments. This should be called when an existing receiver its
-  // associated streams updated. https://crbug.com/webrtc/8315
-  // This may be blocked on supporting multiple streams per sender or else
-  // this may count as the removal and addition of a track?
-  // https://crbug.com/webrtc/7932
-
   // Called when a receiver is completely removed. This is current (Plan B SDP)
   // behavior that occurs when processing the removal of a remote track, and is
   // called when the receiver is removed and the track is muted. When Unified
@@ -1145,6 +1153,30 @@
       rtc::scoped_refptr<RtpReceiverInterface> receiver) {}
 };
 
+// PeerConnectionDependencies holds all of PeerConnections dependencies.
+// A dependency is distinct from a configuration as it defines significant
+// executable code that can be provided by a user of the API.
+//
+// All new dependencies should be added as a unique_ptr to allow the
+// PeerConnection object to be the definitive owner of the dependencies
+// lifetime making injection safer.
+struct PeerConnectionDependencies final {
+  explicit PeerConnectionDependencies(PeerConnectionObserver* observer_in)
+      : observer(observer_in) {}
+  // This object is not copyable or assignable.
+  PeerConnectionDependencies(const PeerConnectionDependencies&) = delete;
+  PeerConnectionDependencies& operator=(const PeerConnectionDependencies&) =
+      delete;
+  // This object is only moveable.
+  PeerConnectionDependencies(PeerConnectionDependencies&&) = default;
+  PeerConnectionDependencies& operator=(PeerConnectionDependencies&&) = default;
+  // Mandatory dependencies
+  PeerConnectionObserver* observer = nullptr;
+  // Optional dependencies
+  std::unique_ptr<cricket::PortAllocator> allocator;
+  std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator;
+};
+
 // PeerConnectionFactoryInterface is the factory interface used for creating
 // PeerConnection, MediaStream and MediaStreamTrack objects.
 //
@@ -1197,8 +1229,18 @@
   // Set the options to be used for subsequently created PeerConnections.
   virtual void SetOptions(const Options& options) = 0;
 
-  // |allocator| and |cert_generator| may be null, in which case default
-  // implementations will be used.
+  // The preferred way to create a new peer connection. Simply provide the
+  // configuration and a PeerConnectionDependencies structure.
+  // TODO(benwright): Make pure virtual once downstream mock PC factory classes
+  // are updated.
+  virtual rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection(
+      const PeerConnectionInterface::RTCConfiguration& configuration,
+      PeerConnectionDependencies dependencies) {
+    return nullptr;
+  }
+
+  // Deprecated; |allocator| and |cert_generator| may be null, in which case
+  // default implementations will be used.
   //
   // |observer| must not be null.
   //
diff --git a/api/peerconnectionproxy.h b/api/peerconnectionproxy.h
index 7235f5b..9325adc 100644
--- a/api/peerconnectionproxy.h
+++ b/api/peerconnectionproxy.h
@@ -70,6 +70,14 @@
                 MediaStreamTrackInterface*,
                 StatsOutputLevel)
   PROXY_METHOD1(void, GetStats, RTCStatsCollectorCallback*)
+  PROXY_METHOD2(void,
+                GetStats,
+                rtc::scoped_refptr<RtpSenderInterface>,
+                rtc::scoped_refptr<RTCStatsCollectorCallback>);
+  PROXY_METHOD2(void,
+                GetStats,
+                rtc::scoped_refptr<RtpReceiverInterface>,
+                rtc::scoped_refptr<RTCStatsCollectorCallback>);
   PROXY_METHOD2(rtc::scoped_refptr<DataChannelInterface>,
                 CreateDataChannel,
                 const std::string&,
diff --git a/api/rtcerror.cc b/api/rtcerror.cc
index f9a31d0..55ac15e 100644
--- a/api/rtcerror.cc
+++ b/api/rtcerror.cc
@@ -93,9 +93,10 @@
   }
 }
 
-std::ostream& operator<<(std::ostream& stream, RTCErrorType error) {
+// TODO(jonasolsson): Change to use absl::string_view when it's available.
+std::string ToString(RTCErrorType error) {
   int index = static_cast<int>(error);
-  return stream << kRTCErrorTypeNames[index];
+  return std::string(kRTCErrorTypeNames[index]);
 }
 
 }  // namespace webrtc
diff --git a/api/rtcerror.h b/api/rtcerror.h
index 962f46d..c87ce91 100644
--- a/api/rtcerror.h
+++ b/api/rtcerror.h
@@ -11,7 +11,9 @@
 #ifndef API_RTCERROR_H_
 #define API_RTCERROR_H_
 
+#ifdef UNIT_TEST
 #include <ostream>
+#endif  // UNIT_TEST
 #include <string>
 #include <utility>  // For std::move.
 
@@ -143,16 +145,24 @@
 // error type.
 //
 // Only intended to be used for logging/disagnostics.
-std::ostream& operator<<(std::ostream& stream, RTCErrorType error);
+std::string ToString(RTCErrorType error);
+
+#ifdef UNIT_TEST
+inline std::ostream& operator<<(  // no-presubmit-check TODO(webrtc:8982)
+    std::ostream& stream,         // no-presubmit-check TODO(webrtc:8982)
+    RTCErrorType error) {
+  return stream << ToString(error);
+}
+#endif  // UNIT_TEST
 
 // Helper macro that can be used by implementations to create an error with a
 // message and log it. |message| should be a string literal or movable
 // std::string.
-#define LOG_AND_RETURN_ERROR_EX(type, message, severity) \
-  {                                                      \
-    RTC_DCHECK(type != RTCErrorType::NONE);              \
-    RTC_LOG(severity) << message << " (" << type << ")"; \
-    return webrtc::RTCError(type, message);              \
+#define LOG_AND_RETURN_ERROR_EX(type, message, severity)           \
+  {                                                                \
+    RTC_DCHECK(type != RTCErrorType::NONE);                        \
+    RTC_LOG(severity) << message << " (" << ToString(type) << ")"; \
+    return webrtc::RTCError(type, message);                        \
   }
 
 #define LOG_AND_RETURN_ERROR(type, message) \
diff --git a/api/rtcerror_unittest.cc b/api/rtcerror_unittest.cc
index d8f7ca6..90593cf 100644
--- a/api/rtcerror_unittest.cc
+++ b/api/rtcerror_unittest.cc
@@ -58,15 +58,6 @@
 
 namespace webrtc {
 
-// Simple test for ostream operator for RTCErrorType.
-TEST(RTCErrorTypeTest, OstreamOperator) {
-  std::ostringstream oss;
-  oss << webrtc::RTCErrorType::NONE << ' '
-      << webrtc::RTCErrorType::INVALID_PARAMETER << ' '
-      << webrtc::RTCErrorType::INTERNAL_ERROR;
-  EXPECT_EQ("NONE INVALID_PARAMETER INTERNAL_ERROR", oss.str());
-}
-
 // Test that the default constructor creates a "no error" error.
 TEST(RTCErrorTest, DefaultConstructor) {
   RTCError e;
diff --git a/api/rtpparameters.cc b/api/rtpparameters.cc
index 79fd3a9..cb9c1cf 100644
--- a/api/rtpparameters.cc
+++ b/api/rtpparameters.cc
@@ -114,6 +114,9 @@
     "http://www.webrtc.org/experiments/rtp-hdrext/video-timing";
 const int RtpExtension::kVideoTimingDefaultId = 8;
 
+const char RtpExtension::kMidUri[] = "urn:ietf:params:rtp-hdrext:sdes:mid";
+const int RtpExtension::kMidDefaultId = 9;
+
 const char RtpExtension::kEncryptHeaderExtensionsUri[] =
     "urn:ietf:params:rtp-hdrext:encrypt";
 
@@ -122,7 +125,8 @@
 
 bool RtpExtension::IsSupportedForAudio(const std::string& uri) {
   return uri == webrtc::RtpExtension::kAudioLevelUri ||
-         uri == webrtc::RtpExtension::kTransportSequenceNumberUri;
+         uri == webrtc::RtpExtension::kTransportSequenceNumberUri ||
+         uri == webrtc::RtpExtension::kMidUri;
 }
 
 bool RtpExtension::IsSupportedForVideo(const std::string& uri) {
@@ -132,7 +136,8 @@
          uri == webrtc::RtpExtension::kTransportSequenceNumberUri ||
          uri == webrtc::RtpExtension::kPlayoutDelayUri ||
          uri == webrtc::RtpExtension::kVideoContentTypeUri ||
-         uri == webrtc::RtpExtension::kVideoTimingUri;
+         uri == webrtc::RtpExtension::kVideoTimingUri ||
+         uri == webrtc::RtpExtension::kMidUri;
 }
 
 bool RtpExtension::IsEncryptionSupported(const std::string& uri) {
@@ -149,7 +154,8 @@
          uri == webrtc::RtpExtension::kVideoRotationUri ||
          uri == webrtc::RtpExtension::kTransportSequenceNumberUri ||
          uri == webrtc::RtpExtension::kPlayoutDelayUri ||
-         uri == webrtc::RtpExtension::kVideoContentTypeUri;
+         uri == webrtc::RtpExtension::kVideoContentTypeUri ||
+         uri == webrtc::RtpExtension::kMidUri;
 }
 
 const RtpExtension* RtpExtension::FindHeaderExtensionByUri(
diff --git a/api/rtpparameters.h b/api/rtpparameters.h
index d9ac1b6..12e0419 100644
--- a/api/rtpparameters.h
+++ b/api/rtpparameters.h
@@ -276,6 +276,11 @@
   static const char kPlayoutDelayUri[];
   static const int kPlayoutDelayDefaultId;
 
+  // Header extension for identifying media section within a transport.
+  // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-49#section-15
+  static const char kMidUri[];
+  static const int kMidDefaultId;
+
   // Encryption of Header Extensions, see RFC 6904 for details:
   // https://tools.ietf.org/html/rfc6904
   static const char kEncryptHeaderExtensionsUri[];
diff --git a/api/rtpsenderinterface.h b/api/rtpsenderinterface.h
index 2ca2edc..01279a5 100644
--- a/api/rtpsenderinterface.h
+++ b/api/rtpsenderinterface.h
@@ -48,8 +48,9 @@
   // to uniquely identify a receiver until we implement Unified Plan SDP.
   virtual std::string id() const = 0;
 
-  // Returns a list of streams associated with this sender's track. Although we
-  // only support one track per stream, in theory the API allows for multiple.
+  // Returns a list of media stream ids associated with this sender's track.
+  // These are signalled in the SDP so that the remote side can associate
+  // tracks.
   virtual std::vector<std::string> stream_ids() const = 0;
 
   virtual RtpParameters GetParameters() const = 0;
diff --git a/api/rtptransceiverinterface.h b/api/rtptransceiverinterface.h
index 3ea75fd..7d2a1df 100644
--- a/api/rtptransceiverinterface.h
+++ b/api/rtptransceiverinterface.h
@@ -14,6 +14,7 @@
 #include <string>
 #include <vector>
 
+#include "api/array_view.h"
 #include "api/optional.h"
 #include "api/rtpreceiverinterface.h"
 #include "api/rtpsenderinterface.h"
@@ -29,9 +30,6 @@
   kInactive
 };
 
-// This is provided as a debugging aid. The format of the output is unspecified.
-std::ostream& operator<<(std::ostream& os, RtpTransceiverDirection direction);
-
 // Structure for initializing an RtpTransceiver in a call to
 // PeerConnectionInterface::AddTransceiver.
 // https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiverinit
@@ -40,9 +38,6 @@
   RtpTransceiverDirection direction = RtpTransceiverDirection::kSendRecv;
 
   // The added RtpTransceiver will be added to these streams.
-  // TODO(shampson): Change name to stream_id & update native wrapper's naming
-  // as well.
-  // TODO(bugs.webrtc.org/7600): Not implemented.
   std::vector<std::string> stream_ids;
 
   // TODO(bugs.webrtc.org/7600): Not implemented.
diff --git a/api/umametrics.h b/api/umametrics.h
index 4de1ce4..f885416 100644
--- a/api/umametrics.h
+++ b/api/umametrics.h
@@ -42,6 +42,9 @@
   kEnumCounterSdpSemanticNegotiated,
   kEnumCounterKeyProtocolMediaType,
   kEnumCounterSdpFormatReceived,
+  // The next 2 counters log the value of srtp_err_status_t defined in libsrtp.
+  kEnumCounterSrtpUnprotectError,
+  kEnumCounterSrtcpUnprotectError,
   kPeerConnectionEnumCounterMax
 };
 
diff --git a/api/video/encoded_frame.cc b/api/video/encoded_frame.cc
new file mode 100644
index 0000000..37da35f
--- /dev/null
+++ b/api/video/encoded_frame.cc
@@ -0,0 +1,19 @@
+/*
+ *  Copyright (c) 2018 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 "api/video/encoded_frame.h"
+
+namespace webrtc {
+namespace video_coding {
+
+bool EncodedFrame::delayed_by_retransmission() const { return 0; }
+
+}  // namespace video_coding
+}  // namespace webrtc
diff --git a/api/video/encoded_frame.h b/api/video/encoded_frame.h
index 1374ea0..1e919d0 100644
--- a/api/video/encoded_frame.h
+++ b/api/video/encoded_frame.h
@@ -16,7 +16,39 @@
 namespace webrtc {
 namespace video_coding {
 
+// NOTE: This class is still under development and may change without notice.
+struct VideoLayerFrameId {
+  // TODO(philipel): The default ctor is currently used internaly, but have a
+  //                 look if we can remove it.
+  VideoLayerFrameId() : picture_id(-1), spatial_layer(0) {}
+  VideoLayerFrameId(int64_t picture_id, uint8_t spatial_layer)
+      : picture_id(picture_id), spatial_layer(spatial_layer) {}
+
+  bool operator==(const VideoLayerFrameId& rhs) const {
+    return picture_id == rhs.picture_id && spatial_layer == rhs.spatial_layer;
+  }
+
+  bool operator!=(const VideoLayerFrameId& rhs) const {
+    return !(*this == rhs);
+  }
+
+  bool operator<(const VideoLayerFrameId& rhs) const {
+    if (picture_id == rhs.picture_id)
+      return spatial_layer < rhs.spatial_layer;
+    return picture_id < rhs.picture_id;
+  }
+
+  bool operator<=(const VideoLayerFrameId& rhs) const { return !(rhs < *this); }
+  bool operator>(const VideoLayerFrameId& rhs) const { return rhs < *this; }
+  bool operator>=(const VideoLayerFrameId& rhs) const { return rhs <= *this; }
+
+  int64_t picture_id;
+  uint8_t spatial_layer;
+};
+
 // TODO(philipel): Remove webrtc::VCMEncodedFrame inheritance.
+// TODO(philipel): Move transport specific info out of EncodedFrame.
+// NOTE: This class is still under development and may change without notice.
 class EncodedFrame : public webrtc::VCMEncodedFrame {
  public:
   static const uint8_t kMaxFrameReferences = 5;
@@ -38,17 +70,13 @@
   // This information is currently needed by the timing calculation class.
   // TODO(philipel): Remove this function when a new timing class has
   //                 been implemented.
-  virtual bool delayed_by_retransmission() const { return 0; }
+  virtual bool delayed_by_retransmission() const;
 
   size_t size() const { return _length; }
 
   bool is_keyframe() const { return num_references == 0; }
 
-  // The tuple (|picture_id|, |spatial_layer|) uniquely identifies a frame
-  // object. For codec types that don't necessarily have picture ids they
-  // have to be constructed from the header data relevant to that codec.
-  int64_t picture_id = 0;
-  uint8_t spatial_layer = 0;
+  VideoLayerFrameId id;
   uint32_t timestamp = 0;
 
   // TODO(philipel): Add simple modify/access functions to prevent adding too
diff --git a/api/video/i420_buffer.h b/api/video/i420_buffer.h
index bdac80b..2bd37bd 100644
--- a/api/video/i420_buffer.h
+++ b/api/video/i420_buffer.h
@@ -13,9 +13,9 @@
 
 #include <memory>
 
-#include "api/video/video_rotation.h"
 #include "api/video/video_frame_buffer.h"
-#include "system_wrappers/include/aligned_malloc.h"
+#include "api/video/video_rotation.h"
+#include "rtc_base/memory/aligned_malloc.h"
 
 namespace webrtc {
 
diff --git a/api/video/video_bitrate_allocation.cc b/api/video/video_bitrate_allocation.cc
new file mode 100644
index 0000000..059eb8f
--- /dev/null
+++ b/api/video/video_bitrate_allocation.cc
@@ -0,0 +1,168 @@
+/*
+ *  Copyright (c) 2018 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 "api/video/video_bitrate_allocation.h"
+
+#include <limits>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/numerics/safe_conversions.h"
+#include "rtc_base/strings/string_builder.h"
+#include "rtc_base/stringutils.h"
+
+namespace webrtc {
+
+VideoBitrateAllocation::VideoBitrateAllocation() : sum_(0) {}
+
+bool VideoBitrateAllocation::SetBitrate(size_t spatial_index,
+                                        size_t temporal_index,
+                                        uint32_t bitrate_bps) {
+  RTC_CHECK_LT(spatial_index, kMaxSpatialLayers);
+  RTC_CHECK_LT(temporal_index, kMaxTemporalStreams);
+  int64_t new_bitrate_sum_bps = sum_;
+  rtc::Optional<uint32_t>& layer_bitrate =
+      bitrates_[spatial_index][temporal_index];
+  if (layer_bitrate) {
+    RTC_DCHECK_LE(*layer_bitrate, sum_);
+    new_bitrate_sum_bps -= *layer_bitrate;
+  }
+  new_bitrate_sum_bps += bitrate_bps;
+  if (new_bitrate_sum_bps > kMaxBitrateBps)
+    return false;
+
+  layer_bitrate = bitrate_bps;
+  sum_ = rtc::dchecked_cast<uint32_t>(new_bitrate_sum_bps);
+  return true;
+}
+
+bool VideoBitrateAllocation::HasBitrate(size_t spatial_index,
+                                        size_t temporal_index) const {
+  RTC_CHECK_LT(spatial_index, kMaxSpatialLayers);
+  RTC_CHECK_LT(temporal_index, kMaxTemporalStreams);
+  return bitrates_[spatial_index][temporal_index].has_value();
+}
+
+uint32_t VideoBitrateAllocation::GetBitrate(size_t spatial_index,
+                                            size_t temporal_index) const {
+  RTC_CHECK_LT(spatial_index, kMaxSpatialLayers);
+  RTC_CHECK_LT(temporal_index, kMaxTemporalStreams);
+  return bitrates_[spatial_index][temporal_index].value_or(0);
+}
+
+// Whether the specific spatial layers has the bitrate set in any of its
+// temporal layers.
+bool VideoBitrateAllocation::IsSpatialLayerUsed(size_t spatial_index) const {
+  RTC_CHECK_LT(spatial_index, kMaxSpatialLayers);
+  for (size_t i = 0; i < kMaxTemporalStreams; ++i) {
+    if (bitrates_[spatial_index][i].has_value())
+      return true;
+  }
+  return false;
+}
+
+// Get the sum of all the temporal layer for a specific spatial layer.
+uint32_t VideoBitrateAllocation::GetSpatialLayerSum(
+    size_t spatial_index) const {
+  RTC_CHECK_LT(spatial_index, kMaxSpatialLayers);
+  return GetTemporalLayerSum(spatial_index, kMaxTemporalStreams - 1);
+}
+
+uint32_t VideoBitrateAllocation::GetTemporalLayerSum(
+    size_t spatial_index,
+    size_t temporal_index) const {
+  RTC_CHECK_LT(spatial_index, kMaxSpatialLayers);
+  RTC_CHECK_LT(temporal_index, kMaxTemporalStreams);
+  uint32_t sum = 0;
+  for (size_t i = 0; i <= temporal_index; ++i) {
+    sum += bitrates_[spatial_index][i].value_or(0);
+  }
+  return sum;
+}
+
+std::vector<uint32_t> VideoBitrateAllocation::GetTemporalLayerAllocation(
+    size_t spatial_index) const {
+  RTC_CHECK_LT(spatial_index, kMaxSpatialLayers);
+  std::vector<uint32_t> temporal_rates;
+
+  // Find the highest temporal layer with a defined bitrate in order to
+  // determine the size of the temporal layer allocation.
+  for (size_t i = kMaxTemporalStreams; i > 0; --i) {
+    if (bitrates_[spatial_index][i - 1].has_value()) {
+      temporal_rates.resize(i);
+      break;
+    }
+  }
+
+  for (size_t i = 0; i < temporal_rates.size(); ++i) {
+    temporal_rates[i] = bitrates_[spatial_index][i].value_or(0);
+  }
+
+  return temporal_rates;
+}
+
+bool VideoBitrateAllocation::operator==(
+    const VideoBitrateAllocation& other) const {
+  for (size_t si = 0; si < kMaxSpatialLayers; ++si) {
+    for (size_t ti = 0; ti < kMaxTemporalStreams; ++ti) {
+      if (bitrates_[si][ti] != other.bitrates_[si][ti])
+        return false;
+    }
+  }
+  return true;
+}
+
+std::string VideoBitrateAllocation::ToString() const {
+  if (sum_ == 0)
+    return "VideoBitrateAllocation [ [] ]";
+
+  // Max string length in practice is 260, but let's have some overhead and
+  // round up to nearest power of two.
+  char string_buf[512];
+  rtc::SimpleStringBuilder ssb(string_buf);
+
+  ssb << "VideoBitrateAllocation [";
+  uint32_t spatial_cumulator = 0;
+  for (size_t si = 0; si < kMaxSpatialLayers; ++si) {
+    RTC_DCHECK_LE(spatial_cumulator, sum_);
+    if (spatial_cumulator == sum_)
+      break;
+
+    const uint32_t layer_sum = GetSpatialLayerSum(si);
+    if (layer_sum == sum_) {
+      ssb << " [";
+    } else {
+      if (si > 0)
+        ssb << ",";
+      ssb << '\n' << "  [";
+    }
+    spatial_cumulator += layer_sum;
+
+    uint32_t temporal_cumulator = 0;
+    for (size_t ti = 0; ti < kMaxTemporalStreams; ++ti) {
+      RTC_DCHECK_LE(temporal_cumulator, layer_sum);
+      if (temporal_cumulator == layer_sum)
+        break;
+
+      if (ti > 0)
+        ssb << ", ";
+
+      uint32_t bitrate = bitrates_[si][ti].value_or(0);
+      ssb << bitrate;
+      temporal_cumulator += bitrate;
+    }
+    ssb << "]";
+  }
+
+  RTC_DCHECK_EQ(spatial_cumulator, sum_);
+  ssb << " ]";
+  return ssb.str();
+}
+
+}  // namespace webrtc
diff --git a/api/video/video_bitrate_allocation.h b/api/video/video_bitrate_allocation.h
new file mode 100644
index 0000000..b748b67
--- /dev/null
+++ b/api/video/video_bitrate_allocation.h
@@ -0,0 +1,85 @@
+/*
+ *  Copyright (c) 2018 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 API_VIDEO_VIDEO_BITRATE_ALLOCATION_H_
+#define API_VIDEO_VIDEO_BITRATE_ALLOCATION_H_
+
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "api/optional.h"
+#include "typedefs.h"  // NOLINT(build/include)
+
+namespace webrtc {
+
+// TODO(sprang): Move back to common_types when include of this is removed.
+enum : int { kMaxSimulcastStreams = 4 };
+enum : int { kMaxSpatialLayers = 5 };
+enum : int { kMaxTemporalStreams = 4 };
+
+// Class that describes how video bitrate, in bps, is allocated across temporal
+// and spatial layers. Not that bitrates are NOT cumulative. Depending on if
+// layers are dependent or not, it is up to the user to aggregate.
+// For each index, the bitrate can also both set and unset. This is used with a
+// set bps = 0 to signal an explicit "turn off" signal.
+class VideoBitrateAllocation {
+ public:
+  static constexpr uint32_t kMaxBitrateBps =
+      std::numeric_limits<uint32_t>::max();
+  VideoBitrateAllocation();
+
+  bool SetBitrate(size_t spatial_index,
+                  size_t temporal_index,
+                  uint32_t bitrate_bps);
+
+  bool HasBitrate(size_t spatial_index, size_t temporal_index) const;
+
+  uint32_t GetBitrate(size_t spatial_index, size_t temporal_index) const;
+
+  // Whether the specific spatial layers has the bitrate set in any of its
+  // temporal layers.
+  bool IsSpatialLayerUsed(size_t spatial_index) const;
+
+  // Get the sum of all the temporal layer for a specific spatial layer.
+  uint32_t GetSpatialLayerSum(size_t spatial_index) const;
+
+  // Sum of bitrates of temporal layers, from layer 0 to |temporal_index|
+  // inclusive, of specified spatial layer |spatial_index|. Bitrates of lower
+  // spatial layers are not included.
+  uint32_t GetTemporalLayerSum(size_t spatial_index,
+                               size_t temporal_index) const;
+
+  // Returns a vector of the temporal layer bitrates for the specific spatial
+  // layer. Length of the returned vector is cropped to the highest temporal
+  // layer with a defined bitrate.
+  std::vector<uint32_t> GetTemporalLayerAllocation(size_t spatial_index) const;
+
+  uint32_t get_sum_bps() const { return sum_; }  // Sum of all bitrates.
+  uint32_t get_sum_kbps() const {
+    // Round down to not exceed the allocated bitrate.
+    return sum_ / 1000;
+  }
+
+  bool operator==(const VideoBitrateAllocation& other) const;
+  inline bool operator!=(const VideoBitrateAllocation& other) const {
+    return !(*this == other);
+  }
+
+  std::string ToString() const;
+
+ private:
+  uint32_t sum_;
+  rtc::Optional<uint32_t> bitrates_[kMaxSpatialLayers][kMaxTemporalStreams];
+};
+
+}  // namespace webrtc
+
+#endif  // API_VIDEO_VIDEO_BITRATE_ALLOCATION_H_
diff --git a/api/video/video_stream_decoder.h b/api/video/video_stream_decoder.h
new file mode 100644
index 0000000..1c4c5ff
--- /dev/null
+++ b/api/video/video_stream_decoder.h
@@ -0,0 +1,51 @@
+/*
+ *  Copyright (c) 2018 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 API_VIDEO_VIDEO_STREAM_DECODER_H_
+#define API_VIDEO_VIDEO_STREAM_DECODER_H_
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "api/video/encoded_frame.h"
+#include "api/video/video_frame.h"
+#include "api/video_codecs/sdp_video_format.h"
+#include "api/video_codecs/video_decoder_factory.h"
+
+namespace webrtc {
+// NOTE: This class is still under development and may change without notice.
+class VideoStreamDecoder {
+ public:
+  class Callbacks {
+   public:
+    virtual ~Callbacks() = default;
+
+    // Called when the VideoStreamDecoder enters a non-decodable state.
+    virtual void OnNonDecodableState() = 0;
+
+    // Called with the last continuous frame.
+    virtual void OnContinuousUntil(
+        const video_coding::VideoLayerFrameId& key) = 0;
+
+    // Called with the decoded frame.
+    virtual void OnDecodedFrame(VideoFrame decodedImage,
+                                rtc::Optional<int> decode_time_ms,
+                                rtc::Optional<int> qp) = 0;
+  };
+
+  virtual ~VideoStreamDecoder() = default;
+
+  virtual void OnFrame(std::unique_ptr<video_coding::EncodedFrame> frame) = 0;
+};
+
+}  // namespace webrtc
+
+#endif  // API_VIDEO_VIDEO_STREAM_DECODER_H_
diff --git a/api/video/video_stream_decoder_create.cc b/api/video/video_stream_decoder_create.cc
new file mode 100644
index 0000000..e756096
--- /dev/null
+++ b/api/video/video_stream_decoder_create.cc
@@ -0,0 +1,24 @@
+/*
+ *  Copyright (c) 2018 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 "api/video/video_stream_decoder_create.h"
+
+#include "rtc_base/ptr_util.h"
+#include "video/video_stream_decoder_impl.h"
+
+namespace webrtc {
+std::unique_ptr<VideoStreamDecoder> CreateVideoStreamDecoder(
+    VideoStreamDecoder::Callbacks* callbacks,
+    VideoDecoderFactory* decoder_factory,
+    std::map<int, std::pair<SdpVideoFormat, int>> decoder_settings) {
+  return rtc::MakeUnique<VideoStreamDecoderImpl>(callbacks, decoder_factory,
+                                                 std::move(decoder_settings));
+}
+}  // namespace webrtc
diff --git a/api/video/video_stream_decoder_create.h b/api/video/video_stream_decoder_create.h
new file mode 100644
index 0000000..0468290
--- /dev/null
+++ b/api/video/video_stream_decoder_create.h
@@ -0,0 +1,32 @@
+/*
+ *  Copyright (c) 2018 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 API_VIDEO_VIDEO_STREAM_DECODER_CREATE_H_
+#define API_VIDEO_VIDEO_STREAM_DECODER_CREATE_H_
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "api/video/video_stream_decoder.h"
+
+namespace webrtc {
+// The |decoder_settings| parameter is a map between:
+// <payload type> -->  <<video format>, <number of cores>>.
+// The video format is used when instantiating a decoder, and
+// the number of cores is used when initializing the decoder.
+std::unique_ptr<VideoStreamDecoder> CreateVideoStreamDecoder(
+    VideoStreamDecoder::Callbacks* callbacks,
+    VideoDecoderFactory* decoder_factory,
+    std::map<int, std::pair<SdpVideoFormat, int>> decoder_settings);
+
+}  // namespace webrtc
+
+#endif  // API_VIDEO_VIDEO_STREAM_DECODER_CREATE_H_
diff --git a/api/videosourceinterface.cc b/api/videosourceinterface.cc
index 5eda369..8cf8202 100644
--- a/api/videosourceinterface.cc
+++ b/api/videosourceinterface.cc
@@ -13,6 +13,7 @@
 namespace rtc {
 
 VideoSinkWants::VideoSinkWants() = default;
+VideoSinkWants::VideoSinkWants(const VideoSinkWants&) = default;
 VideoSinkWants::~VideoSinkWants() = default;
 
 }  // namespace rtc
diff --git a/api/videosourceinterface.h b/api/videosourceinterface.h
index ffb017a..065e2dc 100644
--- a/api/videosourceinterface.h
+++ b/api/videosourceinterface.h
@@ -22,6 +22,7 @@
 // should have when it is delivered to a certain sink.
 struct VideoSinkWants {
   VideoSinkWants();
+  VideoSinkWants(const VideoSinkWants&);
   ~VideoSinkWants();
   // Tells the source whether the sink wants frames with rotation applied.
   // By default, any rotation must be applied by the sink.
diff --git a/audio/BUILD.gn b/audio/BUILD.gn
index b086971..8981dd3 100644
--- a/audio/BUILD.gn
+++ b/audio/BUILD.gn
@@ -52,6 +52,7 @@
     "../api:optional",
     "../api:transport_api",
     "../api/audio:aec3_factory",
+    "../api/audio:audio_frame_api",
     "../api/audio:audio_mixer_api",
     "../api/audio_codecs:audio_codecs_api",
     "../api/audio_codecs:builtin_audio_encoder_factory",
@@ -62,7 +63,6 @@
     "../common_audio:common_audio_c",
     "../logging:rtc_event_audio",
     "../logging:rtc_event_log_api",
-    "../modules:module_api",
     "../modules/audio_coding",
     "../modules/audio_coding:audio_format_conversion",
     "../modules/audio_coding:audio_network_adaptor_config",
@@ -75,6 +75,7 @@
     "../modules/rtp_rtcp",
     "../modules/rtp_rtcp:rtp_rtcp_format",
     "../modules/utility",
+    "../rtc_base:audio_format_to_string",
     "../rtc_base:checks",
     "../rtc_base:rate_limiter",
     "../rtc_base:rtc_base",
@@ -126,13 +127,13 @@
       ":audio",
       ":audio_end_to_end_test",
       "../api:mock_audio_mixer",
+      "../api/audio:audio_frame_api",
       "../call:mock_call_interfaces",
       "../call:mock_rtp_interfaces",
       "../call:rtp_interfaces",
       "../call:rtp_receiver",
       "../common_audio",
       "../logging:mocks",
-      "../modules:module_api",
       "../modules/audio_device:mock_audio_device",
       "../modules/audio_mixer:audio_mixer_impl",
       "../modules/audio_processing:audio_processing_statistics",
@@ -189,11 +190,34 @@
       }
 
       data = [
-        "../resources/voice_engine/audio_dtx16.wav",
         "../resources/voice_engine/audio_tiny16.wav",
         "../resources/voice_engine/audio_tiny48.wav",
-        "test/low_bandwidth_audio_test.py",
       ]
+
+      if (!build_with_chromium && is_clang) {
+        # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163)
+        suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+      }
+    }
+
+    group("low_bandwidth_audio_perf_test") {
+      testonly = true
+
+      deps = [
+        ":low_bandwidth_audio_test",
+      ]
+
+      data = [
+        "test/low_bandwidth_audio_test.py",
+        "../resources/voice_engine/audio_tiny16.wav",
+        "../resources/voice_engine/audio_tiny48.wav",
+      ]
+      if (is_win) {
+        data += [ "${root_out_dir}/low_bandwidth_audio_test.exe" ]
+      } else {
+        data += [ "${root_out_dir}/low_bandwidth_audio_test" ]
+      }
+
       if (is_linux || is_android) {
         data += [
           "../tools_webrtc/audio_quality/linux/PolqaOem64",
@@ -212,10 +236,7 @@
         data += [ "../tools_webrtc/audio_quality/mac/pesq" ]
       }
 
-      if (!build_with_chromium && is_clang) {
-        # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163)
-        suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
-      }
+      write_runtime_deps = "${root_out_dir}/${target_name}.runtime_deps"
     }
   }
 
diff --git a/audio/audio_level.cc b/audio/audio_level.cc
index ca52522..f1c5b68 100644
--- a/audio/audio_level.cc
+++ b/audio/audio_level.cc
@@ -10,8 +10,8 @@
 
 #include "audio/audio_level.h"
 
+#include "api/audio/audio_frame.h"
 #include "common_audio/signal_processing/include/signal_processing_library.h"
-#include "modules/include/module_common_types.h"
 
 namespace webrtc {
 namespace voe {
diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc
index 8a1fbed..352261e 100644
--- a/audio/audio_receive_stream.cc
+++ b/audio/audio_receive_stream.cc
@@ -70,13 +70,12 @@
   RTC_DCHECK(audio_state);
   internal::AudioState* internal_audio_state =
       static_cast<internal::AudioState*>(audio_state);
-  return std::unique_ptr<voe::ChannelProxy>(new voe::ChannelProxy(
-      std::unique_ptr<voe::Channel>(new voe::Channel(
-              module_process_thread,
-              internal_audio_state->audio_device_module(),
-              config.jitter_buffer_max_packets,
-              config.jitter_buffer_fast_accelerate,
-              config.decoder_factory))));
+  return std::unique_ptr<voe::ChannelProxy>(
+      new voe::ChannelProxy(std::unique_ptr<voe::Channel>(new voe::Channel(
+          module_process_thread, internal_audio_state->audio_device_module(),
+          nullptr /* RtcpRttStats */, config.jitter_buffer_max_packets,
+          config.jitter_buffer_fast_accelerate, config.decoder_factory,
+          config.codec_pair_id))));
 }
 }  // namespace
 
diff --git a/audio/audio_receive_stream.h b/audio/audio_receive_stream.h
index a47b59c..09007f0 100644
--- a/audio/audio_receive_stream.h
+++ b/audio/audio_receive_stream.h
@@ -15,6 +15,7 @@
 #include <vector>
 
 #include "api/audio/audio_mixer.h"
+#include "api/rtp_headers.h"
 #include "audio/audio_state.h"
 #include "call/audio_receive_stream.h"
 #include "call/rtp_packet_sink_interface.h"
diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc
index 04dffcd..8798d43 100644
--- a/audio/audio_send_stream.cc
+++ b/audio/audio_send_stream.cc
@@ -23,6 +23,7 @@
 #include "rtc_base/event.h"
 #include "rtc_base/function_view.h"
 #include "rtc_base/logging.h"
+#include "rtc_base/strings/audio_format_to_string.h"
 #include "rtc_base/task_queue.h"
 #include "rtc_base/timeutils.h"
 #include "system_wrappers/include/field_trial.h"
@@ -46,15 +47,15 @@
 std::unique_ptr<voe::ChannelProxy> CreateChannelAndProxy(
     webrtc::AudioState* audio_state,
     rtc::TaskQueue* worker_queue,
-    ProcessThread* module_process_thread) {
+    ProcessThread* module_process_thread,
+    RtcpRttStats* rtcp_rtt_stats) {
   RTC_DCHECK(audio_state);
   internal::AudioState* internal_audio_state =
       static_cast<internal::AudioState*>(audio_state);
-  return std::unique_ptr<voe::ChannelProxy>(new voe::ChannelProxy(
-      std::unique_ptr<voe::Channel>(new voe::Channel(
-          worker_queue,
-          module_process_thread,
-          internal_audio_state->audio_device_module()))));
+  return std::unique_ptr<voe::ChannelProxy>(
+      new voe::ChannelProxy(std::unique_ptr<voe::Channel>(new voe::Channel(
+          worker_queue, module_process_thread,
+          internal_audio_state->audio_device_module(), rtcp_rtt_stats))));
 }
 }  // namespace
 
@@ -103,7 +104,8 @@
                       overall_call_lifetime,
                       CreateChannelAndProxy(audio_state.get(),
                                             worker_queue,
-                                            module_process_thread)) {}
+                                            module_process_thread,
+                                            rtcp_rtt_stats)) {}
 
 AudioSendStream::AudioSendStream(
     const webrtc::AudioSendStream::Config& config,
@@ -138,7 +140,6 @@
   RTC_DCHECK(overall_call_lifetime_);
 
   channel_proxy_->SetRtcEventLog(event_log_);
-  channel_proxy_->SetRtcpRttStats(rtcp_rtt_stats);
   channel_proxy_->SetRTCPStatus(true);
   RtpReceiver* rtpReceiver = nullptr;  // Unused, but required for call.
   channel_proxy_->GetRtpRtcp(&rtp_rtcp_module_, &rtpReceiver);
@@ -159,7 +160,6 @@
   channel_proxy_->RegisterTransport(nullptr);
   channel_proxy_->ResetSenderCongestionControlObjects();
   channel_proxy_->SetRtcEventLog(nullptr);
-  channel_proxy_->SetRtcpRttStats(nullptr);
   // Lifetime can only be updated after deregistering
   // |timed_send_transport_adapter_| in the underlying channel object to avoid
   // data races in |active_lifetime_|.
@@ -185,6 +185,8 @@
       ids.audio_level = extension.id;
     } else if (extension.uri == RtpExtension::kTransportSequenceNumberUri) {
       ids.transport_sequence_number = extension.id;
+    } else if (extension.uri == RtpExtension::kMidUri) {
+      ids.mid = extension.id;
     }
   }
   return ids;
@@ -261,6 +263,13 @@
                                                           bandwidth_observer);
   }
 
+  // MID RTP header extension.
+  if ((first_time || new_ids.mid != old_ids.mid ||
+       new_config.rtp.mid != old_config.rtp.mid) &&
+      new_ids.mid != 0 && !new_config.rtp.mid.empty()) {
+    channel_proxy->SetMid(new_config.rtp.mid, new_ids.mid);
+  }
+
   if (!ReconfigureSendCodec(stream, new_config)) {
     RTC_LOG(LS_ERROR) << "Failed to set up send codec state.";
   }
@@ -277,14 +286,16 @@
     return;
   }
 
+  bool has_transport_sequence_number =
+      FindExtensionIds(config_.rtp.extensions).transport_sequence_number != 0;
   if (config_.min_bitrate_bps != -1 && config_.max_bitrate_bps != -1 &&
-      (FindExtensionIds(config_.rtp.extensions).transport_sequence_number !=
-           0 ||
+      (has_transport_sequence_number ||
        !webrtc::field_trial::IsEnabled("WebRTC-Audio-SendSideBwe"))) {
     // Audio BWE is enabled.
     transport_->packet_sender()->SetAccountForAudioPackets(true);
     ConfigureBitrateObserver(config_.min_bitrate_bps, config_.max_bitrate_bps,
-                             config_.bitrate_priority);
+                             config_.bitrate_priority,
+                             has_transport_sequence_number);
   }
   channel_proxy_->StartSend();
   sending_ = true;
@@ -495,11 +506,12 @@
 
   RTC_DCHECK(new_config.encoder_factory);
   std::unique_ptr<AudioEncoder> encoder =
-      new_config.encoder_factory->MakeAudioEncoder(spec.payload_type,
-                                                   spec.format, rtc::nullopt);
+      new_config.encoder_factory->MakeAudioEncoder(
+          spec.payload_type, spec.format, new_config.codec_pair_id);
 
   if (!encoder) {
-    RTC_DLOG(LS_ERROR) << "Unable to create encoder for " << spec.format;
+    RTC_DLOG(LS_ERROR) << "Unable to create encoder for "
+                       << rtc::ToString(spec.format);
     return false;
   }
   // If a bitrate has been specified for the codec, use it over the
@@ -670,12 +682,13 @@
     return;
   }
 
+  bool has_transport_sequence_number = new_transport_seq_num_id != 0;
   if (new_config.min_bitrate_bps != -1 && new_config.max_bitrate_bps != -1 &&
-      (new_transport_seq_num_id != 0 ||
+      (has_transport_sequence_number ||
        !webrtc::field_trial::IsEnabled("WebRTC-Audio-SendSideBwe"))) {
-    stream->ConfigureBitrateObserver(new_config.min_bitrate_bps,
-                                     new_config.max_bitrate_bps,
-                                     new_config.bitrate_priority);
+    stream->ConfigureBitrateObserver(
+        new_config.min_bitrate_bps, new_config.max_bitrate_bps,
+        new_config.bitrate_priority, has_transport_sequence_number);
   } else {
     stream->RemoveBitrateObserver();
   }
@@ -683,7 +696,8 @@
 
 void AudioSendStream::ConfigureBitrateObserver(int min_bitrate_bps,
                                                int max_bitrate_bps,
-                                               double bitrate_priority) {
+                                               double bitrate_priority,
+                                               bool has_packet_feedback) {
   RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
   RTC_DCHECK_GE(max_bitrate_bps, min_bitrate_bps);
   rtc::Event thread_sync_event(false /* manual_reset */, false);
@@ -694,8 +708,11 @@
     config_.max_bitrate_bps = max_bitrate_bps;
     config_.bitrate_priority = bitrate_priority;
     // This either updates the current observer or adds a new observer.
-    bitrate_allocator_->AddObserver(this, min_bitrate_bps, max_bitrate_bps, 0,
-                                    true, config_.track_id, bitrate_priority);
+    bitrate_allocator_->AddObserver(
+        this, MediaStreamAllocationConfig{
+                  static_cast<uint32_t>(min_bitrate_bps),
+                  static_cast<uint32_t>(max_bitrate_bps), 0, true,
+                  config_.track_id, bitrate_priority, has_packet_feedback});
     thread_sync_event.Set();
   });
   thread_sync_event.Wait(rtc::Event::kForever);
diff --git a/audio/audio_send_stream.h b/audio/audio_send_stream.h
index 1cda778..c51c7a3 100644
--- a/audio/audio_send_stream.h
+++ b/audio/audio_send_stream.h
@@ -119,7 +119,8 @@
 
   void ConfigureBitrateObserver(int min_bitrate_bps,
                                 int max_bitrate_bps,
-                                double bitrate_priority);
+                                double bitrate_priority,
+                                bool has_packet_feedback);
   void RemoveBitrateObserver();
 
   void RegisterCngPayloadType(int payload_type, int clockrate_hz);
@@ -157,6 +158,7 @@
   struct ExtensionIds {
     int audio_level = 0;
     int transport_sequence_number = 0;
+    int mid = 0;
   };
   static ExtensionIds FindExtensionIds(
       const std::vector<RtpExtension>& extensions);
diff --git a/audio/audio_send_stream_unittest.cc b/audio/audio_send_stream_unittest.cc
index d8ff0fd..8fb7e7e 100644
--- a/audio/audio_send_stream_unittest.cc
+++ b/audio/audio_send_stream_unittest.cc
@@ -75,10 +75,11 @@
 
 class MockLimitObserver : public BitrateAllocator::LimitObserver {
  public:
-  MOCK_METHOD3(OnAllocationLimitsChanged,
+  MOCK_METHOD4(OnAllocationLimitsChanged,
                void(uint32_t min_send_bitrate_bps,
                     uint32_t max_padding_bitrate_bps,
-                    uint32_t total_bitrate_bps));
+                    uint32_t total_bitrate_bps,
+                    bool has_packet_feedback));
 };
 
 std::unique_ptr<MockAudioEncoder> SetupAudioEncoderMock(
@@ -227,9 +228,6 @@
     EXPECT_CALL(*channel_proxy_, SetRtcEventLog(testing::NotNull())).Times(1);
     EXPECT_CALL(*channel_proxy_, SetRtcEventLog(testing::IsNull()))
         .Times(1);  // Destructor resets the event log
-    EXPECT_CALL(*channel_proxy_, SetRtcpRttStats(&rtcp_rtt_stats_)).Times(1);
-    EXPECT_CALL(*channel_proxy_, SetRtcpRttStats(testing::IsNull()))
-        .Times(1);  // Destructor resets the rtt stats.
   }
 
   void SetupMockForSetupSendCodec(bool expect_set_encoder_call) {
diff --git a/audio/channel.cc b/audio/channel.cc
index d50c161..df34ca2 100644
--- a/audio/channel.cc
+++ b/audio/channel.cc
@@ -26,7 +26,6 @@
 #include "modules/audio_coding/codecs/audio_format_conversion.h"
 #include "modules/audio_device/include/audio_device.h"
 #include "modules/audio_processing/include/audio_processing.h"
-#include "modules/include/module_common_types.h"
 #include "modules/pacing/packet_router.h"
 #include "modules/rtp_rtcp/include/receive_statistics.h"
 #include "modules/rtp_rtcp/include/rtp_payload_registry.h"
@@ -94,34 +93,6 @@
   RTC_DISALLOW_COPY_AND_ASSIGN(RtcEventLogProxy);
 };
 
-class RtcpRttStatsProxy final : public RtcpRttStats {
- public:
-  RtcpRttStatsProxy() : rtcp_rtt_stats_(nullptr) {}
-
-  void OnRttUpdate(int64_t rtt) override {
-    rtc::CritScope lock(&crit_);
-    if (rtcp_rtt_stats_)
-      rtcp_rtt_stats_->OnRttUpdate(rtt);
-  }
-
-  int64_t LastProcessedRtt() const override {
-    rtc::CritScope lock(&crit_);
-    if (!rtcp_rtt_stats_)
-      return 0;
-    return rtcp_rtt_stats_->LastProcessedRtt();
-  }
-
-  void SetRtcpRttStats(RtcpRttStats* rtcp_rtt_stats) {
-    rtc::CritScope lock(&crit_);
-    rtcp_rtt_stats_ = rtcp_rtt_stats;
-  }
-
- private:
-  rtc::CriticalSection crit_;
-  RtcpRttStats* rtcp_rtt_stats_ RTC_GUARDED_BY(crit_);
-  RTC_DISALLOW_COPY_AND_ASSIGN(RtcpRttStatsProxy);
-};
-
 class TransportFeedbackProxy : public TransportFeedbackObserver {
  public:
   TransportFeedbackProxy() : feedback_observer_(nullptr) {
@@ -390,23 +361,6 @@
   _rtpRtcpModule->SetRemoteSSRC(ssrc);
 }
 
-void Channel::OnIncomingCSRCChanged(uint32_t CSRC, bool added) {
-  // TODO(saza): remove.
-}
-
-int32_t Channel::OnInitializeDecoder(int payload_type,
-                                     const SdpAudioFormat& audio_format,
-                                     uint32_t rate) {
-  if (!audio_coding_->RegisterReceiveCodec(payload_type, audio_format)) {
-    RTC_DLOG(LS_WARNING) << "Channel::OnInitializeDecoder() invalid codec (pt="
-                         << payload_type << ", " << audio_format
-                         << ") received -1";
-    return -1;
-  }
-
-  return 0;
-}
-
 int32_t Channel::OnReceivedPayloadData(const uint8_t* payloadData,
                                        size_t payloadSize,
                                        const WebRtcRTPHeader* rtpHeader) {
@@ -552,23 +506,27 @@
 
 Channel::Channel(rtc::TaskQueue* encoder_queue,
                  ProcessThread* module_process_thread,
-                 AudioDeviceModule* audio_device_module)
+                 AudioDeviceModule* audio_device_module,
+                 RtcpRttStats* rtcp_rtt_stats)
     : Channel(module_process_thread,
               audio_device_module,
+              rtcp_rtt_stats,
               0,
               false,
-              rtc::scoped_refptr<AudioDecoderFactory>()) {
+              rtc::scoped_refptr<AudioDecoderFactory>(),
+              rtc::nullopt) {
   RTC_DCHECK(encoder_queue);
   encoder_queue_ = encoder_queue;
 }
 
 Channel::Channel(ProcessThread* module_process_thread,
                  AudioDeviceModule* audio_device_module,
+                 RtcpRttStats* rtcp_rtt_stats,
                  size_t jitter_buffer_max_packets,
                  bool jitter_buffer_fast_playout,
-                 rtc::scoped_refptr<AudioDecoderFactory> decoder_factory)
+                 rtc::scoped_refptr<AudioDecoderFactory> decoder_factory,
+                 rtc::Optional<AudioCodecPairId> codec_pair_id)
     : event_log_proxy_(new RtcEventLogProxy()),
-      rtcp_rtt_stats_proxy_(new RtcpRttStatsProxy()),
       rtp_payload_registry_(new RTPPayloadRegistry()),
       rtp_receive_statistics_(
           ReceiveStatistics::Create(Clock::GetRealTimeClock())),
@@ -610,6 +568,7 @@
   RTC_DCHECK(audio_device_module);
   AudioCodingModule::Config acm_config;
   acm_config.decoder_factory = decoder_factory;
+  acm_config.neteq_config.codec_pair_id = codec_pair_id;
   acm_config.neteq_config.max_packets_in_buffer = jitter_buffer_max_packets;
   acm_config.neteq_config.enable_fast_accelerate = jitter_buffer_fast_playout;
   acm_config.neteq_config.enable_muted_state = true;
@@ -630,7 +589,7 @@
     configuration.transport_feedback_callback = feedback_observer_proxy_.get();
   }
   configuration.event_log = &(*event_log_proxy_);
-  configuration.rtt_stats = &(*rtcp_rtt_stats_proxy_);
+  configuration.rtt_stats = rtcp_rtt_stats;
   configuration.retransmission_rate_limiter =
       retransmission_rate_limiter_.get();
 
@@ -1078,6 +1037,12 @@
   return 0;
 }
 
+void Channel::SetMid(const std::string& mid, int extension_id) {
+  int ret = SetSendRtpHeaderExtension(true, kRtpExtensionMid, extension_id);
+  RTC_DCHECK_EQ(0, ret);
+  _rtpRtcpModule->SetMid(mid);
+}
+
 int Channel::GetRemoteSSRC(unsigned int& ssrc) {
   ssrc = rtp_receiver_->SSRC();
   return 0;
@@ -1323,10 +1288,6 @@
   event_log_proxy_->SetEventLog(event_log);
 }
 
-void Channel::SetRtcpRttStats(RtcpRttStats* rtcp_rtt_stats) {
-  rtcp_rtt_stats_proxy_->SetRtcpRttStats(rtcp_rtt_stats);
-}
-
 void Channel::UpdateOverheadForEncoder() {
   size_t overhead_per_packet =
       transport_overhead_per_packet_ + rtp_overhead_per_packet_;
diff --git a/audio/channel.h b/audio/channel.h
index e59cae1..ab25aad 100644
--- a/audio/channel.h
+++ b/audio/channel.h
@@ -90,7 +90,6 @@
 namespace voe {
 
 class RtcEventLogProxy;
-class RtcpRttStatsProxy;
 class RtpPacketSenderProxy;
 class TransportFeedbackProxy;
 class TransportSequenceNumberProxy;
@@ -151,13 +150,16 @@
   // Used for send streams.
   Channel(rtc::TaskQueue* encoder_queue,
           ProcessThread* module_process_thread,
-          AudioDeviceModule* audio_device_module);
+          AudioDeviceModule* audio_device_module,
+          RtcpRttStats* rtcp_rtt_stats);
   // Used for receive streams.
   Channel(ProcessThread* module_process_thread,
           AudioDeviceModule* audio_device_module,
+          RtcpRttStats* rtcp_rtt_stats,
           size_t jitter_buffer_max_packets,
           bool jitter_buffer_fast_playout,
-          rtc::scoped_refptr<AudioDecoderFactory> decoder_factory);
+          rtc::scoped_refptr<AudioDecoderFactory> decoder_factory,
+          rtc::Optional<AudioCodecPairId> codec_pair_id);
   virtual ~Channel();
 
   void SetSink(AudioSinkInterface* sink);
@@ -217,6 +219,7 @@
 
   // RTP+RTCP
   int SetLocalSSRC(unsigned int ssrc);
+  void SetMid(const std::string& mid, int extension_id);
   int SetSendAudioLevelIndicationStatus(bool enable, unsigned char id);
   void EnableSendTransportSequenceNumber(int id);
 
@@ -246,11 +249,7 @@
                                 const WebRtcRTPHeader* rtpHeader) override;
 
   // From RtpFeedback in the RTP/RTCP module
-  int32_t OnInitializeDecoder(int payload_type,
-                              const SdpAudioFormat& audio_format,
-                              uint32_t rate) override;
   void OnIncomingSSRCChanged(uint32_t ssrc) override;
-  void OnIncomingCSRCChanged(uint32_t CSRC, bool added) override;
 
   // From Transport (called by the RTP/RTCP module)
   bool SendRtp(const uint8_t* data,
@@ -287,7 +286,6 @@
   // Set a RtcEventLog logging object.
   void SetRtcEventLog(RtcEventLog* event_log);
 
-  void SetRtcpRttStats(RtcpRttStats* rtcp_rtt_stats);
   void SetTransportOverhead(size_t transport_overhead_per_packet);
 
   // From OverheadObserver in the RTP/RTCP module
@@ -343,7 +341,6 @@
   ChannelState channel_state_;
 
   std::unique_ptr<voe::RtcEventLogProxy> event_log_proxy_;
-  std::unique_ptr<voe::RtcpRttStatsProxy> rtcp_rtt_stats_proxy_;
 
   std::unique_ptr<RTPPayloadRegistry> rtp_payload_registry_;
   std::unique_ptr<ReceiveStatistics> rtp_receive_statistics_;
diff --git a/audio/channel_proxy.cc b/audio/channel_proxy.cc
index 1a546f6..b8d520d 100644
--- a/audio/channel_proxy.cc
+++ b/audio/channel_proxy.cc
@@ -53,6 +53,11 @@
   RTC_DCHECK_EQ(0, error);
 }
 
+void ChannelProxy::SetMid(const std::string& mid, int extension_id) {
+  RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
+  channel_->SetMid(mid, extension_id);
+}
+
 void ChannelProxy::SetRTCP_CNAME(const std::string& c_name) {
   RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
   // Note: VoERTP_RTCP::SetRTCP_CNAME() accepts a char[256] array.
@@ -283,11 +288,6 @@
   }
 }
 
-void ChannelProxy::SetRtcpRttStats(RtcpRttStats* rtcp_rtt_stats) {
-  RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
-  channel_->SetRtcpRttStats(rtcp_rtt_stats);
-}
-
 bool ChannelProxy::GetRecCodec(CodecInst* codec_inst) const {
   RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
   return channel_->GetRecCodec(*codec_inst) == 0;
diff --git a/audio/channel_proxy.h b/audio/channel_proxy.h
index e11bd2d..eadd686 100644
--- a/audio/channel_proxy.h
+++ b/audio/channel_proxy.h
@@ -62,6 +62,7 @@
 
   virtual void SetRTCPStatus(bool enable);
   virtual void SetLocalSSRC(uint32_t ssrc);
+  virtual void SetMid(const std::string& mid, int extension_id);
   virtual void SetRTCP_CNAME(const std::string& c_name);
   virtual void SetNACKStatus(bool enable, int max_packets);
   virtual void SetSendAudioLevelIndicationStatus(bool enable, int id);
@@ -110,7 +111,6 @@
                           RtpReceiver** rtp_receiver) const;
   virtual uint32_t GetPlayoutTimestamp() const;
   virtual void SetMinimumPlayoutDelay(int delay_ms);
-  virtual void SetRtcpRttStats(RtcpRttStats* rtcp_rtt_stats);
   virtual bool GetRecCodec(CodecInst* codec_inst) const;
   virtual void OnTwccBasedUplinkPacketLossRate(float packet_loss_rate);
   virtual void OnRecoverableUplinkPacketLossRate(
diff --git a/audio/mock_voe_channel_proxy.h b/audio/mock_voe_channel_proxy.h
index 2f6754e..e12b034 100644
--- a/audio/mock_voe_channel_proxy.h
+++ b/audio/mock_voe_channel_proxy.h
@@ -69,7 +69,6 @@
   MOCK_METHOD2(ReceivedRTCPPacket, bool(const uint8_t* packet, size_t length));
   MOCK_METHOD1(SetChannelOutputVolumeScaling, void(float scaling));
   MOCK_METHOD1(SetRtcEventLog, void(RtcEventLog* event_log));
-  MOCK_METHOD1(SetRtcpRttStats, void(RtcpRttStats* rtcp_rtt_stats));
   MOCK_METHOD2(GetAudioFrameWithInfo,
       AudioMixer::Source::AudioFrameInfo(int sample_rate_hz,
                                          AudioFrame* audio_frame));
diff --git a/audio/remix_resample.cc b/audio/remix_resample.cc
index 52a491f..69038cd 100644
--- a/audio/remix_resample.cc
+++ b/audio/remix_resample.cc
@@ -10,11 +10,11 @@
 
 #include "audio/remix_resample.h"
 
+#include "api/audio/audio_frame.h"
 #include "audio/utility/audio_frame_operations.h"
 #include "common_audio/resampler/include/push_resampler.h"
 #include "common_audio/signal_processing/include/signal_processing_library.h"
 #include "common_types.h"  // NOLINT(build/include)
-#include "modules/include/module_common_types.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
 
diff --git a/audio/remix_resample.h b/audio/remix_resample.h
index ddd8086..a45270b 100644
--- a/audio/remix_resample.h
+++ b/audio/remix_resample.h
@@ -11,12 +11,10 @@
 #ifndef AUDIO_REMIX_RESAMPLE_H_
 #define AUDIO_REMIX_RESAMPLE_H_
 
+#include "api/audio/audio_frame.h"
 #include "common_audio/resampler/include/push_resampler.h"
 
 namespace webrtc {
-
-class AudioFrame;
-
 namespace voe {
 
 // Upmix or downmix and resample the audio to |dst_frame|. Expects |dst_frame|
diff --git a/audio/remix_resample_unittest.cc b/audio/remix_resample_unittest.cc
index 753584b..1d8cce7 100644
--- a/audio/remix_resample_unittest.cc
+++ b/audio/remix_resample_unittest.cc
@@ -12,8 +12,8 @@
 
 #include "audio/remix_resample.h"
 #include "common_audio/resampler/include/push_resampler.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/arraysize.h"
+#include "rtc_base/checks.h"
 #include "rtc_base/format_macros.h"
 #include "test/gtest.h"
 
diff --git a/audio/transport_feedback_packet_loss_tracker.h b/audio/transport_feedback_packet_loss_tracker.h
index 7e73210..4ad4902 100644
--- a/audio/transport_feedback_packet_loss_tracker.h
+++ b/audio/transport_feedback_packet_loss_tracker.h
@@ -15,7 +15,6 @@
 #include <vector>
 
 #include "api/optional.h"
-#include "modules/include/module_common_types.h"
 
 namespace webrtc {
 
diff --git a/audio/utility/BUILD.gn b/audio/utility/BUILD.gn
index aa8445c..fe39b7f 100644
--- a/audio/utility/BUILD.gn
+++ b/audio/utility/BUILD.gn
@@ -23,8 +23,7 @@
   deps = [
     "../..:webrtc_common",
     "../../:typedefs",
-    "../../modules:module_api",
-    "../../modules/audio_coding:audio_format_conversion",
+    "../../api/audio:audio_frame_api",
     "../../rtc_base:checks",
     "../../rtc_base:rtc_base_approved",
   ]
@@ -38,7 +37,6 @@
     ]
     deps = [
       ":audio_frame_operations",
-      "../../modules:module_api",
       "../../rtc_base:checks",
       "../../rtc_base:rtc_base_approved",
       "../../test:test_support",
diff --git a/audio/utility/audio_frame_operations.cc b/audio/utility/audio_frame_operations.cc
index a7c7782..ed7b7a8 100644
--- a/audio/utility/audio_frame_operations.cc
+++ b/audio/utility/audio_frame_operations.cc
@@ -10,9 +10,9 @@
 
 #include "audio/utility/audio_frame_operations.h"
 
+#include <string.h>
 #include <algorithm>
 
-#include "modules/include/module_common_types.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/numerics/safe_conversions.h"
 
diff --git a/audio/utility/audio_frame_operations.h b/audio/utility/audio_frame_operations.h
index cd55f19..65a2bad 100644
--- a/audio/utility/audio_frame_operations.h
+++ b/audio/utility/audio_frame_operations.h
@@ -13,12 +13,11 @@
 
 #include <stddef.h>
 
+#include "api/audio/audio_frame.h"
 #include "typedefs.h"  // NOLINT(build/include)
 
 namespace webrtc {
 
-class AudioFrame;
-
 // TODO(andrew): consolidate this with utility.h and audio_frame_manipulator.h.
 // Change reference parameters to pointers. Consider using a namespace rather
 // than a class.
diff --git a/audio/utility/audio_frame_operations_unittest.cc b/audio/utility/audio_frame_operations_unittest.cc
index 6d23731..1d08d7e 100644
--- a/audio/utility/audio_frame_operations_unittest.cc
+++ b/audio/utility/audio_frame_operations_unittest.cc
@@ -9,7 +9,6 @@
  */
 
 #include "audio/utility/audio_frame_operations.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/checks.h"
 #include "test/gtest.h"
 
diff --git a/common_audio/BUILD.gn b/common_audio/BUILD.gn
index 13b1e55..057b11c 100644
--- a/common_audio/BUILD.gn
+++ b/common_audio/BUILD.gn
@@ -68,6 +68,8 @@
     "../rtc_base:checks",
     "../rtc_base:gtest_prod",
     "../rtc_base:rtc_base_approved",
+    "../rtc_base/memory:aligned_array",
+    "../rtc_base/memory:aligned_malloc",
     "../system_wrappers",
     "../system_wrappers:cpu_features_api",
   ]
@@ -131,7 +133,7 @@
     } else {
       sources += [ "signal_processing/filter_ar_fast_q12.c" ]
     }
-    deps += [ "../system_wrappers:asm_defines" ]
+    deps += [ "../rtc_base/system:asm_defines" ]
   }
 }
 
@@ -259,6 +261,7 @@
     "../:typedefs",
     "../rtc_base:gtest_prod",
     "../rtc_base:rtc_base_approved",
+    "../rtc_base/memory:aligned_malloc",
     "../system_wrappers",
   ]
 }
@@ -300,7 +303,7 @@
       "resampler/sinc_resampler_sse.cc",
     ]
 
-    if (is_posix) {
+    if (is_posix || is_fuchsia) {
       cflags = [ "-msse2" ]
     }
 
@@ -313,7 +316,7 @@
       ":sinc_resampler",
       "../rtc_base:checks",
       "../rtc_base:rtc_base_approved",
-      "../system_wrappers",
+      "../rtc_base/memory:aligned_malloc",
     ]
   }
 }
@@ -354,7 +357,7 @@
       ":sinc_resampler",
       "../rtc_base:checks",
       "../rtc_base:rtc_base_approved",
-      "../system_wrappers",
+      "../rtc_base/memory:aligned_malloc",
     ]
   }
 
diff --git a/common_audio/fir_filter_neon.cc b/common_audio/fir_filter_neon.cc
index d9f91b7..e27f21b 100644
--- a/common_audio/fir_filter_neon.cc
+++ b/common_audio/fir_filter_neon.cc
@@ -14,7 +14,7 @@
 #include <string.h>
 
 #include "rtc_base/checks.h"
-#include "system_wrappers/include/aligned_malloc.h"
+#include "rtc_base/memory/aligned_malloc.h"
 
 namespace webrtc {
 
diff --git a/common_audio/fir_filter_neon.h b/common_audio/fir_filter_neon.h
index 5696df8..1ffefd8 100644
--- a/common_audio/fir_filter_neon.h
+++ b/common_audio/fir_filter_neon.h
@@ -14,7 +14,7 @@
 #include <memory>
 
 #include "common_audio/fir_filter.h"
-#include "system_wrappers/include/aligned_malloc.h"
+#include "rtc_base/memory/aligned_malloc.h"
 
 namespace webrtc {
 
diff --git a/common_audio/fir_filter_sse.cc b/common_audio/fir_filter_sse.cc
index 3302d56..0da23fc 100644
--- a/common_audio/fir_filter_sse.cc
+++ b/common_audio/fir_filter_sse.cc
@@ -15,7 +15,7 @@
 #include <xmmintrin.h>
 
 #include "rtc_base/checks.h"
-#include "system_wrappers/include/aligned_malloc.h"
+#include "rtc_base/memory/aligned_malloc.h"
 
 namespace webrtc {
 
diff --git a/common_audio/fir_filter_sse.h b/common_audio/fir_filter_sse.h
index 6506024..7707f93 100644
--- a/common_audio/fir_filter_sse.h
+++ b/common_audio/fir_filter_sse.h
@@ -14,7 +14,7 @@
 #include <memory>
 
 #include "common_audio/fir_filter.h"
-#include "system_wrappers/include/aligned_malloc.h"
+#include "rtc_base/memory/aligned_malloc.h"
 
 namespace webrtc {
 
diff --git a/common_audio/lapped_transform.h b/common_audio/lapped_transform.h
index fe3a8cd..c97cd16 100644
--- a/common_audio/lapped_transform.h
+++ b/common_audio/lapped_transform.h
@@ -16,7 +16,7 @@
 
 #include "common_audio/blocker.h"
 #include "common_audio/real_fourier.h"
-#include "system_wrappers/include/aligned_array.h"
+#include "rtc_base/memory/aligned_array.h"
 
 namespace webrtc {
 
diff --git a/common_audio/real_fourier.cc b/common_audio/real_fourier.cc
index cb0a005..f01c8eb 100644
--- a/common_audio/real_fourier.cc
+++ b/common_audio/real_fourier.cc
@@ -14,10 +14,6 @@
 #include "common_audio/signal_processing/include/signal_processing_library.h"
 #include "rtc_base/checks.h"
 
-#ifdef RTC_USE_OPENMAX_DL
-#include "common_audio/real_fourier_openmax.h"
-#endif
-
 namespace webrtc {
 
 using std::complex;
@@ -25,11 +21,7 @@
 const size_t RealFourier::kFftBufferAlignment = 32;
 
 std::unique_ptr<RealFourier> RealFourier::Create(int fft_order) {
-#if defined(RTC_USE_OPENMAX_DL)
-  return std::unique_ptr<RealFourier>(new RealFourierOpenmax(fft_order));
-#else
   return std::unique_ptr<RealFourier>(new RealFourierOoura(fft_order));
-#endif
 }
 
 int RealFourier::FftOrder(size_t length) {
diff --git a/common_audio/real_fourier.h b/common_audio/real_fourier.h
index 4c69c3c..d16149b 100644
--- a/common_audio/real_fourier.h
+++ b/common_audio/real_fourier.h
@@ -14,7 +14,7 @@
 #include <complex>
 #include <memory>
 
-#include "system_wrappers/include/aligned_malloc.h"
+#include "rtc_base/memory/aligned_malloc.h"
 
 // Uniform interface class for the real DFT and its inverse, for power-of-2
 // input lengths. Also contains helper functions for buffer allocation, taking
diff --git a/common_audio/real_fourier_openmax.cc b/common_audio/real_fourier_openmax.cc
index 6c5c9ce..3c4d0e5 100644
--- a/common_audio/real_fourier_openmax.cc
+++ b/common_audio/real_fourier_openmax.cc
@@ -8,6 +8,9 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
+// TODO(http://bugs.webrtc.org/9071): Required by downstream projects.
+#ifdef RTC_USE_OPENMAX_DL
+
 #include "common_audio/real_fourier_openmax.h"
 
 #include <cstdlib>
@@ -67,3 +70,4 @@
 
 }  // namespace webrtc
 
+#endif  // 0
diff --git a/common_audio/real_fourier_openmax.h b/common_audio/real_fourier_openmax.h
index af91dde..e132280 100644
--- a/common_audio/real_fourier_openmax.h
+++ b/common_audio/real_fourier_openmax.h
@@ -11,9 +11,8 @@
 #ifndef COMMON_AUDIO_REAL_FOURIER_OPENMAX_H_
 #define COMMON_AUDIO_REAL_FOURIER_OPENMAX_H_
 
-#ifndef RTC_USE_OPENMAX_DL
-#error "Only include this header if RTC_USE_OPENMAX_DL is defined."
-#endif
+// TODO(http://bugs.webrtc.org/9071): Required by downstream projects.
+#ifdef RTC_USE_OPENMAX_DL
 
 #include <complex>
 
@@ -44,4 +43,6 @@
 
 }  // namespace webrtc
 
+#endif  // RTC_USE_OPENMAX_DL
+
 #endif  // COMMON_AUDIO_REAL_FOURIER_OPENMAX_H_
diff --git a/common_audio/real_fourier_unittest.cc b/common_audio/real_fourier_unittest.cc
index 5ac39b2..e6ec012 100644
--- a/common_audio/real_fourier_unittest.cc
+++ b/common_audio/real_fourier_unittest.cc
@@ -72,11 +72,7 @@
   const RealFourier::fft_cplx_scoper cplx_buffer_;
 };
 
-using FftTypes = ::testing::Types<
-#if defined(RTC_USE_OPENMAX_DL)
-    RealFourierOpenmax,
-#endif
-    RealFourierOoura>;
+using FftTypes = ::testing::Types<RealFourierOoura>;
 TYPED_TEST_CASE(RealFourierTest, FftTypes);
 
 TYPED_TEST(RealFourierTest, SimpleForwardTransform) {
diff --git a/common_audio/resampler/sinc_resampler.h b/common_audio/resampler/sinc_resampler.h
index d011224..774a9b7 100644
--- a/common_audio/resampler/sinc_resampler.h
+++ b/common_audio/resampler/sinc_resampler.h
@@ -18,7 +18,7 @@
 
 #include "rtc_base/constructormagic.h"
 #include "rtc_base/gtest_prod_util.h"
-#include "system_wrappers/include/aligned_malloc.h"
+#include "rtc_base/memory/aligned_malloc.h"
 #include "typedefs.h"  // NOLINT(build/include)
 
 namespace webrtc {
diff --git a/common_audio/signal_processing/complex_bit_reverse_arm.S b/common_audio/signal_processing/complex_bit_reverse_arm.S
index c70349a..be8e181 100644
--- a/common_audio/signal_processing/complex_bit_reverse_arm.S
+++ b/common_audio/signal_processing/complex_bit_reverse_arm.S
@@ -12,7 +12,7 @@
 @ for ARMv5 platforms.
 @ Reference C code is in file complex_bit_reverse.c. Bit-exact.
 
-#include "system_wrappers/include/asm_defines.h"
+#include "rtc_base/system/asm_defines.h"
 
 GLOBAL_FUNCTION WebRtcSpl_ComplexBitReverse
 .align  2
diff --git a/common_audio/signal_processing/downsample_fast.c b/common_audio/signal_processing/downsample_fast.c
index 9a2ea05..80fdc58 100644
--- a/common_audio/signal_processing/downsample_fast.c
+++ b/common_audio/signal_processing/downsample_fast.c
@@ -42,8 +42,13 @@
     out_s32 = 2048;  // Round value, 0.5 in Q12.
 
     for (j = 0; j < coefficients_length; j++) {
-      rtc_MsanCheckInitialized(&data_in[i - j], sizeof(data_in[0]), 1);
-      out_s32 += coefficients[j] * data_in[i - j];  // Q12.
+      // Negative overflow is permitted here, because this is
+      // auto-regressive filters, and the state for each batch run is
+      // stored in the "negative" positions of the output vector.
+      rtc_MsanCheckInitialized(&data_in[(ptrdiff_t) i - (ptrdiff_t) j],
+          sizeof(data_in[0]), 1);
+      // out_s32 is in Q12 domain.
+      out_s32 += coefficients[j] * data_in[(ptrdiff_t) i - (ptrdiff_t) j];
     }
 
     out_s32 >>= 12;  // Q0.
diff --git a/common_audio/signal_processing/filter_ar.c b/common_audio/signal_processing/filter_ar.c
index 49d5d61..2471cd1 100644
--- a/common_audio/signal_processing/filter_ar.c
+++ b/common_audio/signal_processing/filter_ar.c
@@ -17,6 +17,8 @@
 
 #include "common_audio/signal_processing/include/signal_processing_library.h"
 
+#include "rtc_base/checks.h"
+
 size_t WebRtcSpl_FilterAR(const int16_t* a,
                           size_t a_length,
                           const int16_t* x,
@@ -40,8 +42,10 @@
     {
         // Calculate filtered[i] and filtered_low[i]
         const int16_t* a_ptr = &a[1];
-        int16_t* filtered_ptr = &filtered[i - 1];
-        int16_t* filtered_low_ptr = &filtered_low[i - 1];
+        // The index can become negative, but the arrays will never be indexed
+        // with it when negative. Nevertheless, the index cannot be a size_t
+        // because of this.
+        int filtered_ix = (int)i - 1;
         int16_t* state_ptr = &state[state_length - 1];
         int16_t* state_low_ptr = &state_low[state_length - 1];
 
@@ -51,8 +55,10 @@
         stop = (i < a_length) ? i + 1 : a_length;
         for (j = 1; j < stop; j++)
         {
-          o -= *a_ptr * *filtered_ptr--;
-          oLOW -= *a_ptr++ * *filtered_low_ptr--;
+          RTC_DCHECK_GE(filtered_ix, 0);
+          o -= *a_ptr * filtered[filtered_ix];
+          oLOW -= *a_ptr++ * filtered_low[filtered_ix];
+          --filtered_ix;
         }
         for (j = i + 1; j < a_length; j++)
         {
diff --git a/common_audio/signal_processing/filter_ar_fast_q12.c b/common_audio/signal_processing/filter_ar_fast_q12.c
index df9e518..8b8bdb1 100644
--- a/common_audio/signal_processing/filter_ar_fast_q12.c
+++ b/common_audio/signal_processing/filter_ar_fast_q12.c
@@ -8,6 +8,8 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
+#include "stddef.h"
+
 #include "rtc_base/checks.h"
 #include "common_audio/signal_processing/include/signal_processing_library.h"
 
@@ -29,7 +31,10 @@
     int64_t sum = 0;
 
     for (j = coefficients_length - 1; j > 0; j--) {
-      sum += coefficients[j] * data_out[i - j];
+      // Negative overflow is permitted here, because this is
+      // auto-regressive filters, and the state for each batch run is
+      // stored in the "negative" positions of the output vector.
+      sum += coefficients[j] * data_out[(ptrdiff_t) i - (ptrdiff_t) j];
     }
 
     output = coefficients[0] * data_in[i];
diff --git a/common_audio/signal_processing/filter_ar_fast_q12_armv7.S b/common_audio/signal_processing/filter_ar_fast_q12_armv7.S
index c6397c2..60319d2 100644
--- a/common_audio/signal_processing/filter_ar_fast_q12_armv7.S
+++ b/common_audio/signal_processing/filter_ar_fast_q12_armv7.S
@@ -35,7 +35,7 @@
 @ r11: Scratch
 @ r12: &coefficients[j]
 
-#include "system_wrappers/include/asm_defines.h"
+#include "rtc_base/system/asm_defines.h"
 
 GLOBAL_FUNCTION WebRtcSpl_FilterARFastQ12
 .align  2
diff --git a/common_audio/signal_processing/filter_ma_fast_q12.c b/common_audio/signal_processing/filter_ma_fast_q12.c
index 9596ef1..329d47e 100644
--- a/common_audio/signal_processing/filter_ma_fast_q12.c
+++ b/common_audio/signal_processing/filter_ma_fast_q12.c
@@ -37,7 +37,10 @@
 
         for (j = 0; j < B_length; j++)
         {
-          o += B[j] * in_ptr[i - j];
+          // Negative overflow is permitted here, because this is
+          // auto-regressive filters, and the state for each batch run is
+          // stored in the "negative" positions of the output vector.
+          o += B[j] * in_ptr[(ptrdiff_t) i - (ptrdiff_t) j];
         }
 
         // If output is higher than 32768, saturate it. Same with negative side
diff --git a/common_audio/signal_processing/include/signal_processing_library.h b/common_audio/signal_processing/include/signal_processing_library.h
index 73cdc0d..27fea6f 100644
--- a/common_audio/signal_processing/include/signal_processing_library.h
+++ b/common_audio/signal_processing/include/signal_processing_library.h
@@ -110,7 +110,7 @@
 // C code will be assigned.
 // Note that this function MUST be called in any application that uses SPL
 // functions.
-void WebRtcSpl_Init();
+void WebRtcSpl_Init(void);
 
 int16_t WebRtcSpl_GetScalingSquare(int16_t* in_vector,
                                    size_t in_vector_length,
diff --git a/common_audio/signal_processing/spl_init.c b/common_audio/signal_processing/spl_init.c
index 0f41bc1..80e9197 100644
--- a/common_audio/signal_processing/spl_init.c
+++ b/common_audio/signal_processing/spl_init.c
@@ -30,7 +30,7 @@
 
 #if (!defined(WEBRTC_HAS_NEON)) && !defined(MIPS32_LE)
 /* Initialize function pointers to the generic C version. */
-static void InitPointersToC() {
+static void InitPointersToC(void) {
   WebRtcSpl_MaxAbsValueW16 = WebRtcSpl_MaxAbsValueW16C;
   WebRtcSpl_MaxAbsValueW32 = WebRtcSpl_MaxAbsValueW32C;
   WebRtcSpl_MaxValueW16 = WebRtcSpl_MaxValueW16C;
@@ -46,7 +46,7 @@
 
 #if defined(WEBRTC_HAS_NEON)
 /* Initialize function pointers to the Neon version. */
-static void InitPointersToNeon() {
+static void InitPointersToNeon(void) {
   WebRtcSpl_MaxAbsValueW16 = WebRtcSpl_MaxAbsValueW16Neon;
   WebRtcSpl_MaxAbsValueW32 = WebRtcSpl_MaxAbsValueW32Neon;
   WebRtcSpl_MaxValueW16 = WebRtcSpl_MaxValueW16Neon;
@@ -62,7 +62,7 @@
 
 #if defined(MIPS32_LE)
 /* Initialize function pointers to the MIPS version. */
-static void InitPointersToMIPS() {
+static void InitPointersToMIPS(void) {
   WebRtcSpl_MaxAbsValueW16 = WebRtcSpl_MaxAbsValueW16_mips;
   WebRtcSpl_MaxValueW16 = WebRtcSpl_MaxValueW16_mips;
   WebRtcSpl_MaxValueW32 = WebRtcSpl_MaxValueW32_mips;
@@ -128,6 +128,6 @@
  */
 #endif  /* WEBRTC_POSIX */
 
-void WebRtcSpl_Init() {
+void WebRtcSpl_Init(void) {
   once(InitFunctionPointers);
 }
diff --git a/common_audio/signal_processing/spl_sqrt_floor_arm.S b/common_audio/signal_processing/spl_sqrt_floor_arm.S
index 29e6d4d..228e68e 100644
--- a/common_audio/signal_processing/spl_sqrt_floor_arm.S
+++ b/common_audio/signal_processing/spl_sqrt_floor_arm.S
@@ -32,7 +32,7 @@
 @ Output:             r0 = INT (SQRT (r0)), precision is 16 bits
 @ Registers touched:  r1, r2
 
-#include "system_wrappers/include/asm_defines.h"
+#include "rtc_base/system/asm_defines.h"
 
 GLOBAL_FUNCTION WebRtcSpl_SqrtFloor
 .align  2
diff --git a/common_audio/vad/include/webrtc_vad.h b/common_audio/vad/include/webrtc_vad.h
index 7d71b9b..353dbf0 100644
--- a/common_audio/vad/include/webrtc_vad.h
+++ b/common_audio/vad/include/webrtc_vad.h
@@ -27,7 +27,7 @@
 #endif
 
 // Creates an instance to the VAD structure.
-VadInst* WebRtcVad_Create();
+VadInst* WebRtcVad_Create(void);
 
 // Frees the dynamic memory of a specified VAD instance.
 //
diff --git a/common_audio/wav_file.cc b/common_audio/wav_file.cc
index 37f249e..1217e40 100644
--- a/common_audio/wav_file.cc
+++ b/common_audio/wav_file.cc
@@ -18,6 +18,7 @@
 #include "common_audio/include/audio_util.h"
 #include "common_audio/wav_header.h"
 #include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
 #include "rtc_base/numerics/safe_conversions.h"
 
 namespace webrtc {
@@ -47,8 +48,21 @@
 }
 
 WavReader::WavReader(const std::string& filename)
-    : file_handle_(fopen(filename.c_str(), "rb")) {
-  RTC_CHECK(file_handle_) << "Could not open wav file for reading.";
+    : WavReader(rtc::OpenPlatformFileReadOnly(filename)) {}
+
+WavReader::WavReader(rtc::PlatformFile file) {
+  RTC_CHECK_NE(file, rtc::kInvalidPlatformFileValue)
+      << "Invalid file. Could not create file handle for wav file.";
+  file_handle_ = rtc::FdopenPlatformFile(file, "rb");
+  if (!file_handle_) {
+    RTC_LOG(LS_ERROR) << "Could not open wav file for reading: " << errno;
+    // Even though we failed to open a FILE*, the file is still open
+    // and needs to be closed.
+    if (!rtc::ClosePlatformFile(file)) {
+      RTC_LOG(LS_ERROR) << "Can't close file.";
+    }
+    FATAL() << "Could not open wav file for reading.";
+  }
 
   ReadableWavFile readable(file_handle_);
   WavFormat format;
@@ -110,13 +124,31 @@
   file_handle_ = nullptr;
 }
 
-WavWriter::WavWriter(const std::string& filename, int sample_rate,
+WavWriter::WavWriter(const std::string& filename,
+                     int sample_rate,
                      size_t num_channels)
-    : sample_rate_(sample_rate),
-      num_channels_(num_channels),
-      num_samples_(0),
-      file_handle_(fopen(filename.c_str(), "wb")) {
-  RTC_CHECK(file_handle_) << "Could not open wav file for writing.";
+    // Unlike plain fopen, CreatePlatformFile takes care of filename utf8 ->
+    // wchar conversion on windows.
+    : WavWriter(rtc::CreatePlatformFile(filename), sample_rate, num_channels) {}
+
+WavWriter::WavWriter(rtc::PlatformFile file,
+                     int sample_rate,
+                     size_t num_channels)
+    : sample_rate_(sample_rate), num_channels_(num_channels), num_samples_(0) {
+  // Handle errors from the CreatePlatformFile call in above constructor.
+  RTC_CHECK_NE(file, rtc::kInvalidPlatformFileValue)
+      << "Invalid file. Could not create wav file.";
+  file_handle_ = rtc::FdopenPlatformFile(file, "wb");
+  if (!file_handle_) {
+    RTC_LOG(LS_ERROR) << "Could not open wav file for writing.";
+    // Even though we failed to open a FILE*, the file is still open
+    // and needs to be closed.
+    if (!rtc::ClosePlatformFile(file)) {
+      RTC_LOG(LS_ERROR) << "Can't close file.";
+    }
+    FATAL() << "Could not open wav file for writing.";
+  }
+
   RTC_CHECK(CheckWavParameters(num_channels_, sample_rate_, kWavFormat,
                                kBytesPerSample, num_samples_));
 
diff --git a/common_audio/wav_file.h b/common_audio/wav_file.h
index f7afe92..befe733 100644
--- a/common_audio/wav_file.h
+++ b/common_audio/wav_file.h
@@ -18,6 +18,7 @@
 #include <string>
 
 #include "rtc_base/constructormagic.h"
+#include "rtc_base/platform_file.h"
 
 namespace webrtc {
 
@@ -41,6 +42,9 @@
   // Open a new WAV file for writing.
   WavWriter(const std::string& filename, int sample_rate, size_t num_channels);
 
+  // Open a new WAV file for writing.
+  WavWriter(rtc::PlatformFile file, int sample_rate, size_t num_channels);
+
   // Close the WAV file, after writing its header.
   ~WavWriter() override;
 
@@ -70,6 +74,9 @@
   // Opens an existing WAV file for reading.
   explicit WavReader(const std::string& filename);
 
+  // Opens an existing WAV file for reading.
+  explicit WavReader(rtc::PlatformFile file);
+
   // Close the WAV file.
   ~WavReader() override;
 
diff --git a/common_audio/wav_file_unittest.cc b/common_audio/wav_file_unittest.cc
index 7113b47..248273b 100644
--- a/common_audio/wav_file_unittest.cc
+++ b/common_audio/wav_file_unittest.cc
@@ -174,4 +174,72 @@
   }
 }
 
+// Write a tiny WAV file with the the std::FILE interface and verify the
+// result.
+TEST(WavWriterTest, CPPFileDescriptor) {
+  const std::string outfile = test::OutputPath() + "wavtest1.wav";
+  static constexpr size_t kNumSamples = 3;
+  {
+    WavWriter w(rtc::CreatePlatformFile(outfile), 14099, 1);
+    EXPECT_EQ(14099, w.sample_rate());
+    EXPECT_EQ(1u, w.num_channels());
+    EXPECT_EQ(0u, w.num_samples());
+    w.WriteSamples(kSamples, kNumSamples);
+    EXPECT_EQ(kNumSamples, w.num_samples());
+  }
+  // Write some extra "metadata" to the file that should be silently ignored
+  // by WavReader. We don't use WavWriter directly for this because it doesn't
+  // support metadata.
+  static constexpr uint8_t kMetadata[] = {101, 202};
+  {
+    FILE* f = fopen(outfile.c_str(), "ab");
+    ASSERT_TRUE(f);
+    ASSERT_EQ(1u, fwrite(kMetadata, sizeof(kMetadata), 1, f));
+    fclose(f);
+  }
+  static const uint8_t kExpectedContents[] = {
+      // clang-format off
+    'R', 'I', 'F', 'F',
+    42, 0, 0, 0,       // size of whole file - 8: 6 + 44 - 8
+    'W', 'A', 'V', 'E',
+    'f', 'm', 't', ' ',
+    16, 0, 0, 0,       // size of fmt block - 8: 24 - 8
+    1, 0,              // format: PCM (1)
+    1, 0,              // channels: 1
+    0x13, 0x37, 0, 0,  // sample rate: 14099
+    0x26, 0x6e, 0, 0,  // byte rate: 2 * 14099
+    2, 0,              // block align: NumChannels * BytesPerSample
+    16, 0,             // bits per sample: 2 * 8
+    'd', 'a', 't', 'a',
+    6, 0, 0, 0,        // size of payload: 6
+    0, 0,              // first sample: 0.0
+    10, 0,             // second sample: 10.0
+    0xff, 0x7f,        // third sample: 4e4 (saturated)
+    kMetadata[0], kMetadata[1],
+      // clang-format on
+  };
+  static constexpr size_t kContentSize =
+      kWavHeaderSize + kNumSamples * sizeof(int16_t) + sizeof(kMetadata);
+  static_assert(sizeof(kExpectedContents) == kContentSize, "");
+  EXPECT_EQ(kContentSize, test::GetFileSize(outfile));
+  FILE* f = fopen(outfile.c_str(), "rb");
+  ASSERT_TRUE(f);
+  uint8_t contents[kContentSize];
+  ASSERT_EQ(1u, fread(contents, kContentSize, 1, f));
+  EXPECT_EQ(0, fclose(f));
+  EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize));
+
+  {
+    WavReader r(rtc::OpenPlatformFileReadOnly(outfile));
+    EXPECT_EQ(14099, r.sample_rate());
+    EXPECT_EQ(1u, r.num_channels());
+    EXPECT_EQ(kNumSamples, r.num_samples());
+    static constexpr float kTruncatedSamples[] = {0.0, 10.0, 32767.0};
+    float samples[kNumSamples];
+    EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples));
+    EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples)));
+    EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples));
+  }
+}
+
 }  // namespace webrtc
diff --git a/common_audio/wav_header_unittest.cc b/common_audio/wav_header_unittest.cc
index c6f605f..8b30530 100644
--- a/common_audio/wav_header_unittest.cc
+++ b/common_audio/wav_header_unittest.cc
@@ -8,6 +8,7 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
+#include <string.h>
 #include <limits>
 
 #include "common_audio/wav_header.h"
diff --git a/common_types.h b/common_types.h
index ee4ddf7..f717094 100644
--- a/common_types.h
+++ b/common_types.h
@@ -13,12 +13,13 @@
 
 #include <stddef.h>
 #include <string.h>
-#include <ostream>
 #include <string>
 #include <vector>
 
 #include "api/array_view.h"
 #include "api/optional.h"
+// TODO(sprang): Remove this include when all usage includes it directly.
+#include "api/video/video_bitrate_allocation.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/deprecation.h"
 #include "typedefs.h"  // NOLINT(build/include)
@@ -29,14 +30,6 @@
 #pragma warning(disable : 4351)
 #endif
 
-#if defined(WEBRTC_EXPORT)
-#define WEBRTC_DLLEXPORT _declspec(dllexport)
-#elif defined(WEBRTC_DLL)
-#define WEBRTC_DLLEXPORT _declspec(dllimport)
-#else
-#define WEBRTC_DLLEXPORT
-#endif
-
 #ifndef NULL
 #define NULL 0
 #endif
@@ -55,34 +48,6 @@
 
 namespace webrtc {
 
-class RewindableStream {
- public:
-  virtual ~RewindableStream() {}
-  virtual int Rewind() = 0;
-};
-
-class InStream : public RewindableStream {
- public:
-  // Reads |len| bytes from file to |buf|. Returns the number of bytes read
-  // or -1 on error.
-  virtual int Read(void* buf, size_t len) = 0;
-};
-
-class OutStream : public RewindableStream {
- public:
-  // Writes |len| bytes from |buf| to file. The actual writing may happen
-  // some time later. Call Flush() to force a write.
-  virtual bool Write(const void* buf, size_t len) = 0;
-};
-
-// For the deprecated MediaFile module.
-enum FileFormats {
-  kFileFormatWavFile = 1,
-  kFileFormatPcm16kHzFile = 7,
-  kFileFormatPcm8kHzFile = 8,
-  kFileFormatPcm32kHzFile = 9,
-};
-
 enum FrameType {
   kEmptyFrame = 0,
   kAudioFrameSpeech = 1,
@@ -186,14 +151,6 @@
       const RtcpPacketTypeCounter& packet_counter) = 0;
 };
 
-// Rate statistics for a stream.
-struct BitrateStatistics {
-  BitrateStatistics() : bitrate_bps(0), packet_rate(0) {}
-
-  uint32_t bitrate_bps;  // Bitrate in bits per second.
-  uint32_t packet_rate;  // Packet rate in packets per second.
-};
-
 // Callback, used to notify an observer whenever new rates have been estimated.
 class BitrateStatisticsObserver {
  public:
@@ -268,35 +225,11 @@
   }
 
   bool operator!=(const CodecInst& other) const { return !(*this == other); }
-
-  friend std::ostream& operator<<(std::ostream& os, const CodecInst& ci) {
-    os << "{pltype: " << ci.pltype;
-    os << ", plname: " << ci.plname;
-    os << ", plfreq: " << ci.plfreq;
-    os << ", pacsize: " << ci.pacsize;
-    os << ", channels: " << ci.channels;
-    os << ", rate: " << ci.rate << "}";
-    return os;
-  }
 };
 
 // RTP
 enum { kRtpCsrcSize = 15 };  // RFC 3550 page 13
 
-enum PayloadFrequencies {
-  kFreq8000Hz = 8000,
-  kFreq16000Hz = 16000,
-  kFreq32000Hz = 32000
-};
-
-// Degree of bandwidth reduction.
-enum VadModes {
-  kVadConventional = 0,  // lowest reduction
-  kVadAggressiveLow,
-  kVadAggressiveMid,
-  kVadAggressiveHigh  // highest reduction
-};
-
 // NETEQ statistics.
 struct NetworkStatistics {
   // current jitter buffer size in ms
@@ -397,10 +330,6 @@
 };
 
 // Video codec
-enum { kMaxSimulcastStreams = 4 };
-enum { kMaxSpatialLayers = 5 };
-enum { kMaxTemporalStreams = 4 };
-
 enum VideoCodecComplexity {
   kComplexityNormal = 0,
   kComplexityHigh = 1,
@@ -408,34 +337,36 @@
   kComplexityMax = 3
 };
 
-enum VP8ResilienceMode {
-  kResilienceOff,    // The stream produced by the encoder requires a
-                     // recovery frame (typically a key frame) to be
-                     // decodable after a packet loss.
-  kResilientStream,  // A stream produced by the encoder is resilient to
-                     // packet losses, but packets within a frame subsequent
-                     // to a loss can't be decoded.
-  kResilientFrames   // Same as kResilientStream but with added resilience
-                     // within a frame.
-};
-
-class TemporalLayersFactory;
 // VP8 specific
 struct VideoCodecVP8 {
+  bool operator==(const VideoCodecVP8& other) const;
+  bool operator!=(const VideoCodecVP8& other) const {
+    return !(*this == other);
+  }
   VideoCodecComplexity complexity;
-  VP8ResilienceMode resilience;
   unsigned char numberOfTemporalLayers;
   bool denoisingOn;
   bool automaticResizeOn;
   bool frameDroppingOn;
   int keyFrameInterval;
-  TemporalLayersFactory* tl_factory;
+};
+
+enum class InterLayerPredMode {
+  kOn,       // Allow inter-layer prediction for all frames.
+             // Frame of low spatial layer can be used for
+             // prediction of next spatial layer frame.
+  kOff,      // Encoder produces independent spatial layers.
+  kOnKeyPic  // Allow inter-layer prediction only for frames
+             // within key picture.
 };
 
 // VP9 specific.
 struct VideoCodecVP9 {
+  bool operator==(const VideoCodecVP9& other) const;
+  bool operator!=(const VideoCodecVP9& other) const {
+    return !(*this == other);
+  }
   VideoCodecComplexity complexity;
-  bool resilienceOn;
   unsigned char numberOfTemporalLayers;
   bool denoisingOn;
   bool frameDroppingOn;
@@ -444,6 +375,7 @@
   bool automaticResizeOn;
   unsigned char numberOfSpatialLayers;
   bool flexibleMode;
+  InterLayerPredMode interLayerPred;
 };
 
 // TODO(magjed): Move this and other H264 related classes out to their own file.
@@ -461,6 +393,10 @@
 
 // H264 specific.
 struct VideoCodecH264 {
+  bool operator==(const VideoCodecH264& other) const;
+  bool operator!=(const VideoCodecH264& other) const {
+    return !(*this == other);
+  }
   bool frameDroppingOn;
   int keyFrameInterval;
   // These are NULL/0 if not externally negotiated.
@@ -496,6 +432,9 @@
 };
 
 struct SpatialLayer {
+  bool operator==(const SpatialLayer& other) const;
+  bool operator!=(const SpatialLayer& other) const { return !(*this == other); }
+
   unsigned short width;
   unsigned short height;
   unsigned char numberOfTemporalLayers;
@@ -576,45 +515,8 @@
   VideoCodecUnion codec_specific_;
 };
 
-class BitrateAllocation {
- public:
-  static const uint32_t kMaxBitrateBps;
-  BitrateAllocation();
-
-  bool SetBitrate(size_t spatial_index,
-                  size_t temporal_index,
-                  uint32_t bitrate_bps);
-
-  bool HasBitrate(size_t spatial_index, size_t temporal_index) const;
-
-  uint32_t GetBitrate(size_t spatial_index, size_t temporal_index) const;
-
-  // Whether the specific spatial layers has the bitrate set in any of its
-  // temporal layers.
-  bool IsSpatialLayerUsed(size_t spatial_index) const;
-
-  // Get the sum of all the temporal layer for a specific spatial layer.
-  uint32_t GetSpatialLayerSum(size_t spatial_index) const;
-
-  uint32_t get_sum_bps() const { return sum_; }  // Sum of all bitrates.
-  uint32_t get_sum_kbps() const { return (sum_ + 500) / 1000; }
-
-  inline bool operator==(const BitrateAllocation& other) const {
-    return memcmp(bitrates_, other.bitrates_, sizeof(bitrates_)) == 0;
-  }
-  inline bool operator!=(const BitrateAllocation& other) const {
-    return !(*this == other);
-  }
-
-  // Expensive, please use only in tests.
-  std::string ToString() const;
-  std::ostream& operator<<(std::ostream& os) const;
-
- private:
-  uint32_t sum_;
-  uint32_t bitrates_[kMaxSpatialLayers][kMaxTemporalStreams];
-  bool has_bitrate_[kMaxSpatialLayers][kMaxTemporalStreams];
-};
+// TODO(sprang): Remove this when downstream projects have been updated.
+using BitrateAllocation = VideoBitrateAllocation;
 
 // Bandwidth over-use detector options.  These are used to drive
 // experimentation with bandwidth estimation parameters.
diff --git a/modules/audio_coding/BUILD.gn b/modules/audio_coding/BUILD.gn
index c20bd2b..7dfe70b 100644
--- a/modules/audio_coding/BUILD.gn
+++ b/modules/audio_coding/BUILD.gn
@@ -97,7 +97,6 @@
     "include/audio_coding_module_typedefs.h",
   ]
   deps = [
-    "..:module_api",
     "../..:typedefs",
     "../..:webrtc_common",
   ]
@@ -105,6 +104,7 @@
 
 rtc_static_library("audio_coding") {
   visibility += [ "*" ]
+  allow_poison = [ "audio_codecs" ]  # TODO(bugs.webrtc.org/8396): Remove.
   sources = [
     "acm2/acm_receiver.cc",
     "acm2/acm_receiver.h",
@@ -136,18 +136,19 @@
   }
 
   deps = audio_coding_deps + [
+           "../../api/audio:audio_frame_api",
+           "..:module_api",
            "../../common_audio:common_audio_c",
            "../..:typedefs",
            "../../rtc_base:deprecation",
            "../../rtc_base:checks",
            "../../system_wrappers:metrics_api",
-           "..:module_api",
            "../../api:array_view",
            "../../api/audio_codecs:audio_codecs_api",
-           "../../api/audio_codecs:builtin_audio_decoder_factory",
            ":audio_coding_module_typedefs",
            ":neteq",
            ":rent_a_codec",
+           "../../rtc_base:audio_format_to_string",
            "../../rtc_base:rtc_base_approved",
            "../../api:optional",
            "../../logging:rtc_event_log_api",
@@ -220,6 +221,7 @@
 
 rtc_static_library("g711") {
   visibility += [ "*" ]
+  poisonous = [ "audio_codecs" ]
   sources = [
     "codecs/g711/audio_decoder_pcm.cc",
     "codecs/g711/audio_decoder_pcm.h",
@@ -242,6 +244,7 @@
 }
 
 rtc_source_set("g711_c") {
+  poisonous = [ "audio_codecs" ]
   sources = [
     "codecs/g711/g711.c",
     "codecs/g711/g711.h",
@@ -260,6 +263,7 @@
 
 rtc_static_library("g722") {
   visibility += [ "*" ]
+  poisonous = [ "audio_codecs" ]
   sources = [
     "codecs/g722/audio_decoder_g722.cc",
     "codecs/g722/audio_decoder_g722.h",
@@ -283,6 +287,7 @@
 }
 
 rtc_source_set("g722_c") {
+  poisonous = [ "audio_codecs" ]
   sources = [
     "codecs/g722/g722_decode.c",
     "codecs/g722/g722_enc_dec.h",
@@ -302,6 +307,7 @@
 
 rtc_static_library("ilbc") {
   visibility += webrtc_default_visibility
+  poisonous = [ "audio_codecs" ]
   sources = [
     "codecs/ilbc/audio_decoder_ilbc.cc",
     "codecs/ilbc/audio_decoder_ilbc.h",
@@ -326,6 +332,7 @@
 }
 
 rtc_source_set("ilbc_c") {
+  poisonous = [ "audio_codecs" ]
   sources = [
     "codecs/ilbc/abs_quant.c",
     "codecs/ilbc/abs_quant.h",
@@ -483,6 +490,7 @@
 }
 
 rtc_static_library("isac_common") {
+  poisonous = [ "audio_codecs" ]
   sources = [
     "codecs/isac/audio_decoder_isac_t.h",
     "codecs/isac/audio_decoder_isac_t_impl.h",
@@ -508,6 +516,7 @@
 
 rtc_static_library("isac") {
   visibility += [ "*" ]
+  poisonous = [ "audio_codecs" ]
   sources = [
     "codecs/isac/main/include/audio_decoder_isac.h",
     "codecs/isac/main/include/audio_encoder_isac.h",
@@ -525,6 +534,7 @@
 }
 
 rtc_static_library("isac_c") {
+  poisonous = [ "audio_codecs" ]
   sources = [
     "codecs/isac/main/include/isac.h",
     "codecs/isac/main/source/arith_routines.c",
@@ -602,6 +612,7 @@
 
 rtc_static_library("isac_fix") {
   visibility += [ "*" ]
+  poisonous = [ "audio_codecs" ]
   sources = [
     "codecs/isac/fix/source/audio_decoder_isacfix.cc",
     "codecs/isac/fix/source/audio_encoder_isacfix.cc",
@@ -625,6 +636,7 @@
 }
 
 rtc_source_set("isac_fix_common") {
+  poisonous = [ "audio_codecs" ]
   sources = [
     "codecs/isac/fix/source/codec.h",
     "codecs/isac/fix/source/entropy_coding.h",
@@ -633,6 +645,7 @@
     "codecs/isac/fix/source/filterbank_internal.h",
     "codecs/isac/fix/source/settings.h",
     "codecs/isac/fix/source/structs.h",
+    "codecs/isac/fix/source/transform_tables.c",
   ]
   public_configs = [ ":isac_fix_config" ]
   deps = [
@@ -644,6 +657,7 @@
 }
 
 rtc_source_set("isac_fix_c_arm_asm") {
+  poisonous = [ "audio_codecs" ]
   sources = []
   if (current_cpu == "arm" && arm_version >= 7) {
     sources += [
@@ -652,12 +666,13 @@
     ]
     deps = [
       ":isac_fix_common",
-      "../../system_wrappers:asm_defines",
+      "../../rtc_base/system:asm_defines",
     ]
   }
 }
 
 rtc_source_set("isac_fix_c") {
+  poisonous = [ "audio_codecs" ]
   sources = [
     "codecs/isac/fix/include/audio_decoder_isacfix.h",
     "codecs/isac/fix/include/audio_encoder_isacfix.h",
@@ -698,7 +713,6 @@
     "codecs/isac/fix/source/spectrum_ar_model_tables.c",
     "codecs/isac/fix/source/spectrum_ar_model_tables.h",
     "codecs/isac/fix/source/transform.c",
-    "codecs/isac/fix/source/transform_tables.c",
   ]
 
   public_configs = [ ":isac_fix_config" ]
@@ -760,6 +774,7 @@
 
 if (rtc_build_with_neon) {
   rtc_static_library("isac_neon") {
+    poisonous = [ "audio_codecs" ]
     sources = [
       "codecs/isac/fix/source/entropy_coding_neon.c",
       "codecs/isac/fix/source/filterbanks_neon.c",
@@ -801,6 +816,7 @@
 
 rtc_static_library("pcm16b") {
   visibility += [ "*" ]
+  poisonous = [ "audio_codecs" ]
   sources = [
     "codecs/pcm16b/audio_decoder_pcm16b.cc",
     "codecs/pcm16b/audio_decoder_pcm16b.h",
@@ -825,6 +841,7 @@
 }
 
 rtc_source_set("pcm16b_c") {
+  poisonous = [ "audio_codecs" ]
   sources = [
     "codecs/pcm16b/pcm16b.c",
     "codecs/pcm16b/pcm16b.h",
@@ -839,6 +856,7 @@
 
 rtc_static_library("webrtc_opus") {
   visibility += webrtc_default_visibility
+  poisonous = [ "audio_codecs" ]
   sources = [
     "codecs/opus/audio_decoder_opus.cc",
     "codecs/opus/audio_decoder_opus.h",
@@ -874,6 +892,7 @@
 }
 
 rtc_source_set("webrtc_opus_c") {
+  poisonous = [ "audio_codecs" ]
   sources = [
     "codecs/opus/opus_inst.h",
     "codecs/opus/opus_interface.c",
@@ -971,6 +990,7 @@
     "../../rtc_base:checks",
     "../../rtc_base:protobuf_utils",
     "../../rtc_base:rtc_base_approved",
+    "../../rtc_base/system:file_wrapper",
     "../../system_wrappers",
     "../../system_wrappers:field_trial_api",
   ]
@@ -1038,6 +1058,8 @@
     "neteq/dtmf_tone_generator.h",
     "neteq/expand.cc",
     "neteq/expand.h",
+    "neteq/expand_uma_logger.cc",
+    "neteq/expand_uma_logger.h",
     "neteq/include/neteq.h",
     "neteq/merge.cc",
     "neteq/merge.h",
@@ -1083,9 +1105,11 @@
     "../..:webrtc_common",
     "../../api:libjingle_peerconnection_api",
     "../../api:optional",
+    "../../api/audio:audio_frame_api",
     "../../api/audio_codecs:audio_codecs_api",
     "../../common_audio",
     "../../common_audio:common_audio_c",
+    "../../rtc_base:audio_format_to_string",
     "../../rtc_base:checks",
     "../../rtc_base:gtest_prod",
     "../../rtc_base:rtc_base_approved",
@@ -1125,11 +1149,11 @@
 
   deps = [
     ":neteq",
-    "..:module_api",
     "../..:typedefs",
     "../..:webrtc_common",
     "../../api:libjingle_peerconnection_api",
     "../../api:optional",
+    "../../api/audio:audio_frame_api",
     "../../api/audio_codecs:audio_codecs_api",
     "../../api/audio_codecs:builtin_audio_decoder_factory",
     "../../rtc_base:checks",
@@ -1164,7 +1188,6 @@
 
   deps = [
     ":pcm16b",
-    "..:module_api",
     "../..:typedefs",
     "../..:webrtc_common",
     "../../api:array_view",
@@ -1206,6 +1229,8 @@
     "neteq/tools/neteq_delay_analyzer.h",
     "neteq/tools/neteq_replacement_input.cc",
     "neteq/tools/neteq_replacement_input.h",
+    "neteq/tools/neteq_stats_getter.cc",
+    "neteq/tools/neteq_stats_getter.h",
   ]
 
   public_configs = [ ":neteq_tools_config" ]
@@ -1216,6 +1241,7 @@
   }
 
   deps = [
+    "..:module_api",
     "../..:typedefs",
     "../..:webrtc_common",
     "../../api:array_view",
@@ -1375,8 +1401,11 @@
       "../..:typedefs",
       "../..:webrtc_common",
       "../../api:optional",
+      "../../api/audio:audio_frame_api",
       "../../api/audio_codecs:builtin_audio_decoder_factory",
+      "../../rtc_base:checks",
       "../../rtc_base:rtc_base_approved",
+      "../../rtc_base/synchronization:rw_lock_wrapper",
       "../../system_wrappers",
       "../../test:fileutils",
       "../../test:test_support",
@@ -1433,6 +1462,7 @@
     defines = audio_coding_defines
 
     deps = audio_coding_deps + [
+             "..:module_api",
              ":audio_coding",
              ":audio_format_conversion",
              "../../api/audio_codecs:audio_codecs_api",
@@ -1454,9 +1484,11 @@
     defines = audio_coding_defines
 
     deps = audio_coding_deps + [
+             "../../api/audio:audio_frame_api",
              "../../rtc_base:checks",
              ":audio_coding",
              ":neteq_tools",
+             "../../api/audio_codecs:builtin_audio_decoder_factory",
              "../../api/audio_codecs:audio_codecs_api",
              "../../rtc_base:rtc_base_approved",
              "../../test:test_support",
@@ -1484,6 +1516,9 @@
       "../..:typedefs",
       "../../:webrtc_common",
       "../../api:optional",
+      "../../api/audio:audio_frame_api",
+      "../../api/audio_codecs:builtin_audio_decoder_factory",
+      "../../rtc_base:checks",
       "../../rtc_base:rtc_base_approved",
       "../../system_wrappers",
       "../../system_wrappers:system_wrappers_default",
@@ -1516,6 +1551,9 @@
       "../..:typedefs",
       "../../:webrtc_common",
       "../../api:optional",
+      "../../api/audio:audio_frame_api",
+      "../../api/audio_codecs:builtin_audio_decoder_factory",
+      "../../rtc_base:checks",
       "../../rtc_base:rtc_base_approved",
       "../../system_wrappers",
       "../../system_wrappers:system_wrappers_default",
@@ -1594,7 +1632,6 @@
       testonly = true
       defines = []
       deps = [
-        "..:module_api",
         "../..:typedefs",
         "../../rtc_base:checks",
         "../../test:fileutils",
@@ -1705,9 +1742,9 @@
       ":neteq",
       ":neteq_test_tools",
       ":pcm16b",
-      "..:module_api",
       "../..:typedefs",
       "../..:webrtc_common",
+      "../../api/audio:audio_frame_api",
       "../../api/audio_codecs:audio_codecs_api",
       "../../api/audio_codecs:builtin_audio_decoder_factory",
       "../../rtc_base:checks",
@@ -1734,7 +1771,6 @@
     deps = [
       ":neteq",
       ":neteq_test_tools",
-      "..:module_api",
       "../..:typedefs",
       "../..:webrtc_common",
       "../../api/audio_codecs:builtin_audio_decoder_factory",
@@ -1753,6 +1789,7 @@
              "../..:typedefs",
              ":audio_coding",
              ":neteq_input_audio_tools",
+             "../../api/audio:audio_frame_api",
              "../../api/audio_codecs/g711:audio_encoder_g711",
              "../../api/audio_codecs/L16:audio_encoder_L16",
              "../../api/audio_codecs/g722:audio_encoder_g722",
@@ -2214,6 +2251,7 @@
       "..:module_api",
       "../..:typedefs",
       "../..:webrtc_common",
+      "../../api/audio:audio_frame_api",
       "../../api/audio_codecs:audio_codecs_api",
       "../../api/audio_codecs:builtin_audio_decoder_factory",
       "../../api/audio_codecs:builtin_audio_encoder_factory",
diff --git a/modules/audio_coding/acm2/acm_receive_test.cc b/modules/audio_coding/acm2/acm_receive_test.cc
index 082506a..473b651 100644
--- a/modules/audio_coding/acm2/acm_receive_test.cc
+++ b/modules/audio_coding/acm2/acm_receive_test.cc
@@ -21,6 +21,7 @@
 #include "modules/audio_coding/neteq/tools/audio_sink.h"
 #include "modules/audio_coding/neteq/tools/packet.h"
 #include "modules/audio_coding/neteq/tools/packet_source.h"
+#include "modules/include/module_common_types.h"
 #include "test/gtest.h"
 
 namespace webrtc {
diff --git a/modules/audio_coding/acm2/acm_receiver.cc b/modules/audio_coding/acm2/acm_receiver.cc
index 0d5dcae..41a23a7 100644
--- a/modules/audio_coding/acm2/acm_receiver.cc
+++ b/modules/audio_coding/acm2/acm_receiver.cc
@@ -22,10 +22,12 @@
 #include "modules/audio_coding/acm2/call_statistics.h"
 #include "modules/audio_coding/acm2/rent_a_codec.h"
 #include "modules/audio_coding/neteq/include/neteq.h"
+#include "modules/include/module_common_types.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/format_macros.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/numerics/safe_conversions.h"
+#include "rtc_base/strings/audio_format_to_string.h"
 #include "system_wrappers/include/clock.h"
 
 namespace webrtc {
@@ -260,7 +262,8 @@
       neteq_->RegisterPayloadType(rtp_payload_type, audio_format);
   if (!success) {
     RTC_LOG(LERROR) << "AcmReceiver::AddCodec failed for payload type "
-                    << rtp_payload_type << ", decoder format " << audio_format;
+                    << rtp_payload_type << ", decoder format "
+                    << rtc::ToString(audio_format);
   }
   return success;
 }
diff --git a/modules/audio_coding/acm2/acm_receiver.h b/modules/audio_coding/acm2/acm_receiver.h
index 5c6b36f..ce1e1f2 100644
--- a/modules/audio_coding/acm2/acm_receiver.h
+++ b/modules/audio_coding/acm2/acm_receiver.h
@@ -16,6 +16,7 @@
 #include <string>
 #include <vector>
 
+#include "api/audio/audio_frame.h"
 #include "api/array_view.h"
 #include "api/optional.h"
 #include "common_audio/vad/include/webrtc_vad.h"
@@ -23,7 +24,6 @@
 #include "modules/audio_coding/acm2/call_statistics.h"
 #include "modules/audio_coding/include/audio_coding_module.h"
 #include "modules/audio_coding/neteq/include/neteq.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/criticalsection.h"
 #include "rtc_base/thread_annotations.h"
 #include "typedefs.h"  // NOLINT(build/include)
diff --git a/modules/audio_coding/acm2/acm_receiver_unittest.cc b/modules/audio_coding/acm2/acm_receiver_unittest.cc
index 8d0b2f1..7877821 100644
--- a/modules/audio_coding/acm2/acm_receiver_unittest.cc
+++ b/modules/audio_coding/acm2/acm_receiver_unittest.cc
@@ -17,6 +17,7 @@
 #include "modules/audio_coding/acm2/rent_a_codec.h"
 #include "modules/audio_coding/include/audio_coding_module.h"
 #include "modules/audio_coding/neteq/tools/rtp_generator.h"
+#include "modules/include/module_common_types.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/numerics/safe_conversions.h"
 #include "system_wrappers/include/clock.h"
diff --git a/modules/audio_coding/acm2/acm_send_test.cc b/modules/audio_coding/acm2/acm_send_test.cc
index 307c906..09a6c80 100644
--- a/modules/audio_coding/acm2/acm_send_test.cc
+++ b/modules/audio_coding/acm2/acm_send_test.cc
@@ -15,6 +15,7 @@
 #include <string.h>
 
 #include "api/audio_codecs/audio_encoder.h"
+#include "api/audio_codecs/builtin_audio_decoder_factory.h"
 #include "modules/audio_coding/include/audio_coding_module.h"
 #include "modules/audio_coding/neteq/tools/input_audio_file.h"
 #include "modules/audio_coding/neteq/tools/packet.h"
@@ -28,7 +29,12 @@
                                      int source_rate_hz,
                                      int test_duration_ms)
     : clock_(0),
-      acm_(webrtc::AudioCodingModule::Create(&clock_)),
+      acm_(webrtc::AudioCodingModule::Create([this] {
+        AudioCodingModule::Config config;
+        config.clock = &clock_;
+        config.decoder_factory = CreateBuiltinAudioDecoderFactory();
+        return config;
+      }())),
       audio_source_(audio_source),
       source_rate_hz_(source_rate_hz),
       input_block_size_samples_(
diff --git a/modules/audio_coding/acm2/acm_send_test.h b/modules/audio_coding/acm2/acm_send_test.h
index 6aea0f1..68ba9e1 100644
--- a/modules/audio_coding/acm2/acm_send_test.h
+++ b/modules/audio_coding/acm2/acm_send_test.h
@@ -14,6 +14,7 @@
 #include <memory>
 #include <vector>
 
+#include "api/audio/audio_frame.h"
 #include "modules/audio_coding/include/audio_coding_module.h"
 #include "modules/audio_coding/neteq/tools/packet_source.h"
 #include "rtc_base/constructormagic.h"
diff --git a/modules/audio_coding/acm2/audio_coding_module.cc b/modules/audio_coding/acm2/audio_coding_module.cc
index 53b9177..4545810 100644
--- a/modules/audio_coding/acm2/audio_coding_module.cc
+++ b/modules/audio_coding/acm2/audio_coding_module.cc
@@ -12,11 +12,11 @@
 
 #include <algorithm>
 
-#include "api/audio_codecs/builtin_audio_decoder_factory.h"
 #include "modules/audio_coding/acm2/acm_receiver.h"
 #include "modules/audio_coding/acm2/acm_resampler.h"
 #include "modules/audio_coding/acm2/codec_manager.h"
 #include "modules/audio_coding/acm2/rent_a_codec.h"
+#include "modules/include/module_common_types.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/numerics/safe_conversions.h"
@@ -1243,8 +1243,11 @@
 
 }  // namespace
 
-AudioCodingModule::Config::Config()
-    : neteq_config(), clock(Clock::GetRealTimeClock()) {
+AudioCodingModule::Config::Config(
+    rtc::scoped_refptr<AudioDecoderFactory> decoder_factory)
+    : neteq_config(),
+      clock(Clock::GetRealTimeClock()),
+      decoder_factory(decoder_factory) {
   // Post-decode VAD is disabled by default in NetEq, however, Audio
   // Conference Mixer relies on VAD decisions and fails without them.
   neteq_config.enable_post_decode_vad = true;
@@ -1253,34 +1256,7 @@
 AudioCodingModule::Config::Config(const Config&) = default;
 AudioCodingModule::Config::~Config() = default;
 
-AudioCodingModule* AudioCodingModule::Create(int id) {
-  RTC_UNUSED(id);
-  return Create();
-}
-
-// Create module
-AudioCodingModule* AudioCodingModule::Create() {
-  Config config;
-  config.clock = Clock::GetRealTimeClock();
-  config.decoder_factory = CreateBuiltinAudioDecoderFactory();
-  return Create(config);
-}
-
-AudioCodingModule* AudioCodingModule::Create(Clock* clock) {
-  Config config;
-  config.clock = clock;
-  config.decoder_factory = CreateBuiltinAudioDecoderFactory();
-  return Create(config);
-}
-
 AudioCodingModule* AudioCodingModule::Create(const Config& config) {
-  if (!config.decoder_factory) {
-    // TODO(ossu): Backwards compatibility. Will be removed after a deprecation
-    // cycle.
-    Config config_copy = config;
-    config_copy.decoder_factory = CreateBuiltinAudioDecoderFactory();
-    return new AudioCodingModuleImpl(config_copy);
-  }
   return new AudioCodingModuleImpl(config);
 }
 
diff --git a/modules/audio_coding/acm2/audio_coding_module_unittest.cc b/modules/audio_coding/acm2/audio_coding_module_unittest.cc
index aaa4230..ff5431d 100644
--- a/modules/audio_coding/acm2/audio_coding_module_unittest.cc
+++ b/modules/audio_coding/acm2/audio_coding_module_unittest.cc
@@ -32,7 +32,6 @@
 #include "modules/audio_coding/neteq/tools/output_wav_file.h"
 #include "modules/audio_coding/neteq/tools/packet.h"
 #include "modules/audio_coding/neteq/tools/rtp_file_source.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/criticalsection.h"
 #include "rtc_base/messagedigest.h"
 #include "rtc_base/numerics/safe_conversions.h"
@@ -166,7 +165,12 @@
   void TearDown() {}
 
   void SetUp() {
-    acm_.reset(AudioCodingModule::Create(clock_));
+    acm_.reset(AudioCodingModule::Create([this] {
+      AudioCodingModule::Config config;
+      config.clock = clock_;
+      config.decoder_factory = CreateBuiltinAudioDecoderFactory();
+      return config;
+    }()));
 
     rtp_utility_->Populate(&rtp_header_);
 
diff --git a/modules/audio_coding/acm2/call_statistics.h b/modules/audio_coding/acm2/call_statistics.h
index 9dd052f..9dced64 100644
--- a/modules/audio_coding/acm2/call_statistics.h
+++ b/modules/audio_coding/acm2/call_statistics.h
@@ -11,8 +11,8 @@
 #ifndef MODULES_AUDIO_CODING_ACM2_CALL_STATISTICS_H_
 #define MODULES_AUDIO_CODING_ACM2_CALL_STATISTICS_H_
 
+#include "api/audio/audio_frame.h"
 #include "common_types.h"  // NOLINT(build/include)
-#include "modules/include/module_common_types.h"
 
 //
 // This class is for book keeping of calls to ACM. It is not useful to log API
diff --git a/modules/audio_coding/acm2/rent_a_codec_unittest.cc b/modules/audio_coding/acm2/rent_a_codec_unittest.cc
index c949c1c..ca469e7 100644
--- a/modules/audio_coding/acm2/rent_a_codec_unittest.cc
+++ b/modules/audio_coding/acm2/rent_a_codec_unittest.cc
@@ -10,6 +10,7 @@
 
 #include <memory>
 
+#include "common_types.h"
 #include "modules/audio_coding/acm2/rent_a_codec.h"
 #include "rtc_base/arraysize.h"
 #include "test/gtest.h"
diff --git a/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl_unittest.cc b/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl_unittest.cc
index c437918..574b699 100644
--- a/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl_unittest.cc
+++ b/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl_unittest.cc
@@ -314,12 +314,12 @@
 
   auto ana_stats = states.audio_network_adaptor->GetStats();
 
-  EXPECT_EQ(ana_stats.bitrate_action_counter, 2);
-  EXPECT_EQ(ana_stats.channel_action_counter, 2);
-  EXPECT_EQ(ana_stats.dtx_action_counter, 2);
-  EXPECT_EQ(ana_stats.fec_action_counter, 2);
-  EXPECT_EQ(ana_stats.frame_length_increase_counter, 1);
-  EXPECT_EQ(ana_stats.frame_length_decrease_counter, 1);
+  EXPECT_EQ(ana_stats.bitrate_action_counter, 2u);
+  EXPECT_EQ(ana_stats.channel_action_counter, 2u);
+  EXPECT_EQ(ana_stats.dtx_action_counter, 2u);
+  EXPECT_EQ(ana_stats.fec_action_counter, 2u);
+  EXPECT_EQ(ana_stats.frame_length_increase_counter, 1u);
+  EXPECT_EQ(ana_stats.frame_length_decrease_counter, 1u);
   EXPECT_EQ(ana_stats.uplink_packet_loss_fraction, 0.1f);
 }
 
diff --git a/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h b/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h
index e40c832..b464eb6 100644
--- a/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h
+++ b/modules/audio_coding/audio_network_adaptor/debug_dump_writer.h
@@ -17,7 +17,7 @@
 #include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor.h"
 #include "rtc_base/constructormagic.h"
 #include "rtc_base/ignore_wundef.h"
-#include "system_wrappers/include/file_wrapper.h"
+#include "rtc_base/system/file_wrapper.h"
 #if WEBRTC_ENABLE_PROTOBUF
 RTC_PUSH_IGNORING_WUNDEF()
 #ifdef WEBRTC_ANDROID_PLATFORM_BUILD
diff --git a/modules/audio_coding/codecs/cng/audio_encoder_cng.cc b/modules/audio_coding/codecs/cng/audio_encoder_cng.cc
index 78148ab..dc4c21e 100644
--- a/modules/audio_coding/codecs/cng/audio_encoder_cng.cc
+++ b/modules/audio_coding/codecs/cng/audio_encoder_cng.cc
@@ -44,19 +44,19 @@
 }
 
 AudioEncoderCng::AudioEncoderCng(Config&& config)
-    : speech_encoder_(
-          ([&] { RTC_CHECK(config.IsOk()) << "Invalid configuration."; }(),
-           std::move(config.speech_encoder))),
+    : speech_encoder_((static_cast<void>([&] {
+                         RTC_CHECK(config.IsOk()) << "Invalid configuration.";
+                       }()),
+                       std::move(config.speech_encoder))),
       cng_payload_type_(config.payload_type),
       num_cng_coefficients_(config.num_cng_coefficients),
       sid_frame_interval_ms_(config.sid_frame_interval_ms),
       last_frame_active_(true),
       vad_(config.vad ? std::unique_ptr<Vad>(config.vad)
-           : CreateVad(config.vad_mode)),
+                      : CreateVad(config.vad_mode)),
       cng_encoder_(new ComfortNoiseEncoder(SampleRateHz(),
                                            sid_frame_interval_ms_,
-                                           num_cng_coefficients_)) {
-}
+                                           num_cng_coefficients_)) {}
 
 AudioEncoderCng::~AudioEncoderCng() = default;
 
diff --git a/modules/audio_coding/codecs/ilbc/cb_construct.c b/modules/audio_coding/codecs/ilbc/cb_construct.c
index e2ae361..1e9a704 100644
--- a/modules/audio_coding/codecs/ilbc/cb_construct.c
+++ b/modules/audio_coding/codecs/ilbc/cb_construct.c
@@ -21,6 +21,15 @@
 #include "modules/audio_coding/codecs/ilbc/defines.h"
 #include "modules/audio_coding/codecs/ilbc/gain_dequant.h"
 #include "modules/audio_coding/codecs/ilbc/get_cd_vec.h"
+#include "rtc_base/sanitizer.h"
+
+// An arithmetic operation that is allowed to overflow. (It's still undefined
+// behavior, so not a good idea; this just makes UBSan ignore the violation, so
+// that our old code can continue to do what it's always been doing.)
+static inline int32_t RTC_NO_SANITIZE("signed-integer-overflow")
+    OverflowingAddS32S32ToS32(int32_t a, int32_t b) {
+  return a + b;
+}
 
 /*----------------------------------------------------------------*
  *  Construct decoded vector from codebook and gains.
@@ -62,7 +71,7 @@
   for (j=0;j<veclen;j++) {
     a32 = (*gainPtr++) * cbvec0[j];
     a32 += (*gainPtr++) * cbvec1[j];
-    a32 += (*gainPtr) * cbvec2[j];
+    a32 = OverflowingAddS32S32ToS32(a32, (*gainPtr) * cbvec2[j]);
     gainPtr -= 2;
     decvector[j] = (int16_t)((a32 + 8192) >> 14);
   }
diff --git a/modules/audio_coding/codecs/isac/fix/source/codec.h b/modules/audio_coding/codecs/isac/fix/source/codec.h
index 9b87c40..9876bd6 100644
--- a/modules/audio_coding/codecs/isac/fix/source/codec.h
+++ b/modules/audio_coding/codecs/isac/fix/source/codec.h
@@ -67,7 +67,7 @@
 
 /* transform functions */
 
-void WebRtcIsacfix_InitTransform();
+void WebRtcIsacfix_InitTransform(void);
 
 typedef void (*Time2Spec)(int16_t* inre1Q9,
                           int16_t* inre2Q9,
diff --git a/modules/audio_coding/codecs/isac/fix/source/filterbanks_neon.c b/modules/audio_coding/codecs/isac/fix/source/filterbanks_neon.c
index fd29ccb..a31cea6 100644
--- a/modules/audio_coding/codecs/isac/fix/source/filterbanks_neon.c
+++ b/modules/audio_coding/codecs/isac/fix/source/filterbanks_neon.c
@@ -15,6 +15,7 @@
 
 #include <arm_neon.h>
 
+#include "modules/audio_coding/codecs/isac/fix/source/filterbank_internal.h"
 #include "rtc_base/checks.h"
 
 void WebRtcIsacfix_AllpassFilter2FixDec16Neon(
diff --git a/modules/audio_coding/codecs/isac/fix/source/lattice.c b/modules/audio_coding/codecs/isac/fix/source/lattice.c
index 1089549..7bbf4e0 100644
--- a/modules/audio_coding/codecs/isac/fix/source/lattice.c
+++ b/modules/audio_coding/codecs/isac/fix/source/lattice.c
@@ -279,8 +279,7 @@
 
     for (i=0;i<HALF_SUBFRAMELEN;i++)
     {
-
-      tmp32 = lat_inQ25[i + temp1] * (1 << 1);  // Q25->Q26
+      tmp32 = OverflowingLShiftS32(lat_inQ25[i + temp1], 1);  // Q25->Q26
       tmp32 = WEBRTC_SPL_MUL_16_32_RSFT16(inv_gain16, tmp32); //lat_in[]*inv_gain in (Q(18-sh)*Q26)>>16 = Q(28-sh)
       tmp32 = WEBRTC_SPL_SHIFT_W32(tmp32, -(28-sh)); // lat_in[]*inv_gain in Q0
 
diff --git a/modules/audio_coding/codecs/isac/fix/source/lattice_armv7.S b/modules/audio_coding/codecs/isac/fix/source/lattice_armv7.S
index 67ca4a4..4c63227 100644
--- a/modules/audio_coding/codecs/isac/fix/source/lattice_armv7.S
+++ b/modules/audio_coding/codecs/isac/fix/source/lattice_armv7.S
@@ -25,8 +25,8 @@
 @ r12: constant #16384
 @ r6, r7, r8, r10, r11: scratch
 
-#include "system_wrappers/include/asm_defines.h"
 #include "modules/audio_coding/codecs/isac/fix/source/settings.h"
+#include "rtc_base/system/asm_defines.h"
 
 GLOBAL_FUNCTION WebRtcIsacfix_FilterArLoop
 .align  2
diff --git a/modules/audio_coding/codecs/isac/fix/source/pitch_filter_armv6.S b/modules/audio_coding/codecs/isac/fix/source/pitch_filter_armv6.S
index d5b5541..0659468 100644
--- a/modules/audio_coding/codecs/isac/fix/source/pitch_filter_armv6.S
+++ b/modules/audio_coding/codecs/isac/fix/source/pitch_filter_armv6.S
@@ -13,8 +13,8 @@
 @
 @ Output is bit-exact with the reference C code in pitch_filter.c.
 
-#include "system_wrappers/include/asm_defines.h"
 #include "modules/audio_coding/codecs/isac/fix/source/settings.h"
+#include "rtc_base/system/asm_defines.h"
 
 GLOBAL_FUNCTION WebRtcIsacfix_PitchFilterCore
 .align  2
diff --git a/modules/audio_coding/codecs/isac/main/source/isac.c b/modules/audio_coding/codecs/isac/main/source/isac.c
index 79dc7e2..525e0f3 100644
--- a/modules/audio_coding/codecs/isac/main/source/isac.c
+++ b/modules/audio_coding/codecs/isac/main/source/isac.c
@@ -1266,8 +1266,10 @@
 
         /* It might be less due to garbage. */
         if ((numDecodedBytesUB != lenNextStream) &&
-            (numDecodedBytesUB != (lenNextStream -
-                encoded[numDecodedBytesLB + 1 + numDecodedBytesUB]))) {
+            (numDecodedBytesLB + 1 + numDecodedBytesUB >= lenEncodedBytes ||
+             numDecodedBytesUB !=
+                 (lenNextStream -
+                  encoded[numDecodedBytesLB + 1 + numDecodedBytesUB]))) {
           instISAC->errorCode = ISAC_LENGTH_MISMATCH;
           return -1;
         }
diff --git a/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc b/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc
index dfef682..bd34118 100644
--- a/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc
+++ b/modules/audio_coding/codecs/opus/audio_encoder_opus_unittest.cc
@@ -21,6 +21,7 @@
 #include "modules/audio_coding/neteq/tools/audio_loop.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/fakeclock.h"
+#include "rtc_base/ptr_util.h"
 #include "test/field_trial.h"
 #include "test/gmock.h"
 #include "test/gtest.h"
@@ -54,43 +55,39 @@
 }
 
 struct AudioEncoderOpusStates {
-  std::shared_ptr<MockAudioNetworkAdaptor*> mock_audio_network_adaptor;
+  MockAudioNetworkAdaptor* mock_audio_network_adaptor;
   MockSmoothingFilter* mock_bitrate_smoother;
   std::unique_ptr<AudioEncoderOpusImpl> encoder;
   std::unique_ptr<rtc::ScopedFakeClock> fake_clock;
   AudioEncoderOpusConfig config;
 };
 
-AudioEncoderOpusStates CreateCodec(size_t num_channels) {
-  AudioEncoderOpusStates states;
-  states.mock_audio_network_adaptor =
-      std::make_shared<MockAudioNetworkAdaptor*>(nullptr);
-  states.fake_clock.reset(new rtc::ScopedFakeClock());
-  states.fake_clock->SetTimeMicros(kInitialTimeUs);
-  std::weak_ptr<MockAudioNetworkAdaptor*> mock_ptr(
-      states.mock_audio_network_adaptor);
+std::unique_ptr<AudioEncoderOpusStates> CreateCodec(size_t num_channels) {
+  std::unique_ptr<AudioEncoderOpusStates> states =
+      rtc::MakeUnique<AudioEncoderOpusStates>();
+  states->mock_audio_network_adaptor = nullptr;
+  states->fake_clock.reset(new rtc::ScopedFakeClock());
+  states->fake_clock->SetTimeMicros(kInitialTimeUs);
+
+  MockAudioNetworkAdaptor** mock_ptr = &states->mock_audio_network_adaptor;
   AudioEncoderOpusImpl::AudioNetworkAdaptorCreator creator =
       [mock_ptr](const std::string&, RtcEventLog* event_log) {
         std::unique_ptr<MockAudioNetworkAdaptor> adaptor(
             new NiceMock<MockAudioNetworkAdaptor>());
         EXPECT_CALL(*adaptor, Die());
-        if (auto sp = mock_ptr.lock()) {
-          *sp = adaptor.get();
-        } else {
-          RTC_NOTREACHED();
-        }
+        *mock_ptr = adaptor.get();
         return adaptor;
       };
 
   CodecInst codec_inst = kDefaultOpusSettings;
   codec_inst.channels = num_channels;
-  states.config = CreateConfig(codec_inst);
+  states->config = CreateConfig(codec_inst);
   std::unique_ptr<MockSmoothingFilter> bitrate_smoother(
       new MockSmoothingFilter());
-  states.mock_bitrate_smoother = bitrate_smoother.get();
+  states->mock_bitrate_smoother = bitrate_smoother.get();
 
-  states.encoder.reset(new AudioEncoderOpusImpl(
-      states.config, codec_inst.pltype, std::move(creator),
+  states->encoder.reset(new AudioEncoderOpusImpl(
+      states->config, codec_inst.pltype, std::move(creator),
       std::move(bitrate_smoother)));
   return states;
 }
@@ -145,77 +142,77 @@
 TEST(AudioEncoderOpusTest, DefaultApplicationModeMono) {
   auto states = CreateCodec(1);
   EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kVoip,
-            states.encoder->application());
+            states->encoder->application());
 }
 
 TEST(AudioEncoderOpusTest, DefaultApplicationModeStereo) {
   auto states = CreateCodec(2);
   EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kAudio,
-            states.encoder->application());
+            states->encoder->application());
 }
 
 TEST(AudioEncoderOpusTest, ChangeApplicationMode) {
   auto states = CreateCodec(2);
   EXPECT_TRUE(
-      states.encoder->SetApplication(AudioEncoder::Application::kSpeech));
+      states->encoder->SetApplication(AudioEncoder::Application::kSpeech));
   EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kVoip,
-            states.encoder->application());
+            states->encoder->application());
 }
 
 TEST(AudioEncoderOpusTest, ResetWontChangeApplicationMode) {
   auto states = CreateCodec(2);
 
   // Trigger a reset.
-  states.encoder->Reset();
+  states->encoder->Reset();
   // Verify that the mode is still kAudio.
   EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kAudio,
-            states.encoder->application());
+            states->encoder->application());
 
   // Now change to kVoip.
   EXPECT_TRUE(
-      states.encoder->SetApplication(AudioEncoder::Application::kSpeech));
+      states->encoder->SetApplication(AudioEncoder::Application::kSpeech));
   EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kVoip,
-            states.encoder->application());
+            states->encoder->application());
 
   // Trigger a reset again.
-  states.encoder->Reset();
+  states->encoder->Reset();
   // Verify that the mode is still kVoip.
   EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kVoip,
-            states.encoder->application());
+            states->encoder->application());
 }
 
 TEST(AudioEncoderOpusTest, ToggleDtx) {
   auto states = CreateCodec(2);
   // Enable DTX
-  EXPECT_TRUE(states.encoder->SetDtx(true));
-  EXPECT_TRUE(states.encoder->GetDtx());
+  EXPECT_TRUE(states->encoder->SetDtx(true));
+  EXPECT_TRUE(states->encoder->GetDtx());
   // Turn off DTX.
-  EXPECT_TRUE(states.encoder->SetDtx(false));
-  EXPECT_FALSE(states.encoder->GetDtx());
+  EXPECT_TRUE(states->encoder->SetDtx(false));
+  EXPECT_FALSE(states->encoder->GetDtx());
 }
 
 TEST(AudioEncoderOpusTest,
      OnReceivedUplinkBandwidthWithoutAudioNetworkAdaptor) {
   auto states = CreateCodec(1);
-  // Constants are replicated from audio_states.encoderopus.cc.
+  // Constants are replicated from audio_states->encoderopus.cc.
   const int kMinBitrateBps = 6000;
   const int kMaxBitrateBps = 510000;
   // Set a too low bitrate.
-  states.encoder->OnReceivedUplinkBandwidth(kMinBitrateBps - 1, rtc::nullopt);
-  EXPECT_EQ(kMinBitrateBps, states.encoder->GetTargetBitrate());
+  states->encoder->OnReceivedUplinkBandwidth(kMinBitrateBps - 1, rtc::nullopt);
+  EXPECT_EQ(kMinBitrateBps, states->encoder->GetTargetBitrate());
   // Set a too high bitrate.
-  states.encoder->OnReceivedUplinkBandwidth(kMaxBitrateBps + 1, rtc::nullopt);
-  EXPECT_EQ(kMaxBitrateBps, states.encoder->GetTargetBitrate());
+  states->encoder->OnReceivedUplinkBandwidth(kMaxBitrateBps + 1, rtc::nullopt);
+  EXPECT_EQ(kMaxBitrateBps, states->encoder->GetTargetBitrate());
   // Set the minimum rate.
-  states.encoder->OnReceivedUplinkBandwidth(kMinBitrateBps, rtc::nullopt);
-  EXPECT_EQ(kMinBitrateBps, states.encoder->GetTargetBitrate());
+  states->encoder->OnReceivedUplinkBandwidth(kMinBitrateBps, rtc::nullopt);
+  EXPECT_EQ(kMinBitrateBps, states->encoder->GetTargetBitrate());
   // Set the maximum rate.
-  states.encoder->OnReceivedUplinkBandwidth(kMaxBitrateBps, rtc::nullopt);
-  EXPECT_EQ(kMaxBitrateBps, states.encoder->GetTargetBitrate());
+  states->encoder->OnReceivedUplinkBandwidth(kMaxBitrateBps, rtc::nullopt);
+  EXPECT_EQ(kMaxBitrateBps, states->encoder->GetTargetBitrate());
   // Set rates from kMaxBitrateBps up to 32000 bps.
   for (int rate = kMinBitrateBps; rate <= 32000; rate += 1000) {
-    states.encoder->OnReceivedUplinkBandwidth(rate, rtc::nullopt);
-    EXPECT_EQ(rate, states.encoder->GetTargetBitrate());
+    states->encoder->OnReceivedUplinkBandwidth(rate, rtc::nullopt);
+    EXPECT_EQ(rate, states->encoder->GetTargetBitrate());
   }
 }
 
@@ -237,7 +234,7 @@
 // Sets the packet loss rate to each number in the vector in turn, and verifies
 // that the loss rate as reported by the encoder is |expected_return| for all
 // of them.
-void TestSetPacketLossRate(AudioEncoderOpusStates* states,
+void TestSetPacketLossRate(const AudioEncoderOpusStates* states,
                            const std::vector<float>& losses,
                            float expected_return) {
   // |kSampleIntervalMs| is chosen to ease the calculation since
@@ -262,17 +259,17 @@
   // Note that the order of the following calls is critical.
 
   // clang-format off
-  TestSetPacketLossRate(&states, I(0.00f      , 0.01f - eps), 0.00f);
-  TestSetPacketLossRate(&states, I(0.01f + eps, 0.06f - eps), 0.01f);
-  TestSetPacketLossRate(&states, I(0.06f + eps, 0.11f - eps), 0.05f);
-  TestSetPacketLossRate(&states, I(0.11f + eps, 0.22f - eps), 0.10f);
-  TestSetPacketLossRate(&states, I(0.22f + eps, 1.00f      ), 0.20f);
+  TestSetPacketLossRate(states.get(), I(0.00f      , 0.01f - eps), 0.00f);
+  TestSetPacketLossRate(states.get(), I(0.01f + eps, 0.06f - eps), 0.01f);
+  TestSetPacketLossRate(states.get(), I(0.06f + eps, 0.11f - eps), 0.05f);
+  TestSetPacketLossRate(states.get(), I(0.11f + eps, 0.22f - eps), 0.10f);
+  TestSetPacketLossRate(states.get(), I(0.22f + eps, 1.00f      ), 0.20f);
 
-  TestSetPacketLossRate(&states, I(1.00f      , 0.18f + eps), 0.20f);
-  TestSetPacketLossRate(&states, I(0.18f - eps, 0.09f + eps), 0.10f);
-  TestSetPacketLossRate(&states, I(0.09f - eps, 0.04f + eps), 0.05f);
-  TestSetPacketLossRate(&states, I(0.04f - eps, 0.01f + eps), 0.01f);
-  TestSetPacketLossRate(&states, I(0.01f - eps, 0.00f      ), 0.00f);
+  TestSetPacketLossRate(states.get(), I(1.00f      , 0.18f + eps), 0.20f);
+  TestSetPacketLossRate(states.get(), I(0.18f - eps, 0.09f + eps), 0.10f);
+  TestSetPacketLossRate(states.get(), I(0.09f - eps, 0.04f + eps), 0.05f);
+  TestSetPacketLossRate(states.get(), I(0.04f - eps, 0.01f + eps), 0.01f);
+  TestSetPacketLossRate(states.get(), I(0.01f - eps, 0.00f      ), 0.00f);
   // clang-format on
 }
 
@@ -282,85 +279,85 @@
   // |supported_frame_lengths_ms| should contain only the frame length being
   // used.
   using ::testing::ElementsAre;
-  EXPECT_THAT(states.encoder->supported_frame_lengths_ms(),
-              ElementsAre(states.encoder->next_frame_length_ms()));
-  states.encoder->SetReceiverFrameLengthRange(0, 12345);
-  states.encoder->SetReceiverFrameLengthRange(21, 60);
-  EXPECT_THAT(states.encoder->supported_frame_lengths_ms(), ElementsAre(60));
-  states.encoder->SetReceiverFrameLengthRange(20, 59);
-  EXPECT_THAT(states.encoder->supported_frame_lengths_ms(), ElementsAre(20));
+  EXPECT_THAT(states->encoder->supported_frame_lengths_ms(),
+              ElementsAre(states->encoder->next_frame_length_ms()));
+  states->encoder->SetReceiverFrameLengthRange(0, 12345);
+  states->encoder->SetReceiverFrameLengthRange(21, 60);
+  EXPECT_THAT(states->encoder->supported_frame_lengths_ms(), ElementsAre(60));
+  states->encoder->SetReceiverFrameLengthRange(20, 59);
+  EXPECT_THAT(states->encoder->supported_frame_lengths_ms(), ElementsAre(20));
 }
 
 TEST(AudioEncoderOpusTest,
      InvokeAudioNetworkAdaptorOnReceivedUplinkPacketLossFraction) {
   auto states = CreateCodec(2);
-  states.encoder->EnableAudioNetworkAdaptor("", nullptr);
+  states->encoder->EnableAudioNetworkAdaptor("", nullptr);
 
   auto config = CreateEncoderRuntimeConfig();
-  EXPECT_CALL(**states.mock_audio_network_adaptor, GetEncoderRuntimeConfig())
+  EXPECT_CALL(*states->mock_audio_network_adaptor, GetEncoderRuntimeConfig())
       .WillOnce(Return(config));
 
   // Since using mock audio network adaptor, any packet loss fraction is fine.
   constexpr float kUplinkPacketLoss = 0.1f;
-  EXPECT_CALL(**states.mock_audio_network_adaptor,
+  EXPECT_CALL(*states->mock_audio_network_adaptor,
               SetUplinkPacketLossFraction(kUplinkPacketLoss));
-  states.encoder->OnReceivedUplinkPacketLossFraction(kUplinkPacketLoss);
+  states->encoder->OnReceivedUplinkPacketLossFraction(kUplinkPacketLoss);
 
-  CheckEncoderRuntimeConfig(states.encoder.get(), config);
+  CheckEncoderRuntimeConfig(states->encoder.get(), config);
 }
 
 TEST(AudioEncoderOpusTest, InvokeAudioNetworkAdaptorOnReceivedUplinkBandwidth) {
   auto states = CreateCodec(2);
-  states.encoder->EnableAudioNetworkAdaptor("", nullptr);
+  states->encoder->EnableAudioNetworkAdaptor("", nullptr);
 
   auto config = CreateEncoderRuntimeConfig();
-  EXPECT_CALL(**states.mock_audio_network_adaptor, GetEncoderRuntimeConfig())
+  EXPECT_CALL(*states->mock_audio_network_adaptor, GetEncoderRuntimeConfig())
       .WillOnce(Return(config));
 
   // Since using mock audio network adaptor, any target audio bitrate is fine.
   constexpr int kTargetAudioBitrate = 30000;
   constexpr int64_t kProbingIntervalMs = 3000;
-  EXPECT_CALL(**states.mock_audio_network_adaptor,
+  EXPECT_CALL(*states->mock_audio_network_adaptor,
               SetTargetAudioBitrate(kTargetAudioBitrate));
-  EXPECT_CALL(*states.mock_bitrate_smoother,
+  EXPECT_CALL(*states->mock_bitrate_smoother,
               SetTimeConstantMs(kProbingIntervalMs * 4));
-  EXPECT_CALL(*states.mock_bitrate_smoother, AddSample(kTargetAudioBitrate));
-  states.encoder->OnReceivedUplinkBandwidth(kTargetAudioBitrate,
-                                            kProbingIntervalMs);
+  EXPECT_CALL(*states->mock_bitrate_smoother, AddSample(kTargetAudioBitrate));
+  states->encoder->OnReceivedUplinkBandwidth(kTargetAudioBitrate,
+                                             kProbingIntervalMs);
 
-  CheckEncoderRuntimeConfig(states.encoder.get(), config);
+  CheckEncoderRuntimeConfig(states->encoder.get(), config);
 }
 
 TEST(AudioEncoderOpusTest, InvokeAudioNetworkAdaptorOnReceivedRtt) {
   auto states = CreateCodec(2);
-  states.encoder->EnableAudioNetworkAdaptor("", nullptr);
+  states->encoder->EnableAudioNetworkAdaptor("", nullptr);
 
   auto config = CreateEncoderRuntimeConfig();
-  EXPECT_CALL(**states.mock_audio_network_adaptor, GetEncoderRuntimeConfig())
+  EXPECT_CALL(*states->mock_audio_network_adaptor, GetEncoderRuntimeConfig())
       .WillOnce(Return(config));
 
   // Since using mock audio network adaptor, any rtt is fine.
   constexpr int kRtt = 30;
-  EXPECT_CALL(**states.mock_audio_network_adaptor, SetRtt(kRtt));
-  states.encoder->OnReceivedRtt(kRtt);
+  EXPECT_CALL(*states->mock_audio_network_adaptor, SetRtt(kRtt));
+  states->encoder->OnReceivedRtt(kRtt);
 
-  CheckEncoderRuntimeConfig(states.encoder.get(), config);
+  CheckEncoderRuntimeConfig(states->encoder.get(), config);
 }
 
 TEST(AudioEncoderOpusTest, InvokeAudioNetworkAdaptorOnReceivedOverhead) {
   auto states = CreateCodec(2);
-  states.encoder->EnableAudioNetworkAdaptor("", nullptr);
+  states->encoder->EnableAudioNetworkAdaptor("", nullptr);
 
   auto config = CreateEncoderRuntimeConfig();
-  EXPECT_CALL(**states.mock_audio_network_adaptor, GetEncoderRuntimeConfig())
+  EXPECT_CALL(*states->mock_audio_network_adaptor, GetEncoderRuntimeConfig())
       .WillOnce(Return(config));
 
   // Since using mock audio network adaptor, any overhead is fine.
   constexpr size_t kOverhead = 64;
-  EXPECT_CALL(**states.mock_audio_network_adaptor, SetOverhead(kOverhead));
-  states.encoder->OnReceivedOverhead(kOverhead);
+  EXPECT_CALL(*states->mock_audio_network_adaptor, SetOverhead(kOverhead));
+  states->encoder->OnReceivedOverhead(kOverhead);
 
-  CheckEncoderRuntimeConfig(states.encoder.get(), config);
+  CheckEncoderRuntimeConfig(states->encoder.get(), config);
 }
 
 TEST(AudioEncoderOpusTest,
@@ -376,18 +373,18 @@
   constexpr int64_t kSecondSampleTimeMs = 6931;
 
   // First time, no filtering.
-  states.encoder->OnReceivedUplinkPacketLossFraction(kPacketLossFraction_1);
-  EXPECT_FLOAT_EQ(0.01f, states.encoder->packet_loss_rate());
+  states->encoder->OnReceivedUplinkPacketLossFraction(kPacketLossFraction_1);
+  EXPECT_FLOAT_EQ(0.01f, states->encoder->packet_loss_rate());
 
-  states.fake_clock->AdvanceTime(
+  states->fake_clock->AdvanceTime(
       rtc::TimeDelta::FromMilliseconds(kSecondSampleTimeMs));
-  states.encoder->OnReceivedUplinkPacketLossFraction(kPacketLossFraction_2);
+  states->encoder->OnReceivedUplinkPacketLossFraction(kPacketLossFraction_2);
 
   // Now the output of packet loss fraction smoother should be
   // (0.02 + 0.198) / 2 = 0.109, which reach the threshold for the optimized
   // packet loss rate to increase to 0.05. If no smoothing has been made, the
   // optimized packet loss rate should have been increase to 0.1.
-  EXPECT_FLOAT_EQ(0.05f, states.encoder->packet_loss_rate());
+  EXPECT_FLOAT_EQ(0.05f, states->encoder->packet_loss_rate());
 }
 
 TEST(AudioEncoderOpusTest, DoNotInvokeSetTargetBitrateIfOverheadUnknown) {
@@ -396,12 +393,12 @@
 
   auto states = CreateCodec(2);
 
-  states.encoder->OnReceivedUplinkBandwidth(kDefaultOpusSettings.rate * 2,
-                                            rtc::nullopt);
+  states->encoder->OnReceivedUplinkBandwidth(kDefaultOpusSettings.rate * 2,
+                                             rtc::nullopt);
 
   // Since |OnReceivedOverhead| has not been called, the codec bitrate should
   // not change.
-  EXPECT_EQ(kDefaultOpusSettings.rate, states.encoder->GetTargetBitrate());
+  EXPECT_EQ(kDefaultOpusSettings.rate, states->encoder->GetTargetBitrate());
 }
 
 TEST(AudioEncoderOpusTest, OverheadRemovedFromTargetAudioBitrate) {
@@ -411,15 +408,15 @@
   auto states = CreateCodec(2);
 
   constexpr size_t kOverheadBytesPerPacket = 64;
-  states.encoder->OnReceivedOverhead(kOverheadBytesPerPacket);
+  states->encoder->OnReceivedOverhead(kOverheadBytesPerPacket);
 
   constexpr int kTargetBitrateBps = 40000;
-  states.encoder->OnReceivedUplinkBandwidth(kTargetBitrateBps, rtc::nullopt);
+  states->encoder->OnReceivedUplinkBandwidth(kTargetBitrateBps, rtc::nullopt);
 
   int packet_rate = rtc::CheckedDivExact(48000, kDefaultOpusSettings.pacsize);
   EXPECT_EQ(kTargetBitrateBps -
                 8 * static_cast<int>(kOverheadBytesPerPacket) * packet_rate,
-            states.encoder->GetTargetBitrate());
+            states->encoder->GetTargetBitrate());
 }
 
 TEST(AudioEncoderOpusTest, BitrateBounded) {
@@ -432,7 +429,7 @@
   auto states = CreateCodec(2);
 
   constexpr size_t kOverheadBytesPerPacket = 64;
-  states.encoder->OnReceivedOverhead(kOverheadBytesPerPacket);
+  states->encoder->OnReceivedOverhead(kOverheadBytesPerPacket);
 
   int packet_rate = rtc::CheckedDivExact(48000, kDefaultOpusSettings.pacsize);
 
@@ -440,15 +437,15 @@
   // subtracted. The eventual codec rate should be bounded by |kMinBitrateBps|.
   int target_bitrate =
       kOverheadBytesPerPacket * 8 * packet_rate + kMinBitrateBps - 1;
-  states.encoder->OnReceivedUplinkBandwidth(target_bitrate, rtc::nullopt);
-  EXPECT_EQ(kMinBitrateBps, states.encoder->GetTargetBitrate());
+  states->encoder->OnReceivedUplinkBandwidth(target_bitrate, rtc::nullopt);
+  EXPECT_EQ(kMinBitrateBps, states->encoder->GetTargetBitrate());
 
   // Set a target rate that is greater than |kMaxBitrateBps| when overhead is
   // subtracted. The eventual codec rate should be bounded by |kMaxBitrateBps|.
   target_bitrate =
       kOverheadBytesPerPacket * 8 * packet_rate + kMaxBitrateBps + 1;
-  states.encoder->OnReceivedUplinkBandwidth(target_bitrate, rtc::nullopt);
-  EXPECT_EQ(kMaxBitrateBps, states.encoder->GetTargetBitrate());
+  states->encoder->OnReceivedUplinkBandwidth(target_bitrate, rtc::nullopt);
+  EXPECT_EQ(kMaxBitrateBps, states->encoder->GetTargetBitrate());
 }
 
 // Verifies that the complexity adaptation in the config works as intended.
@@ -534,50 +531,50 @@
 
 TEST(AudioEncoderOpusTest, EmptyConfigDoesNotAffectEncoderSettings) {
   auto states = CreateCodec(2);
-  states.encoder->EnableAudioNetworkAdaptor("", nullptr);
+  states->encoder->EnableAudioNetworkAdaptor("", nullptr);
 
   auto config = CreateEncoderRuntimeConfig();
   AudioEncoderRuntimeConfig empty_config;
 
-  EXPECT_CALL(**states.mock_audio_network_adaptor, GetEncoderRuntimeConfig())
+  EXPECT_CALL(*states->mock_audio_network_adaptor, GetEncoderRuntimeConfig())
       .WillOnce(Return(config))
       .WillOnce(Return(empty_config));
 
   constexpr size_t kOverhead = 64;
-  EXPECT_CALL(**states.mock_audio_network_adaptor, SetOverhead(kOverhead))
+  EXPECT_CALL(*states->mock_audio_network_adaptor, SetOverhead(kOverhead))
       .Times(2);
-  states.encoder->OnReceivedOverhead(kOverhead);
-  states.encoder->OnReceivedOverhead(kOverhead);
+  states->encoder->OnReceivedOverhead(kOverhead);
+  states->encoder->OnReceivedOverhead(kOverhead);
 
-  CheckEncoderRuntimeConfig(states.encoder.get(), config);
+  CheckEncoderRuntimeConfig(states->encoder.get(), config);
 }
 
 TEST(AudioEncoderOpusTest, UpdateUplinkBandwidthInAudioNetworkAdaptor) {
   auto states = CreateCodec(2);
-  states.encoder->EnableAudioNetworkAdaptor("", nullptr);
+  states->encoder->EnableAudioNetworkAdaptor("", nullptr);
   std::array<int16_t, 480 * 2> audio;
   audio.fill(0);
   rtc::Buffer encoded;
-  EXPECT_CALL(*states.mock_bitrate_smoother, GetAverage())
+  EXPECT_CALL(*states->mock_bitrate_smoother, GetAverage())
       .WillOnce(Return(50000));
-  EXPECT_CALL(**states.mock_audio_network_adaptor, SetUplinkBandwidth(50000));
-  states.encoder->Encode(
+  EXPECT_CALL(*states->mock_audio_network_adaptor, SetUplinkBandwidth(50000));
+  states->encoder->Encode(
       0, rtc::ArrayView<const int16_t>(audio.data(), audio.size()), &encoded);
 
   // Repeat update uplink bandwidth tests.
   for (int i = 0; i < 5; i++) {
     // Don't update till it is time to update again.
-    states.fake_clock->AdvanceTime(rtc::TimeDelta::FromMilliseconds(
-        states.config.uplink_bandwidth_update_interval_ms - 1));
-    states.encoder->Encode(
+    states->fake_clock->AdvanceTime(rtc::TimeDelta::FromMilliseconds(
+        states->config.uplink_bandwidth_update_interval_ms - 1));
+    states->encoder->Encode(
         0, rtc::ArrayView<const int16_t>(audio.data(), audio.size()), &encoded);
 
     // Update when it is time to update.
-    EXPECT_CALL(*states.mock_bitrate_smoother, GetAverage())
+    EXPECT_CALL(*states->mock_bitrate_smoother, GetAverage())
         .WillOnce(Return(40000));
-    EXPECT_CALL(**states.mock_audio_network_adaptor, SetUplinkBandwidth(40000));
-    states.fake_clock->AdvanceTime(rtc::TimeDelta::FromMilliseconds(1));
-    states.encoder->Encode(
+    EXPECT_CALL(*states->mock_audio_network_adaptor, SetUplinkBandwidth(40000));
+    states->fake_clock->AdvanceTime(rtc::TimeDelta::FromMilliseconds(1));
+    states->encoder->Encode(
         0, rtc::ArrayView<const int16_t>(audio.data(), audio.size()), &encoded);
   }
 }
@@ -586,25 +583,25 @@
   auto states = CreateCodec(1);
   constexpr int kNumPacketsToEncode = 2;
   auto audio_frames =
-      Create10msAudioBlocks(states.encoder, kNumPacketsToEncode * 20);
+      Create10msAudioBlocks(states->encoder, kNumPacketsToEncode * 20);
   ASSERT_TRUE(audio_frames) << "Create10msAudioBlocks failed";
   rtc::Buffer encoded;
   uint32_t rtp_timestamp = 12345;  // Just a number not important to this test.
 
-  states.encoder->OnReceivedUplinkBandwidth(0, rtc::nullopt);
+  states->encoder->OnReceivedUplinkBandwidth(0, rtc::nullopt);
   for (int packet_index = 0; packet_index < kNumPacketsToEncode;
        packet_index++) {
     // Make sure we are not encoding before we have enough data for
     // a 20ms packet.
     for (int index = 0; index < 1; index++) {
-      states.encoder->Encode(rtp_timestamp, audio_frames->GetNextBlock(),
-                             &encoded);
+      states->encoder->Encode(rtp_timestamp, audio_frames->GetNextBlock(),
+                              &encoded);
       EXPECT_EQ(0u, encoded.size());
     }
 
     // Should encode now.
-    states.encoder->Encode(rtp_timestamp, audio_frames->GetNextBlock(),
-                           &encoded);
+    states->encoder->Encode(rtp_timestamp, audio_frames->GetNextBlock(),
+                            &encoded);
     EXPECT_GT(encoded.size(), 0u);
     encoded.Clear();
   }
diff --git a/modules/audio_coding/include/audio_coding_module.h b/modules/audio_coding/include/audio_coding_module.h
index 12c98ee..3c193a4 100644
--- a/modules/audio_coding/include/audio_coding_module.h
+++ b/modules/audio_coding/include/audio_coding_module.h
@@ -21,7 +21,6 @@
 #include "common_types.h"  // NOLINT(build/include)
 #include "modules/audio_coding/include/audio_coding_module_typedefs.h"
 #include "modules/audio_coding/neteq/include/neteq.h"
-#include "modules/include/module.h"
 #include "rtc_base/deprecation.h"
 #include "rtc_base/function_view.h"
 #include "system_wrappers/include/clock.h"
@@ -66,7 +65,8 @@
 
  public:
   struct Config {
-    Config();
+    explicit Config(
+        rtc::scoped_refptr<AudioDecoderFactory> decoder_factory = nullptr);
     Config(const Config&);
     ~Config();
 
@@ -75,17 +75,6 @@
     rtc::scoped_refptr<AudioDecoderFactory> decoder_factory;
   };
 
-  ///////////////////////////////////////////////////////////////////////////
-  // Creation and destruction of a ACM.
-  //
-  // The second method is used for testing where a simulated clock can be
-  // injected into ACM. ACM will take the ownership of the object clock and
-  // delete it when destroyed.
-  //
-  // TODO(solenberg): Remove once downstream projects are updated.
-  RTC_DEPRECATED static AudioCodingModule* Create(int id);
-  static AudioCodingModule* Create();
-  static AudioCodingModule* Create(Clock* clock);
   static AudioCodingModule* Create(const Config& config);
   virtual ~AudioCodingModule() = default;
 
@@ -288,9 +277,7 @@
   //
   // Input:
   //   -audio_frame        : the input audio frame, containing raw audio
-  //                         sampling frequency etc.,
-  //                         c.f. module_common_types.h for definition of
-  //                         AudioFrame.
+  //                         sampling frequency etc.
   //
   // Return value:
   //   >= 0   number of bytes encoded.
@@ -673,9 +660,7 @@
   //
   // Output:
   //   -audio_frame        : output audio frame which contains raw audio data
-  //                         and other relevant parameters, c.f.
-  //                         module_common_types.h for the definition of
-  //                         AudioFrame.
+  //                         and other relevant parameters.
   //   -muted              : if true, the sample data in audio_frame is not
   //                         populated, and must be interpreted as all zero.
   //
diff --git a/modules/audio_coding/include/audio_coding_module_typedefs.h b/modules/audio_coding/include/audio_coding_module_typedefs.h
index ad71ef1..85a6bf9 100644
--- a/modules/audio_coding/include/audio_coding_module_typedefs.h
+++ b/modules/audio_coding/include/audio_coding_module_typedefs.h
@@ -13,7 +13,6 @@
 
 #include <map>
 
-#include "modules/include/module_common_types.h"
 #include "typedefs.h"  // NOLINT(build/include)
 
 namespace webrtc {
diff --git a/modules/audio_coding/neteq/background_noise.cc b/modules/audio_coding/neteq/background_noise.cc
index eda5c75..50ffa86 100644
--- a/modules/audio_coding/neteq/background_noise.cc
+++ b/modules/audio_coding/neteq/background_noise.cc
@@ -27,8 +27,7 @@
 
 BackgroundNoise::BackgroundNoise(size_t num_channels)
     : num_channels_(num_channels),
-      channel_parameters_(new ChannelParameters[num_channels_]),
-      mode_(NetEq::kBgnOn) {
+      channel_parameters_(new ChannelParameters[num_channels_]) {
   Reset();
 }
 
@@ -39,7 +38,6 @@
   for (size_t channel = 0; channel < num_channels_; ++channel) {
     channel_parameters_[channel].Reset();
   }
-  // Keep _bgnMode as it is.
 }
 
 void BackgroundNoise::Update(const AudioMultiVector& input,
diff --git a/modules/audio_coding/neteq/background_noise.h b/modules/audio_coding/neteq/background_noise.h
index 718f41d..a6f1395 100644
--- a/modules/audio_coding/neteq/background_noise.h
+++ b/modules/audio_coding/neteq/background_noise.h
@@ -68,11 +68,6 @@
 
   // Accessors.
   bool initialized() const { return initialized_; }
-  NetEq::BackgroundNoiseMode mode() const { return mode_; }
-
-  // Sets the mode of the background noise playout for cases when there is long
-  // duration of packet loss.
-  void set_mode(NetEq::BackgroundNoiseMode mode) { mode_ = mode; }
 
  private:
   static const int kThresholdIncrement = 229;  // 0.0035 in Q16.
@@ -128,7 +123,6 @@
   size_t num_channels_;
   std::unique_ptr<ChannelParameters[]> channel_parameters_;
   bool initialized_;
-  NetEq::BackgroundNoiseMode mode_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(BackgroundNoise);
 };
diff --git a/modules/audio_coding/neteq/decision_logic.cc b/modules/audio_coding/neteq/decision_logic.cc
index 966d5c3..6ab2716 100644
--- a/modules/audio_coding/neteq/decision_logic.cc
+++ b/modules/audio_coding/neteq/decision_logic.cc
@@ -19,6 +19,7 @@
 #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"
 
 namespace webrtc {
 
diff --git a/modules/audio_coding/neteq/decision_logic_normal.cc b/modules/audio_coding/neteq/decision_logic_normal.cc
index 10f501a..1429bb7 100644
--- a/modules/audio_coding/neteq/decision_logic_normal.cc
+++ b/modules/audio_coding/neteq/decision_logic_normal.cc
@@ -20,7 +20,6 @@
 #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"
 
 namespace webrtc {
 
diff --git a/modules/audio_coding/neteq/decision_logic_unittest.cc b/modules/audio_coding/neteq/decision_logic_unittest.cc
index 1a7bab9..be1b854 100644
--- a/modules/audio_coding/neteq/decision_logic_unittest.cc
+++ b/modules/audio_coding/neteq/decision_logic_unittest.cc
@@ -26,7 +26,7 @@
   int fs_hz = 8000;
   int output_size_samples = fs_hz / 100;  // Samples per 10 ms.
   DecoderDatabase decoder_database(
-      new rtc::RefCountedObject<MockAudioDecoderFactory>);
+      new rtc::RefCountedObject<MockAudioDecoderFactory>, rtc::nullopt);
   TickTimer tick_timer;
   PacketBuffer packet_buffer(10, &tick_timer);
   DelayPeakDetector delay_peak_detector(&tick_timer);
diff --git a/modules/audio_coding/neteq/decoder_database.cc b/modules/audio_coding/neteq/decoder_database.cc
index 5ddaf04..5b940ae 100644
--- a/modules/audio_coding/neteq/decoder_database.cc
+++ b/modules/audio_coding/neteq/decoder_database.cc
@@ -15,40 +15,51 @@
 #include "api/audio_codecs/audio_decoder.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
+#include "rtc_base/strings/audio_format_to_string.h"
 
 namespace webrtc {
 
 DecoderDatabase::DecoderDatabase(
-    const rtc::scoped_refptr<AudioDecoderFactory>& decoder_factory)
+    const rtc::scoped_refptr<AudioDecoderFactory>& decoder_factory,
+    rtc::Optional<AudioCodecPairId> codec_pair_id)
     : active_decoder_type_(-1),
       active_cng_decoder_type_(-1),
-      decoder_factory_(decoder_factory) {}
+      decoder_factory_(decoder_factory),
+      codec_pair_id_(codec_pair_id) {}
 
 DecoderDatabase::~DecoderDatabase() = default;
 
-DecoderDatabase::DecoderInfo::DecoderInfo(const SdpAudioFormat& audio_format,
-                                          AudioDecoderFactory* factory,
-                                          const std::string& codec_name)
+DecoderDatabase::DecoderInfo::DecoderInfo(
+    const SdpAudioFormat& audio_format,
+    rtc::Optional<AudioCodecPairId> codec_pair_id,
+    AudioDecoderFactory* factory,
+    const std::string& codec_name)
     : name_(codec_name),
       audio_format_(audio_format),
+      codec_pair_id_(codec_pair_id),
       factory_(factory),
       external_decoder_(nullptr),
       cng_decoder_(CngDecoder::Create(audio_format)),
       subtype_(SubtypeFromFormat(audio_format)) {}
 
-DecoderDatabase::DecoderInfo::DecoderInfo(const SdpAudioFormat& audio_format,
-                                          AudioDecoderFactory* factory)
-    : DecoderInfo(audio_format, factory, audio_format.name) {}
+DecoderDatabase::DecoderInfo::DecoderInfo(
+    const SdpAudioFormat& audio_format,
+    rtc::Optional<AudioCodecPairId> codec_pair_id,
+    AudioDecoderFactory* factory)
+    : DecoderInfo(audio_format, codec_pair_id, factory, audio_format.name) {}
 
-DecoderDatabase::DecoderInfo::DecoderInfo(NetEqDecoder ct,
-                                          AudioDecoderFactory* factory)
-    : DecoderInfo(*NetEqDecoderToSdpAudioFormat(ct), factory) {}
+DecoderDatabase::DecoderInfo::DecoderInfo(
+    NetEqDecoder ct,
+    rtc::Optional<AudioCodecPairId> codec_pair_id,
+    AudioDecoderFactory* factory)
+    : DecoderInfo(*NetEqDecoderToSdpAudioFormat(ct), codec_pair_id, factory) {}
 
 DecoderDatabase::DecoderInfo::DecoderInfo(const SdpAudioFormat& audio_format,
                                           AudioDecoder* ext_dec,
                                           const std::string& codec_name)
     : name_(codec_name),
       audio_format_(audio_format),
+      codec_pair_id_(rtc::nullopt),
       factory_(nullptr),
       external_decoder_(ext_dec),
       subtype_(Subtype::kNormal) {
@@ -83,9 +94,9 @@
     // TODO(ossu): Keep a check here for now, since a number of tests create
     // DecoderInfos without factories.
     RTC_DCHECK(factory_);
-    decoder_ = factory_->MakeAudioDecoder(audio_format_, rtc::nullopt);
+    decoder_ = factory_->MakeAudioDecoder(audio_format_, codec_pair_id_);
   }
-  RTC_DCHECK(decoder_) << "Failed to create: " << audio_format_;
+  RTC_DCHECK(decoder_) << "Failed to create: " << rtc::ToString(audio_format_);
   return decoder_.get();
 }
 
@@ -156,7 +167,8 @@
     RTC_DCHECK_LE(rtp_payload_type, 0x7f);
     if (decoders_.count(rtp_payload_type) == 0) {
       decoders_.insert(std::make_pair(
-          rtp_payload_type, DecoderInfo(audio_format, decoder_factory_.get())));
+          rtp_payload_type,
+          DecoderInfo(audio_format, codec_pair_id_, decoder_factory_.get())));
     } else {
       // The mapping for this payload type hasn't changed.
     }
@@ -178,7 +190,7 @@
   if (!opt_format) {
     return kCodecNotSupported;
   }
-  DecoderInfo info(*opt_format, decoder_factory_, name);
+  DecoderInfo info(*opt_format, codec_pair_id_, decoder_factory_, name);
   if (!info.CanGetDecoder()) {
     return kCodecNotSupported;
   }
@@ -197,7 +209,8 @@
     return kInvalidRtpPayloadType;
   }
   const auto ret = decoders_.insert(std::make_pair(
-      rtp_payload_type, DecoderInfo(audio_format, decoder_factory_.get())));
+      rtp_payload_type,
+      DecoderInfo(audio_format, codec_pair_id_, decoder_factory_.get())));
   if (ret.second == false) {
     // Database already contains a decoder with type |rtp_payload_type|.
     return kDecoderExists;
@@ -217,7 +230,8 @@
   }
 
   const auto opt_db_format = NetEqDecoderToSdpAudioFormat(codec_type);
-  const SdpAudioFormat format = opt_db_format.value_or({"arbitrary", 0, 0});
+  const SdpAudioFormat format =
+      opt_db_format.value_or(SdpAudioFormat("arbitrary", 0, 0));
 
   std::pair<DecoderMap::iterator, bool> ret;
   DecoderInfo info(format, decoder, codec_name);
diff --git a/modules/audio_coding/neteq/decoder_database.h b/modules/audio_coding/neteq/decoder_database.h
index 5f0d173..f769e39 100644
--- a/modules/audio_coding/neteq/decoder_database.h
+++ b/modules/audio_coding/neteq/decoder_database.h
@@ -43,11 +43,14 @@
   class DecoderInfo {
    public:
     DecoderInfo(const SdpAudioFormat& audio_format,
+                rtc::Optional<AudioCodecPairId> codec_pair_id,
                 AudioDecoderFactory* factory,
                 const std::string& codec_name);
     explicit DecoderInfo(const SdpAudioFormat& audio_format,
+                         rtc::Optional<AudioCodecPairId> codec_pair_id,
                          AudioDecoderFactory* factory = nullptr);
     explicit DecoderInfo(NetEqDecoder ct,
+                         rtc::Optional<AudioCodecPairId> codec_pair_id,
                          AudioDecoderFactory* factory = nullptr);
     DecoderInfo(const SdpAudioFormat& audio_format,
                 AudioDecoder* ext_dec,
@@ -108,6 +111,7 @@
     const std::string name_;
 
     const SdpAudioFormat audio_format_;
+    const rtc::Optional<AudioCodecPairId> codec_pair_id_;
     AudioDecoderFactory* const factory_;
     mutable std::unique_ptr<AudioDecoder> decoder_;
 
@@ -138,7 +142,8 @@
   static const uint8_t kRtpPayloadTypeError = 0xFF;
 
   DecoderDatabase(
-      const rtc::scoped_refptr<AudioDecoderFactory>& decoder_factory);
+      const rtc::scoped_refptr<AudioDecoderFactory>& decoder_factory,
+      rtc::Optional<AudioCodecPairId> codec_pair_id);
 
   virtual ~DecoderDatabase();
 
@@ -242,6 +247,7 @@
   int active_cng_decoder_type_;
   mutable std::unique_ptr<ComfortNoiseDecoder> active_cng_decoder_;
   rtc::scoped_refptr<AudioDecoderFactory> decoder_factory_;
+  const rtc::Optional<AudioCodecPairId> codec_pair_id_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(DecoderDatabase);
 };
diff --git a/modules/audio_coding/neteq/decoder_database_unittest.cc b/modules/audio_coding/neteq/decoder_database_unittest.cc
index 7f9b38e..a6b9689 100644
--- a/modules/audio_coding/neteq/decoder_database_unittest.cc
+++ b/modules/audio_coding/neteq/decoder_database_unittest.cc
@@ -28,7 +28,8 @@
 namespace webrtc {
 
 TEST(DecoderDatabase, CreateAndDestroy) {
-  DecoderDatabase db(new rtc::RefCountedObject<MockAudioDecoderFactory>);
+  DecoderDatabase db(new rtc::RefCountedObject<MockAudioDecoderFactory>,
+                     rtc::nullopt);
   EXPECT_EQ(0, db.Size());
   EXPECT_TRUE(db.Empty());
 }
@@ -41,7 +42,7 @@
         EXPECT_EQ("pcmu", format.name);
         return true;
       }));
-  DecoderDatabase db(factory);
+  DecoderDatabase db(factory, rtc::nullopt);
   const uint8_t kPayloadType = 0;
   const std::string kCodecName = "Robert\'); DROP TABLE Students;";
   EXPECT_EQ(
@@ -66,7 +67,7 @@
         EXPECT_EQ("pcma", format.name);
         return true;
       }));
-  DecoderDatabase db(factory);
+  DecoderDatabase db(factory, rtc::nullopt);
   const std::string kCodecName1 = "Robert\'); DROP TABLE Students;";
   const std::string kCodecName2 = "https://xkcd.com/327/";
   EXPECT_EQ(DecoderDatabase::kOK,
@@ -96,7 +97,7 @@
         EXPECT_EQ("pcmu", format.name);
         dec->reset(decoder);
       }));
-  DecoderDatabase db(factory);
+  DecoderDatabase db(factory, rtc::nullopt);
   const uint8_t kPayloadType = 0;
   const std::string kCodecName = "Robert\'); DROP TABLE Students;";
   EXPECT_EQ(
@@ -113,7 +114,7 @@
 }
 
 TEST(DecoderDatabase, GetDecoder) {
-  DecoderDatabase db(CreateBuiltinAudioDecoderFactory());
+  DecoderDatabase db(CreateBuiltinAudioDecoderFactory(), rtc::nullopt);
   const uint8_t kPayloadType = 0;
   const std::string kCodecName = "Robert\'); DROP TABLE Students;";
   EXPECT_EQ(DecoderDatabase::kOK,
@@ -131,7 +132,7 @@
         EXPECT_EQ("pcmu", format.name);
         return true;
       }));
-  DecoderDatabase db(factory);
+  DecoderDatabase db(factory, rtc::nullopt);
   const uint8_t kPayloadTypePcmU = 0;
   const uint8_t kPayloadTypeCng = 13;
   const uint8_t kPayloadTypeDtmf = 100;
@@ -166,7 +167,8 @@
 }
 
 TEST(DecoderDatabase, ExternalDecoder) {
-  DecoderDatabase db(new rtc::RefCountedObject<MockAudioDecoderFactory>);
+  DecoderDatabase db(new rtc::RefCountedObject<MockAudioDecoderFactory>,
+                     rtc::nullopt);
   const uint8_t kPayloadType = 0;
   const std::string kCodecName = "Robert\'); DROP TABLE Students;";
   MockAudioDecoder decoder;
@@ -203,7 +205,7 @@
         EXPECT_EQ("pcmu", format.name);
         return true;
       }));
-  DecoderDatabase db(factory);
+  DecoderDatabase db(factory, rtc::nullopt);
   // Load a number of payloads into the database. Payload types are 0, 1, ...,
   // while the decoder type is the same for all payload types (this does not
   // matter for the test).
@@ -243,7 +245,7 @@
 
 // Test the methods for setting and getting active speech and CNG decoders.
 TEST(DecoderDatabase, IF_ISAC(ActiveDecoders)) {
-  DecoderDatabase db(CreateBuiltinAudioDecoderFactory());
+  DecoderDatabase db(CreateBuiltinAudioDecoderFactory(), rtc::nullopt);
   // Load payload types.
   ASSERT_EQ(DecoderDatabase::kOK,
             db.RegisterPayload(0, NetEqDecoder::kDecoderPCMu, "pcmu"));
diff --git a/modules/audio_coding/neteq/expand.cc b/modules/audio_coding/neteq/expand.cc
index 3fb09ea..03bcc77 100644
--- a/modules/audio_coding/neteq/expand.cc
+++ b/modules/audio_coding/neteq/expand.cc
@@ -911,46 +911,10 @@
 
     // Unmute the background noise.
     int16_t bgn_mute_factor = background_noise_->MuteFactor(channel);
-    NetEq::BackgroundNoiseMode bgn_mode = background_noise_->mode();
-    if (bgn_mode == NetEq::kBgnFade && too_many_expands &&
-        bgn_mute_factor > 0) {
-      // Fade BGN to zero.
-      // Calculate muting slope, approximately -2^18 / fs_hz.
-      int mute_slope;
-      if (fs_hz_ == 8000) {
-        mute_slope = -32;
-      } else if (fs_hz_ == 16000) {
-        mute_slope = -16;
-      } else if (fs_hz_ == 32000) {
-        mute_slope = -8;
-      } else {
-        mute_slope = -5;
-      }
-      // Use UnmuteSignal function with negative slope.
-      // |bgn_mute_factor| is in Q14. |mute_slope| is in Q20.
-      DspHelper::UnmuteSignal(noise_samples,
-                              num_noise_samples,
-                              &bgn_mute_factor,
-                              mute_slope,
-                              noise_samples);
-    } else if (bgn_mute_factor < 16384) {
-      // If mode is kBgnOn, or if kBgnFade has started fading,
-      // use regular |mute_slope|.
-      if (!stop_muting_ && bgn_mode != NetEq::kBgnOff &&
-          !(bgn_mode == NetEq::kBgnFade && too_many_expands)) {
-        DspHelper::UnmuteSignal(noise_samples,
-                                static_cast<int>(num_noise_samples),
-                                &bgn_mute_factor,
-                                mute_slope,
-                                noise_samples);
-      } else {
-        // kBgnOn and stop muting, or
-        // kBgnOff (mute factor is always 0), or
-        // kBgnFade has reached 0.
-        WebRtcSpl_AffineTransformVector(noise_samples, noise_samples,
-                                        bgn_mute_factor, 8192, 14,
-                                        num_noise_samples);
-      }
+    if (bgn_mute_factor < 16384) {
+      WebRtcSpl_AffineTransformVector(noise_samples, noise_samples,
+                                      bgn_mute_factor, 8192, 14,
+                                      num_noise_samples);
     }
     // Update mute_factor in BackgroundNoise class.
     background_noise_->SetMuteFactor(channel, bgn_mute_factor);
diff --git a/modules/audio_coding/neteq/expand_uma_logger.cc b/modules/audio_coding/neteq/expand_uma_logger.cc
new file mode 100644
index 0000000..c656eed
--- /dev/null
+++ b/modules/audio_coding/neteq/expand_uma_logger.cc
@@ -0,0 +1,69 @@
+/*  Copyright (c) 2018 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/expand_uma_logger.h"
+#include "rtc_base/checks.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace webrtc {
+namespace {
+std::unique_ptr<TickTimer::Countdown> GetNewCountdown(
+    const TickTimer& tick_timer,
+    int logging_period_s) {
+  return tick_timer.GetNewCountdown((logging_period_s * 1000) /
+                                    tick_timer.ms_per_tick());
+}
+}  // namespace
+
+ExpandUmaLogger::ExpandUmaLogger(std::string uma_name,
+                                 int logging_period_s,
+                                 const TickTimer* tick_timer)
+    : uma_name_(uma_name),
+      logging_period_s_(logging_period_s),
+      tick_timer_(*tick_timer),
+      timer_(GetNewCountdown(tick_timer_, logging_period_s_)) {
+  RTC_DCHECK(tick_timer);
+  RTC_DCHECK_GT(logging_period_s_, 0);
+}
+
+ExpandUmaLogger::~ExpandUmaLogger() = default;
+
+void ExpandUmaLogger::UpdateSampleCounter(uint64_t samples,
+                                          int sample_rate_hz) {
+  if ((last_logged_value_ && *last_logged_value_ > samples) ||
+      sample_rate_hz_ != sample_rate_hz) {
+    // Sanity checks. The incremental counter moved backwards, or sample rate
+    // changed.
+    last_logged_value_.reset();
+  }
+  last_value_ = samples;
+  sample_rate_hz_ = sample_rate_hz;
+  if (!last_logged_value_) {
+    last_logged_value_ = rtc::Optional<uint64_t>(samples);
+  }
+
+  if (!timer_->Finished()) {
+    // Not yet time to log.
+    return;
+  }
+
+  RTC_DCHECK(last_logged_value_);
+  RTC_DCHECK_GE(last_value_, *last_logged_value_);
+  const uint64_t diff = last_value_ - *last_logged_value_;
+  last_logged_value_ = rtc::Optional<uint64_t>(last_value_);
+  // Calculate rate in percent.
+  RTC_DCHECK_GT(sample_rate_hz, 0);
+  const int rate = (100 * diff) / (sample_rate_hz * logging_period_s_);
+  RTC_DCHECK_GE(rate, 0);
+  RTC_DCHECK_LE(rate, 100);
+  RTC_HISTOGRAM_PERCENTAGE_SPARSE(uma_name_, rate);
+  timer_ = GetNewCountdown(tick_timer_, logging_period_s_);
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_coding/neteq/expand_uma_logger.h b/modules/audio_coding/neteq/expand_uma_logger.h
new file mode 100644
index 0000000..70af39b
--- /dev/null
+++ b/modules/audio_coding/neteq/expand_uma_logger.h
@@ -0,0 +1,54 @@
+/*  Copyright (c) 2018 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_EXPAND_UMA_LOGGER_H_
+#define MODULES_AUDIO_CODING_NETEQ_EXPAND_UMA_LOGGER_H_
+
+#include <memory>
+#include <string>
+
+#include "api/optional.h"
+#include "modules/audio_coding/neteq/tick_timer.h"
+#include "rtc_base/constructormagic.h"
+
+namespace webrtc {
+
+// This class is used to periodically log values to a UMA histogram. The caller
+// is expected to update this class with an incremental sample counter which
+// counts expand samples. At the end of each logging period, the class will
+// calculate the fraction of samples that were expand samples during that period
+// and report that in percent. The logging period must be strictly positive.
+// Does not take ownership of tick_timer and the pointer must refer to a valid
+// object that outlives the one constructed.
+class ExpandUmaLogger {
+ public:
+  ExpandUmaLogger(std::string uma_name,
+                  int logging_period_s,
+                  const TickTimer* tick_timer);
+
+  ~ExpandUmaLogger();
+
+  // In this call, value should be an incremental sample counter. The sample
+  // rate must be strictly positive.
+  void UpdateSampleCounter(uint64_t value, int sample_rate_hz);
+
+ private:
+  const std::string uma_name_;
+  const int logging_period_s_;
+  const TickTimer& tick_timer_;
+  std::unique_ptr<TickTimer::Countdown> timer_;
+  rtc::Optional<uint64_t> last_logged_value_;
+  uint64_t last_value_ = 0;
+  int sample_rate_hz_ = 0;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(ExpandUmaLogger);
+};
+
+}  // namespace webrtc
+#endif  // MODULES_AUDIO_CODING_NETEQ_EXPAND_UMA_LOGGER_H_
diff --git a/modules/audio_coding/neteq/include/neteq.h b/modules/audio_coding/neteq/include/neteq.h
index f5bd8cd..310a227 100644
--- a/modules/audio_coding/neteq/include/neteq.h
+++ b/modules/audio_coding/neteq/include/neteq.h
@@ -16,6 +16,7 @@
 #include <string>
 #include <vector>
 
+#include "api/audio_codecs/audio_codec_pair_id.h"
 #include "api/audio_codecs/audio_decoder.h"
 #include "api/optional.h"
 #include "api/rtp_headers.h"
@@ -83,33 +84,24 @@
 // This is the interface class for NetEq.
 class NetEq {
  public:
-  enum BackgroundNoiseMode {
-    kBgnOn,    // Default behavior with eternal noise.
-    kBgnFade,  // Noise fades to zero after some time.
-    kBgnOff    // Background noise is always zero.
-  };
-
   struct Config {
-    Config()
-        : sample_rate_hz(16000),
-          enable_post_decode_vad(false),
-          max_packets_in_buffer(50),
-          // |max_delay_ms| has the same effect as calling SetMaximumDelay().
-          max_delay_ms(2000),
-          background_noise_mode(kBgnOff),
-          playout_mode(kPlayoutOn),
-          enable_fast_accelerate(false) {}
+    Config();
+    Config(const Config&);
+    Config(Config&&);
+    ~Config();
+    Config& operator=(const Config&);
+    Config& operator=(Config&&);
 
     std::string ToString() const;
 
-    int sample_rate_hz;  // Initial value. Will change with input data.
-    bool enable_post_decode_vad;
-    size_t max_packets_in_buffer;
-    int max_delay_ms;
-    BackgroundNoiseMode background_noise_mode;
-    NetEqPlayoutMode playout_mode;
-    bool enable_fast_accelerate;
+    int sample_rate_hz = 16000;  // Initial value. Will change with input data.
+    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;
+    rtc::Optional<AudioCodecPairId> codec_pair_id;
   };
 
   enum ReturnCodes {
diff --git a/modules/audio_coding/neteq/mock/mock_decoder_database.h b/modules/audio_coding/neteq/mock/mock_decoder_database.h
index 049b693..a4240ce 100644
--- a/modules/audio_coding/neteq/mock/mock_decoder_database.h
+++ b/modules/audio_coding/neteq/mock/mock_decoder_database.h
@@ -23,7 +23,7 @@
  public:
   explicit MockDecoderDatabase(
       rtc::scoped_refptr<AudioDecoderFactory> factory = nullptr)
-      : DecoderDatabase(factory) {}
+      : DecoderDatabase(factory, rtc::nullopt) {}
   virtual ~MockDecoderDatabase() { Die(); }
   MOCK_METHOD0(Die, void());
   MOCK_CONST_METHOD0(Empty,
diff --git a/modules/audio_coding/neteq/nack_tracker.cc b/modules/audio_coding/neteq/nack_tracker.cc
index d187883..c62cdf8 100644
--- a/modules/audio_coding/neteq/nack_tracker.cc
+++ b/modules/audio_coding/neteq/nack_tracker.cc
@@ -14,7 +14,6 @@
 
 #include <algorithm>  // For std::max.
 
-#include "modules/include/module_common_types.h"
 #include "rtc_base/checks.h"
 
 namespace webrtc {
diff --git a/modules/audio_coding/neteq/nack_tracker.h b/modules/audio_coding/neteq/nack_tracker.h
index 4f88d91..66383ce 100644
--- a/modules/audio_coding/neteq/nack_tracker.h
+++ b/modules/audio_coding/neteq/nack_tracker.h
@@ -15,6 +15,7 @@
 #include <map>
 
 #include "modules/audio_coding/include/audio_coding_module_typedefs.h"
+#include "modules/include/module_common_types.h"
 #include "rtc_base/gtest_prod_util.h"
 
 //
diff --git a/modules/audio_coding/neteq/neteq.cc b/modules/audio_coding/neteq/neteq.cc
index 8b74973..db12589 100644
--- a/modules/audio_coding/neteq/neteq.cc
+++ b/modules/audio_coding/neteq/neteq.cc
@@ -11,19 +11,26 @@
 #include "modules/audio_coding/neteq/include/neteq.h"
 
 #include <memory>
-#include <sstream>
 
 #include "modules/audio_coding/neteq/neteq_impl.h"
+#include "rtc_base/strings/string_builder.h"
 
 namespace webrtc {
 
+NetEq::Config::Config() = default;
+NetEq::Config::Config(const Config&) = default;
+NetEq::Config::Config(Config&&) = default;
+NetEq::Config::~Config() = default;
+NetEq::Config& NetEq::Config::operator=(const Config&) = default;
+NetEq::Config& NetEq::Config::operator=(Config&&) = default;
+
 std::string NetEq::Config::ToString() const {
-  std::stringstream ss;
+  char buf[1024];
+  rtc::SimpleStringBuilder ss(buf);
   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
-     << ", background_noise_mode=" << background_noise_mode
      << ", playout_mode=" << playout_mode
      << ", enable_fast_accelerate="
      << (enable_fast_accelerate ? " true": "false")
diff --git a/modules/audio_coding/neteq/neteq_external_decoder_unittest.cc b/modules/audio_coding/neteq/neteq_external_decoder_unittest.cc
index ec16627..03f5aa3 100644
--- a/modules/audio_coding/neteq/neteq_external_decoder_unittest.cc
+++ b/modules/audio_coding/neteq/neteq_external_decoder_unittest.cc
@@ -12,13 +12,13 @@
 
 #include <memory>
 
+#include "api/audio/audio_frame.h"
 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
 #include "common_types.h"  // NOLINT(build/include)
 #include "modules/audio_coding/neteq/mock/mock_external_decoder_pcm16b.h"
 #include "modules/audio_coding/neteq/tools/input_audio_file.h"
 #include "modules/audio_coding/neteq/tools/neteq_external_decoder_test.h"
 #include "modules/audio_coding/neteq/tools/rtp_generator.h"
-#include "modules/include/module_common_types.h"
 #include "test/gmock.h"
 #include "test/testsupport/fileutils.h"
 
diff --git a/modules/audio_coding/neteq/neteq_impl.cc b/modules/audio_coding/neteq/neteq_impl.cc
index b107626..80bfdaf 100644
--- a/modules/audio_coding/neteq/neteq_impl.cc
+++ b/modules/audio_coding/neteq/neteq_impl.cc
@@ -41,11 +41,11 @@
 #include "modules/audio_coding/neteq/sync_buffer.h"
 #include "modules/audio_coding/neteq/tick_timer.h"
 #include "modules/audio_coding/neteq/timestamp_scaler.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/numerics/safe_conversions.h"
 #include "rtc_base/sanitizer.h"
+#include "rtc_base/strings/audio_format_to_string.h"
 #include "rtc_base/system/fallthrough.h"
 #include "rtc_base/trace_event.h"
 #include "system_wrappers/include/field_trial.h"
@@ -57,7 +57,8 @@
     const rtc::scoped_refptr<AudioDecoderFactory>& decoder_factory)
     : tick_timer(new TickTimer),
       buffer_level_filter(new BufferLevelFilter),
-      decoder_database(new DecoderDatabase(decoder_factory)),
+      decoder_database(
+          new DecoderDatabase(decoder_factory, config.codec_pair_id)),
       delay_peak_detector(new DelayPeakDetector(tick_timer.get())),
       delay_manager(new DelayManager(config.max_packets_in_buffer,
                                      delay_peak_detector.get(),
@@ -100,11 +101,16 @@
       reset_decoder_(false),
       ssrc_(0),
       first_packet_(true),
-      background_noise_mode_(config.background_noise_mode),
       playout_mode_(config.playout_mode),
       enable_fast_accelerate_(config.enable_fast_accelerate),
       nack_enabled_(false),
-      enable_muted_state_(config.enable_muted_state) {
+      enable_muted_state_(config.enable_muted_state),
+      expand_uma_logger_("WebRTC.Audio.ExpandRatePercent",
+                         10,  // Report once every 10 s.
+                         tick_timer_.get()),
+      speech_expand_uma_logger_("WebRTC.Audio.SpeechExpandRatePercent",
+                                10,  // Report once every 10 s.
+                                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) {
@@ -261,7 +267,8 @@
 bool NetEqImpl::RegisterPayloadType(int rtp_payload_type,
                                     const SdpAudioFormat& audio_format) {
   RTC_LOG(LS_VERBOSE) << "NetEqImpl::RegisterPayloadType: payload type "
-                      << rtp_payload_type << ", codec " << audio_format;
+                      << rtp_payload_type << ", codec "
+                      << rtc::ToString(audio_format);
   rtc::CritScope lock(&crit_sect_);
   return decoder_database_->RegisterPayload(rtp_payload_type, audio_format) ==
          DecoderDatabase::kOK;
@@ -834,6 +841,11 @@
   last_decoded_timestamps_.clear();
   tick_timer_->Increment();
   stats_.IncreaseCounter(output_size_samples_, fs_hz_);
+  const auto lifetime_stats = stats_.GetLifetimeStatistics();
+  expand_uma_logger_.UpdateSampleCounter(lifetime_stats.concealed_samples,
+                                         fs_hz_);
+  speech_expand_uma_logger_.UpdateSampleCounter(
+      lifetime_stats.voice_concealed_samples, fs_hz_);
 
   // Check for muted state.
   if (enable_muted_state_ && expand_->Muted() && packet_buffer_->Empty()) {
@@ -2075,7 +2087,6 @@
 
   // Delete BackgroundNoise object and create a new one.
   background_noise_.reset(new BackgroundNoise(channels));
-  background_noise_->set_mode(background_noise_mode_);
 
   // Reset random vector.
   random_vector_.Reset();
diff --git a/modules/audio_coding/neteq/neteq_impl.h b/modules/audio_coding/neteq/neteq_impl.h
index bdeb020..ce75ce0 100644
--- a/modules/audio_coding/neteq/neteq_impl.h
+++ b/modules/audio_coding/neteq/neteq_impl.h
@@ -15,15 +15,16 @@
 #include <string>
 
 #include "api/optional.h"
+#include "api/audio/audio_frame.h"
 #include "modules/audio_coding/neteq/audio_multi_vector.h"
 #include "modules/audio_coding/neteq/defines.h"
+#include "modules/audio_coding/neteq/expand_uma_logger.h"
 #include "modules/audio_coding/neteq/include/neteq.h"
 #include "modules/audio_coding/neteq/packet.h"  // Declare PacketList.
 #include "modules/audio_coding/neteq/random_vector.h"
 #include "modules/audio_coding/neteq/rtcp.h"
 #include "modules/audio_coding/neteq/statistics_calculator.h"
 #include "modules/audio_coding/neteq/tick_timer.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/constructormagic.h"
 #include "rtc_base/criticalsection.h"
 #include "rtc_base/thread_annotations.h"
@@ -429,7 +430,6 @@
       RTC_GUARDED_BY(crit_sect_);
   uint32_t ssrc_ RTC_GUARDED_BY(crit_sect_);
   bool first_packet_ RTC_GUARDED_BY(crit_sect_);
-  const BackgroundNoiseMode background_noise_mode_ 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_);
@@ -440,6 +440,8 @@
   std::unique_ptr<TickTimer::Stopwatch> generated_noise_stopwatch_
       RTC_GUARDED_BY(crit_sect_);
   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_);
 
  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 12eabfa..de24cda 100644
--- a/modules/audio_coding/neteq/neteq_impl_unittest.cc
+++ b/modules/audio_coding/neteq/neteq_impl_unittest.cc
@@ -27,7 +27,6 @@
 #include "modules/audio_coding/neteq/preemptive_expand.h"
 #include "modules/audio_coding/neteq/sync_buffer.h"
 #include "modules/audio_coding/neteq/timestamp_scaler.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/numerics/safe_conversions.h"
 #include "test/gmock.h"
 #include "test/gtest.h"
@@ -335,7 +334,7 @@
 
         *dec = std::move(mock_decoder);
       }));
-  DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderPCMu,
+  DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderPCMu, rtc::nullopt,
                                     mock_decoder_factory);
 
   // Expectations for decoder database.
diff --git a/modules/audio_coding/neteq/neteq_network_stats_unittest.cc b/modules/audio_coding/neteq/neteq_network_stats_unittest.cc
index 334715f..b317099 100644
--- a/modules/audio_coding/neteq/neteq_network_stats_unittest.cc
+++ b/modules/audio_coding/neteq/neteq_network_stats_unittest.cc
@@ -10,10 +10,10 @@
 
 #include <memory>
 
+#include "api/audio/audio_frame.h"
 #include "common_types.h"  // NOLINT(build/include)
 #include "modules/audio_coding/neteq/tools/neteq_external_decoder_test.h"
 #include "modules/audio_coding/neteq/tools/rtp_generator.h"
-#include "modules/include/module_common_types.h"
 #include "test/gmock.h"
 
 namespace webrtc {
diff --git a/modules/audio_coding/neteq/neteq_stereo_unittest.cc b/modules/audio_coding/neteq/neteq_stereo_unittest.cc
index 1bef9c8..49facdd 100644
--- a/modules/audio_coding/neteq/neteq_stereo_unittest.cc
+++ b/modules/audio_coding/neteq/neteq_stereo_unittest.cc
@@ -15,13 +15,13 @@
 #include <string>
 #include <list>
 
+#include "api/audio/audio_frame.h"
 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
 #include "common_types.h"  // NOLINT(build/include)
 #include "modules/audio_coding/codecs/pcm16b/pcm16b.h"
 #include "modules/audio_coding/neteq/include/neteq.h"
 #include "modules/audio_coding/neteq/tools/input_audio_file.h"
 #include "modules/audio_coding/neteq/tools/rtp_generator.h"
-#include "modules/include/module_common_types.h"
 #include "test/gtest.h"
 #include "test/testsupport/fileutils.h"
 
diff --git a/modules/audio_coding/neteq/neteq_unittest.cc b/modules/audio_coding/neteq/neteq_unittest.cc
index ca93cf5..4cd3014 100644
--- a/modules/audio_coding/neteq/neteq_unittest.cc
+++ b/modules/audio_coding/neteq/neteq_unittest.cc
@@ -20,12 +20,12 @@
 #include <string>
 #include <vector>
 
+#include "api/audio/audio_frame.h"
 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
 #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/rtp_file_source.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/ignore_wundef.h"
 #include "rtc_base/messagedigest.h"
 #include "rtc_base/numerics/safe_conversions.h"
@@ -950,9 +950,6 @@
 
 class NetEqBgnTest : public NetEqDecodingTest {
  protected:
-  virtual void TestCondition(double sum_squared_noise,
-                             bool should_be_faded) = 0;
-
   void CheckBgn(int sampling_rate_hz) {
     size_t expected_samples_per_channel = 0;
     uint8_t payload_type = 0xFF;  // Invalid.
@@ -1044,7 +1041,7 @@
         for (size_t k = 0;
              k < output.num_channels_ * output.samples_per_channel_; ++k)
           sum_squared += output_data[k] * output_data[k];
-        TestCondition(sum_squared, n > kFadingThreshold);
+        EXPECT_EQ(0, sum_squared);
       } else {
         EXPECT_EQ(AudioFrame::kPLC, output.speech_type_);
       }
@@ -1053,53 +1050,7 @@
   }
 };
 
-class NetEqBgnTestOn : public NetEqBgnTest {
- protected:
-  NetEqBgnTestOn() : NetEqBgnTest() {
-    config_.background_noise_mode = NetEq::kBgnOn;
-  }
-
-  void TestCondition(double sum_squared_noise, bool /*should_be_faded*/) {
-    EXPECT_NE(0, sum_squared_noise);
-  }
-};
-
-class NetEqBgnTestOff : public NetEqBgnTest {
- protected:
-  NetEqBgnTestOff() : NetEqBgnTest() {
-    config_.background_noise_mode = NetEq::kBgnOff;
-  }
-
-  void TestCondition(double sum_squared_noise, bool /*should_be_faded*/) {
-    EXPECT_EQ(0, sum_squared_noise);
-  }
-};
-
-class NetEqBgnTestFade : public NetEqBgnTest {
- protected:
-  NetEqBgnTestFade() : NetEqBgnTest() {
-    config_.background_noise_mode = NetEq::kBgnFade;
-  }
-
-  void TestCondition(double sum_squared_noise, bool should_be_faded) {
-    if (should_be_faded)
-      EXPECT_EQ(0, sum_squared_noise);
-  }
-};
-
-TEST_F(NetEqBgnTestOn, RunTest) {
-  CheckBgn(8000);
-  CheckBgn(16000);
-  CheckBgn(32000);
-}
-
-TEST_F(NetEqBgnTestOff, RunTest) {
-  CheckBgn(8000);
-  CheckBgn(16000);
-  CheckBgn(32000);
-}
-
-TEST_F(NetEqBgnTestFade, RunTest) {
+TEST_F(NetEqBgnTest, RunTest) {
   CheckBgn(8000);
   CheckBgn(16000);
   CheckBgn(32000);
diff --git a/modules/audio_coding/neteq/packet_buffer_unittest.cc b/modules/audio_coding/neteq/packet_buffer_unittest.cc
index 0ddeb8a..1aaed8b 100644
--- a/modules/audio_coding/neteq/packet_buffer_unittest.cc
+++ b/modules/audio_coding/neteq/packet_buffer_unittest.cc
@@ -175,7 +175,8 @@
 
   MockDecoderDatabase decoder_database;
   auto factory = CreateBuiltinAudioDecoderFactory();
-  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderPCMu, factory);
+  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderPCMu,
+                                          rtc::nullopt, factory);
   EXPECT_CALL(decoder_database, GetDecoderInfo(0))
       .WillRepeatedly(Return(&info));
 
@@ -219,10 +220,12 @@
 
   MockDecoderDatabase decoder_database;
   auto factory = CreateBuiltinAudioDecoderFactory();
-  const DecoderDatabase::DecoderInfo info0(NetEqDecoder::kDecoderPCMu, factory);
+  const DecoderDatabase::DecoderInfo info0(NetEqDecoder::kDecoderPCMu,
+                                           rtc::nullopt, factory);
   EXPECT_CALL(decoder_database, GetDecoderInfo(0))
       .WillRepeatedly(Return(&info0));
-  const DecoderDatabase::DecoderInfo info1(NetEqDecoder::kDecoderPCMa, factory);
+  const DecoderDatabase::DecoderInfo info1(NetEqDecoder::kDecoderPCMa,
+                                           rtc::nullopt, factory);
   EXPECT_CALL(decoder_database, GetDecoderInfo(1))
       .WillRepeatedly(Return(&info1));
 
@@ -404,7 +407,8 @@
 
   MockDecoderDatabase decoder_database;
   auto factory = CreateBuiltinAudioDecoderFactory();
-  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderPCMu, factory);
+  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderPCMu,
+                                          rtc::nullopt, factory);
   EXPECT_CALL(decoder_database, GetDecoderInfo(0))
       .WillRepeatedly(Return(&info));
   rtc::Optional<uint8_t> current_pt;
@@ -444,11 +448,11 @@
   MockDecoderDatabase decoder_database;
   auto factory = CreateBuiltinAudioDecoderFactory();
   const DecoderDatabase::DecoderInfo info_cng(NetEqDecoder::kDecoderCNGnb,
-                                              factory);
+                                              rtc::nullopt, factory);
   EXPECT_CALL(decoder_database, GetDecoderInfo(kCngPt))
       .WillRepeatedly(Return(&info_cng));
   const DecoderDatabase::DecoderInfo info_speech(NetEqDecoder::kDecoderPCM16Bwb,
-                                                 factory);
+                                                 rtc::nullopt, factory);
   EXPECT_CALL(decoder_database, GetDecoderInfo(kSpeechPt))
       .WillRepeatedly(Return(&info_speech));
 
@@ -545,7 +549,8 @@
   list.push_back(gen.NextPacket(payload_len));  // Valid packet.
   MockDecoderDatabase decoder_database;
   auto factory = CreateBuiltinAudioDecoderFactory();
-  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderPCMu, factory);
+  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderPCMu,
+                                          rtc::nullopt, factory);
   EXPECT_CALL(decoder_database, GetDecoderInfo(0))
       .WillRepeatedly(Return(&info));
   rtc::Optional<uint8_t> current_pt;
diff --git a/modules/audio_coding/neteq/red_payload_splitter_unittest.cc b/modules/audio_coding/neteq/red_payload_splitter_unittest.cc
index 4f511ad..077c8ea 100644
--- a/modules/audio_coding/neteq/red_payload_splitter_unittest.cc
+++ b/modules/audio_coding/neteq/red_payload_splitter_unittest.cc
@@ -299,7 +299,7 @@
   // easier to just register the payload types and let the actual implementation
   // do its job.
   DecoderDatabase decoder_database(
-      new rtc::RefCountedObject<MockAudioDecoderFactory>);
+      new rtc::RefCountedObject<MockAudioDecoderFactory>, rtc::nullopt);
   decoder_database.RegisterPayload(0, NetEqDecoder::kDecoderCNGnb, "cng-nb");
   decoder_database.RegisterPayload(1, NetEqDecoder::kDecoderPCMu, "pcmu");
   decoder_database.RegisterPayload(2, NetEqDecoder::kDecoderAVT, "avt");
diff --git a/modules/audio_coding/neteq/rtcp.cc b/modules/audio_coding/neteq/rtcp.cc
index 2885398..551eb5f 100644
--- a/modules/audio_coding/neteq/rtcp.cc
+++ b/modules/audio_coding/neteq/rtcp.cc
@@ -15,8 +15,6 @@
 
 #include <algorithm>
 
-#include "modules/include/module_common_types.h"
-
 namespace webrtc {
 
 void Rtcp::Init(uint16_t start_sequence_number) {
diff --git a/modules/audio_coding/neteq/sync_buffer.h b/modules/audio_coding/neteq/sync_buffer.h
index ab9ff52..d880356 100644
--- a/modules/audio_coding/neteq/sync_buffer.h
+++ b/modules/audio_coding/neteq/sync_buffer.h
@@ -11,8 +11,8 @@
 #ifndef MODULES_AUDIO_CODING_NETEQ_SYNC_BUFFER_H_
 #define MODULES_AUDIO_CODING_NETEQ_SYNC_BUFFER_H_
 
+#include "api/audio/audio_frame.h"
 #include "modules/audio_coding/neteq/audio_multi_vector.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/constructormagic.h"
 #include "typedefs.h"  // NOLINT(build/include)
 
diff --git a/modules/audio_coding/neteq/timestamp_scaler_unittest.cc b/modules/audio_coding/neteq/timestamp_scaler_unittest.cc
index b3c1bb0..1a7b71a 100644
--- a/modules/audio_coding/neteq/timestamp_scaler_unittest.cc
+++ b/modules/audio_coding/neteq/timestamp_scaler_unittest.cc
@@ -25,7 +25,8 @@
   MockDecoderDatabase db;
   auto factory = CreateBuiltinAudioDecoderFactory();
   // Use PCMu, because it doesn't use scaled timestamps.
-  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderPCMu, factory);
+  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderPCMu,
+                                          rtc::nullopt, factory);
   static const uint8_t kRtpPayloadType = 0;
   EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType))
       .WillRepeatedly(Return(&info));
@@ -46,7 +47,8 @@
   MockDecoderDatabase db;
   auto factory = CreateBuiltinAudioDecoderFactory();
   // Use PCMu, because it doesn't use scaled timestamps.
-  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderPCMu, factory);
+  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderPCMu,
+                                          rtc::nullopt, factory);
   static const uint8_t kRtpPayloadType = 0;
   EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType))
       .WillRepeatedly(Return(&info));
@@ -72,7 +74,8 @@
   MockDecoderDatabase db;
   auto factory = CreateBuiltinAudioDecoderFactory();
   // Use G722, which has a factor 2 scaling.
-  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderG722, factory);
+  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderG722,
+                                          rtc::nullopt, factory);
   static const uint8_t kRtpPayloadType = 17;
   EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType))
       .WillRepeatedly(Return(&info));
@@ -97,7 +100,8 @@
   MockDecoderDatabase db;
   auto factory = CreateBuiltinAudioDecoderFactory();
   // Use G722, which has a factor 2 scaling.
-  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderG722, factory);
+  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderG722,
+                                          rtc::nullopt, factory);
   static const uint8_t kRtpPayloadType = 17;
   EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType))
       .WillRepeatedly(Return(&info));
@@ -127,9 +131,9 @@
   auto factory = CreateBuiltinAudioDecoderFactory();
   // Use G722, which has a factor 2 scaling.
   const DecoderDatabase::DecoderInfo info_g722(NetEqDecoder::kDecoderG722,
-                                               factory);
+                                               rtc::nullopt, factory);
   const DecoderDatabase::DecoderInfo info_cng(NetEqDecoder::kDecoderCNGwb,
-                                              factory);
+                                              rtc::nullopt, factory);
   static const uint8_t kRtpPayloadTypeG722 = 17;
   static const uint8_t kRtpPayloadTypeCng = 13;
   EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadTypeG722))
@@ -170,7 +174,8 @@
   MockDecoderDatabase db;
   auto factory = CreateBuiltinAudioDecoderFactory();
   // Use G722, which has a factor 2 scaling.
-  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderG722, factory);
+  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderG722,
+                                          rtc::nullopt, factory);
   static const uint8_t kRtpPayloadType = 17;
   EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType))
       .WillRepeatedly(Return(&info));
@@ -199,7 +204,8 @@
   MockDecoderDatabase db;
   auto factory = CreateBuiltinAudioDecoderFactory();
   // Use G722, which has a factor 2 scaling.
-  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderG722, factory);
+  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderG722,
+                                          rtc::nullopt, factory);
   static const uint8_t kRtpPayloadType = 17;
   EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType))
       .WillRepeatedly(Return(&info));
@@ -232,7 +238,8 @@
   MockDecoderDatabase db;
   auto factory = CreateBuiltinAudioDecoderFactory();
   // Use G722, which has a factor 2 scaling.
-  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderG722, factory);
+  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderG722,
+                                          rtc::nullopt, factory);
   static const uint8_t kRtpPayloadType = 17;
   EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType))
       .WillRepeatedly(Return(&info));
@@ -272,7 +279,8 @@
 TEST(TimestampScaler, TestOpusLargeStep) {
   MockDecoderDatabase db;
   auto factory = CreateBuiltinAudioDecoderFactory();
-  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderOpus, factory);
+  const DecoderDatabase::DecoderInfo info(NetEqDecoder::kDecoderOpus,
+                                          rtc::nullopt, factory);
   static const uint8_t kRtpPayloadType = 17;
   EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType))
       .WillRepeatedly(Return(&info));
diff --git a/modules/audio_coding/neteq/tools/audio_sink.h b/modules/audio_coding/neteq/tools/audio_sink.h
index ecec51b..18ac6fc 100644
--- a/modules/audio_coding/neteq/tools/audio_sink.h
+++ b/modules/audio_coding/neteq/tools/audio_sink.h
@@ -11,7 +11,7 @@
 #ifndef MODULES_AUDIO_CODING_NETEQ_TOOLS_AUDIO_SINK_H_
 #define MODULES_AUDIO_CODING_NETEQ_TOOLS_AUDIO_SINK_H_
 
-#include "modules/include/module_common_types.h"
+#include "api/audio/audio_frame.h"
 #include "rtc_base/constructormagic.h"
 #include "typedefs.h"  // NOLINT(build/include)
 
diff --git a/modules/audio_coding/neteq/tools/encode_neteq_input.h b/modules/audio_coding/neteq/tools/encode_neteq_input.h
index b44d4ac..13b39b3 100644
--- a/modules/audio_coding/neteq/tools/encode_neteq_input.h
+++ b/modules/audio_coding/neteq/tools/encode_neteq_input.h
@@ -15,7 +15,6 @@
 
 #include "api/audio_codecs/audio_encoder.h"
 #include "modules/audio_coding/neteq/tools/neteq_input.h"
-#include "modules/include/module_common_types.h"
 
 namespace webrtc {
 namespace test {
diff --git a/modules/audio_coding/neteq/tools/input_audio_file.cc b/modules/audio_coding/neteq/tools/input_audio_file.cc
index 31ebf98..330a874 100644
--- a/modules/audio_coding/neteq/tools/input_audio_file.cc
+++ b/modules/audio_coding/neteq/tools/input_audio_file.cc
@@ -56,13 +56,18 @@
   RTC_CHECK_NE(EOF, file_size) << "Error returned when getting file position.";
   // Find new position.
   long new_pos = current_pos + sizeof(int16_t) * samples;  // Samples to bytes.
-  RTC_CHECK_GE(new_pos, 0)
-      << "Trying to move to before the beginning of the file";
   if (loop_at_end_) {
     new_pos = new_pos % file_size;  // Wrap around the end of the file.
+    if (new_pos < 0) {
+      // For negative values of new_pos, newpos % file_size will also be
+      // negative. To get the correct result it's needed to add file_size.
+      new_pos += file_size;
+    }
   } else {
     new_pos = new_pos > file_size ? file_size : new_pos;  // Don't loop.
   }
+  RTC_CHECK_GE(new_pos, 0)
+      << "Trying to move to before the beginning of the file";
   // Move to new position relative to the beginning of the file.
   RTC_CHECK_EQ(0, fseek(fp_, new_pos, SEEK_SET));
   return true;
diff --git a/modules/audio_coding/neteq/tools/neteq_delay_analyzer.cc b/modules/audio_coding/neteq/tools/neteq_delay_analyzer.cc
index 882f823..ba0b217 100644
--- a/modules/audio_coding/neteq/tools/neteq_delay_analyzer.cc
+++ b/modules/audio_coding/neteq/tools/neteq_delay_analyzer.cc
@@ -17,6 +17,7 @@
 #include <limits>
 #include <utility>
 
+#include "modules/include/module_common_types.h"
 #include "rtc_base/checks.h"
 
 namespace webrtc {
diff --git a/modules/audio_coding/neteq/tools/neteq_external_decoder_test.cc b/modules/audio_coding/neteq/tools/neteq_external_decoder_test.cc
index 68dde52..2c23e5c 100644
--- a/modules/audio_coding/neteq/tools/neteq_external_decoder_test.cc
+++ b/modules/audio_coding/neteq/tools/neteq_external_decoder_test.cc
@@ -11,6 +11,7 @@
 
 #include "modules/audio_coding/neteq/tools/neteq_external_decoder_test.h"
 
+#include "api/audio/audio_frame.h"
 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
 #include "rtc_base/format_macros.h"
 #include "test/gtest.h"
diff --git a/modules/audio_coding/neteq/tools/neteq_external_decoder_test.h b/modules/audio_coding/neteq/tools/neteq_external_decoder_test.h
index aefa62e..b8670a3 100644
--- a/modules/audio_coding/neteq/tools/neteq_external_decoder_test.h
+++ b/modules/audio_coding/neteq/tools/neteq_external_decoder_test.h
@@ -17,7 +17,6 @@
 #include "api/audio_codecs/audio_decoder.h"
 #include "common_types.h"  // NOLINT(build/include)
 #include "modules/audio_coding/neteq/include/neteq.h"
-#include "modules/include/module_common_types.h"
 
 namespace webrtc {
 namespace test {
diff --git a/modules/audio_coding/neteq/tools/neteq_performance_test.cc b/modules/audio_coding/neteq/tools/neteq_performance_test.cc
index 27ecdf4..80aa809 100644
--- a/modules/audio_coding/neteq/tools/neteq_performance_test.cc
+++ b/modules/audio_coding/neteq/tools/neteq_performance_test.cc
@@ -10,13 +10,13 @@
 
 #include "modules/audio_coding/neteq/tools/neteq_performance_test.h"
 
+#include "api/audio/audio_frame.h"
 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
 #include "common_types.h"  // NOLINT(build/include)
 #include "modules/audio_coding/codecs/pcm16b/pcm16b.h"
 #include "modules/audio_coding/neteq/include/neteq.h"
 #include "modules/audio_coding/neteq/tools/audio_loop.h"
 #include "modules/audio_coding/neteq/tools/rtp_generator.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/checks.h"
 #include "system_wrappers/include/clock.h"
 #include "test/testsupport/fileutils.h"
@@ -103,7 +103,7 @@
         return -1;
       payload_len = WebRtcPcm16b_Encode(input_samples.data(),
                                         input_samples.size(), input_payload);
-      assert(payload_len == kInputBlockSizeSamples * sizeof(int16_t));
+      RTC_DCHECK_EQ(payload_len, kInputBlockSizeSamples * sizeof(int16_t));
     }
 
     // Get output audio, but don't do anything with it.
@@ -113,8 +113,7 @@
     if (error != NetEq::kOK)
       return -1;
 
-    assert(out_frame.samples_per_channel_ ==
-           static_cast<size_t>(kSampRateHz * 10 / 1000));
+    RTC_DCHECK_EQ(out_frame.samples_per_channel_, (kSampRateHz * 10) / 1000);
 
     static const int kOutputBlockSizeMs = 10;
     time_now_ms += kOutputBlockSizeMs;
diff --git a/modules/audio_coding/neteq/tools/neteq_quality_test.h b/modules/audio_coding/neteq/tools/neteq_quality_test.h
index 531a080..2b82b0a 100644
--- a/modules/audio_coding/neteq/tools/neteq_quality_test.h
+++ b/modules/audio_coding/neteq/tools/neteq_quality_test.h
@@ -19,7 +19,6 @@
 #include "modules/audio_coding/neteq/tools/audio_sink.h"
 #include "modules/audio_coding/neteq/tools/input_audio_file.h"
 #include "modules/audio_coding/neteq/tools/rtp_generator.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/flags.h"
 #include "test/gtest.h"
 #include "typedefs.h"  // NOLINT(build/include)
diff --git a/modules/audio_coding/neteq/tools/neteq_rtpplay.cc b/modules/audio_coding/neteq/tools/neteq_rtpplay.cc
index 8c1fa38..7c071db 100644
--- a/modules/audio_coding/neteq/tools/neteq_rtpplay.cc
+++ b/modules/audio_coding/neteq/tools/neteq_rtpplay.cc
@@ -10,29 +10,24 @@
 
 #include <errno.h>
 #include <inttypes.h>
+#include <iostream>
 #include <limits.h>  // For ULONG_MAX returned by strtoul.
+#include <memory>
 #include <stdio.h>
 #include <stdlib.h>  // For strtoul.
-#include <string.h>
-
-#include <algorithm>
-#include <ios>
-#include <iostream>
-#include <memory>
-#include <numeric>
 #include <string>
 
 #include "modules/audio_coding/neteq/include/neteq.h"
 #include "modules/audio_coding/neteq/tools/fake_decode_from_file.h"
 #include "modules/audio_coding/neteq/tools/input_audio_file.h"
 #include "modules/audio_coding/neteq/tools/neteq_delay_analyzer.h"
+#include "modules/audio_coding/neteq/tools/neteq_stats_getter.h"
 #include "modules/audio_coding/neteq/tools/neteq_packet_source_input.h"
 #include "modules/audio_coding/neteq/tools/neteq_replacement_input.h"
 #include "modules/audio_coding/neteq/tools/neteq_test.h"
 #include "modules/audio_coding/neteq/tools/output_audio_file.h"
 #include "modules/audio_coding/neteq/tools/output_wav_file.h"
 #include "modules/audio_coding/neteq/tools/rtp_file_source.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/flags.h"
 #include "test/testsupport/fileutils.h"
@@ -309,166 +304,6 @@
   rtc::Optional<uint32_t> last_ssrc_;
 };
 
-class StatsGetter : public NetEqGetAudioCallback {
- public:
-  // This struct is a replica of webrtc::NetEqNetworkStatistics, but with all
-  // values stored in double precision.
-  struct Stats {
-    double current_buffer_size_ms = 0.0;
-    double preferred_buffer_size_ms = 0.0;
-    double jitter_peaks_found = 0.0;
-    double packet_loss_rate = 0.0;
-    double expand_rate = 0.0;
-    double speech_expand_rate = 0.0;
-    double preemptive_rate = 0.0;
-    double accelerate_rate = 0.0;
-    double secondary_decoded_rate = 0.0;
-    double secondary_discarded_rate = 0.0;
-    double clockdrift_ppm = 0.0;
-    double added_zero_samples = 0.0;
-    double mean_waiting_time_ms = 0.0;
-    double median_waiting_time_ms = 0.0;
-    double min_waiting_time_ms = 0.0;
-    double max_waiting_time_ms = 0.0;
-  };
-
-  struct ConcealmentEvent {
-    uint64_t duration_ms;
-    size_t concealment_event_number;
-    int64_t time_from_previous_event_end_ms;
-
-    friend std::ostream& operator<<(std::ostream& stream,
-                                    const ConcealmentEvent& concealment_event) {
-      stream << "ConcealmentEvent duration_ms:" << concealment_event.duration_ms
-             << " event_number:" << concealment_event.concealment_event_number
-             << " time_from_previous_event_end_ms:"
-             << concealment_event.time_from_previous_event_end_ms << "\n";
-      return stream;
-    }
-  };
-
-  // Takes a pointer to another callback object, which will be invoked after
-  // this object finishes. This does not transfer ownership, and null is a
-  // valid value.
-  explicit StatsGetter(NetEqGetAudioCallback* other_callback)
-      : other_callback_(other_callback) {}
-
-  void BeforeGetAudio(NetEq* neteq) override {
-    if (other_callback_) {
-      other_callback_->BeforeGetAudio(neteq);
-    }
-  }
-
-  void AfterGetAudio(int64_t time_now_ms,
-                     const AudioFrame& audio_frame,
-                     bool muted,
-                     NetEq* neteq) override {
-    if (++counter_ >= 100) {
-      counter_ = 0;
-      NetEqNetworkStatistics stats;
-      RTC_CHECK_EQ(neteq->NetworkStatistics(&stats), 0);
-      stats_.push_back(stats);
-    }
-    const auto lifetime_stat = neteq->GetLifetimeStatistics();
-    if (current_concealment_event_ != lifetime_stat.concealment_events) {
-      if (last_event_end_time_ms_ > 0) {
-        // Do not account for the first event to avoid start of the call
-        // skewing.
-        ConcealmentEvent concealment_event;
-        uint64_t last_event_voice_concealed_samples =
-            lifetime_stat.voice_concealed_samples -
-            voice_concealed_samples_until_last_event_;
-        RTC_CHECK_GT(last_event_voice_concealed_samples, 0);
-        concealment_event.duration_ms = last_event_voice_concealed_samples /
-                                        (audio_frame.sample_rate_hz_ / 1000);
-        concealment_event.concealment_event_number = current_concealment_event_;
-        concealment_event.time_from_previous_event_end_ms =
-            time_now_ms - last_event_end_time_ms_;
-        concealment_events_.emplace_back(concealment_event);
-        voice_concealed_samples_until_last_event_ =
-            lifetime_stat.voice_concealed_samples;
-      }
-      last_event_end_time_ms_ = time_now_ms;
-      voice_concealed_samples_until_last_event_ =
-          lifetime_stat.voice_concealed_samples;
-      current_concealment_event_ = lifetime_stat.concealment_events;
-    }
-
-    if (other_callback_) {
-      other_callback_->AfterGetAudio(time_now_ms, audio_frame, muted, neteq);
-    }
-  }
-
-  double AverageSpeechExpandRate() const {
-    double sum_speech_expand =
-        std::accumulate(stats_.begin(), stats_.end(), double{0.0},
-                        [](double a, NetEqNetworkStatistics b) {
-                          return a + static_cast<double>(b.speech_expand_rate);
-                        });
-    return sum_speech_expand / 16384.0 / stats_.size();
-  }
-
-  const std::vector<ConcealmentEvent>& concealment_events() {
-    // Do not account for the last concealment event to avoid potential end
-    // call skewing.
-    return concealment_events_;
-  }
-
-  Stats AverageStats() const {
-    Stats sum_stats = std::accumulate(
-        stats_.begin(), stats_.end(), Stats(),
-        [](Stats a, NetEqNetworkStatistics b) {
-          a.current_buffer_size_ms += b.current_buffer_size_ms;
-          a.preferred_buffer_size_ms += b.preferred_buffer_size_ms;
-          a.jitter_peaks_found += b.jitter_peaks_found;
-          a.packet_loss_rate += b.packet_loss_rate / 16384.0;
-          a.expand_rate += b.expand_rate / 16384.0;
-          a.speech_expand_rate += b.speech_expand_rate / 16384.0;
-          a.preemptive_rate += b.preemptive_rate / 16384.0;
-          a.accelerate_rate += b.accelerate_rate / 16384.0;
-          a.secondary_decoded_rate += b.secondary_decoded_rate / 16384.0;
-          a.secondary_discarded_rate += b.secondary_discarded_rate / 16384.0;
-          a.clockdrift_ppm += b.clockdrift_ppm;
-          a.added_zero_samples += b.added_zero_samples;
-          a.mean_waiting_time_ms += b.mean_waiting_time_ms;
-          a.median_waiting_time_ms += b.median_waiting_time_ms;
-          a.min_waiting_time_ms =
-              std::min(a.min_waiting_time_ms,
-                       static_cast<double>(b.min_waiting_time_ms));
-          a.max_waiting_time_ms =
-              std::max(a.max_waiting_time_ms,
-                       static_cast<double>(b.max_waiting_time_ms));
-          return a;
-        });
-
-    sum_stats.current_buffer_size_ms /= stats_.size();
-    sum_stats.preferred_buffer_size_ms /= stats_.size();
-    sum_stats.jitter_peaks_found /= stats_.size();
-    sum_stats.packet_loss_rate /= stats_.size();
-    sum_stats.expand_rate /= stats_.size();
-    sum_stats.speech_expand_rate /= stats_.size();
-    sum_stats.preemptive_rate /= stats_.size();
-    sum_stats.accelerate_rate /= stats_.size();
-    sum_stats.secondary_decoded_rate /= stats_.size();
-    sum_stats.secondary_discarded_rate /= stats_.size();
-    sum_stats.clockdrift_ppm /= stats_.size();
-    sum_stats.added_zero_samples /= stats_.size();
-    sum_stats.mean_waiting_time_ms /= stats_.size();
-    sum_stats.median_waiting_time_ms /= stats_.size();
-
-    return sum_stats;
-  }
-
- private:
-  NetEqGetAudioCallback* other_callback_;
-  size_t counter_ = 0;
-  std::vector<NetEqNetworkStatistics> stats_;
-  size_t current_concealment_event_ = 1;
-  uint64_t voice_concealed_samples_until_last_event_ = 0;
-  std::vector<ConcealmentEvent> concealment_events_;
-  int64_t last_event_end_time_ms_ = 0;
-};
-
 int RunTest(int argc, char* argv[]) {
   std::string program_name = argv[0];
   std::string usage = "Tool for decoding an RTP dump file using NetEq.\n"
@@ -674,7 +509,7 @@
 
   SsrcSwitchDetector ssrc_switch_detector(delay_analyzer.get());
   callbacks.post_insert_packet = &ssrc_switch_detector;
-  StatsGetter stats_getter(delay_analyzer.get());
+  NetEqStatsGetter stats_getter(std::move(delay_analyzer));
   callbacks.get_audio_callback = &stats_getter;
   NetEq::Config config;
   config.sample_rate_hz = *sample_rate_hz;
@@ -720,11 +555,10 @@
   printf("  current_buffer_size_ms: %f ms\n", stats.current_buffer_size_ms);
   printf("  preferred_buffer_size_ms: %f ms\n", stats.preferred_buffer_size_ms);
   if (FLAG_concealment_events) {
-    std::cout << " concealment_events_ms:"
-              << "\n";
+    std::cout << " concealment_events_ms:" << std::endl;
     for (auto concealment_event : stats_getter.concealment_events())
-      std::cout << concealment_event;
-    std::cout << " end of concealment_events_ms\n";
+      std::cout << concealment_event.ToString() << std::endl;
+    std::cout << " end of concealment_events_ms" << std::endl;
   }
   return 0;
 }
diff --git a/modules/audio_coding/neteq/tools/neteq_stats_getter.cc b/modules/audio_coding/neteq/tools/neteq_stats_getter.cc
new file mode 100644
index 0000000..6474e21
--- /dev/null
+++ b/modules/audio_coding/neteq/tools/neteq_stats_getter.cc
@@ -0,0 +1,146 @@
+/*
+ *  Copyright (c) 2018 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/tools/neteq_stats_getter.h"
+
+#include <algorithm>
+#include <numeric>
+#include <utility>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/strings/string_builder.h"
+#include "rtc_base/timeutils.h"
+
+namespace webrtc {
+namespace test {
+
+std::string NetEqStatsGetter::ConcealmentEvent::ToString() const {
+  char ss_buf[256];
+  rtc::SimpleStringBuilder ss(ss_buf);
+  ss << "ConcealmentEvent duration_ms:" << duration_ms
+     << " event_number:" << concealment_event_number
+     << " time_from_previous_event_end_ms:"
+     << time_from_previous_event_end_ms;
+  return ss.str();
+}
+
+NetEqStatsGetter::NetEqStatsGetter(
+    std::unique_ptr<NetEqDelayAnalyzer> delay_analyzer)
+    : delay_analyzer_(std::move(delay_analyzer)) {}
+
+void NetEqStatsGetter::BeforeGetAudio(NetEq* neteq) {
+  if (delay_analyzer_) {
+    delay_analyzer_->BeforeGetAudio(neteq);
+  }
+}
+
+void NetEqStatsGetter::AfterGetAudio(int64_t time_now_ms,
+                                     const AudioFrame& audio_frame,
+                                     bool muted,
+                                     NetEq* neteq) {
+  // TODO(minyue): Get stats should better not be called as a call back after
+  // get audio. It is called independently from get audio in practice.
+  if (last_stats_query_time_ms_ == 0 ||
+      rtc::TimeDiff(time_now_ms, last_stats_query_time_ms_) >=
+          stats_query_interval_ms_) {
+    NetEqNetworkStatistics stats;
+    RTC_CHECK_EQ(neteq->NetworkStatistics(&stats), 0);
+    stats_.push_back(std::make_pair(time_now_ms, stats));
+    last_stats_query_time_ms_ = time_now_ms;
+  }
+  const auto lifetime_stat = neteq->GetLifetimeStatistics();
+  if (current_concealment_event_ != lifetime_stat.concealment_events &&
+      voice_concealed_samples_until_last_event_ <
+          lifetime_stat.voice_concealed_samples) {
+    if (last_event_end_time_ms_ > 0) {
+      // Do not account for the first event to avoid start of the call
+      // skewing.
+      ConcealmentEvent concealment_event;
+      uint64_t last_event_voice_concealed_samples =
+          lifetime_stat.voice_concealed_samples -
+          voice_concealed_samples_until_last_event_;
+      RTC_CHECK_GT(last_event_voice_concealed_samples, 0);
+      concealment_event.duration_ms = last_event_voice_concealed_samples /
+                                      (audio_frame.sample_rate_hz_ / 1000);
+      concealment_event.concealment_event_number = current_concealment_event_;
+      concealment_event.time_from_previous_event_end_ms =
+          time_now_ms - last_event_end_time_ms_;
+      concealment_events_.emplace_back(concealment_event);
+      voice_concealed_samples_until_last_event_ =
+          lifetime_stat.voice_concealed_samples;
+    }
+    last_event_end_time_ms_ = time_now_ms;
+    voice_concealed_samples_until_last_event_ =
+        lifetime_stat.voice_concealed_samples;
+    current_concealment_event_ = lifetime_stat.concealment_events;
+  }
+
+  if (delay_analyzer_) {
+    delay_analyzer_->AfterGetAudio(time_now_ms, audio_frame, muted, neteq);
+  }
+}
+
+double NetEqStatsGetter::AverageSpeechExpandRate() const {
+  double sum_speech_expand = std::accumulate(
+      stats_.begin(), stats_.end(), double{0.0},
+      [](double a, std::pair<int64_t, NetEqNetworkStatistics> b) {
+        return a + static_cast<double>(b.second.speech_expand_rate);
+      });
+  return sum_speech_expand / 16384.0 / stats_.size();
+}
+
+NetEqStatsGetter::Stats NetEqStatsGetter::AverageStats() const {
+  Stats sum_stats = std::accumulate(
+      stats_.begin(), stats_.end(), Stats(),
+      [](Stats a, std::pair<int64_t, NetEqNetworkStatistics> bb) {
+        const auto& b = bb.second;
+        a.current_buffer_size_ms += b.current_buffer_size_ms;
+        a.preferred_buffer_size_ms += b.preferred_buffer_size_ms;
+        a.jitter_peaks_found += b.jitter_peaks_found;
+        a.packet_loss_rate += b.packet_loss_rate / 16384.0;
+        a.expand_rate += b.expand_rate / 16384.0;
+        a.speech_expand_rate += b.speech_expand_rate / 16384.0;
+        a.preemptive_rate += b.preemptive_rate / 16384.0;
+        a.accelerate_rate += b.accelerate_rate / 16384.0;
+        a.secondary_decoded_rate += b.secondary_decoded_rate / 16384.0;
+        a.secondary_discarded_rate += b.secondary_discarded_rate / 16384.0;
+        a.clockdrift_ppm += b.clockdrift_ppm;
+        a.added_zero_samples += b.added_zero_samples;
+        a.mean_waiting_time_ms += b.mean_waiting_time_ms;
+        a.median_waiting_time_ms += b.median_waiting_time_ms;
+        a.min_waiting_time_ms =
+            std::min(a.min_waiting_time_ms,
+                     static_cast<double>(b.min_waiting_time_ms));
+        a.max_waiting_time_ms =
+            std::max(a.max_waiting_time_ms,
+                     static_cast<double>(b.max_waiting_time_ms));
+        return a;
+      });
+
+  sum_stats.current_buffer_size_ms /= stats_.size();
+  sum_stats.preferred_buffer_size_ms /= stats_.size();
+  sum_stats.jitter_peaks_found /= stats_.size();
+  sum_stats.packet_loss_rate /= stats_.size();
+  sum_stats.expand_rate /= stats_.size();
+  sum_stats.speech_expand_rate /= stats_.size();
+  sum_stats.preemptive_rate /= stats_.size();
+  sum_stats.accelerate_rate /= stats_.size();
+  sum_stats.secondary_decoded_rate /= stats_.size();
+  sum_stats.secondary_discarded_rate /= stats_.size();
+  sum_stats.clockdrift_ppm /= stats_.size();
+  sum_stats.added_zero_samples /= stats_.size();
+  sum_stats.mean_waiting_time_ms /= stats_.size();
+  sum_stats.median_waiting_time_ms /= stats_.size();
+
+  return sum_stats;
+}
+
+}  // namespace test
+}  // namespace webrtc
diff --git a/modules/audio_coding/neteq/tools/neteq_stats_getter.h b/modules/audio_coding/neteq/tools/neteq_stats_getter.h
new file mode 100644
index 0000000..dbb396a
--- /dev/null
+++ b/modules/audio_coding/neteq/tools/neteq_stats_getter.h
@@ -0,0 +1,102 @@
+/*
+ *  Copyright (c) 2018 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_TOOLS_NETEQ_STATS_GETTER_H_
+#define MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_STATS_GETTER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "modules/audio_coding/neteq/tools/neteq_delay_analyzer.h"
+#include "modules/audio_coding/neteq/tools/neteq_test.h"
+
+namespace webrtc {
+namespace test {
+
+class NetEqStatsGetter : public NetEqGetAudioCallback {
+ public:
+  // This struct is a replica of webrtc::NetEqNetworkStatistics, but with all
+  // values stored in double precision.
+  struct Stats {
+    double current_buffer_size_ms = 0.0;
+    double preferred_buffer_size_ms = 0.0;
+    double jitter_peaks_found = 0.0;
+    double packet_loss_rate = 0.0;
+    double expand_rate = 0.0;
+    double speech_expand_rate = 0.0;
+    double preemptive_rate = 0.0;
+    double accelerate_rate = 0.0;
+    double secondary_decoded_rate = 0.0;
+    double secondary_discarded_rate = 0.0;
+    double clockdrift_ppm = 0.0;
+    double added_zero_samples = 0.0;
+    double mean_waiting_time_ms = 0.0;
+    double median_waiting_time_ms = 0.0;
+    double min_waiting_time_ms = 0.0;
+    double max_waiting_time_ms = 0.0;
+  };
+
+  struct ConcealmentEvent {
+    uint64_t duration_ms;
+    size_t concealment_event_number;
+    int64_t time_from_previous_event_end_ms;
+    std::string ToString() const;
+  };
+
+  // Takes a pointer to another callback object, which will be invoked after
+  // this object finishes. This does not transfer ownership, and null is a
+  // valid value.
+  explicit NetEqStatsGetter(std::unique_ptr<NetEqDelayAnalyzer> delay_analyzer);
+
+  void set_stats_query_interval_ms(int64_t stats_query_interval_ms) {
+    stats_query_interval_ms_ = stats_query_interval_ms;
+  }
+
+  void BeforeGetAudio(NetEq* neteq) override;
+
+  void AfterGetAudio(int64_t time_now_ms,
+                     const AudioFrame& audio_frame,
+                     bool muted,
+                     NetEq* neteq) override;
+
+  double AverageSpeechExpandRate() const;
+
+  NetEqDelayAnalyzer* delay_analyzer() const {
+    return delay_analyzer_.get();
+  }
+
+  const std::vector<ConcealmentEvent>& concealment_events() const {
+    // Do not account for the last concealment event to avoid potential end
+    // call skewing.
+    return concealment_events_;
+  }
+
+  const std::vector<std::pair<int64_t, NetEqNetworkStatistics>>& stats() const {
+    return stats_;
+  }
+
+  Stats AverageStats() const;
+
+ private:
+  std::unique_ptr<NetEqDelayAnalyzer> delay_analyzer_;
+  int64_t stats_query_interval_ms_ = 1000;
+  int64_t last_stats_query_time_ms_ = 0;
+  std::vector<std::pair<int64_t, NetEqNetworkStatistics>> stats_;
+  size_t current_concealment_event_ = 1;
+  uint64_t voice_concealed_samples_until_last_event_ = 0;
+  std::vector<ConcealmentEvent> concealment_events_;
+  int64_t last_event_end_time_ms_ = 0;
+};
+
+}  // namespace test
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_STATS_GETTER_H_
diff --git a/modules/audio_coding/neteq/tools/packet.cc b/modules/audio_coding/neteq/tools/packet.cc
index 71337b6..9505a29 100644
--- a/modules/audio_coding/neteq/tools/packet.cc
+++ b/modules/audio_coding/neteq/tools/packet.cc
@@ -14,7 +14,6 @@
 
 #include <memory>
 
-#include "modules/include/module_common_types.h"
 #include "modules/rtp_rtcp/include/rtp_header_parser.h"
 #include "rtc_base/checks.h"
 
diff --git a/modules/audio_coding/neteq/tools/rtc_event_log_source.cc b/modules/audio_coding/neteq/tools/rtc_event_log_source.cc
index d6224ff..b853248 100644
--- a/modules/audio_coding/neteq/tools/rtc_event_log_source.cc
+++ b/modules/audio_coding/neteq/tools/rtc_event_log_source.cc
@@ -40,7 +40,7 @@
   for (; rtp_packet_index_ < parsed_stream_.GetNumberOfEvents();
        rtp_packet_index_++) {
     if (parsed_stream_.GetEventType(rtp_packet_index_) ==
-        ParsedRtcEventLog::RTP_EVENT) {
+        ParsedRtcEventLogNew::RTP_EVENT) {
       PacketDirection direction;
       size_t header_length;
       size_t packet_length;
@@ -66,7 +66,7 @@
       }
 
       if (parsed_stream_.GetMediaType(packet->header().ssrc, direction) !=
-          webrtc::ParsedRtcEventLog::MediaType::AUDIO) {
+          ParsedRtcEventLogNew::MediaType::AUDIO) {
         continue;
       }
 
@@ -84,13 +84,11 @@
 int64_t RtcEventLogSource::NextAudioOutputEventMs() {
   while (audio_output_index_ < parsed_stream_.GetNumberOfEvents()) {
     if (parsed_stream_.GetEventType(audio_output_index_) ==
-        ParsedRtcEventLog::AUDIO_PLAYOUT_EVENT) {
-      uint64_t timestamp_us = parsed_stream_.GetTimestamp(audio_output_index_);
-      // We call GetAudioPlayout only to check that the protobuf event is
-      // well-formed.
-      parsed_stream_.GetAudioPlayout(audio_output_index_, nullptr);
+        ParsedRtcEventLogNew::AUDIO_PLAYOUT_EVENT) {
+      LoggedAudioPlayoutEvent playout_event =
+          parsed_stream_.GetAudioPlayout(audio_output_index_);
       audio_output_index_++;
-      return timestamp_us / 1000;
+      return playout_event.timestamp_us / 1000;
     }
     audio_output_index_++;
   }
diff --git a/modules/audio_coding/neteq/tools/rtc_event_log_source.h b/modules/audio_coding/neteq/tools/rtc_event_log_source.h
index df01e06..db4eb19 100644
--- a/modules/audio_coding/neteq/tools/rtc_event_log_source.h
+++ b/modules/audio_coding/neteq/tools/rtc_event_log_source.h
@@ -14,7 +14,7 @@
 #include <memory>
 #include <string>
 
-#include "logging/rtc_event_log/rtc_event_log_parser.h"
+#include "logging/rtc_event_log/rtc_event_log_parser_new.h"
 #include "modules/audio_coding/neteq/tools/packet_source.h"
 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
 #include "rtc_base/constructormagic.h"
@@ -53,7 +53,7 @@
   size_t rtp_packet_index_ = 0;
   size_t audio_output_index_ = 0;
 
-  ParsedRtcEventLog parsed_stream_;
+  ParsedRtcEventLogNew parsed_stream_;
   std::unique_ptr<RtpHeaderParser> parser_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(RtcEventLogSource);
diff --git a/modules/audio_coding/neteq/tools/rtp_encode.cc b/modules/audio_coding/neteq/tools/rtp_encode.cc
index ce07199..66e7a28 100644
--- a/modules/audio_coding/neteq/tools/rtp_encode.cc
+++ b/modules/audio_coding/neteq/tools/rtp_encode.cc
@@ -21,6 +21,7 @@
 #include <map>
 #include <string>
 
+#include "api/audio/audio_frame.h"
 #include "api/audio_codecs/L16/audio_encoder_L16.h"
 #include "api/audio_codecs/g711/audio_encoder_g711.h"
 #include "api/audio_codecs/g722/audio_encoder_g722.h"
diff --git a/modules/audio_coding/neteq/tools/rtp_file_source.cc b/modules/audio_coding/neteq/tools/rtp_file_source.cc
index c9ae5f2..0945667 100644
--- a/modules/audio_coding/neteq/tools/rtp_file_source.cc
+++ b/modules/audio_coding/neteq/tools/rtp_file_source.cc
@@ -12,9 +12,7 @@
 
 #include <assert.h>
 #include <string.h>
-#ifdef WIN32
-#include <winsock2.h>
-#else
+#ifndef WIN32
 #include <netinet/in.h>
 #endif
 
diff --git a/modules/audio_processing/BUILD.gn b/modules/audio_processing/BUILD.gn
index 09b0bd4..a64fc6f 100644
--- a/modules/audio_processing/BUILD.gn
+++ b/modules/audio_processing/BUILD.gn
@@ -29,6 +29,7 @@
 
 rtc_static_library("audio_processing") {
   visibility = [ "*" ]
+  allow_poison = [ "audio_codecs" ]  # TODO(bugs.webrtc.org/8396): Remove.
   configs += [ ":apm_debug_dump" ]
   sources = [
     "aec/aec_resampler.cc",
@@ -122,25 +123,26 @@
     ":audio_generator_interface",
     ":audio_processing_c",
     ":audio_processing_statistics",
-    "..:module_api",
     "../..:typedefs",
     "../..:webrtc_common",
     "../../api:array_view",
     "../../api:optional",
     "../../api/audio:aec3_config",
+    "../../api/audio:audio_frame_api",
     "../../api/audio:echo_control",
     "../../audio/utility:audio_frame_operations",
     "../../common_audio:common_audio_c",
     "../../rtc_base:checks",
     "../../rtc_base:deprecation",
     "../../rtc_base:gtest_prod",
-    "../../rtc_base:protobuf_utils",
     "../../rtc_base:safe_minmax",
     "../../rtc_base:sanitizer",
     "../../system_wrappers:cpu_features_api",
     "../../system_wrappers:field_trial_api",
     "../../system_wrappers:metrics_api",
-    "agc2",
+    "agc2:adaptive_digital",
+    "agc2:fixed_digital",
+    "agc2:gain_applier",
     "vad",
   ]
 
@@ -148,11 +150,6 @@
     defines += [ "WEBRTC_UNTRUSTED_DELAY" ]
   }
 
-  if (rtc_enable_protobuf) {
-    defines += [ "WEBRTC_AUDIOPROC_DEBUG_DUMP" ]
-    deps += [ ":audioproc_debug_proto" ]
-  }
-
   if (rtc_enable_intelligibility_enhancer) {
     defines += [ "WEBRTC_INTELLIGIBILITY_ENHANCER=1" ]
     sources += [
@@ -212,6 +209,7 @@
   deps = [
     ":audio_frame_view",
     "../../api:array_view",
+    "../../api/audio:audio_frame_api",
     "../../rtc_base:rtc_base_approved",
   ]
 }
@@ -392,7 +390,7 @@
       "utility/ooura_fft_sse2.cc",
       "utility/ooura_fft_tables_neon_sse2.h",
     ]
-    if (is_posix) {
+    if (is_posix || is_fuchsia) {
       cflags += [ "-msse2" ]
     }
   }
@@ -524,7 +522,6 @@
       ":audioproc_test_utils",
       ":file_audio_generator_unittests",
       ":mocks",
-      "..:module_api",
       "../..:typedefs",
       "../..:webrtc_common",
       "../../api:array_view",
@@ -539,13 +536,17 @@
       "../../rtc_base:rtc_base",
       "../../rtc_base:rtc_base_approved",
       "../../rtc_base:safe_minmax",
+      "../../rtc_base/system:file_wrapper",
       "../../system_wrappers",
       "../../system_wrappers:cpu_features_api",
       "../../test:fileutils",
       "../../test:test_support",
       "../audio_coding:neteq_input_audio_tools",
       "aec_dump:mock_aec_dump_unittests",
+      "agc2:adaptive_digital_unittests",
       "agc2:fixed_digital_unittests",
+      "agc2:noise_estimator_unittests",
+      "agc2/rnn_vad:unittests",
       "test/conversational_speech:unittest",
       "vad:vad_unittests",
       "//testing/gtest",
@@ -576,6 +577,7 @@
         ":audioproc_protobuf_utils",
         ":audioproc_test_utils",
         ":audioproc_unittest_proto",
+        "../../api/audio:audio_frame_api",
         "../../rtc_base:rtc_task_queue",
         "aec_dump",
         "aec_dump:aec_dump_unittests",
@@ -624,7 +626,6 @@
       ":audio_processing",
       ":audioproc_test_utils",
       "../../api:array_view",
-      "../../modules:module_api",
       "../../rtc_base:protobuf_utils",
       "../../rtc_base:rtc_base_approved",
       "../../system_wrappers",
@@ -663,8 +664,9 @@
     ]
     deps = [
       "../../api:array_view",
+      "../../api:optional",
+      "../../api/audio:audio_frame_api",
       "../../common_audio:common_audio",
-      "../../modules:module_api",
       "../../rtc_base:checks",
       "../../rtc_base:rtc_base_approved",
     ]
@@ -738,9 +740,9 @@
 
     deps = [
       ":audio_processing",
-      "..:module_api",
       "../../api:array_view",
       "../../api:optional",
+      "../../api/audio:audio_frame_api",
       "../../common_audio",
       "../../rtc_base:checks",
       "../../rtc_base:rtc_base_approved",
@@ -761,11 +763,11 @@
     ]
     deps = [
       ":audio_processing",
-      "..:module_api",
       "../..:typedefs",
       "../..:webrtc_common",
       "../../common_audio:common_audio",
       "../../rtc_base:rtc_base_approved",
+      "../../rtc_base/system:file_wrapper",
       "../../system_wrappers",
       "../../system_wrappers:metrics_default",
       "../../test:fileutils",
@@ -785,6 +787,7 @@
       ":audio_processing",
       "../..:typedefs",
       "../..:webrtc_common",
+      "../../rtc_base/system:file_wrapper",
       "../../system_wrappers",
       "../../system_wrappers:metrics_default",
     ]
diff --git a/modules/audio_processing/aec/aec_core.h b/modules/audio_processing/aec/aec_core.h
index 78596ec..9e25292 100644
--- a/modules/audio_processing/aec/aec_core.h
+++ b/modules/audio_processing/aec/aec_core.h
@@ -22,7 +22,6 @@
 extern "C" {
 #include "common_audio/ring_buffer.h"
 }
-#include "common_audio/wav_file.h"
 #include "modules/audio_processing/aec/aec_common.h"
 #include "modules/audio_processing/utility/block_mean_calculator.h"
 #include "modules/audio_processing/utility/ooura_fft.h"
diff --git a/modules/audio_processing/aec/echo_cancellation.cc b/modules/audio_processing/aec/echo_cancellation.cc
index 864db53..1633068 100644
--- a/modules/audio_processing/aec/echo_cancellation.cc
+++ b/modules/audio_processing/aec/echo_cancellation.cc
@@ -677,7 +677,7 @@
 }
 
 static void ProcessExtended(Aec* self,
-                            const float* const* near,
+                            const float* const* nearend,
                             size_t num_bands,
                             float* const* out,
                             size_t num_samples,
@@ -709,8 +709,8 @@
   if (!self->farend_started) {
     for (i = 0; i < num_bands; ++i) {
       // Only needed if they don't already point to the same place.
-      if (near[i] != out[i]) {
-        memcpy(out[i], near[i], sizeof(near[i][0]) * num_samples);
+      if (nearend[i] != out[i]) {
+        memcpy(out[i], nearend[i], sizeof(nearend[i][0]) * num_samples);
       }
     }
     return;
@@ -746,7 +746,7 @@
     const int adjusted_known_delay =
         WEBRTC_SPL_MAX(0, self->knownDelay + delay_diff_offset);
 
-    WebRtcAec_ProcessFrames(self->aec, near, num_bands, num_samples,
+    WebRtcAec_ProcessFrames(self->aec, nearend, num_bands, num_samples,
                             adjusted_known_delay, out);
   }
 }
diff --git a/modules/audio_processing/aec3/BUILD.gn b/modules/audio_processing/aec3/BUILD.gn
index 372b30f..9658f6b 100644
--- a/modules/audio_processing/aec3/BUILD.gn
+++ b/modules/audio_processing/aec3/BUILD.gn
@@ -11,6 +11,7 @@
 
 rtc_static_library("aec3") {
   visibility = [ "*" ]
+  allow_poison = [ "audio_codecs" ]  # TODO(bugs.webrtc.org/8396): Remove.
   configs += [ "..:apm_debug_dump" ]
   sources = [
     "adaptive_fir_filter.cc",
@@ -29,6 +30,8 @@
     "block_processor_metrics.h",
     "cascaded_biquad_filter.cc",
     "cascaded_biquad_filter.h",
+    "coherence_gain.cc",
+    "coherence_gain.h",
     "comfort_noise_generator.cc",
     "comfort_noise_generator.h",
     "decimator.cc",
@@ -36,6 +39,8 @@
     "delay_estimate.h",
     "downsampled_render_buffer.cc",
     "downsampled_render_buffer.h",
+    "echo_audibility.cc",
+    "echo_audibility.h",
     "echo_canceller3.cc",
     "echo_canceller3.h",
     "echo_path_delay_estimator.cc",
@@ -53,6 +58,8 @@
     "fft_buffer.cc",
     "fft_buffer.h",
     "fft_data.h",
+    "filter_analyzer.cc",
+    "filter_analyzer.h",
     "frame_blocker.cc",
     "frame_blocker.h",
     "main_filter_update_gain.cc",
@@ -63,8 +70,6 @@
     "matched_filter_lag_aggregator.h",
     "matrix_buffer.cc",
     "matrix_buffer.h",
-    "output_selector.cc",
-    "output_selector.h",
     "render_buffer.cc",
     "render_buffer.h",
     "render_delay_buffer.cc",
@@ -81,6 +86,8 @@
     "shadow_filter_update_gain.h",
     "skew_estimator.cc",
     "skew_estimator.h",
+    "stationarity_estimator.cc",
+    "stationarity_estimator.h",
     "subtractor.cc",
     "subtractor.h",
     "subtractor_output.h",
@@ -169,7 +176,6 @@
         "main_filter_update_gain_unittest.cc",
         "matched_filter_lag_aggregator_unittest.cc",
         "matched_filter_unittest.cc",
-        "output_selector_unittest.cc",
         "render_buffer_unittest.cc",
         "render_delay_buffer_unittest.cc",
         "render_delay_controller_metrics_unittest.cc",
diff --git a/modules/audio_processing/aec3/adaptive_fir_filter.cc b/modules/audio_processing/aec3/adaptive_fir_filter.cc
index 9bea40b..f44fe3d 100644
--- a/modules/audio_processing/aec3/adaptive_fir_filter.cc
+++ b/modules/audio_processing/aec3/adaptive_fir_filter.cc
@@ -504,6 +504,9 @@
   H_.resize(current_size_partitions_);
   H2_.resize(current_size_partitions_);
   h_.resize(GetTimeDomainLength(current_size_partitions_));
+  RTC_DCHECK_LT(0, current_size_partitions_);
+  partition_to_constrain_ =
+      std::min(partition_to_constrain_, current_size_partitions_ - 1);
 }
 
 void AdaptiveFirFilter::UpdateSize() {
diff --git a/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc b/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc
index 9fb11cd..9561dff 100644
--- a/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc
+++ b/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc
@@ -351,7 +351,7 @@
     CascadedBiQuadFilter y_hp_filter(kHighPassFilterCoefficients, 1);
 
     SCOPED_TRACE(ProduceDebugText(delay_samples));
-    for (size_t k = 0; k < kNumBlocksToProcess; ++k) {
+    for (size_t j = 0; j < kNumBlocksToProcess; ++j) {
       RandomizeSampleVector(&random_generator, x[0]);
       delay_buffer.Delay(x[0], y);
 
@@ -365,13 +365,14 @@
       y_hp_filter.Process(y);
 
       render_delay_buffer->Insert(x);
-      if (k == 0) {
+      if (j == 0) {
         render_delay_buffer->Reset();
       }
       render_delay_buffer->PrepareCaptureProcessing();
       const auto& render_buffer = render_delay_buffer->GetRenderBuffer();
 
-      render_signal_analyzer.Update(*render_buffer, aec_state.FilterDelay());
+      render_signal_analyzer.Update(*render_buffer,
+                                    aec_state.FilterDelayBlocks());
 
       filter.Filter(*render_buffer, &S);
       fft.Ifft(S, &s_scratch);
@@ -392,15 +393,14 @@
       filter.Adapt(*render_buffer, G);
       aec_state.HandleEchoPathChange(EchoPathVariability(
           false, EchoPathVariability::DelayAdjustment::kNone, false));
+
       aec_state.Update(delay_estimate, filter.FilterFrequencyResponse(),
-                       filter.FilterImpulseResponse(), true, *render_buffer,
-                       E2_main, Y2, s, false);
+                       filter.FilterImpulseResponse(), true, false,
+                       *render_buffer, E2_main, Y2, s);
     }
     // Verify that the filter is able to perform well.
     EXPECT_LT(1000 * std::inner_product(e.begin(), e.end(), e.begin(), 0.f),
               std::inner_product(y.begin(), y.end(), y.begin(), 0.f));
-    EXPECT_EQ(delay_samples / kBlockSize,
-              static_cast<size_t>(aec_state.FilterDelay()));
   }
 }
 }  // namespace aec3
diff --git a/modules/audio_processing/aec3/aec3_fft.cc b/modules/audio_processing/aec3/aec3_fft.cc
index d669036..b02f342 100644
--- a/modules/audio_processing/aec3/aec3_fft.cc
+++ b/modules/audio_processing/aec3/aec3_fft.cc
@@ -33,6 +33,41 @@
     0.15088159f, 0.11697778f, 0.08688061f, 0.06088921f, 0.03926189f,
     0.0222136f,  0.00991376f, 0.00248461f, 0.f};
 
+// Hanning window from Matlab command win = sqrt(hanning(128)).
+const float kSqrtHanning128[kFftLength] = {
+    0.00000000000000f, 0.02454122852291f, 0.04906767432742f, 0.07356456359967f,
+    0.09801714032956f, 0.12241067519922f, 0.14673047445536f, 0.17096188876030f,
+    0.19509032201613f, 0.21910124015687f, 0.24298017990326f, 0.26671275747490f,
+    0.29028467725446f, 0.31368174039889f, 0.33688985339222f, 0.35989503653499f,
+    0.38268343236509f, 0.40524131400499f, 0.42755509343028f, 0.44961132965461f,
+    0.47139673682600f, 0.49289819222978f, 0.51410274419322f, 0.53499761988710f,
+    0.55557023301960f, 0.57580819141785f, 0.59569930449243f, 0.61523159058063f,
+    0.63439328416365f, 0.65317284295378f, 0.67155895484702f, 0.68954054473707f,
+    0.70710678118655f, 0.72424708295147f, 0.74095112535496f, 0.75720884650648f,
+    0.77301045336274f, 0.78834642762661f, 0.80320753148064f, 0.81758481315158f,
+    0.83146961230255f, 0.84485356524971f, 0.85772861000027f, 0.87008699110871f,
+    0.88192126434835f, 0.89322430119552f, 0.90398929312344f, 0.91420975570353f,
+    0.92387953251129f, 0.93299279883474f, 0.94154406518302f, 0.94952818059304f,
+    0.95694033573221f, 0.96377606579544f, 0.97003125319454f, 0.97570213003853f,
+    0.98078528040323f, 0.98527764238894f, 0.98917650996478f, 0.99247953459871f,
+    0.99518472667220f, 0.99729045667869f, 0.99879545620517f, 0.99969881869620f,
+    1.00000000000000f, 0.99969881869620f, 0.99879545620517f, 0.99729045667869f,
+    0.99518472667220f, 0.99247953459871f, 0.98917650996478f, 0.98527764238894f,
+    0.98078528040323f, 0.97570213003853f, 0.97003125319454f, 0.96377606579544f,
+    0.95694033573221f, 0.94952818059304f, 0.94154406518302f, 0.93299279883474f,
+    0.92387953251129f, 0.91420975570353f, 0.90398929312344f, 0.89322430119552f,
+    0.88192126434835f, 0.87008699110871f, 0.85772861000027f, 0.84485356524971f,
+    0.83146961230255f, 0.81758481315158f, 0.80320753148064f, 0.78834642762661f,
+    0.77301045336274f, 0.75720884650648f, 0.74095112535496f, 0.72424708295147f,
+    0.70710678118655f, 0.68954054473707f, 0.67155895484702f, 0.65317284295378f,
+    0.63439328416365f, 0.61523159058063f, 0.59569930449243f, 0.57580819141785f,
+    0.55557023301960f, 0.53499761988710f, 0.51410274419322f, 0.49289819222978f,
+    0.47139673682600f, 0.44961132965461f, 0.42755509343028f, 0.40524131400499f,
+    0.38268343236509f, 0.35989503653499f, 0.33688985339222f, 0.31368174039889f,
+    0.29028467725446f, 0.26671275747490f, 0.24298017990326f, 0.21910124015687f,
+    0.19509032201613f, 0.17096188876030f, 0.14673047445536f, 0.12241067519922f,
+    0.09801714032956f, 0.07356456359967f, 0.04906767432742f, 0.02454122852291f};
+
 }  // namespace
 
 // TODO(peah): Change x to be std::array once the rest of the code allows this.
@@ -52,6 +87,9 @@
                      fft.begin() + kFftLengthBy2,
                      [](float a, float b) { return a * b; });
       break;
+    case Window::kSqrtHanning:
+      RTC_NOTREACHED();
+      break;
     default:
       RTC_NOTREACHED();
   }
@@ -60,15 +98,33 @@
 }
 
 void Aec3Fft::PaddedFft(rtc::ArrayView<const float> x,
-                        rtc::ArrayView<float> x_old,
+                        rtc::ArrayView<const float> x_old,
+                        Window window,
                         FftData* X) const {
   RTC_DCHECK(X);
   RTC_DCHECK_EQ(kFftLengthBy2, x.size());
   RTC_DCHECK_EQ(kFftLengthBy2, x_old.size());
   std::array<float, kFftLength> fft;
-  std::copy(x_old.begin(), x_old.end(), fft.begin());
-  std::copy(x.begin(), x.end(), fft.begin() + x_old.size());
-  std::copy(x.begin(), x.end(), x_old.begin());
+
+  switch (window) {
+    case Window::kRectangular:
+      std::copy(x_old.begin(), x_old.end(), fft.begin());
+      std::copy(x.begin(), x.end(), fft.begin() + x_old.size());
+      break;
+    case Window::kHanning:
+      RTC_NOTREACHED();
+      break;
+    case Window::kSqrtHanning:
+      std::transform(x_old.begin(), x_old.end(), std::begin(kSqrtHanning128),
+                     fft.begin(), std::multiplies<float>());
+      std::transform(x.begin(), x.end(),
+                     std::begin(kSqrtHanning128) + x_old.size(),
+                     fft.begin() + x_old.size(), std::multiplies<float>());
+      break;
+    default:
+      RTC_NOTREACHED();
+  }
+
   Fft(&fft, X);
 }
 
diff --git a/modules/audio_processing/aec3/aec3_fft.h b/modules/audio_processing/aec3/aec3_fft.h
index f3dddb3..b700222 100644
--- a/modules/audio_processing/aec3/aec3_fft.h
+++ b/modules/audio_processing/aec3/aec3_fft.h
@@ -25,7 +25,7 @@
 // FftData type.
 class Aec3Fft {
  public:
-  enum class Window { kRectangular, kHanning };
+  enum class Window { kRectangular, kHanning, kSqrtHanning };
 
   Aec3Fft() = default;
   // Computes the FFT. Note that both the input and output are modified.
@@ -51,7 +51,15 @@
   // Concatenates the kFftLengthBy2 values long x and x_old before computing the
   // Fft. After that, x is copied to x_old.
   void PaddedFft(rtc::ArrayView<const float> x,
-                 rtc::ArrayView<float> x_old,
+                 rtc::ArrayView<const float> x_old,
+                 FftData* X) const {
+    PaddedFft(x, x_old, Window::kRectangular, X);
+  }
+
+  // Padded Fft using a time-domain window.
+  void PaddedFft(rtc::ArrayView<const float> x,
+                 rtc::ArrayView<const float> x_old,
+                 Window window,
                  FftData* X) const;
 
  private:
diff --git a/modules/audio_processing/aec3/aec3_fft_unittest.cc b/modules/audio_processing/aec3/aec3_fft_unittest.cc
index 87fe7a8..82d6e76 100644
--- a/modules/audio_processing/aec3/aec3_fft_unittest.cc
+++ b/modules/audio_processing/aec3/aec3_fft_unittest.cc
@@ -199,6 +199,7 @@
     std::for_each(x_ref.begin(), x_ref.end(), [](float& a) { a *= 64.f; });
 
     fft.PaddedFft(x_in, x_old, &X);
+    std::copy(x_in.begin(), x_in.end(), x_old.begin());
     fft.Ifft(X, &x_out);
 
     for (size_t j = 0; j < x_out.size(); ++j) {
diff --git a/modules/audio_processing/aec3/aec_state.cc b/modules/audio_processing/aec3/aec_state.cc
index 533290c..5f36e65 100644
--- a/modules/audio_processing/aec3/aec_state.cc
+++ b/modules/audio_processing/aec3/aec_state.cc
@@ -23,35 +23,14 @@
 namespace webrtc {
 namespace {
 
-// Computes delay of the adaptive filter.
-int EstimateFilterDelay(
-    const std::vector<std::array<float, kFftLengthBy2Plus1>>&
-        adaptive_filter_frequency_response) {
-  const auto& H2 = adaptive_filter_frequency_response;
-  constexpr size_t kUpperBin = kFftLengthBy2 - 5;
-  RTC_DCHECK_GE(kMaxAdaptiveFilterLength, H2.size());
-  std::array<int, kMaxAdaptiveFilterLength> delays;
-  delays.fill(0);
-  for (size_t k = 1; k < kUpperBin; ++k) {
-    // Find the maximum of H2[j].
-    size_t peak = 0;
-    for (size_t j = 0; j < H2.size(); ++j) {
-      if (H2[j][k] > H2[peak][k]) {
-        peak = j;
-      }
-    }
-    ++delays[peak];
-  }
-
-  return std::distance(delays.begin(),
-                       std::max_element(delays.begin(), delays.end()));
-}
-
 float ComputeGainRampupIncrease(const EchoCanceller3Config& config) {
   const auto& c = config.echo_removal_control.gain_rampup;
   return powf(1.f / c.first_non_zero_gain, 1.f / c.non_zero_gain_blocks);
 }
 
+constexpr size_t kBlocksSinceConvergencedFilterInit = 10000;
+constexpr size_t kBlocksSinceConsistentEstimateInit = 10000;
+
 }  // namespace
 
 int AecState::instance_count_ = 0;
@@ -64,27 +43,32 @@
       max_render_(config_.filter.main.length_blocks, 0.f),
       reverb_decay_(fabsf(config_.ep_strength.default_len)),
       gain_rampup_increase_(ComputeGainRampupIncrease(config_)),
-      suppression_gain_limiter_(config_) {}
+      suppression_gain_limiter_(config_),
+      filter_analyzer_(config_),
+      blocks_since_converged_filter_(kBlocksSinceConvergencedFilterInit),
+      active_blocks_since_consistent_filter_estimate_(
+          kBlocksSinceConsistentEstimateInit) {}
 
 AecState::~AecState() = default;
 
 void AecState::HandleEchoPathChange(
     const EchoPathVariability& echo_path_variability) {
   const auto full_reset = [&]() {
+    filter_analyzer_.Reset();
     blocks_since_last_saturation_ = 0;
     usable_linear_estimate_ = false;
-    echo_leakage_detected_ = false;
     capture_signal_saturation_ = false;
     echo_saturation_ = false;
-    previous_max_sample_ = 0.f;
     std::fill(max_render_.begin(), max_render_.end(), 0.f);
     blocks_with_proper_filter_adaptation_ = 0;
-    capture_block_counter_ = 0;
+    blocks_since_reset_ = 0;
     filter_has_had_time_to_converge_ = false;
     render_received_ = false;
     blocks_with_active_render_ = 0;
     initial_state_ = true;
     suppression_gain_limiter_.Reset();
+    blocks_since_converged_filter_ = kBlocksSinceConvergencedFilterInit;
+    diverged_blocks_ = 0;
   };
 
   // TODO(peah): Refine the reset scheme according to the type of gain and
@@ -106,30 +90,38 @@
              EchoPathVariability::DelayAdjustment::kNewDetectedDelay) {
     full_reset();
   } else if (echo_path_variability.gain_change) {
-    capture_block_counter_ = kNumBlocksPerSecond;
+    blocks_since_reset_ = kNumBlocksPerSecond;
   }
 }
 
 void AecState::Update(
-    const rtc::Optional<DelayEstimate>& delay_estimate,
+    const rtc::Optional<DelayEstimate>& external_delay,
     const std::vector<std::array<float, kFftLengthBy2Plus1>>&
         adaptive_filter_frequency_response,
     const std::vector<float>& adaptive_filter_impulse_response,
     bool converged_filter,
+    bool diverged_filter,
     const RenderBuffer& render_buffer,
     const std::array<float, kFftLengthBy2Plus1>& E2_main,
     const std::array<float, kFftLengthBy2Plus1>& Y2,
-    const std::array<float, kBlockSize>& s,
-    bool echo_leakage_detected) {
-  // Store input parameters.
-  echo_leakage_detected_ = echo_leakage_detected;
+    const std::array<float, kBlockSize>& s) {
+  // Analyze the filter and compute the delays.
+  filter_analyzer_.Update(adaptive_filter_impulse_response, render_buffer);
+  filter_delay_blocks_ = filter_analyzer_.DelayBlocks();
 
-  // Estimate the filter delay.
-  filter_delay_ = EstimateFilterDelay(adaptive_filter_frequency_response);
-  const std::vector<float>& x = render_buffer.Block(-filter_delay_)[0];
+  if (filter_analyzer_.Consistent()) {
+    internal_delay_ = filter_analyzer_.DelayBlocks();
+  } else {
+    internal_delay_ = rtc::nullopt;
+  }
+
+  external_delay_seen_ = external_delay_seen_ || external_delay;
+
+  const std::vector<float>& x = render_buffer.Block(-filter_delay_blocks_)[0];
 
   // Update counters.
   ++capture_block_counter_;
+  ++blocks_since_reset_;
   const bool active_render_block = DetectActiveRender(x);
   blocks_with_active_render_ += active_render_block ? 1 : 0;
   blocks_with_proper_filter_adaptation_ +=
@@ -137,45 +129,145 @@
 
   // Update the limit on the echo suppression after an echo path change to avoid
   // an initial echo burst.
-  suppression_gain_limiter_.Update(render_buffer.GetRenderActivity());
+  suppression_gain_limiter_.Update(render_buffer.GetRenderActivity(),
+                                   transparent_mode_);
+
+  if (UseStationaryProperties()) {
+    // Update the echo audibility evaluator.
+    echo_audibility_.Update(render_buffer, FilterDelayBlocks(),
+                            external_delay_seen_);
+  }
 
   // Update the ERL and ERLE measures.
-  if (converged_filter && capture_block_counter_ >= 2 * kNumBlocksPerSecond) {
-    const auto& X2 = render_buffer.Spectrum(filter_delay_);
+  if (converged_filter && blocks_since_reset_ >= 2 * kNumBlocksPerSecond) {
+    const auto& X2 = render_buffer.Spectrum(filter_delay_blocks_);
     erle_estimator_.Update(X2, Y2, E2_main);
     erl_estimator_.Update(X2, Y2);
   }
 
-  // Update the echo audibility evaluator.
-  echo_audibility_.Update(x, s, converged_filter);
-
   // Detect and flag echo saturation.
   // TODO(peah): Add the delay in this computation to ensure that the render and
   // capture signals are properly aligned.
   if (config_.ep_strength.echo_can_saturate) {
-    echo_saturation_ = DetectEchoSaturation(x);
+    echo_saturation_ = DetectEchoSaturation(x, EchoPathGain());
   }
 
-  // TODO(peah): Move?
-  filter_has_had_time_to_converge_ =
+  bool filter_has_had_time_to_converge =
       blocks_with_proper_filter_adaptation_ >= 1.5f * kNumBlocksPerSecond;
 
+  if (!filter_should_have_converged_) {
+    filter_should_have_converged_ =
+        blocks_with_proper_filter_adaptation_ > 6 * kNumBlocksPerSecond;
+  }
+
+  // Flag whether the initial state is still active.
   initial_state_ =
       blocks_with_proper_filter_adaptation_ < 5 * kNumBlocksPerSecond;
 
-  // Flag whether the linear filter estimate is usable.
-  usable_linear_estimate_ =
-      !echo_saturation_ &&
-      (converged_filter && filter_has_had_time_to_converge_) &&
-      capture_block_counter_ >= 1.f * kNumBlocksPerSecond && !TransparentMode();
+  // Update counters for the filter divergence and convergence.
+  diverged_blocks_ = diverged_filter ? diverged_blocks_ + 1 : 0;
+  if (diverged_blocks_ >= 60) {
+    blocks_since_converged_filter_ = kBlocksSinceConvergencedFilterInit;
+  } else {
+    blocks_since_converged_filter_ =
+        converged_filter ? 0 : blocks_since_converged_filter_ + 1;
+  }
+  if (converged_filter) {
+    active_blocks_since_converged_filter_ = 0;
+  } else if (active_render_block) {
+    ++active_blocks_since_converged_filter_;
+  }
+
+  bool recently_converged_filter =
+      blocks_since_converged_filter_ < 60 * kNumBlocksPerSecond;
+
+  if (blocks_since_converged_filter_ > 20 * kNumBlocksPerSecond) {
+    converged_filter_count_ = 0;
+  } else if (converged_filter) {
+    ++converged_filter_count_;
+  }
+  if (converged_filter_count_ > 50) {
+    finite_erl_ = true;
+  }
+
+  if (filter_analyzer_.Consistent() && filter_delay_blocks_ < 5) {
+    consistent_filter_seen_ = true;
+    active_blocks_since_consistent_filter_estimate_ = 0;
+  } else if (active_render_block) {
+    ++active_blocks_since_consistent_filter_estimate_;
+  }
+
+  bool consistent_filter_estimate_not_seen;
+  if (!consistent_filter_seen_) {
+    consistent_filter_estimate_not_seen =
+        capture_block_counter_ > 5 * kNumBlocksPerSecond;
+  } else {
+    consistent_filter_estimate_not_seen =
+        active_blocks_since_consistent_filter_estimate_ >
+        30 * kNumBlocksPerSecond;
+  }
+
+  converged_filter_seen_ = converged_filter_seen_ || converged_filter;
+
+  // If no filter convergence is seen for a long time, reset the estimated
+  // properties of the echo path.
+  if (active_blocks_since_converged_filter_ > 60 * kNumBlocksPerSecond) {
+    converged_filter_seen_ = false;
+    finite_erl_ = false;
+  }
 
   // After an amount of active render samples for which an echo should have been
   // detected in the capture signal if the ERL was not infinite, flag that a
   // transparent mode should be entered.
+  transparent_mode_ = !config_.ep_strength.bounded_erl && !finite_erl_;
   transparent_mode_ =
-      !converged_filter &&
-      (blocks_with_active_render_ == 0 ||
-       blocks_with_proper_filter_adaptation_ >= 5 * kNumBlocksPerSecond);
+      transparent_mode_ &&
+      (consistent_filter_estimate_not_seen || !converged_filter_seen_);
+  transparent_mode_ = transparent_mode_ &&
+                      (filter_should_have_converged_ ||
+                       (!external_delay_seen_ &&
+                        capture_block_counter_ > 10 * kNumBlocksPerSecond));
+
+  usable_linear_estimate_ = !echo_saturation_;
+  usable_linear_estimate_ =
+      usable_linear_estimate_ && filter_has_had_time_to_converge;
+  usable_linear_estimate_ =
+      usable_linear_estimate_ && recently_converged_filter;
+  usable_linear_estimate_ = usable_linear_estimate_ && !diverged_filter;
+  usable_linear_estimate_ = usable_linear_estimate_ && external_delay;
+
+  use_linear_filter_output_ = usable_linear_estimate_ && !TransparentMode();
+
+  data_dumper_->DumpRaw("aec3_erle", Erle());
+  data_dumper_->DumpRaw("aec3_erle_onset", erle_estimator_.ErleOnsets());
+  data_dumper_->DumpRaw("aec3_erl", Erl());
+  data_dumper_->DumpRaw("aec3_erle_time_domain", ErleTimeDomain());
+  data_dumper_->DumpRaw("aec3_erl_time_domain", ErlTimeDomain());
+  data_dumper_->DumpRaw("aec3_usable_linear_estimate", UsableLinearEstimate());
+  data_dumper_->DumpRaw("aec3_transparent_mode", transparent_mode_);
+  data_dumper_->DumpRaw("aec3_state_internal_delay",
+                        internal_delay_ ? *internal_delay_ : -1);
+  data_dumper_->DumpRaw("aec3_filter_delay", filter_analyzer_.DelayBlocks());
+
+  data_dumper_->DumpRaw("aec3_consistent_filter",
+                        filter_analyzer_.Consistent());
+  data_dumper_->DumpRaw("aec3_suppression_gain_limit", SuppressionGainLimit());
+  data_dumper_->DumpRaw("aec3_initial_state", InitialState());
+  data_dumper_->DumpRaw("aec3_capture_saturation", SaturatedCapture());
+  data_dumper_->DumpRaw("aec3_echo_saturation", echo_saturation_);
+  data_dumper_->DumpRaw("aec3_converged_filter", converged_filter);
+  data_dumper_->DumpRaw("aec3_diverged_filter", diverged_filter);
+
+  data_dumper_->DumpRaw("aec3_external_delay_avaliable",
+                        external_delay ? 1 : 0);
+  data_dumper_->DumpRaw("aec3_consistent_filter_estimate_not_seen",
+                        consistent_filter_estimate_not_seen);
+  data_dumper_->DumpRaw("aec3_filter_should_have_converged",
+                        filter_should_have_converged_);
+  data_dumper_->DumpRaw("aec3_filter_has_had_time_to_converge",
+                        filter_has_had_time_to_converge);
+  data_dumper_->DumpRaw("aec3_recently_converged_filter",
+                        recently_converged_filter);
 }
 
 void AecState::UpdateReverb(const std::vector<float>& impulse_response) {
@@ -184,8 +276,8 @@
     return;
   }
 
-  if ((!(filter_delay_ && usable_linear_estimate_)) ||
-      (filter_delay_ >
+  if ((!(filter_delay_blocks_ && usable_linear_estimate_)) ||
+      (filter_delay_blocks_ >
        static_cast<int>(config_.filter.main.length_blocks) - 4)) {
     return;
   }
@@ -371,67 +463,22 @@
                         kFftLengthBy2;
 }
 
-bool AecState::DetectEchoSaturation(rtc::ArrayView<const float> x) {
+bool AecState::DetectEchoSaturation(rtc::ArrayView<const float> x,
+                                    float echo_path_gain) {
   RTC_DCHECK_LT(0, x.size());
   const float max_sample = fabs(*std::max_element(
       x.begin(), x.end(), [](float a, float b) { return a * a < b * b; }));
-  previous_max_sample_ = max_sample;
 
   // Set flag for potential presence of saturated echo
-  blocks_since_last_saturation_ =
-      previous_max_sample_ > 200.f && SaturatedCapture()
-          ? 0
-          : blocks_since_last_saturation_ + 1;
-
-  return blocks_since_last_saturation_ < 20;
-}
-
-void AecState::EchoAudibility::Update(rtc::ArrayView<const float> x,
-                                      const std::array<float, kBlockSize>& s,
-                                      bool converged_filter) {
-  auto result_x = std::minmax_element(x.begin(), x.end());
-  auto result_s = std::minmax_element(s.begin(), s.end());
-  const float x_abs = std::max(fabsf(*result_x.first), fabsf(*result_x.second));
-  const float s_abs = std::max(fabsf(*result_s.first), fabsf(*result_s.second));
-
-  if (converged_filter) {
-    if (x_abs < 20.f) {
-      ++low_farend_counter_;
-    } else {
-      low_farend_counter_ = 0;
-    }
+  const float kMargin = 10.f;
+  float peak_echo_amplitude = max_sample * echo_path_gain * kMargin;
+  if (SaturatedCapture() && peak_echo_amplitude > 32000) {
+    blocks_since_last_saturation_ = 0;
   } else {
-    if (x_abs < 100.f) {
-      ++low_farend_counter_;
-    } else {
-      low_farend_counter_ = 0;
-    }
+    ++blocks_since_last_saturation_;
   }
 
-  // The echo is deemed as not audible if the echo estimate is on the level of
-  // the quantization noise in the FFTs and the nearend level is sufficiently
-  // strong to mask that by ensuring that the playout and AGC gains do not boost
-  // any residual echo that is below the quantization noise level. Furthermore,
-  // cases where the render signal is very close to zero are also identified as
-  // not producing audible echo.
-  inaudible_echo_ = (max_nearend_ > 500 && s_abs < 30.f) ||
-                    (!converged_filter && x_abs < 500);
-  inaudible_echo_ = inaudible_echo_ || low_farend_counter_ > 20;
-}
-
-void AecState::EchoAudibility::UpdateWithOutput(rtc::ArrayView<const float> e) {
-  const float e_max = *std::max_element(e.begin(), e.end());
-  const float e_min = *std::min_element(e.begin(), e.end());
-  const float e_abs = std::max(fabsf(e_max), fabsf(e_min));
-
-  if (max_nearend_ < e_abs) {
-    max_nearend_ = e_abs;
-    max_nearend_counter_ = 0;
-  } else {
-    if (++max_nearend_counter_ > 5 * kNumBlocksPerSecond) {
-      max_nearend_ *= 0.995f;
-    }
-  }
+  return blocks_since_last_saturation_ < 5;
 }
 
 }  // namespace webrtc
diff --git a/modules/audio_processing/aec3/aec_state.h b/modules/audio_processing/aec3/aec_state.h
index 6dcd43d..ebfa62f 100644
--- a/modules/audio_processing/aec3/aec_state.h
+++ b/modules/audio_processing/aec3/aec_state.h
@@ -22,9 +22,11 @@
 #include "api/optional.h"
 #include "modules/audio_processing/aec3/aec3_common.h"
 #include "modules/audio_processing/aec3/delay_estimate.h"
+#include "modules/audio_processing/aec3/echo_audibility.h"
 #include "modules/audio_processing/aec3/echo_path_variability.h"
 #include "modules/audio_processing/aec3/erl_estimator.h"
 #include "modules/audio_processing/aec3/erle_estimator.h"
+#include "modules/audio_processing/aec3/filter_analyzer.h"
 #include "modules/audio_processing/aec3/render_buffer.h"
 #include "modules/audio_processing/aec3/suppression_gain_limiter.h"
 #include "rtc_base/constructormagic.h"
@@ -43,12 +45,27 @@
   // echo.
   bool UsableLinearEstimate() const { return usable_linear_estimate_; }
 
-  // Returns whether there has been echo leakage detected.
-  bool EchoLeakageDetected() const { return echo_leakage_detected_; }
+  // Returns whether the echo subtractor output should be used as output.
+  bool UseLinearFilterOutput() const { return use_linear_filter_output_; }
+
+  // Returns the estimated echo path gain.
+  bool EchoPathGain() const { return filter_analyzer_.Gain(); }
 
   // Returns whether the render signal is currently active.
   bool ActiveRender() const { return blocks_with_active_render_ > 200; }
 
+  // Returns the appropriate scaling of the residual echo to match the
+  // audibility.
+  void GetResidualEchoScaling(rtc::ArrayView<float> residual_scaling) const {
+    echo_audibility_.GetResidualEchoScaling(residual_scaling);
+  }
+
+  // Returns whether the stationary properties of the signals are used in the
+  // aec.
+  bool UseStationaryProperties() const {
+    return config_.echo_audibility.use_stationary_properties;
+  }
+
   // Returns the ERLE.
   const std::array<float, kFftLengthBy2Plus1>& Erle() const {
     return erle_estimator_.Erle();
@@ -66,7 +83,10 @@
   float ErlTimeDomain() const { return erl_estimator_.ErlTimeDomain(); }
 
   // Returns the delay estimate based on the linear filter.
-  int FilterDelay() const { return filter_delay_; }
+  int FilterDelayBlocks() const { return filter_delay_blocks_; }
+
+  // Returns the internal delay estimate based on the linear filter.
+  rtc::Optional<int> InternalDelay() const { return internal_delay_; }
 
   // Returns whether the capture signal is saturated.
   bool SaturatedCapture() const { return capture_signal_saturation_; }
@@ -74,9 +94,6 @@
   // Returns whether the echo signal is saturated.
   bool SaturatedEcho() const { return echo_saturation_; }
 
-  // Returns whether the echo path can saturate.
-  bool SaturatingEchoPath() const { return saturating_echo_path_; }
-
   // Updates the capture signal saturation.
   void UpdateCaptureSaturation(bool capture_signal_saturation) {
     capture_signal_saturation_ = capture_signal_saturation;
@@ -96,14 +113,6 @@
     return suppression_gain_limiter_.Limit();
   }
 
-  // Returns whether the echo in the capture signal is audible.
-  bool InaudibleEcho() const { return echo_audibility_.InaudibleEcho(); }
-
-  // Updates the aec state with the AEC output signal.
-  void UpdateWithOutput(rtc::ArrayView<const float> e) {
-    echo_audibility_.UpdateWithOutput(e);
-  }
-
   // Returns whether the linear filter should have been able to properly adapt.
   bool FilterHasHadTimeToConverge() const {
     return filter_has_had_time_to_converge_;
@@ -113,53 +122,38 @@
   bool InitialState() const { return initial_state_; }
 
   // Updates the aec state.
-  void Update(const rtc::Optional<DelayEstimate>& delay_estimate,
+  void Update(const rtc::Optional<DelayEstimate>& external_delay,
               const std::vector<std::array<float, kFftLengthBy2Plus1>>&
                   adaptive_filter_frequency_response,
               const std::vector<float>& adaptive_filter_impulse_response,
               bool converged_filter,
+              bool diverged_filter,
               const RenderBuffer& render_buffer,
               const std::array<float, kFftLengthBy2Plus1>& E2_main,
               const std::array<float, kFftLengthBy2Plus1>& Y2,
-              const std::array<float, kBlockSize>& s_main,
-              bool echo_leakage_detected);
+              const std::array<float, kBlockSize>& s);
 
  private:
-  class EchoAudibility {
-   public:
-    void Update(rtc::ArrayView<const float> x,
-                const std::array<float, kBlockSize>& s,
-                bool converged_filter);
-    void UpdateWithOutput(rtc::ArrayView<const float> e);
-    bool InaudibleEcho() const { return inaudible_echo_; }
-
-   private:
-    float max_nearend_ = 0.f;
-    size_t max_nearend_counter_ = 0;
-    size_t low_farend_counter_ = 0;
-    bool inaudible_echo_ = false;
-  };
-
   void UpdateReverb(const std::vector<float>& impulse_response);
   bool DetectActiveRender(rtc::ArrayView<const float> x) const;
   void UpdateSuppressorGainLimit(bool render_activity);
-  bool DetectEchoSaturation(rtc::ArrayView<const float> x);
+  bool DetectEchoSaturation(rtc::ArrayView<const float> x,
+                            float echo_path_gain);
 
   static int instance_count_;
   std::unique_ptr<ApmDataDumper> data_dumper_;
   ErlEstimator erl_estimator_;
   ErleEstimator erle_estimator_;
   size_t capture_block_counter_ = 0;
+  size_t blocks_since_reset_ = 0;
   size_t blocks_with_proper_filter_adaptation_ = 0;
   size_t blocks_with_active_render_ = 0;
   bool usable_linear_estimate_ = false;
-  bool echo_leakage_detected_ = false;
   bool capture_signal_saturation_ = false;
   bool echo_saturation_ = false;
   bool transparent_mode_ = false;
-  float previous_max_sample_ = 0.f;
   bool render_received_ = false;
-  int filter_delay_ = 0;
+  int filter_delay_blocks_ = 0;
   size_t blocks_since_last_saturation_ = 1000;
   float tail_energy_ = 0.f;
   float accumulated_nz_ = 0.f;
@@ -171,15 +165,27 @@
   bool found_end_of_reverb_decay_ = false;
   bool main_filter_is_adapting_ = true;
   std::array<float, kMaxAdaptiveFilterLength> block_energies_;
-  EchoAudibility echo_audibility_;
   const EchoCanceller3Config config_;
   std::vector<float> max_render_;
   float reverb_decay_ = fabsf(config_.ep_strength.default_len);
-  bool saturating_echo_path_ = false;
   bool filter_has_had_time_to_converge_ = false;
   bool initial_state_ = true;
   const float gain_rampup_increase_;
   SuppressionGainUpperLimiter suppression_gain_limiter_;
+  FilterAnalyzer filter_analyzer_;
+  bool use_linear_filter_output_ = false;
+  rtc::Optional<int> internal_delay_;
+  size_t diverged_blocks_ = 0;
+  bool filter_should_have_converged_ = false;
+  size_t blocks_since_converged_filter_;
+  size_t active_blocks_since_consistent_filter_estimate_;
+  bool converged_filter_seen_ = false;
+  bool consistent_filter_seen_ = false;
+  bool external_delay_seen_ = false;
+  size_t converged_filter_count_ = 0;
+  bool finite_erl_ = false;
+  size_t active_blocks_since_converged_filter_ = 0;
+  EchoAudibility echo_audibility_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(AecState);
 };
diff --git a/modules/audio_processing/aec3/aec_state_unittest.cc b/modules/audio_processing/aec3/aec_state_unittest.cc
index 9008232..83213b5 100644
--- a/modules/audio_processing/aec3/aec_state_unittest.cc
+++ b/modules/audio_processing/aec3/aec_state_unittest.cc
@@ -22,7 +22,8 @@
   ApmDataDumper data_dumper(42);
   EchoCanceller3Config config;
   AecState state(config);
-  rtc::Optional<DelayEstimate> delay_estimate;
+  rtc::Optional<DelayEstimate> delay_estimate =
+      DelayEstimate(DelayEstimate::Quality::kRefined, 10);
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
       RenderDelayBuffer::Create(config, 3));
   std::array<float, kFftLengthBy2Plus1> E2_main = {};
@@ -49,17 +50,17 @@
 
   // Verify that linear AEC usability is false when the filter is diverged.
   state.Update(delay_estimate, diverged_filter_frequency_response,
-               impulse_response, true, *render_delay_buffer->GetRenderBuffer(),
-               E2_main, Y2, s, false);
+               impulse_response, true, false,
+               *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
   EXPECT_FALSE(state.UsableLinearEstimate());
 
   // Verify that linear AEC usability is true when the filter is converged
   std::fill(x[0].begin(), x[0].end(), 101.f);
   for (int k = 0; k < 3000; ++k) {
     render_delay_buffer->Insert(x);
-    state.Update(
-        delay_estimate, converged_filter_frequency_response, impulse_response,
-        true, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s, false);
+    state.Update(delay_estimate, converged_filter_frequency_response,
+                 impulse_response, true, false,
+                 *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
   }
   EXPECT_TRUE(state.UsableLinearEstimate());
 
@@ -68,8 +69,8 @@
   state.HandleEchoPathChange(EchoPathVariability(
       true, EchoPathVariability::DelayAdjustment::kNone, false));
   state.Update(delay_estimate, converged_filter_frequency_response,
-               impulse_response, true, *render_delay_buffer->GetRenderBuffer(),
-               E2_main, Y2, s, false);
+               impulse_response, true, false,
+               *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
   EXPECT_FALSE(state.UsableLinearEstimate());
 
   // Verify that the active render detection works as intended.
@@ -78,29 +79,18 @@
   state.HandleEchoPathChange(EchoPathVariability(
       true, EchoPathVariability::DelayAdjustment::kNewDetectedDelay, false));
   state.Update(delay_estimate, converged_filter_frequency_response,
-               impulse_response, true, *render_delay_buffer->GetRenderBuffer(),
-               E2_main, Y2, s, false);
+               impulse_response, true, false,
+               *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
   EXPECT_FALSE(state.ActiveRender());
 
   for (int k = 0; k < 1000; ++k) {
     render_delay_buffer->Insert(x);
-    state.Update(
-        delay_estimate, converged_filter_frequency_response, impulse_response,
-        true, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s, false);
+    state.Update(delay_estimate, converged_filter_frequency_response,
+                 impulse_response, true, false,
+                 *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
   }
   EXPECT_TRUE(state.ActiveRender());
 
-  // Verify that echo leakage is properly reported.
-  state.Update(delay_estimate, converged_filter_frequency_response,
-               impulse_response, true, *render_delay_buffer->GetRenderBuffer(),
-               E2_main, Y2, s, false);
-  EXPECT_FALSE(state.EchoLeakageDetected());
-
-  state.Update(delay_estimate, converged_filter_frequency_response,
-               impulse_response, true, *render_delay_buffer->GetRenderBuffer(),
-               E2_main, Y2, s, true);
-  EXPECT_TRUE(state.EchoLeakageDetected());
-
   // Verify that the ERL is properly estimated
   for (auto& x_k : x) {
     x_k = std::vector<float>(kBlockSize, 0.f);
@@ -118,9 +108,9 @@
 
   Y2.fill(10.f * 10000.f * 10000.f);
   for (size_t k = 0; k < 1000; ++k) {
-    state.Update(
-        delay_estimate, converged_filter_frequency_response, impulse_response,
-        true, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s, false);
+    state.Update(delay_estimate, converged_filter_frequency_response,
+                 impulse_response, true, false,
+                 *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
   }
 
   ASSERT_TRUE(state.UsableLinearEstimate());
@@ -135,9 +125,9 @@
   E2_main.fill(1.f * 10000.f * 10000.f);
   Y2.fill(10.f * E2_main[0]);
   for (size_t k = 0; k < 1000; ++k) {
-    state.Update(
-        delay_estimate, converged_filter_frequency_response, impulse_response,
-        true, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s, false);
+    state.Update(delay_estimate, converged_filter_frequency_response,
+                 impulse_response, true, false,
+                 *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
   }
   ASSERT_TRUE(state.UsableLinearEstimate());
   {
@@ -145,7 +135,7 @@
     EXPECT_EQ(erle[0], erle[1]);
     constexpr size_t kLowFrequencyLimit = 32;
     for (size_t k = 1; k < kLowFrequencyLimit; ++k) {
-      EXPECT_NEAR(k % 2 == 0 ? 8.f : 1.f, erle[k], 0.1);
+      EXPECT_NEAR(k % 2 == 0 ? 4.f : 1.f, erle[k], 0.1);
     }
     for (size_t k = kLowFrequencyLimit; k < erle.size() - 1; ++k) {
       EXPECT_NEAR(k % 2 == 0 ? 1.5f : 1.f, erle[k], 0.1);
@@ -156,9 +146,9 @@
   E2_main.fill(1.f * 10000.f * 10000.f);
   Y2.fill(5.f * E2_main[0]);
   for (size_t k = 0; k < 1000; ++k) {
-    state.Update(
-        delay_estimate, converged_filter_frequency_response, impulse_response,
-        true, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s, false);
+    state.Update(delay_estimate, converged_filter_frequency_response,
+                 impulse_response, true, false,
+                 *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
   }
 
   ASSERT_TRUE(state.UsableLinearEstimate());
@@ -167,7 +157,7 @@
     EXPECT_EQ(erle[0], erle[1]);
     constexpr size_t kLowFrequencyLimit = 32;
     for (size_t k = 1; k < kLowFrequencyLimit; ++k) {
-      EXPECT_NEAR(k % 2 == 0 ? 5.f : 1.f, erle[k], 0.1);
+      EXPECT_NEAR(k % 2 == 0 ? 4.f : 1.f, erle[k], 0.1);
     }
     for (size_t k = kLowFrequencyLimit; k < erle.size() - 1; ++k) {
       EXPECT_NEAR(k % 2 == 0 ? 1.5f : 1.f, erle[k], 0.1);
@@ -178,7 +168,7 @@
 
 // Verifies the delay for a converged filter is correctly identified.
 TEST(AecState, ConvergedFilterDelay) {
-  constexpr int kFilterLength = 10;
+  constexpr int kFilterLengthBlocks = 10;
   EchoCanceller3Config config;
   AecState state(config);
   std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
@@ -194,25 +184,23 @@
   x.fill(0.f);
 
   std::vector<std::array<float, kFftLengthBy2Plus1>> frequency_response(
-      kFilterLength);
+      kFilterLengthBlocks);
+  for (auto& v : frequency_response) {
+    v.fill(0.01f);
+  }
 
   std::vector<float> impulse_response(
       GetTimeDomainLength(config.filter.main.length_blocks), 0.f);
 
   // Verify that the filter delay for a converged filter is properly identified.
-  for (int k = 0; k < kFilterLength; ++k) {
-    for (auto& v : frequency_response) {
-      v.fill(0.01f);
-    }
-    frequency_response[k].fill(100.f);
-    frequency_response[k][0] = 0.f;
+  for (int k = 0; k < kFilterLengthBlocks; ++k) {
+    std::fill(impulse_response.begin(), impulse_response.end(), 0.f);
+    impulse_response[k * kBlockSize + 1] = 1.f;
+
     state.HandleEchoPathChange(echo_path_variability);
     state.Update(delay_estimate, frequency_response, impulse_response, true,
-                 *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s,
-                 false);
-    if (k != (kFilterLength - 1)) {
-      EXPECT_EQ(k, state.FilterDelay());
-    }
+                 false, *render_delay_buffer->GetRenderBuffer(), E2_main, Y2,
+                 s);
   }
 }
 
diff --git a/modules/audio_processing/aec3/block_processor.cc b/modules/audio_processing/aec3/block_processor.cc
index 7f702ff..f954be3 100644
--- a/modules/audio_processing/aec3/block_processor.cc
+++ b/modules/audio_processing/aec3/block_processor.cc
@@ -43,6 +43,8 @@
 
   void GetMetrics(EchoControl::Metrics* metrics) const override;
 
+  void SetAudioBufferDelay(size_t delay_ms) override;
+
  private:
   static int instance_count_;
   std::unique_ptr<ApmDataDumper> data_dumper_;
@@ -57,6 +59,7 @@
   RenderDelayBuffer::BufferingEvent render_event_;
   size_t capture_call_counter_ = 0;
   rtc::Optional<DelayEstimate> estimated_delay_;
+  rtc::Optional<int> echo_remover_delay_;
   RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorImpl);
 };
 
@@ -158,7 +161,8 @@
   // Compute and and apply the render delay required to achieve proper signal
   // alignment.
   estimated_delay_ = delay_controller_->GetDelay(
-      render_buffer_->GetDownsampledRenderBuffer(), (*capture_block)[0]);
+      render_buffer_->GetDownsampledRenderBuffer(), render_buffer_->Delay(),
+      echo_remover_delay_, (*capture_block)[0]);
 
   if (estimated_delay_) {
     if (render_buffer_->CausalDelay(estimated_delay_->delay)) {
@@ -191,6 +195,10 @@
       echo_path_variability, capture_signal_saturation, estimated_delay_,
       render_buffer_->GetRenderBuffer(), capture_block);
 
+  // Check to see if a refined delay estimate has been obtained from the echo
+  // remover.
+  echo_remover_delay_ = echo_remover_->Delay();
+
   // Update the metrics.
   metrics_.UpdateCapture(false);
 
@@ -228,6 +236,10 @@
   metrics->delay_ms = delay ? static_cast<int>(*delay) * block_size_ms : 0;
 }
 
+void BlockProcessorImpl::SetAudioBufferDelay(size_t delay_ms) {
+  render_buffer_->SetAudioBufferDelay(delay_ms);
+}
+
 }  // namespace
 
 BlockProcessor* BlockProcessor::Create(const EchoCanceller3Config& config,
diff --git a/modules/audio_processing/aec3/block_processor.h b/modules/audio_processing/aec3/block_processor.h
index 8687bc2..a3967ea 100644
--- a/modules/audio_processing/aec3/block_processor.h
+++ b/modules/audio_processing/aec3/block_processor.h
@@ -42,6 +42,9 @@
   // Get current metrics.
   virtual void GetMetrics(EchoControl::Metrics* metrics) const = 0;
 
+  // Provides an optional external estimate of the audio buffer delay.
+  virtual void SetAudioBufferDelay(size_t delay_ms) = 0;
+
   // Processes a block of capture data.
   virtual void ProcessCapture(
       bool echo_path_gain_change,
diff --git a/modules/audio_processing/aec3/block_processor_unittest.cc b/modules/audio_processing/aec3/block_processor_unittest.cc
index 87b5da9..5906018 100644
--- a/modules/audio_processing/aec3/block_processor_unittest.cc
+++ b/modules/audio_processing/aec3/block_processor_unittest.cc
@@ -166,7 +166,7 @@
     EXPECT_CALL(*render_delay_buffer_mock, Delay())
         .Times(kNumBlocks)
         .WillRepeatedly(Return(0));
-    EXPECT_CALL(*render_delay_controller_mock, GetDelay(_, _))
+    EXPECT_CALL(*render_delay_controller_mock, GetDelay(_, _, _, _))
         .Times(kNumBlocks);
     EXPECT_CALL(*echo_remover_mock, ProcessCapture(_, _, _, _, _))
         .Times(kNumBlocks);
diff --git a/modules/audio_processing/aec3/coherence_gain.cc b/modules/audio_processing/aec3/coherence_gain.cc
new file mode 100644
index 0000000..ad33382
--- /dev/null
+++ b/modules/audio_processing/aec3/coherence_gain.cc
@@ -0,0 +1,257 @@
+/*
+ *  Copyright (c) 2018 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_processing/aec3/coherence_gain.h"
+
+#include <math.h>
+
+#include <algorithm>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+namespace {
+
+// Matlab code to produce table:
+// overDriveCurve = [sqrt(linspace(0,1,65))' + 1];
+// fprintf(1, '\t%.4f, %.4f, %.4f, %.4f, %.4f, %.4f,\n', overDriveCurve);
+const float kOverDriveCurve[kFftLengthBy2Plus1] = {
+    1.0000f, 1.1250f, 1.1768f, 1.2165f, 1.2500f, 1.2795f, 1.3062f, 1.3307f,
+    1.3536f, 1.3750f, 1.3953f, 1.4146f, 1.4330f, 1.4507f, 1.4677f, 1.4841f,
+    1.5000f, 1.5154f, 1.5303f, 1.5449f, 1.5590f, 1.5728f, 1.5863f, 1.5995f,
+    1.6124f, 1.6250f, 1.6374f, 1.6495f, 1.6614f, 1.6731f, 1.6847f, 1.6960f,
+    1.7071f, 1.7181f, 1.7289f, 1.7395f, 1.7500f, 1.7603f, 1.7706f, 1.7806f,
+    1.7906f, 1.8004f, 1.8101f, 1.8197f, 1.8292f, 1.8385f, 1.8478f, 1.8570f,
+    1.8660f, 1.8750f, 1.8839f, 1.8927f, 1.9014f, 1.9100f, 1.9186f, 1.9270f,
+    1.9354f, 1.9437f, 1.9520f, 1.9601f, 1.9682f, 1.9763f, 1.9843f, 1.9922f,
+    2.0000f};
+
+// Matlab code to produce table:
+// weightCurve = [0 ; 0.3 * sqrt(linspace(0,1,64))' + 0.1];
+// fprintf(1, '\t%.4f, %.4f, %.4f, %.4f, %.4f, %.4f,\n', weightCurve);
+const float kWeightCurve[kFftLengthBy2Plus1] = {
+    0.0000f, 0.1000f, 0.1378f, 0.1535f, 0.1655f, 0.1756f, 0.1845f, 0.1926f,
+    0.2000f, 0.2069f, 0.2134f, 0.2195f, 0.2254f, 0.2309f, 0.2363f, 0.2414f,
+    0.2464f, 0.2512f, 0.2558f, 0.2604f, 0.2648f, 0.2690f, 0.2732f, 0.2773f,
+    0.2813f, 0.2852f, 0.2890f, 0.2927f, 0.2964f, 0.3000f, 0.3035f, 0.3070f,
+    0.3104f, 0.3138f, 0.3171f, 0.3204f, 0.3236f, 0.3268f, 0.3299f, 0.3330f,
+    0.3360f, 0.3390f, 0.3420f, 0.3449f, 0.3478f, 0.3507f, 0.3535f, 0.3563f,
+    0.3591f, 0.3619f, 0.3646f, 0.3673f, 0.3699f, 0.3726f, 0.3752f, 0.3777f,
+    0.3803f, 0.3828f, 0.3854f, 0.3878f, 0.3903f, 0.3928f, 0.3952f, 0.3976f,
+    0.4000f};
+
+int CmpFloat(const void* a, const void* b) {
+  const float* da = static_cast<const float*>(a);
+  const float* db = static_cast<const float*>(b);
+  return (*da > *db) - (*da < *db);
+}
+
+}  // namespace
+
+CoherenceGain::CoherenceGain(int sample_rate_hz, size_t num_bands_to_compute)
+    : num_bands_to_compute_(num_bands_to_compute),
+      sample_rate_scaler_(sample_rate_hz >= 16000 ? 2 : 1) {
+  spectra_.Cye.Clear();
+  spectra_.Cxy.Clear();
+  spectra_.Pe.fill(0.f);
+  // Initialize to 1 in order to prevent numerical instability in the first
+  // block.
+  spectra_.Py.fill(1.f);
+  spectra_.Px.fill(1.f);
+}
+
+CoherenceGain::~CoherenceGain() = default;
+
+void CoherenceGain::ComputeGain(const FftData& E,
+                                const FftData& X,
+                                const FftData& Y,
+                                rtc::ArrayView<float> gain) {
+  std::array<float, kFftLengthBy2Plus1> coherence_ye;
+  std::array<float, kFftLengthBy2Plus1> coherence_xy;
+
+  UpdateCoherenceSpectra(E, X, Y);
+  ComputeCoherence(coherence_ye, coherence_xy);
+  FormSuppressionGain(coherence_ye, coherence_xy, gain);
+}
+
+// Updates the following smoothed Power Spectral Densities (PSD):
+//  - sd  : near-end
+//  - se  : residual echo
+//  - sx  : far-end
+//  - sde : cross-PSD of near-end and residual echo
+//  - sxd : cross-PSD of near-end and far-end
+//
+void CoherenceGain::UpdateCoherenceSpectra(const FftData& E,
+                                           const FftData& X,
+                                           const FftData& Y) {
+  const float s = sample_rate_scaler_ == 1 ? 0.9f : 0.92f;
+  const float one_minus_s = 1.f - s;
+  auto& c = spectra_;
+
+  for (size_t i = 0; i < c.Py.size(); i++) {
+    c.Py[i] =
+        s * c.Py[i] + one_minus_s * (Y.re[i] * Y.re[i] + Y.im[i] * Y.im[i]);
+    c.Pe[i] =
+        s * c.Pe[i] + one_minus_s * (E.re[i] * E.re[i] + E.im[i] * E.im[i]);
+    // We threshold here to protect against the ill-effects of a zero farend.
+    // The threshold is not arbitrarily chosen, but balances protection and
+    // adverse interaction with the algorithm's tuning.
+
+    // Threshold to protect against the ill-effects of a zero far-end.
+    c.Px[i] =
+        s * c.Px[i] +
+        one_minus_s * std::max(X.re[i] * X.re[i] + X.im[i] * X.im[i], 15.f);
+
+    c.Cye.re[i] =
+        s * c.Cye.re[i] + one_minus_s * (Y.re[i] * E.re[i] + Y.im[i] * E.im[i]);
+    c.Cye.im[i] =
+        s * c.Cye.im[i] + one_minus_s * (Y.re[i] * E.im[i] - Y.im[i] * E.re[i]);
+
+    c.Cxy.re[i] =
+        s * c.Cxy.re[i] + one_minus_s * (Y.re[i] * X.re[i] + Y.im[i] * X.im[i]);
+    c.Cxy.im[i] =
+        s * c.Cxy.im[i] + one_minus_s * (Y.re[i] * X.im[i] - Y.im[i] * X.re[i]);
+  }
+}
+
+void CoherenceGain::FormSuppressionGain(
+    rtc::ArrayView<const float> coherence_ye,
+    rtc::ArrayView<const float> coherence_xy,
+    rtc::ArrayView<float> gain) {
+  RTC_DCHECK_EQ(kFftLengthBy2Plus1, coherence_ye.size());
+  RTC_DCHECK_EQ(kFftLengthBy2Plus1, coherence_xy.size());
+  RTC_DCHECK_EQ(kFftLengthBy2Plus1, gain.size());
+  constexpr int kPrefBandSize = 24;
+  auto& gs = gain_state_;
+  std::array<float, kPrefBandSize> h_nl_pref;
+  float h_nl_fb = 0;
+  float h_nl_fb_low = 0;
+  const int pref_band_size = kPrefBandSize / sample_rate_scaler_;
+  const int min_pref_band = 4 / sample_rate_scaler_;
+
+  float h_nl_de_avg = 0.f;
+  float h_nl_xd_avg = 0.f;
+  for (int i = min_pref_band; i < pref_band_size + min_pref_band; ++i) {
+    h_nl_xd_avg += coherence_xy[i];
+    h_nl_de_avg += coherence_ye[i];
+  }
+  h_nl_xd_avg /= pref_band_size;
+  h_nl_xd_avg = 1 - h_nl_xd_avg;
+  h_nl_de_avg /= pref_band_size;
+
+  if (h_nl_xd_avg < 0.75f && h_nl_xd_avg < gs.h_nl_xd_avg_min) {
+    gs.h_nl_xd_avg_min = h_nl_xd_avg;
+  }
+
+  if (h_nl_de_avg > 0.98f && h_nl_xd_avg > 0.9f) {
+    gs.near_state = true;
+  } else if (h_nl_de_avg < 0.95f || h_nl_xd_avg < 0.8f) {
+    gs.near_state = false;
+  }
+
+  std::array<float, kFftLengthBy2Plus1> h_nl;
+  if (gs.h_nl_xd_avg_min == 1) {
+    gs.overdrive = 15.f;
+
+    if (gs.near_state) {
+      std::copy(coherence_ye.begin(), coherence_ye.end(), h_nl.begin());
+      h_nl_fb = h_nl_de_avg;
+      h_nl_fb_low = h_nl_de_avg;
+    } else {
+      for (size_t i = 0; i < h_nl.size(); ++i) {
+        h_nl[i] = 1 - coherence_xy[i];
+        h_nl[i] = std::max(h_nl[i], 0.f);
+      }
+      h_nl_fb = h_nl_xd_avg;
+      h_nl_fb_low = h_nl_xd_avg;
+    }
+  } else {
+    if (gs.near_state) {
+      std::copy(coherence_ye.begin(), coherence_ye.end(), h_nl.begin());
+      h_nl_fb = h_nl_de_avg;
+      h_nl_fb_low = h_nl_de_avg;
+    } else {
+      for (size_t i = 0; i < h_nl.size(); ++i) {
+        h_nl[i] = std::min(coherence_ye[i], 1 - coherence_xy[i]);
+        h_nl[i] = std::max(h_nl[i], 0.f);
+      }
+
+      // Select an order statistic from the preferred bands.
+      // TODO(peah): Using quicksort now, but a selection algorithm may be
+      // preferred.
+      std::copy(h_nl.begin() + min_pref_band,
+                h_nl.begin() + min_pref_band + pref_band_size,
+                h_nl_pref.begin());
+      std::qsort(h_nl_pref.data(), pref_band_size, sizeof(float), CmpFloat);
+
+      constexpr float kPrefBandQuant = 0.75f;
+      h_nl_fb = h_nl_pref[static_cast<int>(
+          floor(kPrefBandQuant * (pref_band_size - 1)))];
+      constexpr float kPrefBandQuantLow = 0.5f;
+      h_nl_fb_low = h_nl_pref[static_cast<int>(
+          floor(kPrefBandQuantLow * (pref_band_size - 1)))];
+    }
+  }
+
+  // Track the local filter minimum to determine suppression overdrive.
+  if (h_nl_fb_low < 0.6f && h_nl_fb_low < gs.h_nl_fb_local_min) {
+    gs.h_nl_fb_local_min = h_nl_fb_low;
+    gs.h_nl_fb_min = h_nl_fb_low;
+    gs.h_nl_new_min = 1;
+    gs.h_nl_min_ctr = 0;
+  }
+  gs.h_nl_fb_local_min =
+      std::min(gs.h_nl_fb_local_min + 0.0008f / sample_rate_scaler_, 1.f);
+  gs.h_nl_xd_avg_min =
+      std::min(gs.h_nl_xd_avg_min + 0.0006f / sample_rate_scaler_, 1.f);
+
+  if (gs.h_nl_new_min == 1) {
+    ++gs.h_nl_min_ctr;
+  }
+  if (gs.h_nl_min_ctr == 2) {
+    gs.h_nl_new_min = 0;
+    gs.h_nl_min_ctr = 0;
+    constexpr float epsilon = 1e-10f;
+    gs.overdrive = std::max(
+        -18.4f / static_cast<float>(log(gs.h_nl_fb_min + epsilon) + epsilon),
+        15.f);
+  }
+
+  // Smooth the overdrive.
+  if (gs.overdrive < gs.overdrive_scaling) {
+    gs.overdrive_scaling = 0.99f * gs.overdrive_scaling + 0.01f * gs.overdrive;
+  } else {
+    gs.overdrive_scaling = 0.9f * gs.overdrive_scaling + 0.1f * gs.overdrive;
+  }
+
+  // Apply the overdrive.
+  RTC_DCHECK_LE(num_bands_to_compute_, gain.size());
+  for (size_t i = 0; i < num_bands_to_compute_; ++i) {
+    if (h_nl[i] > h_nl_fb) {
+      h_nl[i] = kWeightCurve[i] * h_nl_fb + (1 - kWeightCurve[i]) * h_nl[i];
+    }
+    gain[i] = powf(h_nl[i], gs.overdrive_scaling * kOverDriveCurve[i]);
+  }
+}
+
+void CoherenceGain::ComputeCoherence(rtc::ArrayView<float> coherence_ye,
+                                     rtc::ArrayView<float> coherence_xy) const {
+  const auto& c = spectra_;
+  constexpr float epsilon = 1e-10f;
+  for (size_t i = 0; i < coherence_ye.size(); ++i) {
+    coherence_ye[i] = (c.Cye.re[i] * c.Cye.re[i] + c.Cye.im[i] * c.Cye.im[i]) /
+                      (c.Py[i] * c.Pe[i] + epsilon);
+    coherence_xy[i] = (c.Cxy.re[i] * c.Cxy.re[i] + c.Cxy.im[i] * c.Cxy.im[i]) /
+                      (c.Px[i] * c.Py[i] + epsilon);
+  }
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/aec3/coherence_gain.h b/modules/audio_processing/aec3/coherence_gain.h
new file mode 100644
index 0000000..b6e22fd
--- /dev/null
+++ b/modules/audio_processing/aec3/coherence_gain.h
@@ -0,0 +1,77 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AEC3_COHERENCE_GAIN_H_
+#define MODULES_AUDIO_PROCESSING_AEC3_COHERENCE_GAIN_H_
+
+#include <array>
+
+#include "modules/audio_processing/aec3/aec3_common.h"
+#include "modules/audio_processing/aec3/aec3_fft.h"
+#include "rtc_base/constructormagic.h"
+
+namespace webrtc {
+
+// Class for computing an echo suppression gain based on the coherence measure.
+class CoherenceGain {
+ public:
+  CoherenceGain(int sample_rate_hz, size_t num_bands_to_compute);
+  ~CoherenceGain();
+
+  // Computes the gain based on the FFTs of the filter error output signal, the
+  // render signal and the capture signal.
+  void ComputeGain(const FftData& E,
+                   const FftData& X,
+                   const FftData& Y,
+                   rtc::ArrayView<float> gain);
+
+ private:
+  struct {
+    FftData Cye;
+    FftData Cxy;
+    std::array<float, kFftLengthBy2Plus1> Px;
+    std::array<float, kFftLengthBy2Plus1> Py;
+    std::array<float, kFftLengthBy2Plus1> Pe;
+  } spectra_;
+
+  struct {
+    float h_nl_fb_min = 1;
+    float h_nl_fb_local_min = 1;
+    float h_nl_xd_avg_min = 1.f;
+    int h_nl_new_min = 0;
+    float h_nl_min_ctr = 0;
+    float overdrive = 2;
+    float overdrive_scaling = 2;
+    bool near_state = false;
+  } gain_state_;
+
+  const Aec3Fft fft_;
+  const size_t num_bands_to_compute_;
+  const int sample_rate_scaler_;
+
+  // Updates the spectral estimates used for the coherence computation.
+  void UpdateCoherenceSpectra(const FftData& E,
+                              const FftData& X,
+                              const FftData& Y);
+
+  // Compute the suppression gain based on the coherence.
+  void FormSuppressionGain(rtc::ArrayView<const float> coherence_ye,
+                           rtc::ArrayView<const float> coherence_xy,
+                           rtc::ArrayView<float> h_nl);
+
+  // Compute the coherence.
+  void ComputeCoherence(rtc::ArrayView<float> coherence_ye,
+                        rtc::ArrayView<float> coherence_xy) const;
+
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(CoherenceGain);
+};
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AEC3_COHERENCE_GAIN_H_
diff --git a/modules/audio_processing/aec3/echo_audibility.cc b/modules/audio_processing/aec3/echo_audibility.cc
new file mode 100644
index 0000000..68d2dd1
--- /dev/null
+++ b/modules/audio_processing/aec3/echo_audibility.cc
@@ -0,0 +1,105 @@
+/*
+ *  Copyright (c) 2018 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_processing/aec3/echo_audibility.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "modules/audio_processing/aec3/aec3_common.h"
+#include "modules/audio_processing/aec3/matrix_buffer.h"
+#include "modules/audio_processing/aec3/stationarity_estimator.h"
+#include "modules/audio_processing/aec3/vector_buffer.h"
+
+namespace webrtc {
+
+EchoAudibility::EchoAudibility() {
+  Reset();
+}
+
+EchoAudibility::~EchoAudibility() = default;
+
+void EchoAudibility::Update(const RenderBuffer& render_buffer,
+                            int delay_blocks,
+                            bool external_delay_seen) {
+  UpdateRenderNoiseEstimator(render_buffer.GetSpectrumBuffer(),
+                             render_buffer.GetBlockBuffer(),
+                             external_delay_seen);
+
+  if (external_delay_seen) {
+    UpdateRenderStationarityFlags(render_buffer, delay_blocks);
+  }
+}
+
+void EchoAudibility::Reset() {
+  render_stationarity_.Reset();
+  non_zero_render_seen_ = false;
+  render_spectrum_write_prev_ = rtc::nullopt;
+}
+
+void EchoAudibility::UpdateRenderStationarityFlags(
+    const RenderBuffer& render_buffer,
+    int delay_blocks) {
+  const VectorBuffer& spectrum_buffer = render_buffer.GetSpectrumBuffer();
+  int idx_at_delay =
+      spectrum_buffer.OffsetIndex(spectrum_buffer.read, delay_blocks);
+
+  int num_lookahead = render_buffer.Headroom() - delay_blocks + 1;
+  num_lookahead = std::max(0, num_lookahead);
+
+  render_stationarity_.UpdateStationarityFlags(spectrum_buffer, idx_at_delay,
+                                               num_lookahead);
+}
+
+void EchoAudibility::UpdateRenderNoiseEstimator(
+    const VectorBuffer& spectrum_buffer,
+    const MatrixBuffer& block_buffer,
+    bool external_delay_seen) {
+  if (!render_spectrum_write_prev_) {
+    render_spectrum_write_prev_ = spectrum_buffer.write;
+    render_block_write_prev_ = block_buffer.write;
+    return;
+  }
+  int render_spectrum_write_current = spectrum_buffer.write;
+  if (!non_zero_render_seen_ && !external_delay_seen) {
+    non_zero_render_seen_ = !IsRenderTooLow(block_buffer);
+  }
+  if (non_zero_render_seen_) {
+    for (int idx = render_spectrum_write_prev_.value();
+         idx != render_spectrum_write_current;
+         idx = spectrum_buffer.DecIndex(idx)) {
+      render_stationarity_.UpdateNoiseEstimator(spectrum_buffer.buffer[idx]);
+    }
+  }
+  render_spectrum_write_prev_ = render_spectrum_write_current;
+}
+
+bool EchoAudibility::IsRenderTooLow(const MatrixBuffer& block_buffer) {
+  bool too_low = false;
+  const int render_block_write_current = block_buffer.write;
+  if (render_block_write_current == render_block_write_prev_) {
+    too_low = true;
+  } else {
+    for (int idx = render_block_write_prev_; idx != render_block_write_current;
+         idx = block_buffer.IncIndex(idx)) {
+      auto block = block_buffer.buffer[idx][0];
+      auto r = std::minmax_element(block.cbegin(), block.cend());
+      float max_abs = std::max(std::fabs(*r.first), std::fabs(*r.second));
+      if (max_abs < 10) {
+        too_low = true;  // Discards all blocks if one of them is too low.
+        break;
+      }
+    }
+  }
+  render_block_write_prev_ = render_block_write_current;
+  return too_low;
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/aec3/echo_audibility.h b/modules/audio_processing/aec3/echo_audibility.h
new file mode 100644
index 0000000..038951e
--- /dev/null
+++ b/modules/audio_processing/aec3/echo_audibility.h
@@ -0,0 +1,80 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AEC3_ECHO_AUDIBILITY_H_
+#define MODULES_AUDIO_PROCESSING_AEC3_ECHO_AUDIBILITY_H_
+
+#include <algorithm>
+#include <array>
+#include <limits>
+#include <memory>
+#include <vector>
+
+#include "api/array_view.h"
+#include "api/optional.h"
+#include "modules/audio_processing/aec3/matrix_buffer.h"
+#include "modules/audio_processing/aec3/render_buffer.h"
+#include "modules/audio_processing/aec3/stationarity_estimator.h"
+#include "modules/audio_processing/aec3/vector_buffer.h"
+#include "rtc_base/constructormagic.h"
+
+namespace webrtc {
+
+class ApmDataDumper;
+
+class EchoAudibility {
+ public:
+  EchoAudibility();
+  ~EchoAudibility();
+
+  // Feed new render data to the echo audibility estimator.
+  void Update(const RenderBuffer& render_buffer,
+              int delay_blocks,
+              bool external_delay_seen);
+
+  // Get the residual echo scaling.
+  void GetResidualEchoScaling(rtc::ArrayView<float> residual_scaling) const {
+    for (size_t band = 0; band < residual_scaling.size(); ++band) {
+      if (render_stationarity_.IsBandStationary(band)) {
+        residual_scaling[band] = 0.f;
+      } else {
+        residual_scaling[band] = 1.0f;
+      }
+    }
+  }
+
+ private:
+  // Reset the EchoAudibility class.
+  void Reset();
+
+  // Updates the render stationarity flags for the current frame.
+  void UpdateRenderStationarityFlags(const RenderBuffer& render_buffer,
+                                     int delay_blocks);
+
+  // Updates the noise estimator with the new render data since the previous
+  // call to this method.
+  void UpdateRenderNoiseEstimator(const VectorBuffer& spectrum_buffer,
+                                  const MatrixBuffer& block_buffer,
+                                  bool external_delay_seen);
+
+  // Returns a bool being true if the render signal contains just close to zero
+  // values.
+  bool IsRenderTooLow(const MatrixBuffer& block_buffer);
+
+  rtc::Optional<int> render_spectrum_write_prev_;
+  int render_block_write_prev_;
+  bool non_zero_render_seen_;
+  StationarityEstimator render_stationarity_;
+  RTC_DISALLOW_COPY_AND_ASSIGN(EchoAudibility);
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AEC3_ECHO_AUDIBILITY_H_
diff --git a/modules/audio_processing/aec3/echo_canceller3.cc b/modules/audio_processing/aec3/echo_canceller3.cc
index f0cbbc8..765e4de 100644
--- a/modules/audio_processing/aec3/echo_canceller3.cc
+++ b/modules/audio_processing/aec3/echo_canceller3.cc
@@ -9,10 +9,9 @@
  */
 #include "modules/audio_processing/aec3/echo_canceller3.h"
 
-#include <sstream>
-
 #include "modules/audio_processing/logging/apm_data_dumper.h"
 #include "rtc_base/atomicops.h"
+#include "rtc_base/logging.h"
 
 namespace webrtc {
 
@@ -29,6 +28,43 @@
   return false;
 }
 
+// Method for adjusting config parameter dependencies..
+EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config) {
+  EchoCanceller3Config adjusted_cfg = config;
+
+  // Use customized parameters when the system has clock-drift.
+  if (config.echo_removal_control.has_clock_drift) {
+    RTC_LOG(LS_WARNING)
+        << "Customizing parameters to work well for the clock-drift case.";
+    if (config.ep_strength.bounded_erl) {
+      adjusted_cfg.ep_strength.default_len = 0.85f;
+      adjusted_cfg.ep_strength.lf = 0.01f;
+      adjusted_cfg.ep_strength.mf = 0.01f;
+      adjusted_cfg.ep_strength.hf = 0.01f;
+      adjusted_cfg.echo_model.render_pre_window_size = 1;
+      adjusted_cfg.echo_model.render_post_window_size = 1;
+      adjusted_cfg.echo_model.nonlinear_hold = 3;
+      adjusted_cfg.echo_model.nonlinear_release = 0.001f;
+    } else {
+      adjusted_cfg.ep_strength.bounded_erl = true;
+      adjusted_cfg.delay.down_sampling_factor = 2;
+      adjusted_cfg.ep_strength.default_len = 0.8f;
+      adjusted_cfg.ep_strength.lf = 0.01f;
+      adjusted_cfg.ep_strength.mf = 0.01f;
+      adjusted_cfg.ep_strength.hf = 0.01f;
+      adjusted_cfg.filter.main = {30, 0.1f, 0.8f, 0.001f, 20075344.f};
+      adjusted_cfg.filter.shadow = {30, 0.7f, 20075344.f};
+      adjusted_cfg.filter.main_initial = {30, 0.1f, 1.5f, 0.001f, 20075344.f};
+      adjusted_cfg.filter.shadow_initial = {30, 0.9f, 20075344.f};
+      adjusted_cfg.echo_model.render_pre_window_size = 2;
+      adjusted_cfg.echo_model.render_post_window_size = 2;
+      adjusted_cfg.echo_model.nonlinear_hold = 3;
+      adjusted_cfg.echo_model.nonlinear_release = 0.6f;
+    }
+  }
+  return adjusted_cfg;
+}
+
 void FillSubFrameView(AudioBuffer* frame,
                       size_t sub_frame_index,
                       std::vector<rtc::ArrayView<float>>* sub_frame_view) {
@@ -209,11 +245,12 @@
 EchoCanceller3::EchoCanceller3(const EchoCanceller3Config& config,
                                int sample_rate_hz,
                                bool use_highpass_filter)
-    : EchoCanceller3(config,
-                     sample_rate_hz,
-                     use_highpass_filter,
-                     std::unique_ptr<BlockProcessor>(
-                         BlockProcessor::Create(config, sample_rate_hz))) {}
+    : EchoCanceller3(
+          AdjustConfig(config),
+          sample_rate_hz,
+          use_highpass_filter,
+          std::unique_ptr<BlockProcessor>(
+              BlockProcessor::Create(AdjustConfig(config), sample_rate_hz))) {}
 EchoCanceller3::EchoCanceller3(const EchoCanceller3Config& config,
                                int sample_rate_hz,
                                bool use_highpass_filter,
@@ -338,6 +375,11 @@
   return metrics;
 }
 
+void EchoCanceller3::SetAudioBufferDelay(size_t delay_ms) {
+  RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_);
+  block_processor_->SetAudioBufferDelay(delay_ms);
+}
+
 void EchoCanceller3::EmptyRenderQueue() {
   RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_);
   bool frame_to_buffer =
diff --git a/modules/audio_processing/aec3/echo_canceller3.h b/modules/audio_processing/aec3/echo_canceller3.h
index 8658814..b66b8b1 100644
--- a/modules/audio_processing/aec3/echo_canceller3.h
+++ b/modules/audio_processing/aec3/echo_canceller3.h
@@ -82,6 +82,8 @@
   void ProcessCapture(AudioBuffer* capture, bool level_change) override;
   // Collect current metrics from the echo canceller.
   Metrics GetMetrics() const override;
+  // Provides an optional external estimate of the audio buffer delay.
+  void SetAudioBufferDelay(size_t delay_ms) override;
 
   // Signals whether an external detector has detected echo leakage from the
   // echo canceller.
diff --git a/modules/audio_processing/aec3/echo_canceller3_unittest.cc b/modules/audio_processing/aec3/echo_canceller3_unittest.cc
index d4ad4f6..f652642 100644
--- a/modules/audio_processing/aec3/echo_canceller3_unittest.cc
+++ b/modules/audio_processing/aec3/echo_canceller3_unittest.cc
@@ -104,6 +104,8 @@
 
   void GetMetrics(EchoControl::Metrics* metrics) const override {}
 
+  void SetAudioBufferDelay(size_t delay_ms) override{};
+
  private:
   RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(CaptureTransportVerificationProcessor);
 };
@@ -132,6 +134,8 @@
 
   void GetMetrics(EchoControl::Metrics* metrics) const override {}
 
+  void SetAudioBufferDelay(size_t delay_ms) override{};
+
  private:
   std::deque<std::vector<std::vector<float>>> received_render_blocks_;
   RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderTransportVerificationProcessor);
diff --git a/modules/audio_processing/aec3/echo_remover.cc b/modules/audio_processing/aec3/echo_remover.cc
index da1fa4b..96887fe 100644
--- a/modules/audio_processing/aec3/echo_remover.cc
+++ b/modules/audio_processing/aec3/echo_remover.cc
@@ -22,7 +22,6 @@
 #include "modules/audio_processing/aec3/echo_path_variability.h"
 #include "modules/audio_processing/aec3/echo_remover_metrics.h"
 #include "modules/audio_processing/aec3/fft_data.h"
-#include "modules/audio_processing/aec3/output_selector.h"
 #include "modules/audio_processing/aec3/render_buffer.h"
 #include "modules/audio_processing/aec3/render_delay_buffer.h"
 #include "modules/audio_processing/aec3/residual_echo_estimator.h"
@@ -46,11 +45,20 @@
   }
 }
 
+// Computes a windowed (square root Hanning) padded FFT and updates the related
+// memory.
+void WindowedPaddedFft(const Aec3Fft& fft,
+                       rtc::ArrayView<const float> v,
+                       rtc::ArrayView<float> v_old,
+                       FftData* V) {
+  fft.PaddedFft(v, v_old, Aec3Fft::Window::kSqrtHanning, V);
+  std::copy(v.begin(), v.end(), v_old.begin());
+}
+
 // Class for removing the echo from the capture signal.
 class EchoRemoverImpl final : public EchoRemover {
  public:
-  explicit EchoRemoverImpl(const EchoCanceller3Config& config,
-                           int sample_rate_hz);
+  EchoRemoverImpl(const EchoCanceller3Config& config, int sample_rate_hz);
   ~EchoRemoverImpl() override;
 
   void GetMetrics(EchoControl::Metrics* metrics) const override;
@@ -60,10 +68,15 @@
   // signal.
   void ProcessCapture(const EchoPathVariability& echo_path_variability,
                       bool capture_signal_saturation,
-                      const rtc::Optional<DelayEstimate>& delay_estimate,
+                      const rtc::Optional<DelayEstimate>& external_delay,
                       RenderBuffer* render_buffer,
                       std::vector<std::vector<float>>* capture) override;
 
+  // Returns the internal delay estimate in blocks.
+  rtc::Optional<int> Delay() const override {
+    return aec_state_.InternalDelay();
+  }
+
   // Updates the status on whether echo leakage is detected in the output of the
   // echo remover.
   void UpdateEchoLeakageStatus(bool leakage_detected) override {
@@ -82,12 +95,14 @@
   ComfortNoiseGenerator cng_;
   SuppressionFilter suppression_filter_;
   RenderSignalAnalyzer render_signal_analyzer_;
-  OutputSelector output_selector_;
   ResidualEchoEstimator residual_echo_estimator_;
   bool echo_leakage_detected_ = false;
   AecState aec_state_;
   EchoRemoverMetrics metrics_;
   bool initial_state_ = true;
+  std::array<float, kFftLengthBy2> e_old_;
+  std::array<float, kFftLengthBy2> x_old_;
+  std::array<float, kFftLengthBy2> y_old_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(EchoRemoverImpl);
 };
@@ -103,13 +118,16 @@
       optimization_(DetectOptimization()),
       sample_rate_hz_(sample_rate_hz),
       subtractor_(config, data_dumper_.get(), optimization_),
-      suppression_gain_(config_, optimization_),
+      suppression_gain_(config_, optimization_, sample_rate_hz),
       cng_(optimization_),
       suppression_filter_(sample_rate_hz_),
       render_signal_analyzer_(config_),
       residual_echo_estimator_(config_),
       aec_state_(config_) {
   RTC_DCHECK(ValidFullBandRate(sample_rate_hz));
+  x_old_.fill(0.f);
+  y_old_.fill(0.f);
+  e_old_.fill(0.f);
 }
 
 EchoRemoverImpl::~EchoRemoverImpl() = default;
@@ -124,7 +142,7 @@
 void EchoRemoverImpl::ProcessCapture(
     const EchoPathVariability& echo_path_variability,
     bool capture_signal_saturation,
-    const rtc::Optional<DelayEstimate>& delay_estimate,
+    const rtc::Optional<DelayEstimate>& external_delay,
     RenderBuffer* render_buffer,
     std::vector<std::vector<float>>* capture) {
   const std::vector<std::vector<float>>& x = render_buffer->Block(0);
@@ -155,21 +173,20 @@
   }
 
   std::array<float, kFftLengthBy2Plus1> Y2;
+  std::array<float, kFftLengthBy2Plus1> E2;
   std::array<float, kFftLengthBy2Plus1> R2;
   std::array<float, kFftLengthBy2Plus1> S2_linear;
   std::array<float, kFftLengthBy2Plus1> G;
   float high_bands_gain;
   FftData Y;
+  FftData E;
   FftData comfort_noise;
   FftData high_band_comfort_noise;
   SubtractorOutput subtractor_output;
-  FftData& E_main_nonwindowed = subtractor_output.E_main_nonwindowed;
-  auto& E2_main = subtractor_output.E2_main_nonwindowed;
-  auto& E2_shadow = subtractor_output.E2_shadow;
-  auto& e_main = subtractor_output.e_main;
 
   // Analyze the render signal.
-  render_signal_analyzer_.Update(*render_buffer, aec_state_.FilterDelay());
+  render_signal_analyzer_.Update(*render_buffer,
+                                 aec_state_.FilterDelayBlocks());
 
   // Perform linear echo cancellation.
   if (initial_state_ && !aec_state_.InitialState()) {
@@ -177,27 +194,46 @@
     suppression_gain_.SetInitialState(false);
     initial_state_ = false;
   }
+
+  // If the delay is known, use the echo subtractor.
   subtractor_.Process(*render_buffer, y0, render_signal_analyzer_, aec_state_,
                       &subtractor_output);
+  const auto& e = subtractor_output.e_main;
 
   // Compute spectra.
-  // fft_.ZeroPaddedFft(y0, Aec3Fft::Window::kHanning, &Y);
-  fft_.ZeroPaddedFft(y0, Aec3Fft::Window::kRectangular, &Y);
-  LinearEchoPower(E_main_nonwindowed, Y, &S2_linear);
+  WindowedPaddedFft(fft_, y0, y_old_, &Y);
+  WindowedPaddedFft(fft_, e, e_old_, &E);
+  LinearEchoPower(E, Y, &S2_linear);
   Y.Spectrum(optimization_, Y2);
+  E.Spectrum(optimization_, E2);
 
   // Update the AEC state information.
-  aec_state_.Update(delay_estimate, subtractor_.FilterFrequencyResponse(),
+  aec_state_.Update(external_delay, subtractor_.FilterFrequencyResponse(),
                     subtractor_.FilterImpulseResponse(),
-                    subtractor_.ConvergedFilter(), *render_buffer, E2_main, Y2,
-                    subtractor_output.s_main, echo_leakage_detected_);
+                    subtractor_.ConvergedFilter(), subtractor_.DivergedFilter(),
+                    *render_buffer, E2, Y2, subtractor_output.s_main);
+
+  // Compute spectra.
+  const bool suppression_gain_uses_ffts =
+      config_.suppressor.bands_with_reliable_coherence > 0;
+  FftData X;
+  if (suppression_gain_uses_ffts) {
+    auto& x_aligned = render_buffer->Block(-aec_state_.FilterDelayBlocks())[0];
+    WindowedPaddedFft(fft_, x_aligned, x_old_, &X);
+  } else {
+    X.Clear();
+  }
 
   // Choose the linear output.
-  output_selector_.FormLinearOutput(!aec_state_.TransparentMode(), e_main, y0);
+  data_dumper_->DumpWav("aec3_output_linear2", kBlockSize, &e[0],
+                        LowestBandRate(sample_rate_hz_), 1);
+  if (aec_state_.UseLinearFilterOutput()) {
+    std::copy(e.begin(), e.end(), y0.begin());
+  }
+  const auto& Y_fft = aec_state_.UseLinearFilterOutput() ? E : Y;
+
   data_dumper_->DumpWav("aec3_output_linear", kBlockSize, &y0[0],
                         LowestBandRate(sample_rate_hz_), 1);
-  data_dumper_->DumpRaw("aec3_output_linear", y0);
-  const auto& E2 = output_selector_.UseSubtractorOutput() ? E2_main : Y2;
 
   // Estimate the residual echo power.
   residual_echo_estimator_.Estimate(aec_state_, *render_buffer, S2_linear, Y2,
@@ -206,19 +242,19 @@
   // Estimate the comfort noise.
   cng_.Compute(aec_state_, Y2, &comfort_noise, &high_band_comfort_noise);
 
-  // A choose and apply echo suppression gain.
-  suppression_gain_.GetGain(E2, R2, cng_.NoiseSpectrum(),
+
+
+  // Compute and apply the suppression gain.
+  suppression_gain_.GetGain(E2, R2, cng_.NoiseSpectrum(), E, X, Y,
                             render_signal_analyzer_, aec_state_, x,
                             &high_bands_gain, &G);
+
   suppression_filter_.ApplyGain(comfort_noise, high_band_comfort_noise, G,
-                                high_bands_gain, y);
+                                high_bands_gain, Y_fft, y);
 
   // Update the metrics.
   metrics_.Update(aec_state_, cng_.NoiseSpectrum(), G);
 
-  // Update the aec state with the aec output characteristics.
-  aec_state_.UpdateWithOutput(y0);
-
   // Debug outputs for the purpose of development and analysis.
   data_dumper_->DumpWav("aec3_echo_estimate", kBlockSize,
                         &subtractor_output.s_main[0],
@@ -232,19 +268,14 @@
                         rtc::ArrayView<const float>(&y0[0], kBlockSize),
                         LowestBandRate(sample_rate_hz_), 1);
   data_dumper_->DumpRaw("aec3_using_subtractor_output",
-                        output_selector_.UseSubtractorOutput() ? 1 : 0);
+                        aec_state_.UseLinearFilterOutput() ? 1 : 0);
   data_dumper_->DumpRaw("aec3_E2", E2);
-  data_dumper_->DumpRaw("aec3_E2_main", E2_main);
-  data_dumper_->DumpRaw("aec3_E2_shadow", E2_shadow);
   data_dumper_->DumpRaw("aec3_S2_linear", S2_linear);
   data_dumper_->DumpRaw("aec3_Y2", Y2);
-  data_dumper_->DumpRaw("aec3_X2", render_buffer->Spectrum(0));
+  data_dumper_->DumpRaw(
+      "aec3_X2", render_buffer->Spectrum(aec_state_.FilterDelayBlocks()));
   data_dumper_->DumpRaw("aec3_R2", R2);
-  data_dumper_->DumpRaw("aec3_erle", aec_state_.Erle());
-  data_dumper_->DumpRaw("aec3_erl", aec_state_.Erl());
-  data_dumper_->DumpRaw("aec3_usable_linear_estimate",
-                        aec_state_.UsableLinearEstimate());
-  data_dumper_->DumpRaw("aec3_filter_delay", aec_state_.FilterDelay());
+  data_dumper_->DumpRaw("aec3_filter_delay", aec_state_.FilterDelayBlocks());
   data_dumper_->DumpRaw("aec3_capture_saturation",
                         aec_state_.SaturatedCapture() ? 1 : 0);
 }
diff --git a/modules/audio_processing/aec3/echo_remover.h b/modules/audio_processing/aec3/echo_remover.h
index 08fc3db..61d2999 100644
--- a/modules/audio_processing/aec3/echo_remover.h
+++ b/modules/audio_processing/aec3/echo_remover.h
@@ -38,10 +38,13 @@
   virtual void ProcessCapture(
       const EchoPathVariability& echo_path_variability,
       bool capture_signal_saturation,
-      const rtc::Optional<DelayEstimate>& delay_estimate,
+      const rtc::Optional<DelayEstimate>& external_delay,
       RenderBuffer* render_buffer,
       std::vector<std::vector<float>>* capture) = 0;
 
+  // Returns the internal delay estimate in blocks.
+  virtual rtc::Optional<int> Delay() const = 0;
+
   // Updates the status on whether echo leakage is detected in the output of the
   // echo remover.
   virtual void UpdateEchoLeakageStatus(bool leakage_detected) = 0;
diff --git a/modules/audio_processing/aec3/echo_remover_metrics.cc b/modules/audio_processing/aec3/echo_remover_metrics.cc
index bc815eb..c970649 100644
--- a/modules/audio_processing/aec3/echo_remover_metrics.cc
+++ b/modules/audio_processing/aec3/echo_remover_metrics.cc
@@ -237,7 +237,7 @@
             static_cast<int>(
                 active_render_count_ > kMetricsCollectionBlocksBy2 ? 1 : 0));
         RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.EchoCanceller.FilterDelay",
-                                    aec_state.FilterDelay(), 0, 30, 31);
+                                    aec_state.FilterDelayBlocks(), 0, 30, 31);
         RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.EchoCanceller.CaptureSaturation",
                               static_cast<int>(saturated_capture_ ? 1 : 0));
         break;
diff --git a/modules/audio_processing/aec3/erle_estimator.cc b/modules/audio_processing/aec3/erle_estimator.cc
index 0e4cbe1..18763cb 100644
--- a/modules/audio_processing/aec3/erle_estimator.cc
+++ b/modules/audio_processing/aec3/erle_estimator.cc
@@ -24,7 +24,9 @@
       max_erle_lf_(max_erle_lf),
       max_erle_hf_(max_erle_hf) {
   erle_.fill(min_erle_);
+  erle_onsets_.fill(min_erle_);
   hold_counters_.fill(0);
+  coming_onset_.fill(true);
   erle_time_domain_ = min_erle_;
   hold_counter_time_domain_ = 0;
 }
@@ -43,29 +45,55 @@
 
   // Corresponds of WGN of power -46 dBFS.
   constexpr float kX2Min = 44015068.0f;
+  constexpr int kOnsetSizeBlocks = 4;
+  constexpr int kErleHold = 100;
+  constexpr int kErleOnsetHold = kErleHold + kOnsetSizeBlocks;
+
+  auto erle_band_update = [](float erle_band, float new_erle, float alpha_inc,
+                             float alpha_dec, float min_erle, float max_erle) {
+    float alpha = new_erle > erle_band ? alpha_inc : alpha_dec;
+    float erle_band_out = erle_band;
+    erle_band_out = erle_band + alpha * (new_erle - erle_band);
+    erle_band_out = rtc::SafeClamp(erle_band_out, min_erle, max_erle);
+    return erle_band_out;
+  };
 
   // Update the estimates in a clamped minimum statistics manner.
   auto erle_update = [&](size_t start, size_t stop, float max_erle) {
     for (size_t k = start; k < stop; ++k) {
       if (X2[k] > kX2Min && E2[k] > 0.f) {
         const float new_erle = Y2[k] / E2[k];
-        if (new_erle > erle_[k]) {
-          hold_counters_[k - 1] = 100;
-          erle_[k] += 0.1f * (new_erle - erle_[k]);
-          erle_[k] = rtc::SafeClamp(erle_[k], min_erle_, max_erle);
+
+        if (coming_onset_[k - 1]) {
+          hold_counters_[k - 1] = kErleOnsetHold;
+          coming_onset_[k - 1] = false;
         }
+        if (hold_counters_[k - 1] > kErleHold) {
+          erle_onsets_[k] = erle_band_update(erle_onsets_[k], new_erle, 0.05f,
+                                             0.1f, min_erle_, max_erle);
+        } else {
+          hold_counters_[k - 1] = kErleHold;
+        }
+        erle_[k] = erle_band_update(erle_[k], new_erle, 0.01f, 0.02f, min_erle_,
+                                    max_erle);
       }
     }
   };
-  erle_update(1, kFftLengthBy2 / 2, max_erle_lf_);
-  erle_update(kFftLengthBy2 / 2, kFftLengthBy2, max_erle_hf_);
 
-  std::for_each(hold_counters_.begin(), hold_counters_.end(),
-                [](int& a) { --a; });
-  std::transform(hold_counters_.begin(), hold_counters_.end(),
-                 erle_.begin() + 1, erle_.begin() + 1, [&](int a, float b) {
-                   return a > 0 ? b : std::max(min_erle_, 0.97f * b);
-                 });
+  constexpr size_t kFftLengthBy4 = kFftLengthBy2 / 2;
+  erle_update(1, kFftLengthBy4, max_erle_lf_);
+  erle_update(kFftLengthBy4, kFftLengthBy2, max_erle_hf_);
+
+  for (size_t k = 0; k < hold_counters_.size(); ++k) {
+    hold_counters_[k]--;
+    if (hold_counters_[k] <= 0) {
+      coming_onset_[k] = true;
+      if (erle_[k + 1] > erle_onsets_[k + 1]) {
+        erle_[k + 1] = std::max(erle_onsets_[k + 1], 0.97f * erle_[k + 1]);
+        RTC_DCHECK_LE(min_erle_, erle_[k + 1]);
+      }
+    }
+  }
 
   erle_[0] = erle_[1];
   erle_[kFftLengthBy2] = erle_[kFftLengthBy2 - 1];
@@ -77,7 +105,7 @@
     const float Y2_sum = std::accumulate(Y2.begin(), Y2.end(), 0.0f);
     const float new_erle = Y2_sum / E2_sum;
     if (new_erle > erle_time_domain_) {
-      hold_counter_time_domain_ = 100;
+      hold_counter_time_domain_ = kErleHold;
       erle_time_domain_ += 0.1f * (new_erle - erle_time_domain_);
       erle_time_domain_ =
           rtc::SafeClamp(erle_time_domain_, min_erle_, max_erle_lf_);
diff --git a/modules/audio_processing/aec3/erle_estimator.h b/modules/audio_processing/aec3/erle_estimator.h
index cb9fce6..809466c 100644
--- a/modules/audio_processing/aec3/erle_estimator.h
+++ b/modules/audio_processing/aec3/erle_estimator.h
@@ -32,10 +32,16 @@
 
   // Returns the most recent ERLE estimate.
   const std::array<float, kFftLengthBy2Plus1>& Erle() const { return erle_; }
+  // Returns the ERLE that is estimated during onsets. Use for logging/testing.
+  const std::array<float, kFftLengthBy2Plus1>& ErleOnsets() const {
+    return erle_onsets_;
+  }
   float ErleTimeDomain() const { return erle_time_domain_; }
 
  private:
   std::array<float, kFftLengthBy2Plus1> erle_;
+  std::array<float, kFftLengthBy2Plus1> erle_onsets_;
+  std::array<bool, kFftLengthBy2Minus1> coming_onset_;
   std::array<int, kFftLengthBy2Minus1> hold_counters_;
   float erle_time_domain_;
   int hold_counter_time_domain_;
diff --git a/modules/audio_processing/aec3/erle_estimator_unittest.cc b/modules/audio_processing/aec3/erle_estimator_unittest.cc
index f3dd7d9..9ccdb20 100644
--- a/modules/audio_processing/aec3/erle_estimator_unittest.cc
+++ b/modules/audio_processing/aec3/erle_estimator_unittest.cc
@@ -16,52 +16,109 @@
 namespace {
 
 constexpr int kLowFrequencyLimit = kFftLengthBy2 / 2;
+constexpr float kMaxErleLf = 8.f;
+constexpr float kMaxErleHf = 1.5f;
+constexpr float kMinErle = 1.0f;
+constexpr float kTrueErle = 10.f;
+constexpr float kTrueErleOnsets = 1.0f;
 
-void VerifyErle(const std::array<float, kFftLengthBy2Plus1>& erle,
-                float erle_time_domain,
-                float reference_lf,
-                float reference_hf) {
+void VerifyErleBands(const std::array<float, kFftLengthBy2Plus1>& erle,
+                     float reference_lf,
+                     float reference_hf) {
   std::for_each(
       erle.begin(), erle.begin() + kLowFrequencyLimit,
       [reference_lf](float a) { EXPECT_NEAR(reference_lf, a, 0.001); });
   std::for_each(
       erle.begin() + kLowFrequencyLimit, erle.end(),
       [reference_hf](float a) { EXPECT_NEAR(reference_hf, a, 0.001); });
+}
+
+void VerifyErle(const std::array<float, kFftLengthBy2Plus1>& erle,
+                float erle_time_domain,
+                float reference_lf,
+                float reference_hf) {
+  VerifyErleBands(erle, reference_lf, reference_hf);
   EXPECT_NEAR(reference_lf, erle_time_domain, 0.001);
 }
 
+void FormFarendFrame(std::array<float, kFftLengthBy2Plus1>* X2,
+                     std::array<float, kFftLengthBy2Plus1>* E2,
+                     std::array<float, kFftLengthBy2Plus1>* Y2,
+                     float erle) {
+  X2->fill(500 * 1000.f * 1000.f);
+  E2->fill(1000.f * 1000.f);
+  Y2->fill(erle * (*E2)[0]);
+}
+
+void FormNearendFrame(std::array<float, kFftLengthBy2Plus1>* X2,
+                      std::array<float, kFftLengthBy2Plus1>* E2,
+                      std::array<float, kFftLengthBy2Plus1>* Y2) {
+  X2->fill(0.f);
+  Y2->fill(500.f * 1000.f * 1000.f);
+  E2->fill((*Y2)[0]);
+}
+
 }  // namespace
 
-// Verifies that the correct ERLE estimates are achieved.
-TEST(ErleEstimator, Estimates) {
+TEST(ErleEstimator, VerifyErleIncreaseAndHold) {
   std::array<float, kFftLengthBy2Plus1> X2;
   std::array<float, kFftLengthBy2Plus1> E2;
   std::array<float, kFftLengthBy2Plus1> Y2;
 
-  ErleEstimator estimator(1.f, 8.f, 1.5f);
+  ErleEstimator estimator(kMinErle, kMaxErleLf, kMaxErleHf);
 
-  // Verifies that the ERLE estimate is properley increased to higher values.
-  X2.fill(500 * 1000.f * 1000.f);
-  E2.fill(1000.f * 1000.f);
-  Y2.fill(10 * E2[0]);
+  // Verifies that the ERLE estimate is properly increased to higher values.
+  FormFarendFrame(&X2, &E2, &Y2, kTrueErle);
+
   for (size_t k = 0; k < 200; ++k) {
     estimator.Update(X2, Y2, E2);
   }
   VerifyErle(estimator.Erle(), estimator.ErleTimeDomain(), 8.f, 1.5f);
 
-  // Verifies that the ERLE is not immediately decreased when the ERLE in the
-  // data decreases.
-  Y2.fill(0.1f * E2[0]);
+  FormNearendFrame(&X2, &E2, &Y2);
+  // Verifies that the ERLE is not immediately decreased during nearend
+  // activity.
   for (size_t k = 0; k < 98; ++k) {
     estimator.Update(X2, Y2, E2);
   }
   VerifyErle(estimator.Erle(), estimator.ErleTimeDomain(), 8.f, 1.5f);
+}
 
-  // Verifies that the minimum ERLE is eventually achieved.
-  for (size_t k = 0; k < 1000; ++k) {
+TEST(ErleEstimator, VerifyErleTrackingOnOnsets) {
+  std::array<float, kFftLengthBy2Plus1> X2;
+  std::array<float, kFftLengthBy2Plus1> E2;
+  std::array<float, kFftLengthBy2Plus1> Y2;
+
+  ErleEstimator estimator(kMinErle, kMaxErleLf, kMaxErleHf);
+
+  for (size_t burst = 0; burst < 20; ++burst) {
+    FormFarendFrame(&X2, &E2, &Y2, kTrueErleOnsets);
+    for (size_t k = 0; k < 10; ++k) {
+      estimator.Update(X2, Y2, E2);
+    }
+    FormFarendFrame(&X2, &E2, &Y2, kTrueErle);
+    for (size_t k = 0; k < 200; ++k) {
+      estimator.Update(X2, Y2, E2);
+    }
+    FormNearendFrame(&X2, &E2, &Y2);
+    for (size_t k = 0; k < 100; ++k) {
+      estimator.Update(X2, Y2, E2);
+    }
+  }
+  VerifyErleBands(estimator.ErleOnsets(), kMinErle, kMinErle);
+  FormNearendFrame(&X2, &E2, &Y2);
+  for (size_t k = 0; k < 1000; k++) {
     estimator.Update(X2, Y2, E2);
   }
-  VerifyErle(estimator.Erle(), estimator.ErleTimeDomain(), 1.f, 1.f);
+  // Verifies that during ne activity, Erle converges to the Erle for onsets.
+  VerifyErle(estimator.Erle(), estimator.ErleTimeDomain(), kMinErle, kMinErle);
+}
+
+TEST(ErleEstimator, VerifyNoErleUpdateDuringLowActivity) {
+  std::array<float, kFftLengthBy2Plus1> X2;
+  std::array<float, kFftLengthBy2Plus1> E2;
+  std::array<float, kFftLengthBy2Plus1> Y2;
+  ErleEstimator estimator(kMinErle, kMaxErleLf, kMaxErleHf);
 
   // Verifies that the ERLE estimate is is not updated for low-level render
   // signals.
@@ -70,6 +127,7 @@
   for (size_t k = 0; k < 200; ++k) {
     estimator.Update(X2, Y2, E2);
   }
-  VerifyErle(estimator.Erle(), estimator.ErleTimeDomain(), 1.f, 1.f);
+  VerifyErle(estimator.Erle(), estimator.ErleTimeDomain(), kMinErle, kMinErle);
 }
+
 }  // namespace webrtc
diff --git a/modules/audio_processing/aec3/filter_analyzer.cc b/modules/audio_processing/aec3/filter_analyzer.cc
new file mode 100644
index 0000000..363373c
--- /dev/null
+++ b/modules/audio_processing/aec3/filter_analyzer.cc
@@ -0,0 +1,127 @@
+/*
+ *  Copyright (c) 2017 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_processing/aec3/filter_analyzer.h"
+#include <math.h>
+
+#include <algorithm>
+#include <array>
+#include <numeric>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace {
+
+size_t FindPeakIndex(rtc::ArrayView<const float> filter_time_domain) {
+  size_t peak_index = 0;
+  float max_h2 = filter_time_domain[0] * filter_time_domain[0];
+  for (size_t k = 1; k < filter_time_domain.size(); ++k) {
+    float tmp = filter_time_domain[k] * filter_time_domain[k];
+    if (tmp > max_h2) {
+      peak_index = k;
+      max_h2 = tmp;
+    }
+  }
+
+  return peak_index;
+}
+
+}  // namespace
+
+FilterAnalyzer::FilterAnalyzer(const EchoCanceller3Config& config)
+    : bounded_erl_(config.ep_strength.bounded_erl),
+      default_gain_(config.ep_strength.lf),
+      active_render_threshold_(config.render_levels.active_render_limit *
+                               config.render_levels.active_render_limit *
+                               kFftLengthBy2) {
+  Reset();
+}
+
+FilterAnalyzer::~FilterAnalyzer() = default;
+
+void FilterAnalyzer::Reset() {
+  delay_blocks_ = 0;
+  consistent_estimate_ = false;
+  blocks_since_reset_ = 0;
+  consistent_estimate_ = false;
+  consistent_estimate_counter_ = 0;
+  consistent_delay_reference_ = -10;
+  gain_ = default_gain_;
+}
+
+void FilterAnalyzer::Update(rtc::ArrayView<const float> filter_time_domain,
+                            const RenderBuffer& render_buffer) {
+  size_t peak_index = FindPeakIndex(filter_time_domain);
+  delay_blocks_ = peak_index / kBlockSize;
+
+  UpdateFilterGain(filter_time_domain, peak_index);
+
+  float filter_floor = 0;
+  float filter_secondary_peak = 0;
+  size_t limit1 = peak_index < 64 ? 0 : peak_index - 64;
+  size_t limit2 =
+      peak_index > filter_time_domain.size() - 129 ? 0 : peak_index + 128;
+
+  for (size_t k = 0; k < limit1; ++k) {
+    float abs_h = fabsf(filter_time_domain[k]);
+    filter_floor += abs_h;
+    filter_secondary_peak = std::max(filter_secondary_peak, abs_h);
+  }
+  for (size_t k = limit2; k < filter_time_domain.size(); ++k) {
+    float abs_h = fabsf(filter_time_domain[k]);
+    filter_floor += abs_h;
+    filter_secondary_peak = std::max(filter_secondary_peak, abs_h);
+  }
+
+  filter_floor /= (limit1 + filter_time_domain.size() - limit2);
+
+  float abs_peak = fabsf(filter_time_domain[peak_index]);
+  bool significant_peak_index =
+      abs_peak > 10.f * filter_floor && abs_peak > 2.f * filter_secondary_peak;
+
+  if (consistent_delay_reference_ != delay_blocks_ || !significant_peak_index) {
+    consistent_estimate_counter_ = 0;
+    consistent_delay_reference_ = delay_blocks_;
+  } else {
+    const auto& x = render_buffer.Block(-delay_blocks_)[0];
+    const float x_energy =
+        std::inner_product(x.begin(), x.end(), x.begin(), 0.f);
+    const bool active_render_block = x_energy > active_render_threshold_;
+
+    if (active_render_block) {
+      ++consistent_estimate_counter_;
+    }
+  }
+
+  consistent_estimate_ =
+      consistent_estimate_counter_ > 1.5f * kNumBlocksPerSecond;
+}
+
+void FilterAnalyzer::UpdateFilterGain(
+    rtc::ArrayView<const float> filter_time_domain,
+    size_t peak_index) {
+  bool sufficient_time_to_converge =
+      ++blocks_since_reset_ > 5 * kNumBlocksPerSecond;
+
+  if (sufficient_time_to_converge && consistent_estimate_) {
+    gain_ = fabsf(filter_time_domain[peak_index]);
+  } else {
+    if (gain_) {
+      gain_ = std::max(gain_, fabsf(filter_time_domain[peak_index]));
+    }
+  }
+
+  if (bounded_erl_ && gain_) {
+    gain_ = std::max(gain_, 0.01f);
+  }
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/aec3/filter_analyzer.h b/modules/audio_processing/aec3/filter_analyzer.h
new file mode 100644
index 0000000..f02a210
--- /dev/null
+++ b/modules/audio_processing/aec3/filter_analyzer.h
@@ -0,0 +1,68 @@
+/*
+ *  Copyright (c) 2017 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_PROCESSING_AEC3_FILTER_ANALYZER_H_
+#define MODULES_AUDIO_PROCESSING_AEC3_FILTER_ANALYZER_H_
+
+#include <vector>
+
+#include "api/array_view.h"
+#include "api/audio/echo_canceller3_config.h"
+#include "api/optional.h"
+#include "modules/audio_processing/aec3/aec3_common.h"
+#include "modules/audio_processing/aec3/render_buffer.h"
+#include "rtc_base/constructormagic.h"
+
+namespace webrtc {
+
+// Class for analyzing the properties of an adaptive filter.
+class FilterAnalyzer {
+ public:
+  explicit FilterAnalyzer(const EchoCanceller3Config& config);
+  ~FilterAnalyzer();
+
+  // Resets the analysis.
+  void Reset();
+
+  // Updates the estimates with new input data.
+  void Update(rtc::ArrayView<const float> filter_time_domain,
+              const RenderBuffer& render_buffer);
+
+  // Returns the delay of the filter in terms of blocks.
+  int DelayBlocks() const { return delay_blocks_; }
+
+  // Returns whether the filter is consistent in the sense that it does not
+  // change much over time.
+  bool Consistent() const { return consistent_estimate_; }
+
+  // Returns the estimated filter gain.
+  float Gain() const { return gain_; }
+
+ private:
+  void UpdateFilterGain(rtc::ArrayView<const float> filter_time_domain,
+                        size_t max_index);
+
+  const bool bounded_erl_;
+  const float default_gain_;
+  const float active_render_threshold_;
+
+  int delay_blocks_ = 0;
+  size_t blocks_since_reset_ = 0;
+  bool consistent_estimate_ = false;
+  size_t consistent_estimate_counter_ = 0;
+  int consistent_delay_reference_ = -10;
+  float gain_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(FilterAnalyzer);
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AEC3_FILTER_ANALYZER_H_
diff --git a/modules/audio_processing/aec3/main_filter_update_gain_unittest.cc b/modules/audio_processing/aec3/main_filter_update_gain_unittest.cc
index 13747d4..3d0a8c3 100644
--- a/modules/audio_processing/aec3/main_filter_update_gain_unittest.cc
+++ b/modules/audio_processing/aec3/main_filter_update_gain_unittest.cc
@@ -114,7 +114,7 @@
     render_delay_buffer->PrepareCaptureProcessing();
 
     render_signal_analyzer.Update(*render_delay_buffer->GetRenderBuffer(),
-                                  aec_state.FilterDelay());
+                                  aec_state.FilterDelayBlocks());
 
     // Apply the main filter.
     main_filter.Filter(*render_delay_buffer->GetRenderBuffer(), &S);
@@ -162,9 +162,8 @@
     aec_state.HandleEchoPathChange(EchoPathVariability(
         false, EchoPathVariability::DelayAdjustment::kNone, false));
     aec_state.Update(delay_estimate, main_filter.FilterFrequencyResponse(),
-                     main_filter.FilterImpulseResponse(), true,
-                     *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s,
-                     false);
+                     main_filter.FilterImpulseResponse(), true, false,
+                     *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
   }
 
   std::copy(e_main.begin(), e_main.end(), e_last_block->begin());
diff --git a/modules/audio_processing/aec3/matched_filter.h b/modules/audio_processing/aec3/matched_filter.h
index c9bdc46..36c9cad 100644
--- a/modules/audio_processing/aec3/matched_filter.h
+++ b/modules/audio_processing/aec3/matched_filter.h
@@ -15,6 +15,7 @@
 #include <memory>
 #include <vector>
 
+#include "api/array_view.h"
 #include "api/optional.h"
 #include "modules/audio_processing/aec3/aec3_common.h"
 #include "modules/audio_processing/aec3/downsampled_render_buffer.h"
diff --git a/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc b/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc
index 9041924..23cd71a 100644
--- a/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc
+++ b/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc
@@ -70,8 +70,6 @@
     if (histogram_[candidate] > 25) {
       significant_candidate_found_ = true;
       return DelayEstimate(DelayEstimate::Quality::kRefined, candidate);
-    } else if (!significant_candidate_found_) {
-      return DelayEstimate(DelayEstimate::Quality::kCoarse, candidate);
     }
   }
   return rtc::nullopt;
diff --git a/modules/audio_processing/aec3/matched_filter_lag_aggregator_unittest.cc b/modules/audio_processing/aec3/matched_filter_lag_aggregator_unittest.cc
index ce303d4..18b8829 100644
--- a/modules/audio_processing/aec3/matched_filter_lag_aggregator_unittest.cc
+++ b/modules/audio_processing/aec3/matched_filter_lag_aggregator_unittest.cc
@@ -22,7 +22,7 @@
 namespace webrtc {
 namespace {
 
-constexpr size_t kNumLagsBeforeDetection = 25;
+constexpr size_t kNumLagsBeforeDetection = 26;
 
 }  // namespace
 
@@ -37,7 +37,7 @@
   lag_estimates[1] = MatchedFilter::LagEstimate(0.5f, true, kLag2, true);
 
   for (size_t k = 0; k < kNumLagsBeforeDetection; ++k) {
-    EXPECT_TRUE(aggregator.Aggregate(lag_estimates));
+    aggregator.Aggregate(lag_estimates);
   }
 
   rtc::Optional<DelayEstimate> aggregated_lag =
diff --git a/modules/audio_processing/aec3/mock/mock_block_processor.h b/modules/audio_processing/aec3/mock/mock_block_processor.h
index 5fff456..803bf78 100644
--- a/modules/audio_processing/aec3/mock/mock_block_processor.h
+++ b/modules/audio_processing/aec3/mock/mock_block_processor.h
@@ -31,6 +31,7 @@
                void(const std::vector<std::vector<float>>& block));
   MOCK_METHOD1(UpdateEchoLeakageStatus, void(bool leakage_detected));
   MOCK_CONST_METHOD1(GetMetrics, void(EchoControl::Metrics* metrics));
+  MOCK_METHOD1(SetAudioBufferDelay, void(size_t delay_ms));
 };
 
 }  // namespace test
diff --git a/modules/audio_processing/aec3/mock/mock_echo_remover.h b/modules/audio_processing/aec3/mock/mock_echo_remover.h
index 638e3f0..0acf139 100644
--- a/modules/audio_processing/aec3/mock/mock_echo_remover.h
+++ b/modules/audio_processing/aec3/mock/mock_echo_remover.h
@@ -32,7 +32,7 @@
                     const rtc::Optional<DelayEstimate>& delay_estimate,
                     RenderBuffer* render_buffer,
                     std::vector<std::vector<float>>* capture));
-
+  MOCK_CONST_METHOD0(Delay, rtc::Optional<int>());
   MOCK_METHOD1(UpdateEchoLeakageStatus, void(bool leakage_detected));
   MOCK_CONST_METHOD1(GetMetrics, void(EchoControl::Metrics* metrics));
 };
diff --git a/modules/audio_processing/aec3/mock/mock_render_delay_buffer.h b/modules/audio_processing/aec3/mock/mock_render_delay_buffer.h
index 1ed2b40..00c13f4 100644
--- a/modules/audio_processing/aec3/mock/mock_render_delay_buffer.h
+++ b/modules/audio_processing/aec3/mock/mock_render_delay_buffer.h
@@ -47,12 +47,13 @@
                    const std::vector<std::vector<float>>& block));
   MOCK_METHOD0(PrepareCaptureProcessing, RenderDelayBuffer::BufferingEvent());
   MOCK_METHOD1(SetDelay, bool(size_t delay));
-  MOCK_CONST_METHOD0(Delay, rtc::Optional<size_t>());
+  MOCK_CONST_METHOD0(Delay, size_t());
   MOCK_CONST_METHOD0(MaxDelay, size_t());
   MOCK_METHOD0(GetRenderBuffer, RenderBuffer*());
   MOCK_CONST_METHOD0(GetDownsampledRenderBuffer,
                      const DownsampledRenderBuffer&());
   MOCK_CONST_METHOD1(CausalDelay, bool(size_t delay));
+  MOCK_METHOD1(SetAudioBufferDelay, void(size_t delay_ms));
 
  private:
   RenderBuffer* FakeGetRenderBuffer() { return &render_buffer_; }
diff --git a/modules/audio_processing/aec3/mock/mock_render_delay_controller.h b/modules/audio_processing/aec3/mock/mock_render_delay_controller.h
index 8fb7a8e..fab2b65 100644
--- a/modules/audio_processing/aec3/mock/mock_render_delay_controller.h
+++ b/modules/audio_processing/aec3/mock/mock_render_delay_controller.h
@@ -26,9 +26,11 @@
 
   MOCK_METHOD0(Reset, void());
   MOCK_METHOD0(LogRenderCall, void());
-  MOCK_METHOD2(
+  MOCK_METHOD4(
       GetDelay,
       rtc::Optional<DelayEstimate>(const DownsampledRenderBuffer& render_buffer,
+                                   size_t render_delay_buffer_delay,
+                                   const rtc::Optional<int>& echo_remover_delay,
                                    rtc::ArrayView<const float> capture));
 };
 
diff --git a/modules/audio_processing/aec3/output_selector.cc b/modules/audio_processing/aec3/output_selector.cc
deleted file mode 100644
index 4f547d9..0000000
--- a/modules/audio_processing/aec3/output_selector.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- *  Copyright (c) 2017 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_processing/aec3/output_selector.h"
-
-#include <algorithm>
-#include <numeric>
-
-#include "rtc_base/checks.h"
-
-namespace webrtc {
-namespace {
-
-// Performs the transition between the signals in a smooth manner.
-void SmoothFrameTransition(bool from_y_to_e,
-                           rtc::ArrayView<const float> e,
-                           rtc::ArrayView<float> y) {
-  RTC_DCHECK_LT(0u, e.size());
-  RTC_DCHECK_EQ(y.size(), e.size());
-
-  const float change_factor = (from_y_to_e ? 1.f : -1.f) / e.size();
-  float averaging = from_y_to_e ? 0.f : 1.f;
-  for (size_t k = 0; k < e.size(); ++k) {
-    y[k] += averaging * (e[k] - y[k]);
-    averaging += change_factor;
-  }
-  RTC_DCHECK_EQ(from_y_to_e ? 1.f : 0.f, averaging);
-}
-
-}  // namespace
-
-OutputSelector::OutputSelector() = default;
-
-OutputSelector::~OutputSelector() = default;
-
-void OutputSelector::FormLinearOutput(
-    bool use_subtractor_output,
-    rtc::ArrayView<const float> subtractor_output,
-    rtc::ArrayView<float> capture) {
-  RTC_DCHECK_EQ(subtractor_output.size(), capture.size());
-  rtc::ArrayView<const float>& e_main = subtractor_output;
-  rtc::ArrayView<float> y = capture;
-
-  if (use_subtractor_output != use_subtractor_output_) {
-    use_subtractor_output_ = use_subtractor_output;
-    SmoothFrameTransition(use_subtractor_output_, e_main, y);
-  } else if (use_subtractor_output_) {
-    std::copy(e_main.begin(), e_main.end(), y.begin());
-  }
-}
-
-}  // namespace webrtc
diff --git a/modules/audio_processing/aec3/output_selector.h b/modules/audio_processing/aec3/output_selector.h
deleted file mode 100644
index a406c61..0000000
--- a/modules/audio_processing/aec3/output_selector.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- *  Copyright (c) 2017 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_PROCESSING_AEC3_OUTPUT_SELECTOR_H_
-#define MODULES_AUDIO_PROCESSING_AEC3_OUTPUT_SELECTOR_H_
-
-#include "api/array_view.h"
-#include "rtc_base/constructormagic.h"
-
-namespace webrtc {
-
-// Performs the selection between which of the linear aec output and the
-// microphone signal should be used as the echo suppressor output.
-class OutputSelector {
- public:
-  OutputSelector();
-  ~OutputSelector();
-
-  // Forms the most appropriate output signal.
-  void FormLinearOutput(bool use_subtractor_output,
-                        rtc::ArrayView<const float> subtractor_output,
-                        rtc::ArrayView<float> capture);
-
-  // Returns true if the linear aec output is the one used.
-  bool UseSubtractorOutput() const { return use_subtractor_output_; }
-
- private:
-  bool use_subtractor_output_ = false;
-  RTC_DISALLOW_COPY_AND_ASSIGN(OutputSelector);
-};
-
-}  // namespace webrtc
-
-#endif  // MODULES_AUDIO_PROCESSING_AEC3_OUTPUT_SELECTOR_H_
diff --git a/modules/audio_processing/aec3/output_selector_unittest.cc b/modules/audio_processing/aec3/output_selector_unittest.cc
deleted file mode 100644
index c7add1c..0000000
--- a/modules/audio_processing/aec3/output_selector_unittest.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- *  Copyright (c) 2017 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_processing/aec3/output_selector.h"
-
-#include <algorithm>
-#include <array>
-
-#include "modules/audio_processing/aec3/aec3_common.h"
-#include "test/gtest.h"
-
-namespace webrtc {
-
-// Verifies that the switching between the signals in the output works as
-// intended.
-TEST(OutputSelector, ProperSwitching) {
-  OutputSelector selector;
-
-  std::array<float, kBlockSize> y;
-  std::array<float, kBlockSize> e;
-  std::array<float, kBlockSize> e_ref;
-  std::array<float, kBlockSize> y_ref;
-  auto init_blocks = [](std::array<float, kBlockSize>* e,
-                        std::array<float, kBlockSize>* y) {
-    e->fill(10.f);
-    y->fill(20.f);
-  };
-
-  init_blocks(&e_ref, &y_ref);
-
-  init_blocks(&e, &y);
-  selector.FormLinearOutput(false, e, y);
-  EXPECT_EQ(y_ref, y);
-
-  init_blocks(&e, &y);
-  selector.FormLinearOutput(true, e, y);
-  EXPECT_NE(e_ref, y);
-  EXPECT_NE(y_ref, y);
-
-  init_blocks(&e, &y);
-  selector.FormLinearOutput(true, e, y);
-  EXPECT_EQ(e_ref, y);
-
-  init_blocks(&e, &y);
-  selector.FormLinearOutput(true, e, y);
-  EXPECT_EQ(e_ref, y);
-
-  init_blocks(&e, &y);
-  selector.FormLinearOutput(false, e, y);
-  EXPECT_NE(e_ref, y);
-  EXPECT_NE(y_ref, y);
-
-  init_blocks(&e, &y);
-  selector.FormLinearOutput(false, e, y);
-  EXPECT_EQ(y_ref, y);
-
-  init_blocks(&e, &y);
-  selector.FormLinearOutput(false, e, y);
-  EXPECT_EQ(y_ref, y);
-}
-
-}  // namespace webrtc
diff --git a/modules/audio_processing/aec3/render_buffer.h b/modules/audio_processing/aec3/render_buffer.h
index 7789ffd..9419b30 100644
--- a/modules/audio_processing/aec3/render_buffer.h
+++ b/modules/audio_processing/aec3/render_buffer.h
@@ -67,6 +67,28 @@
   // Specifies the recent activity seen in the render signal.
   void SetRenderActivity(bool activity) { render_activity_ = activity; }
 
+  // Returns the headroom between the write and the read positions in the
+  // buffer.
+  int Headroom() const {
+    // The write and read indices are decreased over time.
+    int headroom =
+        fft_buffer_->write < fft_buffer_->read
+            ? fft_buffer_->read - fft_buffer_->write
+            : fft_buffer_->size - fft_buffer_->write + fft_buffer_->read;
+
+    RTC_DCHECK_LE(0, headroom);
+    RTC_DCHECK_GE(fft_buffer_->size, headroom);
+
+    return headroom;
+  }
+
+
+  // Returns a reference to the spectrum buffer.
+  const VectorBuffer& GetSpectrumBuffer() const { return *spectrum_buffer_; }
+
+  // Returns a reference to the block buffer.
+  const MatrixBuffer& GetBlockBuffer() const { return *block_buffer_; }
+
  private:
   const MatrixBuffer* const block_buffer_;
   const VectorBuffer* const spectrum_buffer_;
diff --git a/modules/audio_processing/aec3/render_delay_buffer.cc b/modules/audio_processing/aec3/render_delay_buffer.cc
index 60606bf..5aa432d 100644
--- a/modules/audio_processing/aec3/render_delay_buffer.cc
+++ b/modules/audio_processing/aec3/render_delay_buffer.cc
@@ -38,7 +38,7 @@
   BufferingEvent Insert(const std::vector<std::vector<float>>& block) override;
   BufferingEvent PrepareCaptureProcessing() override;
   bool SetDelay(size_t delay) override;
-  rtc::Optional<size_t> Delay() const override { return delay_; }
+  size_t Delay() const override { return MapInternalDelayToExternalDelay(); }
   size_t MaxDelay() const override {
     return blocks_.buffer.size() - 1 - buffer_headroom_;
   }
@@ -50,6 +50,8 @@
 
   bool CausalDelay(size_t delay) const override;
 
+  void SetAudioBufferDelay(size_t delay_ms) override;
+
  private:
   static int instance_count_;
   std::unique_ptr<ApmDataDumper> data_dumper_;
@@ -75,9 +77,12 @@
   size_t render_call_counter_ = 0;
   bool render_activity_ = false;
   size_t render_activity_counter_ = 0;
+  rtc::Optional<size_t> external_audio_buffer_delay_ms_;
+  bool external_delay_verified_after_reset_ = false;
 
   int LowRateBufferOffset() const { return DelayEstimatorOffset(config_) >> 1; }
-  int MaxExternalDelayToInternalDelay(size_t delay) const;
+  int MapExternalDelayToInternalDelay(size_t external_delay_blocks) const;
+  int MapInternalDelayToExternalDelay() const;
   void ApplyDelay(int delay);
   void InsertBlock(const std::vector<std::vector<float>>& block,
                    int previous_write);
@@ -167,7 +172,7 @@
               kBlockSize),
       spectra_(blocks_.buffer.size(), kFftLengthBy2Plus1),
       ffts_(blocks_.buffer.size()),
-      delay_(config_.delay.min_echo_path_delay_blocks),
+      delay_(config_.delay.default_delay),
       echo_remover_buffer_(&blocks_, &spectra_, &ffts_),
       low_rate_(GetDownSampledBufferSize(config.delay.down_sampling_factor,
                                          config.delay.num_filters)),
@@ -196,12 +201,33 @@
   low_rate_.read = low_rate_.OffsetIndex(
       low_rate_.write, LowRateBufferOffset() * sub_block_size_);
 
-  // Set the render buffer delays to the default delay.
-  ApplyDelay(config_.delay.default_delay);
+  // Check for any external audio buffer delay and whether it is feasible.
+  if (external_audio_buffer_delay_ms_) {
+    constexpr size_t kHeadroom = 5;
+    size_t external_delay_to_set = 0;
+    if (*external_audio_buffer_delay_ms_ < kHeadroom) {
+      external_delay_to_set = 0;
+    } else {
+      external_delay_to_set = *external_audio_buffer_delay_ms_ - kHeadroom;
+    }
 
-  // Unset the delays which are set by ApplyConfig.
-  delay_ = rtc::nullopt;
-  internal_delay_ = rtc::nullopt;
+    constexpr size_t kMaxExternalDelay = 170;
+    external_delay_to_set = std::min(external_delay_to_set, kMaxExternalDelay);
+
+    // When an external delay estimate is available, use that delay as the
+    // initial render buffer delay. Avoid verifying the set delay.
+    external_delay_verified_after_reset_ = true;
+    SetDelay(external_delay_to_set);
+    external_delay_verified_after_reset_ = false;
+  } else {
+    // If an external delay estimate is not available, use that delay as the
+    // initial delay. Set the render buffer delays to the default delay.
+    ApplyDelay(config_.delay.default_delay);
+
+    // Unset the delays which are set by SetDelay.
+    delay_ = rtc::nullopt;
+    internal_delay_ = rtc::nullopt;
+  }
 }
 
 // Inserts a new block into the render buffers.
@@ -215,7 +241,7 @@
     } else {
       if (++num_api_calls_in_a_row_ > max_observed_jitter_) {
         max_observed_jitter_ = num_api_calls_in_a_row_;
-        RTC_LOG(LS_INFO)
+        RTC_LOG(LS_WARNING)
             << "New max number api jitter observed at render block "
             << render_call_counter_ << ":  " << num_api_calls_in_a_row_
             << " blocks";
@@ -263,7 +289,7 @@
     } else {
       if (++num_api_calls_in_a_row_ > max_observed_jitter_) {
         max_observed_jitter_ = num_api_calls_in_a_row_;
-        RTC_LOG(LS_INFO)
+        RTC_LOG(LS_WARNING)
             << "New max number api jitter observed at capture block "
             << capture_call_counter_ << ":  " << num_api_calls_in_a_row_
             << " blocks";
@@ -304,13 +330,22 @@
 
 // Sets the delay and returns a bool indicating whether the delay was changed.
 bool RenderDelayBufferImpl::SetDelay(size_t delay) {
+  if (!external_delay_verified_after_reset_ &&
+      external_audio_buffer_delay_ms_) {
+    int delay_difference = static_cast<int>(*external_audio_buffer_delay_ms_) -
+                           static_cast<int>(delay);
+    RTC_LOG(LS_WARNING) << "Difference between the externally reported delay "
+                           "and the first delay estimate: "
+                        << delay_difference << " ms.";
+    external_delay_verified_after_reset_ = true;
+  }
   if (delay_ && *delay_ == delay) {
     return false;
   }
   delay_ = delay;
 
   // Compute the internal delay and limit the delay to the allowed range.
-  int internal_delay = MaxExternalDelayToInternalDelay(*delay_);
+  int internal_delay = MapExternalDelayToInternalDelay(*delay_);
   internal_delay_ =
       std::min(MaxDelay(), static_cast<size_t>(std::max(internal_delay, 0)));
 
@@ -322,7 +357,7 @@
 // Returns whether the specified delay is causal.
 bool RenderDelayBufferImpl::CausalDelay(size_t delay) const {
   // Compute the internal delay and limit the delay to the allowed range.
-  int internal_delay = MaxExternalDelayToInternalDelay(delay);
+  int internal_delay = MapExternalDelayToInternalDelay(delay);
   internal_delay =
       std::min(MaxDelay(), static_cast<size_t>(std::max(internal_delay, 0)));
 
@@ -330,8 +365,17 @@
          static_cast<int>(config_.delay.min_echo_path_delay_blocks);
 }
 
+void RenderDelayBufferImpl::SetAudioBufferDelay(size_t delay_ms) {
+  if (!external_audio_buffer_delay_ms_) {
+    RTC_LOG(LS_WARNING)
+        << "Receiving a first reported externally buffer delay of " << delay_ms
+        << " ms.";
+  }
+  external_audio_buffer_delay_ms_ = delay_ms;
+}
+
 // Maps the externally computed delay to the delay used internally.
-int RenderDelayBufferImpl::MaxExternalDelayToInternalDelay(
+int RenderDelayBufferImpl::MapExternalDelayToInternalDelay(
     size_t external_delay_blocks) const {
   const int latency = BufferLatency(low_rate_);
   RTC_DCHECK_LT(0, sub_block_size_);
@@ -341,8 +385,20 @@
          DelayEstimatorOffset(config_);
 }
 
+// Maps the internally used delay to the delay used externally.
+int RenderDelayBufferImpl::MapInternalDelayToExternalDelay() const {
+  const int latency = BufferLatency(low_rate_);
+  int latency_blocks = latency / sub_block_size_;
+  int internal_delay = spectra_.read >= spectra_.write
+                           ? spectra_.read - spectra_.write
+                           : spectra_.size + spectra_.read - spectra_.write;
+
+  return internal_delay - latency_blocks + DelayEstimatorOffset(config_);
+}
+
 // Set the read indices according to the delay.
 void RenderDelayBufferImpl::ApplyDelay(int delay) {
+  RTC_LOG(LS_WARNING) << "Applying internal delay of " << delay << " blocks.";
   blocks_.read = blocks_.OffsetIndex(blocks_.write, -delay);
   spectra_.read = spectra_.OffsetIndex(spectra_.write, delay);
   ffts_.read = ffts_.OffsetIndex(ffts_.write, delay);
diff --git a/modules/audio_processing/aec3/render_delay_buffer.h b/modules/audio_processing/aec3/render_delay_buffer.h
index 22b0c7f..e361628 100644
--- a/modules/audio_processing/aec3/render_delay_buffer.h
+++ b/modules/audio_processing/aec3/render_delay_buffer.h
@@ -57,7 +57,7 @@
   virtual bool SetDelay(size_t delay) = 0;
 
   // Gets the buffer delay.
-  virtual rtc::Optional<size_t> Delay() const = 0;
+  virtual size_t Delay() const = 0;
 
   // Gets the buffer delay.
   virtual size_t MaxDelay() const = 0;
@@ -73,6 +73,9 @@
 
   // Returns the maximum non calusal offset that can occur in the delay buffer.
   static int DelayEstimatorOffset(const EchoCanceller3Config& config);
+
+  // Provides an optional external estimate of the audio buffer delay.
+  virtual void SetAudioBufferDelay(size_t delay_ms) = 0;
 };
 
 }  // namespace webrtc
diff --git a/modules/audio_processing/aec3/render_delay_buffer_unittest.cc b/modules/audio_processing/aec3/render_delay_buffer_unittest.cc
index fb9c48d..78f0b5a 100644
--- a/modules/audio_processing/aec3/render_delay_buffer_unittest.cc
+++ b/modules/audio_processing/aec3/render_delay_buffer_unittest.cc
@@ -75,12 +75,14 @@
   EchoCanceller3Config config;
   std::unique_ptr<RenderDelayBuffer> delay_buffer(
       RenderDelayBuffer::Create(config, 1));
-  ASSERT_FALSE(delay_buffer->Delay());
-  for (size_t delay = config.delay.min_echo_path_delay_blocks + 1; delay < 20;
-       ++delay) {
-    delay_buffer->SetDelay(delay);
-    ASSERT_TRUE(delay_buffer->Delay());
-    EXPECT_EQ(delay, *delay_buffer->Delay());
+  ASSERT_TRUE(delay_buffer->Delay());
+  delay_buffer->Reset();
+  size_t initial_internal_delay = config.delay.min_echo_path_delay_blocks +
+                                  config.delay.api_call_jitter_blocks;
+  for (size_t delay = initial_internal_delay;
+       delay < initial_internal_delay + 20; ++delay) {
+    ASSERT_TRUE(delay_buffer->SetDelay(delay));
+    EXPECT_EQ(delay, delay_buffer->Delay());
   }
 }
 
diff --git a/modules/audio_processing/aec3/render_delay_controller.cc b/modules/audio_processing/aec3/render_delay_controller.cc
index db00b9b..fc91108 100644
--- a/modules/audio_processing/aec3/render_delay_controller.cc
+++ b/modules/audio_processing/aec3/render_delay_controller.cc
@@ -40,6 +40,8 @@
   void LogRenderCall() override;
   rtc::Optional<DelayEstimate> GetDelay(
       const DownsampledRenderBuffer& render_buffer,
+      size_t render_delay_buffer_delay,
+      const rtc::Optional<int>& echo_remover_delay,
       rtc::ArrayView<const float> capture) override;
 
  private:
@@ -146,6 +148,8 @@
 
 rtc::Optional<DelayEstimate> RenderDelayControllerImpl::GetDelay(
     const DownsampledRenderBuffer& render_buffer,
+    size_t render_delay_buffer_delay,
+    const rtc::Optional<int>& echo_remover_delay,
     rtc::ArrayView<const float> capture) {
   RTC_DCHECK_EQ(kBlockSize, capture.size());
   ++capture_call_counter_;
@@ -157,6 +161,14 @@
   auto delay_samples =
       delay_estimator_.EstimateDelay(render_buffer, capture_delayed);
 
+  // Overrule the delay estimator delay if the echo remover reports a delay.
+  if (echo_remover_delay) {
+    int total_echo_remover_delay_samples =
+        (render_delay_buffer_delay + *echo_remover_delay) * kBlockSize;
+    delay_samples = DelayEstimate(DelayEstimate::Quality::kRefined,
+                                  total_echo_remover_delay_samples);
+  }
+
   std::copy(capture.begin(), capture.end(),
             delay_buf_.begin() + delay_buf_index_);
   delay_buf_index_ = (delay_buf_index_ + kBlockSize) % delay_buf_.size();
@@ -165,6 +177,9 @@
   rtc::Optional<int> skew = skew_estimator_.GetSkewFromCapture();
 
   if (delay_samples) {
+    // TODO(peah): Refactor the rest of the code to assume a kRefined estimate
+    // quality.
+    RTC_DCHECK(DelayEstimate::Quality::kRefined == delay_samples->quality);
     if (!delay_samples_ || delay_samples->delay != delay_samples_->delay) {
       delay_change_counter_ = 0;
     }
diff --git a/modules/audio_processing/aec3/render_delay_controller.h b/modules/audio_processing/aec3/render_delay_controller.h
index 24d7590..1e1df0d 100644
--- a/modules/audio_processing/aec3/render_delay_controller.h
+++ b/modules/audio_processing/aec3/render_delay_controller.h
@@ -38,6 +38,8 @@
   // Aligns the render buffer content with the capture signal.
   virtual rtc::Optional<DelayEstimate> GetDelay(
       const DownsampledRenderBuffer& render_buffer,
+      size_t render_delay_buffer_delay,
+      const rtc::Optional<int>& echo_remover_delay,
       rtc::ArrayView<const float> capture) = 0;
 };
 }  // namespace webrtc
diff --git a/modules/audio_processing/aec3/render_delay_controller_unittest.cc b/modules/audio_processing/aec3/render_delay_controller_unittest.cc
index 656c5e8..2c9bbef 100644
--- a/modules/audio_processing/aec3/render_delay_controller_unittest.cc
+++ b/modules/audio_processing/aec3/render_delay_controller_unittest.cc
@@ -48,6 +48,7 @@
 TEST(RenderDelayController, NoRenderSignal) {
   std::vector<float> block(kBlockSize, 0.f);
   EchoCanceller3Config config;
+  rtc::Optional<int> echo_remover_delay_;
   for (size_t num_matched_filters = 4; num_matched_filters == 10;
        num_matched_filters++) {
     for (auto down_sampling_factor : kDownSamplingFactors) {
@@ -62,7 +63,8 @@
                 config, RenderDelayBuffer::DelayEstimatorOffset(config), rate));
         for (size_t k = 0; k < 100; ++k) {
           auto delay = delay_controller->GetDelay(
-              delay_buffer->GetDownsampledRenderBuffer(), block);
+              delay_buffer->GetDownsampledRenderBuffer(), delay_buffer->Delay(),
+              echo_remover_delay_, block);
           EXPECT_EQ(config.delay.min_echo_path_delay_blocks, delay->delay);
         }
       }
@@ -74,6 +76,7 @@
 TEST(RenderDelayController, BasicApiCalls) {
   std::vector<float> capture_block(kBlockSize, 0.f);
   rtc::Optional<DelayEstimate> delay_blocks;
+  rtc::Optional<int> echo_remover_delay;
   for (size_t num_matched_filters = 4; num_matched_filters == 10;
        num_matched_filters++) {
     for (auto down_sampling_factor : kDownSamplingFactors) {
@@ -94,7 +97,8 @@
           render_delay_buffer->PrepareCaptureProcessing();
 
           delay_blocks = delay_controller->GetDelay(
-              render_delay_buffer->GetDownsampledRenderBuffer(), capture_block);
+              render_delay_buffer->GetDownsampledRenderBuffer(),
+              render_delay_buffer->Delay(), echo_remover_delay, capture_block);
         }
         EXPECT_TRUE(delay_blocks);
         EXPECT_EQ(config.delay.min_echo_path_delay_blocks, delay_blocks->delay);
@@ -107,6 +111,7 @@
 // simple timeshifts between the signals.
 TEST(RenderDelayController, Alignment) {
   Random random_generator(42U);
+  rtc::Optional<int> echo_remover_delay;
   std::vector<float> capture_block(kBlockSize, 0.f);
   for (size_t num_matched_filters = 4; num_matched_filters == 10;
        num_matched_filters++) {
@@ -136,6 +141,7 @@
             render_delay_buffer->PrepareCaptureProcessing();
             delay_blocks = delay_controller->GetDelay(
                 render_delay_buffer->GetDownsampledRenderBuffer(),
+                render_delay_buffer->Delay(), echo_remover_delay,
                 capture_block);
           }
           ASSERT_TRUE(!!delay_blocks);
@@ -156,6 +162,7 @@
 // delays.
 TEST(RenderDelayController, NonCausalAlignment) {
   Random random_generator(42U);
+  rtc::Optional<int> echo_remover_delay;
   for (size_t num_matched_filters = 4; num_matched_filters == 10;
        num_matched_filters++) {
     for (auto down_sampling_factor : kDownSamplingFactors) {
@@ -186,6 +193,7 @@
             render_delay_buffer->PrepareCaptureProcessing();
             delay_blocks = delay_controller->GetDelay(
                 render_delay_buffer->GetDownsampledRenderBuffer(),
+                render_delay_buffer->Delay(), echo_remover_delay,
                 capture_block[0]);
           }
 
@@ -200,6 +208,7 @@
 // simple timeshifts between the signals when there is jitter in the API calls.
 TEST(RenderDelayController, AlignmentWithJitter) {
   Random random_generator(42U);
+  rtc::Optional<int> echo_remover_delay;
   std::vector<float> capture_block(kBlockSize, 0.f);
   for (size_t num_matched_filters = 4; num_matched_filters == 10;
        num_matched_filters++) {
@@ -237,6 +246,7 @@
               render_delay_buffer->PrepareCaptureProcessing();
               delay_blocks = delay_controller->GetDelay(
                   render_delay_buffer->GetDownsampledRenderBuffer(),
+                  render_delay_buffer->Delay(), echo_remover_delay,
                   capture_block_buffer[k]);
             }
           }
@@ -286,6 +296,7 @@
 TEST(RenderDelayController, WrongCaptureSize) {
   std::vector<float> block(kBlockSize - 1, 0.f);
   EchoCanceller3Config config;
+  rtc::Optional<int> echo_remover_delay;
   for (auto rate : {8000, 16000, 32000, 48000}) {
     SCOPED_TRACE(ProduceDebugText(rate));
     std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
@@ -296,7 +307,7 @@
                 EchoCanceller3Config(),
                 RenderDelayBuffer::DelayEstimatorOffset(config), rate))
             ->GetDelay(render_delay_buffer->GetDownsampledRenderBuffer(),
-                       block),
+                       render_delay_buffer->Delay(), echo_remover_delay, block),
         "");
   }
 }
diff --git a/modules/audio_processing/aec3/residual_echo_estimator.cc b/modules/audio_processing/aec3/residual_echo_estimator.cc
index f0c971d..4b6c959 100644
--- a/modules/audio_processing/aec3/residual_echo_estimator.cc
+++ b/modules/audio_processing/aec3/residual_echo_estimator.cc
@@ -1,3 +1,4 @@
+
 /*
  *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
  *
@@ -16,65 +17,6 @@
 #include "rtc_base/checks.h"
 
 namespace webrtc {
-namespace {
-
-// Estimates the echo generating signal power as gated maximal power over a time
-// window.
-void EchoGeneratingPower(const RenderBuffer& render_buffer,
-                         size_t min_delay,
-                         size_t max_delay,
-                         std::array<float, kFftLengthBy2Plus1>* X2) {
-  X2->fill(0.f);
-  for (size_t k = min_delay; k <= max_delay; ++k) {
-    std::transform(X2->begin(), X2->end(), render_buffer.Spectrum(k).begin(),
-                   X2->begin(),
-                   [](float a, float b) { return std::max(a, b); });
-  }
-
-  // Apply soft noise gate of -78 dBFS.
-  static constexpr float kNoiseGatePower = 27509.42f;
-  std::for_each(X2->begin(), X2->end(), [](float& a) {
-    if (kNoiseGatePower > a) {
-      a = std::max(0.f, a - 0.3f * (kNoiseGatePower - a));
-    }
-  });
-}
-
-constexpr int kNoiseFloorCounterMax = 50;
-constexpr float kNoiseFloorMin = 10.f * 10.f * 128.f * 128.f;
-
-// Updates estimate for the power of the stationary noise component in the
-// render signal.
-void RenderNoisePower(
-    const RenderBuffer& render_buffer,
-    std::array<float, kFftLengthBy2Plus1>* X2_noise_floor,
-    std::array<int, kFftLengthBy2Plus1>* X2_noise_floor_counter) {
-  RTC_DCHECK(X2_noise_floor);
-  RTC_DCHECK(X2_noise_floor_counter);
-
-  const auto render_power = render_buffer.Spectrum(0);
-  RTC_DCHECK_EQ(X2_noise_floor->size(), render_power.size());
-  RTC_DCHECK_EQ(X2_noise_floor_counter->size(), render_power.size());
-
-  // Estimate the stationary noise power in a minimum statistics manner.
-  for (size_t k = 0; k < render_power.size(); ++k) {
-    // Decrease rapidly.
-    if (render_power[k] < (*X2_noise_floor)[k]) {
-      (*X2_noise_floor)[k] = render_power[k];
-      (*X2_noise_floor_counter)[k] = 0;
-    } else {
-      // Increase in a delayed, leaky manner.
-      if ((*X2_noise_floor_counter)[k] >= kNoiseFloorCounterMax) {
-        (*X2_noise_floor)[k] =
-            std::max((*X2_noise_floor)[k] * 1.1f, kNoiseFloorMin);
-      } else {
-        ++(*X2_noise_floor_counter)[k];
-      }
-    }
-  }
-}
-
-}  // namespace
 
 ResidualEchoEstimator::ResidualEchoEstimator(const EchoCanceller3Config& config)
     : config_(config), S2_old_(config_.filter.main.length_blocks) {
@@ -96,44 +38,59 @@
 
   // Estimate the residual echo power.
   if (aec_state.UsableLinearEstimate()) {
-    LinearEstimate(S2_linear, aec_state.Erle(), aec_state.FilterDelay(), R2);
-    AddEchoReverb(S2_linear, aec_state.SaturatedEcho(), aec_state.FilterDelay(),
+    RTC_DCHECK(!aec_state.SaturatedEcho());
+    LinearEstimate(S2_linear, aec_state.Erle(), R2);
+    AddEchoReverb(S2_linear, aec_state.FilterDelayBlocks(),
                   aec_state.ReverbDecay(), R2);
+  } else {
+    // Estimate the echo generating signal power.
+    std::array<float, kFftLengthBy2Plus1> X2;
+
+    // Computes the spectral power over the blocks surrounding the delay.
+    size_t window_start = std::max(
+        0, aec_state.FilterDelayBlocks() -
+               static_cast<int>(config_.echo_model.render_pre_window_size));
+    size_t window_end =
+        aec_state.FilterDelayBlocks() +
+        static_cast<int>(config_.echo_model.render_post_window_size);
+    EchoGeneratingPower(render_buffer, window_start, window_end, &X2);
+
+    // TODO(devicentepena): look if this is competing/completing
+    // with the stationarity estimator
+    // Subtract the stationary noise power to avoid stationary noise causing
+    // excessive echo suppression.
+    std::transform(X2.begin(), X2.end(), X2_noise_floor_.begin(), X2.begin(),
+                   [&](float a, float b) {
+                     return std::max(
+                         0.f, a - config_.echo_model.stationary_gate_slope * b);
+                   });
+
+    NonLinearEstimate(aec_state.EchoPathGain(), X2, Y2, R2);
 
     // If the echo is saturated, estimate the echo power as the maximum echo
     // power with a leakage factor.
     if (aec_state.SaturatedEcho()) {
       R2->fill((*std::max_element(R2->begin(), R2->end())) * 100.f);
     }
-  } else {
-    // Estimate the echo generating signal power.
-    std::array<float, kFftLengthBy2Plus1> X2;
 
-    // Computes the spectral power over the blocks surrounding the delay.
-    EchoGeneratingPower(render_buffer, std::max(0, aec_state.FilterDelay() - 1),
-                        aec_state.FilterDelay() + 10, &X2);
+    AddEchoReverb(*R2, config_.filter.main.length_blocks,
+                  aec_state.ReverbDecay(), R2);
+  }
 
-    // Subtract the stationary noise power to avoid stationary noise causing
-    // excessive echo suppression.
-    std::transform(
-        X2.begin(), X2.end(), X2_noise_floor_.begin(), X2.begin(),
-        [](float a, float b) { return std::max(0.f, a - 10.f * b); });
-
-    NonLinearEstimate(aec_state.FilterHasHadTimeToConverge(),
-                      aec_state.SaturatedEcho(),
-                      config_.ep_strength.bounded_erl,
-                      aec_state.TransparentMode(), X2, Y2, R2);
-
-    if (aec_state.SaturatedEcho()) {
-      // TODO(peah): Modify to make sense theoretically.
-      AddEchoReverb(*R2, aec_state.SaturatedEcho(),
-                    config_.filter.main.length_blocks, aec_state.ReverbDecay(),
-                    R2);
+  if (aec_state.UseStationaryProperties()) {
+    // Scale the echo according to echo audibility.
+    std::array<float, kFftLengthBy2Plus1> residual_scaling;
+    aec_state.GetResidualEchoScaling(residual_scaling);
+    for (size_t k = 0; k < R2->size(); ++k) {
+      (*R2)[k] *= residual_scaling[k];
+      if (residual_scaling[k] == 0.f) {
+        R2_hold_counter_[k] = 0;
+      }
     }
   }
 
   // If the echo is deemed inaudible, set the residual echo to zero.
-  if (aec_state.InaudibleEcho()) {
+  if (aec_state.TransparentMode()) {
     R2->fill(0.f);
     R2_old_.fill(0.f);
     R2_hold_counter_.fill(0.f);
@@ -143,8 +100,8 @@
 }
 
 void ResidualEchoEstimator::Reset() {
-  X2_noise_floor_counter_.fill(kNoiseFloorCounterMax);
-  X2_noise_floor_.fill(kNoiseFloorMin);
+  X2_noise_floor_counter_.fill(config_.echo_model.noise_floor_hold);
+  X2_noise_floor_.fill(config_.echo_model.min_noise_floor_power);
   R2_reverb_.fill(0.f);
   R2_old_.fill(0.f);
   R2_hold_counter_.fill(0.f);
@@ -156,7 +113,6 @@
 void ResidualEchoEstimator::LinearEstimate(
     const std::array<float, kFftLengthBy2Plus1>& S2_linear,
     const std::array<float, kFftLengthBy2Plus1>& erle,
-    size_t delay,
     std::array<float, kFftLengthBy2Plus1>* R2) {
   std::fill(R2_hold_counter_.begin(), R2_hold_counter_.end(), 10.f);
   std::transform(erle.begin(), erle.end(), S2_linear.begin(), R2->begin(),
@@ -167,46 +123,15 @@
 }
 
 void ResidualEchoEstimator::NonLinearEstimate(
-    bool sufficient_filter_updates,
-    bool saturated_echo,
-    bool bounded_erl,
-    bool transparent_mode,
+    float echo_path_gain,
     const std::array<float, kFftLengthBy2Plus1>& X2,
     const std::array<float, kFftLengthBy2Plus1>& Y2,
     std::array<float, kFftLengthBy2Plus1>* R2) {
-  float echo_path_gain_lf;
-  float echo_path_gain_mf;
-  float echo_path_gain_hf;
-
-  // Set echo path gains.
-  if (saturated_echo) {
-    // If the echo could be saturated, use a very conservative gain.
-    echo_path_gain_lf = echo_path_gain_mf = echo_path_gain_hf = 10000.f;
-  } else if (sufficient_filter_updates && !bounded_erl) {
-    // If the filter should have been able to converge, and no assumption is
-    // possible on the ERL, use a low gain.
-    echo_path_gain_lf = echo_path_gain_mf = echo_path_gain_hf = 0.01f;
-  } else if ((sufficient_filter_updates && bounded_erl) || transparent_mode) {
-    // If the filter should have been able to converge, and and it is known that
-    // the ERL is bounded, use a very low gain.
-    echo_path_gain_lf = echo_path_gain_mf = echo_path_gain_hf = 0.001f;
-  } else {
-    // In the initial state, use conservative gains.
-    echo_path_gain_lf = config_.ep_strength.lf;
-    echo_path_gain_mf = config_.ep_strength.mf;
-    echo_path_gain_hf = config_.ep_strength.hf;
-  }
 
   // Compute preliminary residual echo.
-  std::transform(
-      X2.begin(), X2.begin() + 12, R2->begin(),
-      [echo_path_gain_lf](float a) { return a * echo_path_gain_lf; });
-  std::transform(
-      X2.begin() + 12, X2.begin() + 25, R2->begin() + 12,
-      [echo_path_gain_mf](float a) { return a * echo_path_gain_mf; });
-  std::transform(
-      X2.begin() + 25, X2.end(), R2->begin() + 25,
-      [echo_path_gain_hf](float a) { return a * echo_path_gain_hf; });
+  std::transform(X2.begin(), X2.end(), R2->begin(), [echo_path_gain](float a) {
+    return a * echo_path_gain * echo_path_gain;
+  });
 
   for (size_t k = 0; k < R2->size(); ++k) {
     // Update hold counter.
@@ -214,15 +139,17 @@
 
     // Compute the residual echo by holding a maximum echo powers and an echo
     // fading corresponding to a room with an RT60 value of about 50 ms.
-    (*R2)[k] = R2_hold_counter_[k] < 2
-                   ? std::max((*R2)[k], R2_old_[k])
-                   : std::min((*R2)[k] + R2_old_[k] * 0.1f, Y2[k]);
+    (*R2)[k] =
+        R2_hold_counter_[k] < config_.echo_model.nonlinear_hold
+            ? std::max((*R2)[k], R2_old_[k])
+            : std::min(
+                  (*R2)[k] + R2_old_[k] * config_.echo_model.nonlinear_release,
+                  Y2[k]);
   }
 }
 
 void ResidualEchoEstimator::AddEchoReverb(
     const std::array<float, kFftLengthBy2Plus1>& S2,
-    bool saturated_echo,
     size_t delay,
     float reverb_decay_factor,
     std::array<float, kFftLengthBy2Plus1>* R2) {
@@ -249,16 +176,63 @@
       });
 
   // Update the buffer of old echo powers.
-  if (saturated_echo) {
-    S2_old_[S2_old_index_].fill((*std::max_element(S2.begin(), S2.end())) *
-                                100.f);
-  } else {
-    std::copy(S2.begin(), S2.end(), S2_old_[S2_old_index_].begin());
-  }
+  std::copy(S2.begin(), S2.end(), S2_old_[S2_old_index_].begin());
 
   // Add the power of the echo reverb to the residual echo power.
   std::transform(R2->begin(), R2->end(), R2_reverb_.begin(), R2->begin(),
                  std::plus<float>());
 }
 
+void ResidualEchoEstimator::EchoGeneratingPower(
+    const RenderBuffer& render_buffer,
+    size_t min_delay,
+    size_t max_delay,
+    std::array<float, kFftLengthBy2Plus1>* X2) const {
+  X2->fill(0.f);
+  for (size_t k = min_delay; k <= max_delay; ++k) {
+    std::transform(X2->begin(), X2->end(), render_buffer.Spectrum(k).begin(),
+                   X2->begin(),
+                   [](float a, float b) { return std::max(a, b); });
+  }
+
+  // Apply soft noise gate.
+  std::for_each(X2->begin(), X2->end(), [&](float& a) {
+    if (config_.echo_model.noise_gate_power > a) {
+      a = std::max(0.f, a - config_.echo_model.noise_gate_slope *
+                                (config_.echo_model.noise_gate_power - a));
+    }
+  });
+}
+
+void ResidualEchoEstimator::RenderNoisePower(
+    const RenderBuffer& render_buffer,
+    std::array<float, kFftLengthBy2Plus1>* X2_noise_floor,
+    std::array<int, kFftLengthBy2Plus1>* X2_noise_floor_counter) const {
+  RTC_DCHECK(X2_noise_floor);
+  RTC_DCHECK(X2_noise_floor_counter);
+
+  const auto render_power = render_buffer.Spectrum(0);
+  RTC_DCHECK_EQ(X2_noise_floor->size(), render_power.size());
+  RTC_DCHECK_EQ(X2_noise_floor_counter->size(), render_power.size());
+
+  // Estimate the stationary noise power in a minimum statistics manner.
+  for (size_t k = 0; k < render_power.size(); ++k) {
+    // Decrease rapidly.
+    if (render_power[k] < (*X2_noise_floor)[k]) {
+      (*X2_noise_floor)[k] = render_power[k];
+      (*X2_noise_floor_counter)[k] = 0;
+    } else {
+      // Increase in a delayed, leaky manner.
+      if ((*X2_noise_floor_counter)[k] >=
+          static_cast<int>(config_.echo_model.noise_floor_hold)) {
+        (*X2_noise_floor)[k] =
+            std::max((*X2_noise_floor)[k] * 1.1f,
+                     config_.echo_model.min_noise_floor_power);
+      } else {
+        ++(*X2_noise_floor_counter)[k];
+      }
+    }
+  }
+}
+
 }  // namespace webrtc
diff --git a/modules/audio_processing/aec3/residual_echo_estimator.h b/modules/audio_processing/aec3/residual_echo_estimator.h
index f7e2d1d..7b8a9b1 100644
--- a/modules/audio_processing/aec3/residual_echo_estimator.h
+++ b/modules/audio_processing/aec3/residual_echo_estimator.h
@@ -43,15 +43,11 @@
   // (ERLE) and the linear power estimate.
   void LinearEstimate(const std::array<float, kFftLengthBy2Plus1>& S2_linear,
                       const std::array<float, kFftLengthBy2Plus1>& erle,
-                      size_t delay,
                       std::array<float, kFftLengthBy2Plus1>* R2);
 
   // Estimates the residual echo power based on the estimate of the echo path
   // gain.
-  void NonLinearEstimate(bool sufficient_filter_updates,
-                         bool saturated_echo,
-                         bool bounded_erl,
-                         bool transparent_mode,
+  void NonLinearEstimate(float echo_path_gain,
                          const std::array<float, kFftLengthBy2Plus1>& X2,
                          const std::array<float, kFftLengthBy2Plus1>& Y2,
                          std::array<float, kFftLengthBy2Plus1>* R2);
@@ -59,10 +55,24 @@
   // Adds the estimated unmodelled echo power to the residual echo power
   // estimate.
   void AddEchoReverb(const std::array<float, kFftLengthBy2Plus1>& S2,
-                     bool saturated_echo,
                      size_t delay,
                      float reverb_decay_factor,
                      std::array<float, kFftLengthBy2Plus1>* R2);
+
+  // Estimates the echo generating signal power as gated maximal power over a
+  // time window.
+  void EchoGeneratingPower(const RenderBuffer& render_buffer,
+                           size_t min_delay,
+                           size_t max_delay,
+                           std::array<float, kFftLengthBy2Plus1>* X2) const;
+
+  // Updates estimate for the power of the stationary noise component in the
+  // render signal.
+  void RenderNoisePower(
+      const RenderBuffer& render_buffer,
+      std::array<float, kFftLengthBy2Plus1>* X2_noise_floor,
+      std::array<int, kFftLengthBy2Plus1>* X2_noise_floor_counter) const;
+
   const EchoCanceller3Config config_;
   std::array<float, kFftLengthBy2Plus1> R2_old_;
   std::array<int, kFftLengthBy2Plus1> R2_hold_counter_;
diff --git a/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc b/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc
index d46d518..7f9ad8d 100644
--- a/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc
+++ b/modules/audio_processing/aec3/residual_echo_estimator_unittest.cc
@@ -93,9 +93,8 @@
     render_delay_buffer->PrepareCaptureProcessing();
 
     aec_state.HandleEchoPathChange(echo_path_variability);
-    aec_state.Update(delay_estimate, H2, h, true,
-                     *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s,
-                     false);
+    aec_state.Update(delay_estimate, H2, h, true, false,
+                     *render_delay_buffer->GetRenderBuffer(), E2_main, Y2, s);
 
     estimator.Estimate(aec_state, *render_delay_buffer->GetRenderBuffer(),
                        S2_linear, Y2, &R2);
diff --git a/modules/audio_processing/aec3/stationarity_estimator.cc b/modules/audio_processing/aec3/stationarity_estimator.cc
new file mode 100644
index 0000000..8e065d2
--- /dev/null
+++ b/modules/audio_processing/aec3/stationarity_estimator.cc
@@ -0,0 +1,200 @@
+/*
+ *  Copyright (c) 2018 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_processing/aec3/stationarity_estimator.h"
+
+#include <algorithm>
+#include <array>
+#include <vector>
+
+#include "modules/audio_processing/aec3/aec3_common.h"
+#include "modules/audio_processing/aec3/vector_buffer.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/atomicops.h"
+
+namespace webrtc {
+
+namespace {
+constexpr float kMinNoisePower = 10.f;
+constexpr int kHangoverBlocks = kNumBlocksPerSecond / 20;
+constexpr int kNBlocksAverageInitPhase = 20;
+constexpr int kNBlocksInitialPhase = kNumBlocksPerSecond * 2.;
+}  // namespace
+
+StationarityEstimator::StationarityEstimator()
+    : data_dumper_(
+          new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))) {
+  Reset();
+}
+
+StationarityEstimator::~StationarityEstimator() = default;
+
+void StationarityEstimator::Reset() {
+  noise_.Reset();
+  hangovers_.fill(0);
+  stationarity_flags_.fill(false);
+}
+
+// Update just the noise estimator. Usefull until the delay is known
+void StationarityEstimator::UpdateNoiseEstimator(
+    rtc::ArrayView<const float> spectrum) {
+  noise_.Update(spectrum);
+  data_dumper_->DumpRaw("aec3_stationarity_noise_spectrum", noise_.Spectrum());
+}
+
+void StationarityEstimator::UpdateStationarityFlags(
+    const VectorBuffer& spectrum_buffer,
+    int idx_current,
+    int num_lookahead) {
+  std::array<int, kWindowLength> indexes;
+  int num_lookahead_bounded = std::min(num_lookahead, kWindowLength - 1);
+  int idx = idx_current;
+
+  if (num_lookahead_bounded < kWindowLength - 1) {
+    int num_lookback = (kWindowLength - 1) - num_lookahead_bounded;
+    idx = spectrum_buffer.OffsetIndex(idx_current, num_lookback);
+  }
+  // For estimating the stationarity properties of the current frame, the
+  // power for each band is accumulated for several consecutive spectra in the
+  // method EstimateBandStationarity.
+  // In order to avoid getting the indexes of the spectra for every band with
+  // its associated overhead, those indexes are stored in an array and then use
+  // when the estimation is done.
+  indexes[0] = idx;
+  for (size_t k = 1; k < indexes.size(); ++k) {
+    indexes[k] = spectrum_buffer.DecIndex(indexes[k - 1]);
+  }
+  RTC_DCHECK_EQ(
+      spectrum_buffer.DecIndex(indexes[kWindowLength - 1]),
+      spectrum_buffer.OffsetIndex(idx_current, -(num_lookahead_bounded + 1)));
+
+  for (size_t k = 0; k < stationarity_flags_.size(); ++k) {
+    stationarity_flags_[k] =
+        EstimateBandStationarity(spectrum_buffer, indexes, k);
+  }
+  UpdateHangover();
+  SmoothStationaryPerFreq();
+
+}
+
+bool StationarityEstimator::EstimateBandStationarity(
+    const VectorBuffer& spectrum_buffer,
+    const std::array<int, kWindowLength>& indexes,
+    size_t band) const {
+  constexpr float kThrStationarity = 10.f;
+  float acum_power = 0.f;
+  for (auto idx : indexes) {
+    acum_power += spectrum_buffer.buffer[idx][band];
+  }
+  float noise = kWindowLength * GetStationarityPowerBand(band);
+  RTC_CHECK_LT(0.f, noise);
+  bool stationary = acum_power < kThrStationarity * noise;
+  data_dumper_->DumpRaw("aec3_stationarity_long_ratio", acum_power / noise);
+  return stationary;
+}
+
+bool StationarityEstimator::AreAllBandsStationary() {
+  for (auto b : stationarity_flags_) {
+    if (!b)
+      return false;
+  }
+  return true;
+}
+
+void StationarityEstimator::UpdateHangover() {
+  bool reduce_hangover = AreAllBandsStationary();
+  for (size_t k = 0; k < stationarity_flags_.size(); ++k) {
+    if (!stationarity_flags_[k]) {
+      hangovers_[k] = kHangoverBlocks;
+    } else if (reduce_hangover) {
+      hangovers_[k] = std::max(hangovers_[k] - 1, 0);
+    }
+  }
+}
+
+void StationarityEstimator::SmoothStationaryPerFreq() {
+  std::array<bool, kFftLengthBy2Plus1> all_ahead_stationary_smooth;
+  for (size_t k = 1; k < kFftLengthBy2Plus1 - 1; ++k) {
+    all_ahead_stationary_smooth[k] = stationarity_flags_[k - 1] &&
+                                     stationarity_flags_[k] &&
+                                     stationarity_flags_[k + 1];
+  }
+
+  all_ahead_stationary_smooth[0] = all_ahead_stationary_smooth[1];
+  all_ahead_stationary_smooth[kFftLengthBy2Plus1 - 1] =
+      all_ahead_stationary_smooth[kFftLengthBy2Plus1 - 2];
+
+  stationarity_flags_ = all_ahead_stationary_smooth;
+}
+
+int StationarityEstimator::instance_count_ = 0;
+
+StationarityEstimator::NoiseSpectrum::NoiseSpectrum() {
+  Reset();
+}
+
+StationarityEstimator::NoiseSpectrum::~NoiseSpectrum() = default;
+
+void StationarityEstimator::NoiseSpectrum::Reset() {
+  block_counter_ = 0;
+  noise_spectrum_.fill(kMinNoisePower);
+}
+
+void StationarityEstimator::NoiseSpectrum::Update(
+    rtc::ArrayView<const float> spectrum) {
+  RTC_DCHECK_EQ(kFftLengthBy2Plus1, spectrum.size());
+  ++block_counter_;
+  float alpha = GetAlpha();
+  for (size_t k = 0; k < spectrum.size(); ++k) {
+    if (block_counter_ <= kNBlocksAverageInitPhase) {
+      noise_spectrum_[k] += (1.f / kNBlocksAverageInitPhase) * spectrum[k];
+    } else {
+      noise_spectrum_[k] =
+          UpdateBandBySmoothing(spectrum[k], noise_spectrum_[k], alpha);
+    }
+  }
+}
+
+float StationarityEstimator::NoiseSpectrum::GetAlpha() const {
+  constexpr float kAlpha = 0.004f;
+  constexpr float kAlphaInit = 0.04f;
+  constexpr float kTiltAlpha = (kAlphaInit - kAlpha) / kNBlocksInitialPhase;
+
+  if (block_counter_ > (kNBlocksInitialPhase + kNBlocksAverageInitPhase)) {
+    return kAlpha;
+  } else {
+    return kAlphaInit -
+           kTiltAlpha * (block_counter_ - kNBlocksAverageInitPhase);
+  }
+}
+
+float StationarityEstimator::NoiseSpectrum::UpdateBandBySmoothing(
+    float power_band,
+    float power_band_noise,
+    float alpha) const {
+  float power_band_noise_updated = power_band_noise;
+  if (power_band_noise < power_band) {
+    RTC_DCHECK_GT(power_band, 0.f);
+    float alpha_inc = alpha * (power_band_noise / power_band);
+    if (block_counter_ > kNBlocksInitialPhase) {
+      if (10.f * power_band_noise < power_band) {
+        alpha_inc *= 0.1f;
+      }
+    }
+    power_band_noise_updated += alpha_inc * (power_band - power_band_noise);
+  } else {
+    power_band_noise_updated += alpha * (power_band - power_band_noise);
+    power_band_noise_updated =
+        std::max(power_band_noise_updated, kMinNoisePower);
+  }
+  return power_band_noise_updated;
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/aec3/stationarity_estimator.h b/modules/audio_processing/aec3/stationarity_estimator.h
new file mode 100644
index 0000000..8f01790
--- /dev/null
+++ b/modules/audio_processing/aec3/stationarity_estimator.h
@@ -0,0 +1,111 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AEC3_STATIONARITY_ESTIMATOR_H_
+#define MODULES_AUDIO_PROCESSING_AEC3_STATIONARITY_ESTIMATOR_H_
+
+#include <array>
+#include <memory>
+#include <vector>
+
+#include "api/array_view.h"
+#include "modules/audio_processing/aec3/aec3_common.h"
+#include "modules/audio_processing/aec3/vector_buffer.h"
+
+namespace webrtc {
+
+class ApmDataDumper;
+
+class StationarityEstimator {
+ public:
+  StationarityEstimator();
+  ~StationarityEstimator();
+
+  // Reset the stationarity estimator.
+  void Reset();
+
+  // Update just the noise estimator. Usefull until the delay is known
+  void UpdateNoiseEstimator(rtc::ArrayView<const float> spectrum);
+
+  // Update the flag indicating whether this current frame is stationary. For
+  // getting a more robust estimation, it looks at future and/or past frames.
+  void UpdateStationarityFlags(const VectorBuffer& spectrum_buffer,
+                               int idx_current,
+                               int num_lookahead);
+
+  // Returns true if the current band is stationary.
+  bool IsBandStationary(size_t band) const {
+    return stationarity_flags_[band] && (hangovers_[band] == 0);
+  }
+
+ private:
+  static constexpr int kWindowLength = 13;
+  // Returns the power of the stationary noise spectrum at a band.
+  float GetStationarityPowerBand(size_t k) const { return noise_.Power(k); }
+
+  // Get an estimation of the stationarity for the current band by looking
+  // at the past/present/future available data.
+  bool EstimateBandStationarity(const VectorBuffer& spectrum_buffer,
+                                const std::array<int, kWindowLength>& indexes,
+                                size_t band) const;
+
+  // True if all bands at the current point are stationary.
+  bool AreAllBandsStationary();
+
+  // Update the hangover depending on the stationary status of the current
+  // frame.
+  void UpdateHangover();
+
+  // Smooth the stationarity detection by looking at neighbouring frequency
+  // bands.
+  void SmoothStationaryPerFreq();
+
+  class NoiseSpectrum {
+   public:
+    NoiseSpectrum();
+    ~NoiseSpectrum();
+
+    // Reset the noise power spectrum estimate state.
+    void Reset();
+
+    // Update the noise power spectrum with a new frame.
+    void Update(rtc::ArrayView<const float> spectrum);
+
+    // Get the noise estimation power spectrum.
+    rtc::ArrayView<const float> Spectrum() const { return noise_spectrum_; }
+
+    // Get the noise power spectrum at a certain band.
+    float Power(size_t band) const {
+      RTC_DCHECK_LT(band, noise_spectrum_.size());
+      return noise_spectrum_[band];
+    }
+
+   private:
+    // Get the update coefficient to be used for the current frame.
+    float GetAlpha() const;
+
+    // Update the noise power spectrum at a certain band with a new frame.
+    float UpdateBandBySmoothing(float power_band,
+                                float power_band_noise,
+                                float alpha) const;
+    std::array<float, kFftLengthBy2Plus1> noise_spectrum_;
+    size_t block_counter_;
+  };
+
+  static int instance_count_;
+  std::unique_ptr<ApmDataDumper> data_dumper_;
+  NoiseSpectrum noise_;
+  std::array<int, kFftLengthBy2Plus1> hangovers_;
+  std::array<bool, kFftLengthBy2Plus1> stationarity_flags_;
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AEC3_STATIONARITY_ESTIMATOR_H_
diff --git a/modules/audio_processing/aec3/subtractor.cc b/modules/audio_processing/aec3/subtractor.cc
index b6a68af..a72a667 100644
--- a/modules/audio_processing/aec3/subtractor.cc
+++ b/modules/audio_processing/aec3/subtractor.cc
@@ -99,6 +99,7 @@
     shadow_filter_converged_ = false;
     main_filter_.SetSizePartitions(config_.filter.main_initial.length_blocks,
                                    true);
+    main_filter_once_converged_ = false;
     shadow_filter_.SetSizePartitions(
         config_.filter.shadow_initial.length_blocks, true);
   };
@@ -133,7 +134,6 @@
   RTC_DCHECK_EQ(kBlockSize, capture.size());
   rtc::ArrayView<const float> y = capture;
   FftData& E_main = output->E_main;
-  FftData& E_main_nonwindowed = output->E_main_nonwindowed;
   FftData E_shadow;
   std::array<float, kBlockSize>& e_main = output->e_main;
   std::array<float, kBlockSize>& e_shadow = output->e_shadow;
@@ -153,37 +153,26 @@
   PredictionError(fft_, S, y, &e_shadow, nullptr, &shadow_saturation);
   fft_.ZeroPaddedFft(e_shadow, Aec3Fft::Window::kHanning, &E_shadow);
 
-  if (!(main_filter_converged_ || shadow_filter_converged_)) {
-    const auto sum_of_squares = [](float a, float b) { return a + b * b; };
-    const float y2 = std::accumulate(y.begin(), y.end(), 0.f, sum_of_squares);
+  // Check for filter convergence.
+  const auto sum_of_squares = [](float a, float b) { return a + b * b; };
+  const float y2 = std::accumulate(y.begin(), y.end(), 0.f, sum_of_squares);
+  const float e2_main =
+      std::accumulate(e_main.begin(), e_main.end(), 0.f, sum_of_squares);
+  const float e2_shadow =
+      std::accumulate(e_shadow.begin(), e_shadow.end(), 0.f, sum_of_squares);
 
-    if (!main_filter_converged_) {
-      const float e2_main =
-          std::accumulate(e_main.begin(), e_main.end(), 0.f, sum_of_squares);
-      main_filter_converged_ = e2_main > 0.1 * y2;
-    }
-
-    if (!shadow_filter_converged_) {
-      const float e2_shadow = std::accumulate(e_shadow.begin(), e_shadow.end(),
-                                              0.f, sum_of_squares);
-      shadow_filter_converged_ = e2_shadow > 0.1 * y2;
-    }
-  }
+  constexpr float kConvergenceThreshold = 50 * 50 * kBlockSize;
+  main_filter_converged_ = e2_main < 0.2 * y2 && y2 > kConvergenceThreshold;
+  shadow_filter_converged_ =
+      e2_shadow < 0.05 * y2 && y2 > kConvergenceThreshold;
+  main_filter_once_converged_ =
+      main_filter_once_converged_ || main_filter_converged_;
+  main_filter_diverged_ = e2_main > 1.5f * y2 && y2 > 30.f * 30.f * kBlockSize;
 
   // Compute spectra for future use.
   E_shadow.Spectrum(optimization_, output->E2_shadow);
   E_main.Spectrum(optimization_, output->E2_main);
 
-  if (main_filter_converged_ || !shadow_filter_converged_) {
-    fft_.ZeroPaddedFft(e_main, Aec3Fft::Window::kRectangular,
-                       &E_main_nonwindowed);
-    E_main_nonwindowed.Spectrum(optimization_, output->E2_main_nonwindowed);
-  } else {
-    fft_.ZeroPaddedFft(e_shadow, Aec3Fft::Window::kRectangular,
-                       &E_main_nonwindowed);
-    E_main_nonwindowed.Spectrum(optimization_, output->E2_main_nonwindowed);
-  }
-
   // Update the main filter.
   std::array<float, kFftLengthBy2Plus1> X2;
   render_buffer.SpectralSum(main_filter_.SizePartitions(), &X2);
@@ -205,9 +194,7 @@
   data_dumper_->DumpRaw("aec3_subtractor_G_shadow", G.re);
   data_dumper_->DumpRaw("aec3_subtractor_G_shadow", G.im);
 
-  main_filter_.DumpFilter("aec3_subtractor_H_main", "aec3_subtractor_h_main");
-  shadow_filter_.DumpFilter("aec3_subtractor_H_shadow",
-                            "aec3_subtractor_h_shadow");
+  DumpFilters();
 }
 
 }  // namespace webrtc
diff --git a/modules/audio_processing/aec3/subtractor.h b/modules/audio_processing/aec3/subtractor.h
index b3c8506..38fc3c6 100644
--- a/modules/audio_processing/aec3/subtractor.h
+++ b/modules/audio_processing/aec3/subtractor.h
@@ -53,14 +53,14 @@
   // Returns the block-wise frequency response for the main adaptive filter.
   const std::vector<std::array<float, kFftLengthBy2Plus1>>&
   FilterFrequencyResponse() const {
-    return main_filter_converged_ || (!shadow_filter_converged_)
+    return main_filter_once_converged_ || (!shadow_filter_converged_)
                ? main_filter_.FilterFrequencyResponse()
                : shadow_filter_.FilterFrequencyResponse();
   }
 
   // Returns the estimate of the impulse response for the main adaptive filter.
   const std::vector<float>& FilterImpulseResponse() const {
-    return main_filter_converged_ || (!shadow_filter_converged_)
+    return main_filter_once_converged_ || (!shadow_filter_converged_)
                ? main_filter_.FilterImpulseResponse()
                : shadow_filter_.FilterImpulseResponse();
   }
@@ -69,6 +69,14 @@
     return main_filter_converged_ || shadow_filter_converged_;
   }
 
+  bool DivergedFilter() const { return main_filter_diverged_; }
+
+  void DumpFilters() {
+    main_filter_.DumpFilter("aec3_subtractor_H_main", "aec3_subtractor_h_main");
+    shadow_filter_.DumpFilter("aec3_subtractor_H_shadow",
+                              "aec3_subtractor_h_shadow");
+  }
+
  private:
   const Aec3Fft fft_;
   ApmDataDumper* data_dumper_;
@@ -79,7 +87,9 @@
   MainFilterUpdateGain G_main_;
   ShadowFilterUpdateGain G_shadow_;
   bool main_filter_converged_ = false;
+  bool main_filter_once_converged_ = false;
   bool shadow_filter_converged_ = false;
+  bool main_filter_diverged_ = false;
   RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(Subtractor);
 };
 
diff --git a/modules/audio_processing/aec3/subtractor_output.h b/modules/audio_processing/aec3/subtractor_output.h
index 83f6cf5..8655665 100644
--- a/modules/audio_processing/aec3/subtractor_output.h
+++ b/modules/audio_processing/aec3/subtractor_output.h
@@ -24,9 +24,7 @@
   std::array<float, kBlockSize> e_main;
   std::array<float, kBlockSize> e_shadow;
   FftData E_main;
-  FftData E_main_nonwindowed;
   std::array<float, kFftLengthBy2Plus1> E2_main;
-  std::array<float, kFftLengthBy2Plus1> E2_main_nonwindowed;
   std::array<float, kFftLengthBy2Plus1> E2_shadow;
 
   void Reset() {
diff --git a/modules/audio_processing/aec3/subtractor_unittest.cc b/modules/audio_processing/aec3/subtractor_unittest.cc
index 5a8e070..097d7e8 100644
--- a/modules/audio_processing/aec3/subtractor_unittest.cc
+++ b/modules/audio_processing/aec3/subtractor_unittest.cc
@@ -68,7 +68,7 @@
     }
     render_delay_buffer->PrepareCaptureProcessing();
     render_signal_analyzer.Update(*render_delay_buffer->GetRenderBuffer(),
-                                  aec_state.FilterDelay());
+                                  aec_state.FilterDelayBlocks());
 
     // Handle echo path changes.
     if (std::find(blocks_with_echo_path_changes.begin(),
@@ -85,9 +85,9 @@
         false, EchoPathVariability::DelayAdjustment::kNone, false));
     aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
                      subtractor.FilterImpulseResponse(),
-                     subtractor.ConvergedFilter(),
+                     subtractor.ConvergedFilter(), subtractor.DivergedFilter(),
                      *render_delay_buffer->GetRenderBuffer(), E2_main, Y2,
-                     output.s_main, false);
+                     output.s_main);
   }
 
   const float output_power = std::inner_product(
diff --git a/modules/audio_processing/aec3/suppression_filter.cc b/modules/audio_processing/aec3/suppression_filter.cc
index 8c92bf5..87e3008 100644
--- a/modules/audio_processing/aec3/suppression_filter.cc
+++ b/modules/audio_processing/aec3/suppression_filter.cc
@@ -64,7 +64,6 @@
       fft_(),
       e_output_old_(NumBandsForRate(sample_rate_hz_)) {
   RTC_DCHECK(ValidFullBandRate(sample_rate_hz_));
-  e_input_old_.fill(0.f);
   std::for_each(e_output_old_.begin(), e_output_old_.end(),
                 [](std::array<float, kFftLengthBy2>& a) { a.fill(0.f); });
 }
@@ -76,22 +75,14 @@
     const FftData& comfort_noise_high_band,
     const std::array<float, kFftLengthBy2Plus1>& suppression_gain,
     float high_bands_gain,
+    const FftData& E_lowest_band,
     std::vector<std::vector<float>>* e) {
   RTC_DCHECK(e);
   RTC_DCHECK_EQ(e->size(), NumBandsForRate(sample_rate_hz_));
   FftData E;
-  std::array<float, kFftLength> e_extended;
-  constexpr float kIfftNormalization = 2.f / kFftLength;
 
   // Analysis filterbank.
-  std::transform(e_input_old_.begin(), e_input_old_.end(),
-                 std::begin(kSqrtHanning), e_extended.begin(),
-                 std::multiplies<float>());
-  std::transform((*e)[0].begin(), (*e)[0].end(),
-                 std::begin(kSqrtHanning) + kFftLengthBy2,
-                 e_extended.begin() + kFftLengthBy2, std::multiplies<float>());
-  std::copy((*e)[0].begin(), (*e)[0].end(), e_input_old_.begin());
-  fft_.Fft(&e_extended, &E);
+  E.Assign(E_lowest_band);
 
   // Apply gain.
   std::transform(suppression_gain.begin(), suppression_gain.end(), E.re.begin(),
@@ -113,6 +104,9 @@
                  E.im.begin(), E.im.begin(), std::plus<float>());
 
   // Synthesis filterbank.
+  std::array<float, kFftLength> e_extended;
+  constexpr float kIfftNormalization = 2.f / kFftLength;
+
   fft_.Ifft(E, &e_extended);
   std::transform(e_output_old_[0].begin(), e_output_old_[0].end(),
                  std::begin(kSqrtHanning) + kFftLengthBy2, (*e)[0].begin(),
diff --git a/modules/audio_processing/aec3/suppression_filter.h b/modules/audio_processing/aec3/suppression_filter.h
index 5f91dea..237408d 100644
--- a/modules/audio_processing/aec3/suppression_filter.h
+++ b/modules/audio_processing/aec3/suppression_filter.h
@@ -28,13 +28,13 @@
                  const FftData& comfort_noise_high_bands,
                  const std::array<float, kFftLengthBy2Plus1>& suppression_gain,
                  float high_bands_gain,
+                 const FftData& E_lowest_band,
                  std::vector<std::vector<float>>* e);
 
  private:
   const int sample_rate_hz_;
   const OouraFft ooura_fft_;
   const Aec3Fft fft_;
-  std::array<float, kFftLengthBy2> e_input_old_;
   std::vector<std::array<float, kFftLengthBy2>> e_output_old_;
   RTC_DISALLOW_COPY_AND_ASSIGN(SuppressionFilter);
 };
diff --git a/modules/audio_processing/aec3/suppression_filter_unittest.cc b/modules/audio_processing/aec3/suppression_filter_unittest.cc
index 51b3f91..eaa608e 100644
--- a/modules/audio_processing/aec3/suppression_filter_unittest.cc
+++ b/modules/audio_processing/aec3/suppression_filter_unittest.cc
@@ -42,10 +42,11 @@
 TEST(SuppressionFilter, NullOutput) {
   FftData cn;
   FftData cn_high_bands;
+  FftData E;
   std::array<float, kFftLengthBy2Plus1> gain;
 
   EXPECT_DEATH(SuppressionFilter(16000).ApplyGain(cn, cn_high_bands, gain, 1.0f,
-                                                  nullptr),
+                                                  E, nullptr),
                "");
 }
 
@@ -62,7 +63,10 @@
   FftData cn;
   FftData cn_high_bands;
   std::array<float, kFftLengthBy2Plus1> gain;
+  std::array<float, kFftLengthBy2> e_old_;
+  Aec3Fft fft;
 
+  e_old_.fill(0.f);
   gain.fill(1.f);
   cn.re.fill(1.f);
   cn.im.fill(1.f);
@@ -71,7 +75,12 @@
 
   std::vector<std::vector<float>> e(3, std::vector<float>(kBlockSize, 0.f));
   std::vector<std::vector<float>> e_ref = e;
-  filter.ApplyGain(cn, cn_high_bands, gain, 1.f, &e);
+
+  FftData E;
+  fft.PaddedFft(e[0], e_old_, Aec3Fft::Window::kSqrtHanning, &E);
+  std::copy(e[0].begin(), e[0].end(), e_old_.begin());
+
+  filter.ApplyGain(cn, cn_high_bands, gain, 1.f, E, &e);
 
   for (size_t k = 0; k < e.size(); ++k) {
     EXPECT_EQ(e_ref[k], e[k]);
@@ -83,8 +92,11 @@
   SuppressionFilter filter(48000);
   FftData cn;
   FftData cn_high_bands;
+  std::array<float, kFftLengthBy2> e_old_;
+  Aec3Fft fft;
   std::array<float, kFftLengthBy2Plus1> gain;
   std::vector<std::vector<float>> e(3, std::vector<float>(kBlockSize, 0.f));
+  e_old_.fill(0.f);
 
   gain.fill(1.f);
   std::for_each(gain.begin() + 10, gain.end(), [](float& a) { a = 0.f; });
@@ -103,7 +115,12 @@
                     e[0]);
     e0_input =
         std::inner_product(e[0].begin(), e[0].end(), e[0].begin(), e0_input);
-    filter.ApplyGain(cn, cn_high_bands, gain, 1.f, &e);
+
+    FftData E;
+    fft.PaddedFft(e[0], e_old_, Aec3Fft::Window::kSqrtHanning, &E);
+    std::copy(e[0].begin(), e[0].end(), e_old_.begin());
+
+    filter.ApplyGain(cn, cn_high_bands, gain, 1.f, E, &e);
     e0_output =
         std::inner_product(e[0].begin(), e[0].end(), e[0].begin(), e0_output);
   }
@@ -116,10 +133,12 @@
 TEST(SuppressionFilter, SignalTransparency) {
   SuppressionFilter filter(48000);
   FftData cn;
+  std::array<float, kFftLengthBy2> e_old_;
+  Aec3Fft fft;
   FftData cn_high_bands;
   std::array<float, kFftLengthBy2Plus1> gain;
   std::vector<std::vector<float>> e(3, std::vector<float>(kBlockSize, 0.f));
-
+  e_old_.fill(0.f);
   gain.fill(1.f);
   std::for_each(gain.begin() + 30, gain.end(), [](float& a) { a = 0.f; });
 
@@ -137,7 +156,12 @@
                     e[0]);
     e0_input =
         std::inner_product(e[0].begin(), e[0].end(), e[0].begin(), e0_input);
-    filter.ApplyGain(cn, cn_high_bands, gain, 1.f, &e);
+
+    FftData E;
+    fft.PaddedFft(e[0], e_old_, Aec3Fft::Window::kSqrtHanning, &E);
+    std::copy(e[0].begin(), e[0].end(), e_old_.begin());
+
+    filter.ApplyGain(cn, cn_high_bands, gain, 1.f, E, &e);
     e0_output =
         std::inner_product(e[0].begin(), e[0].end(), e[0].begin(), e0_output);
   }
@@ -150,6 +174,8 @@
   SuppressionFilter filter(48000);
   FftData cn;
   FftData cn_high_bands;
+  std::array<float, kFftLengthBy2> e_old_;
+  Aec3Fft fft;
   std::array<float, kFftLengthBy2Plus1> gain;
   std::vector<std::vector<float>> e(3, std::vector<float>(kBlockSize, 0.f));
 
@@ -167,7 +193,11 @@
       }
     }
 
-    filter.ApplyGain(cn, cn_high_bands, gain, 1.f, &e);
+    FftData E;
+    fft.PaddedFft(e[0], e_old_, Aec3Fft::Window::kSqrtHanning, &E);
+    std::copy(e[0].begin(), e[0].end(), e_old_.begin());
+
+    filter.ApplyGain(cn, cn_high_bands, gain, 1.f, E, &e);
     if (k > 2) {
       for (size_t j = 0; j < 2; ++j) {
         for (size_t i = 0; i < kBlockSize; ++i) {
diff --git a/modules/audio_processing/aec3/suppression_gain.cc b/modules/audio_processing/aec3/suppression_gain.cc
index 53fd575..9591c67 100644
--- a/modules/audio_processing/aec3/suppression_gain.cc
+++ b/modules/audio_processing/aec3/suppression_gain.cc
@@ -1,3 +1,4 @@
+
 /*
  *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
  *
@@ -20,21 +21,13 @@
 #include <numeric>
 
 #include "modules/audio_processing/aec3/vector_math.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/atomicops.h"
 #include "rtc_base/checks.h"
 
 namespace webrtc {
 namespace {
 
-// Reduce gain to avoid narrow band echo leakage.
-void NarrowBandAttenuation(int narrow_bin,
-                           std::array<float, kFftLengthBy2Plus1>* gain) {
-  const int upper_bin =
-      std::min(narrow_bin + 6, static_cast<int>(kFftLengthBy2Plus1 - 1));
-  for (int k = std::max(0, narrow_bin - 6); k <= upper_bin; ++k) {
-    (*gain)[k] = std::min((*gain)[k], 0.001f);
-  }
-}
-
 // Adjust the gains according to the presence of known external filters.
 void AdjustForExternalFilters(std::array<float, kFftLengthBy2Plus1>* gain) {
   // Limit the low frequency gains to avoid the impact of the high-pass filter
@@ -107,19 +100,60 @@
   return std::min(gain_below_8_khz, anti_howling_gain);
 }
 
+// Scales the echo according to assessed audibility at the other end.
+void WeightEchoForAudibility(const EchoCanceller3Config& config,
+                             rtc::ArrayView<const float> echo,
+                             rtc::ArrayView<float> weighted_echo,
+                             rtc::ArrayView<float> one_by_weighted_echo) {
+  RTC_DCHECK_EQ(kFftLengthBy2Plus1, echo.size());
+  RTC_DCHECK_EQ(kFftLengthBy2Plus1, weighted_echo.size());
+  RTC_DCHECK_EQ(kFftLengthBy2Plus1, one_by_weighted_echo.size());
+
+  auto weigh = [](float threshold, float normalizer, size_t begin, size_t end,
+                  rtc::ArrayView<const float> echo,
+                  rtc::ArrayView<float> weighted_echo,
+                  rtc::ArrayView<float> one_by_weighted_echo) {
+    for (size_t k = begin; k < end; ++k) {
+      if (echo[k] < threshold) {
+        float tmp = (threshold - echo[k]) * normalizer;
+        weighted_echo[k] = echo[k] * std::max(0.f, 1.f - tmp * tmp);
+      } else {
+        weighted_echo[k] = echo[k];
+      }
+      one_by_weighted_echo[k] =
+          weighted_echo[k] > 0.f ? 1.f / weighted_echo[k] : 1.f;
+    }
+  };
+
+  float threshold = config.echo_audibility.floor_power *
+                    config.echo_audibility.audibility_threshold_lf;
+  float normalizer = 1.f / (threshold - config.echo_audibility.floor_power);
+  weigh(threshold, normalizer, 0, 3, echo, weighted_echo, one_by_weighted_echo);
+
+  threshold = config.echo_audibility.floor_power *
+              config.echo_audibility.audibility_threshold_mf;
+  normalizer = 1.f / (threshold - config.echo_audibility.floor_power);
+  weigh(threshold, normalizer, 3, 7, echo, weighted_echo, one_by_weighted_echo);
+
+  threshold = config.echo_audibility.floor_power *
+              config.echo_audibility.audibility_threshold_hf;
+  normalizer = 1.f / (threshold - config.echo_audibility.floor_power);
+  weigh(threshold, normalizer, 7, kFftLengthBy2Plus1, echo, weighted_echo,
+        one_by_weighted_echo);
+}
+
 // Computes the gain to reduce the echo to a non audible level.
 void GainToNoAudibleEcho(
     const EchoCanceller3Config& config,
     bool low_noise_render,
     bool saturated_echo,
-    bool saturating_echo_path,
     bool linear_echo_estimate,
     const std::array<float, kFftLengthBy2Plus1>& nearend,
-    const std::array<float, kFftLengthBy2Plus1>& echo,
+    const std::array<float, kFftLengthBy2Plus1>& weighted_echo,
     const std::array<float, kFftLengthBy2Plus1>& masker,
     const std::array<float, kFftLengthBy2Plus1>& min_gain,
     const std::array<float, kFftLengthBy2Plus1>& max_gain,
-    const std::array<float, kFftLengthBy2Plus1>& one_by_echo,
+    const std::array<float, kFftLengthBy2Plus1>& one_by_weighted_echo,
     std::array<float, kFftLengthBy2Plus1>* gain) {
   float nearend_masking_margin = 0.f;
   if (linear_echo_estimate) {
@@ -133,8 +167,6 @@
 
   RTC_DCHECK_LE(0.f, nearend_masking_margin);
   RTC_DCHECK_GT(1.f, nearend_masking_margin);
-  const float one_by_one_minus_nearend_masking_margin =
-      1.f / (1.0f - nearend_masking_margin);
 
   const float masker_margin =
       linear_echo_estimate ? config.gain_mask.m1 : config.gain_mask.m8;
@@ -142,15 +174,17 @@
   for (size_t k = 0; k < gain->size(); ++k) {
     const float unity_gain_masker = std::max(nearend[k], masker[k]);
     RTC_DCHECK_LE(0.f, nearend_masking_margin * unity_gain_masker);
-    if (echo[k] <= nearend_masking_margin * unity_gain_masker ||
+    if (weighted_echo[k] <= nearend_masking_margin * unity_gain_masker ||
         unity_gain_masker <= 0.f) {
       (*gain)[k] = 1.f;
     } else {
       RTC_DCHECK_LT(0.f, unity_gain_masker);
-      (*gain)[k] = std::max(0.f, (1.f - 5.f * echo[k] / unity_gain_masker) *
-                                     one_by_one_minus_nearend_masking_margin);
       (*gain)[k] =
-          std::max(masker_margin * masker[k] * one_by_echo[k], (*gain)[k]);
+          std::max(0.f, (1.f - config.gain_mask.gain_curve_slope *
+                                   weighted_echo[k] / unity_gain_masker) *
+                            config.gain_mask.gain_curve_offset);
+      (*gain)[k] = std::max(masker_margin * masker[k] * one_by_weighted_echo[k],
+                            (*gain)[k]);
     }
 
     (*gain)[k] = std::min(std::max((*gain)[k], min_gain[k]), max_gain[k]);
@@ -167,6 +201,20 @@
                   const std::array<float, kFftLengthBy2Plus1>& last_masker,
                   const std::array<float, kFftLengthBy2Plus1>& gain,
                   std::array<float, kFftLengthBy2Plus1>* masker) {
+  // Apply masking over time.
+  float masking_factor = config.gain_mask.temporal_masking_lf;
+  auto limit = config.gain_mask.temporal_masking_lf_bands;
+  std::transform(
+      comfort_noise.begin(), comfort_noise.begin() + limit, last_masker.begin(),
+      masker->begin(),
+      [masking_factor](float a, float b) { return a + masking_factor * b; });
+  masking_factor = config.gain_mask.temporal_masking_hf;
+  std::transform(
+      comfort_noise.begin() + limit, comfort_noise.end(),
+      last_masker.begin() + limit, masker->begin() + limit,
+      [masking_factor](float a, float b) { return a + masking_factor * b; });
+
+  // Apply masking only between lower frequency bands.
   std::array<float, kFftLengthBy2Plus1> side_band_masker;
   float max_nearend_after_gain = 0.f;
   for (size_t k = 0; k < gain.size(); ++k) {
@@ -174,10 +222,8 @@
     max_nearend_after_gain =
         std::max(max_nearend_after_gain, nearend_after_gain);
     side_band_masker[k] = nearend_after_gain + comfort_noise[k];
-    (*masker)[k] = comfort_noise[k] + config.gain_mask.m4 * last_masker[k];
   }
 
-  // Apply masking only between lower frequency bands.
   RTC_DCHECK_LT(kUpperAccurateBandPlus1, gain.size());
   for (size_t k = 1; k < kUpperAccurateBandPlus1; ++k) {
     (*masker)[k] += config.gain_mask.m5 *
@@ -209,27 +255,24 @@
 
 }  // namespace
 
+int SuppressionGain::instance_count_ = 0;
+
 // TODO(peah): Add further optimizations, in particular for the divisions.
 void SuppressionGain::LowerBandGain(
     bool low_noise_render,
-    const rtc::Optional<int>& narrow_peak_band,
     const AecState& aec_state,
     const std::array<float, kFftLengthBy2Plus1>& nearend,
     const std::array<float, kFftLengthBy2Plus1>& echo,
     const std::array<float, kFftLengthBy2Plus1>& comfort_noise,
     std::array<float, kFftLengthBy2Plus1>* gain) {
   const bool saturated_echo = aec_state.SaturatedEcho();
-  const bool saturating_echo_path = aec_state.SaturatingEchoPath();
   const bool linear_echo_estimate = aec_state.UsableLinearEstimate();
 
-  // Count the number of blocks since saturation.
-  no_saturation_counter_ = saturated_echo ? 0 : no_saturation_counter_ + 1;
-
-  // Precompute 1/echo (note that when the echo is zero, the precomputed value
-  // is never used).
-  std::array<float, kFftLengthBy2Plus1> one_by_echo;
-  std::transform(echo.begin(), echo.end(), one_by_echo.begin(),
-                 [](float a) { return a > 0.f ? 1.f / a : 1.f; });
+  // Weight echo power in terms of audibility. // Precompute 1/weighted echo
+  // (note that when the echo is zero, the precomputed value is never used).
+  std::array<float, kFftLengthBy2Plus1> weighted_echo;
+  std::array<float, kFftLengthBy2Plus1> one_by_weighted_echo;
+  WeightEchoForAudibility(config_, echo, weighted_echo, one_by_weighted_echo);
 
   // Compute the minimum gain as the attenuating gain to put the signal just
   // above the zero sample values.
@@ -237,9 +280,9 @@
   const float min_echo_power =
       low_noise_render ? config_.echo_audibility.low_render_limit
                        : config_.echo_audibility.normal_render_limit;
-  if (no_saturation_counter_ > 10) {
+  if (!saturated_echo) {
     for (size_t k = 0; k < nearend.size(); ++k) {
-      const float denom = std::min(nearend[k], echo[k]);
+      const float denom = std::min(nearend[k], weighted_echo[k]);
       min_gain[k] = denom > 0.f ? min_echo_power / denom : 1.f;
       min_gain[k] = std::min(min_gain[k], 1.f);
     }
@@ -259,44 +302,47 @@
   // Iteratively compute the gain required to attenuate the echo to a non
   // noticeable level.
   gain->fill(0.f);
+  std::array<float, kFftLengthBy2Plus1> masker;
   for (int k = 0; k < 2; ++k) {
-    std::array<float, kFftLengthBy2Plus1> masker;
     MaskingPower(config_, nearend, comfort_noise, last_masker_, *gain, &masker);
     GainToNoAudibleEcho(config_, low_noise_render, saturated_echo,
-                        saturating_echo_path, linear_echo_estimate, nearend,
-                        echo, masker, min_gain, max_gain, one_by_echo, gain);
+                        linear_echo_estimate, nearend, weighted_echo, masker,
+                        min_gain, max_gain, one_by_weighted_echo, gain);
     AdjustForExternalFilters(gain);
-    if (narrow_peak_band) {
-      NarrowBandAttenuation(*narrow_peak_band, gain);
-    }
   }
 
   // Adjust the gain for frequencies which have not yet converged.
   AdjustNonConvergedFrequencies(gain);
 
   // Update the allowed maximum gain increase.
-  UpdateGainIncrease(low_noise_render, linear_echo_estimate, echo, *gain);
-
-  // Adjust gain dynamics.
-  const float gain_bound =
-      std::max(0.001f, *std::min_element(gain->begin(), gain->end()) * 10000.f);
-  std::for_each(gain->begin(), gain->end(),
-                [gain_bound](float& a) { a = std::min(a, gain_bound); });
+  UpdateGainIncrease(low_noise_render, linear_echo_estimate, saturated_echo,
+                     weighted_echo, *gain);
 
   // Store data required for the gain computation of the next block.
-  std::copy(echo.begin(), echo.end(), last_echo_.begin());
+  std::copy(weighted_echo.begin(), weighted_echo.end(), last_echo_.begin());
   std::copy(gain->begin(), gain->end(), last_gain_.begin());
   MaskingPower(config_, nearend, comfort_noise, last_masker_, *gain,
                &last_masker_);
   aec3::VectorMath(optimization_).Sqrt(*gain);
+
+  // Debug outputs for the purpose of development and analysis.
+  data_dumper_->DumpRaw("aec3_suppressor_min_gain", min_gain);
+  data_dumper_->DumpRaw("aec3_suppressor_max_gain", max_gain);
+  data_dumper_->DumpRaw("aec3_suppressor_masker", masker);
+  data_dumper_->DumpRaw("aec3_suppressor_last_masker", last_masker_);
 }
 
 SuppressionGain::SuppressionGain(const EchoCanceller3Config& config,
-                                 Aec3Optimization optimization)
-    : optimization_(optimization),
+                                 Aec3Optimization optimization,
+                                 int sample_rate_hz)
+    : data_dumper_(
+          new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
+      optimization_(optimization),
       config_(config),
       state_change_duration_blocks_(
-          static_cast<int>(config_.filter.config_change_duration_blocks)) {
+          static_cast<int>(config_.filter.config_change_duration_blocks)),
+      coherence_gain_(sample_rate_hz,
+                      config_.suppressor.bands_with_reliable_coherence) {
   RTC_DCHECK_LT(0, state_change_duration_blocks_);
   one_by_state_change_duration_blocks_ = 1.f / state_change_duration_blocks_;
   last_gain_.fill(1.f);
@@ -305,10 +351,15 @@
   last_echo_.fill(0.f);
 }
 
+SuppressionGain::~SuppressionGain() = default;
+
 void SuppressionGain::GetGain(
-    const std::array<float, kFftLengthBy2Plus1>& nearend,
-    const std::array<float, kFftLengthBy2Plus1>& echo,
-    const std::array<float, kFftLengthBy2Plus1>& comfort_noise,
+    const std::array<float, kFftLengthBy2Plus1>& nearend_spectrum,
+    const std::array<float, kFftLengthBy2Plus1>& echo_spectrum,
+    const std::array<float, kFftLengthBy2Plus1>& comfort_noise_spectrum,
+    const FftData& linear_aec_fft,
+    const FftData& render_fft,
+    const FftData& capture_fft,
     const RenderSignalAnalyzer& render_signal_analyzer,
     const AecState& aec_state,
     const std::vector<std::vector<float>>& render,
@@ -321,9 +372,21 @@
   bool low_noise_render = low_render_detector_.Detect(render);
   const rtc::Optional<int> narrow_peak_band =
       render_signal_analyzer.NarrowPeakBand();
-  LowerBandGain(low_noise_render, narrow_peak_band, aec_state, nearend, echo,
-                comfort_noise, low_band_gain);
+  LowerBandGain(low_noise_render, aec_state, nearend_spectrum, echo_spectrum,
+                comfort_noise_spectrum, low_band_gain);
 
+  // Adjust the gain for bands where the coherence indicates not echo.
+  if (config_.suppressor.bands_with_reliable_coherence > 0) {
+    std::array<float, kFftLengthBy2Plus1> G_coherence;
+    coherence_gain_.ComputeGain(linear_aec_fft, render_fft, capture_fft,
+                                G_coherence);
+    for (size_t k = 0; k < config_.suppressor.bands_with_reliable_coherence;
+         ++k) {
+      (*low_band_gain)[k] = std::max((*low_band_gain)[k], G_coherence[k]);
+    }
+  }
+
+  // Limit the gain of the lower bands during start up and after resets.
   const float gain_upper_bound = aec_state.SuppressionGainLimit();
   if (gain_upper_bound < 1.f) {
     for (size_t k = 0; k < low_band_gain->size(); ++k) {
@@ -348,6 +411,7 @@
 void SuppressionGain::UpdateGainIncrease(
     bool low_noise_render,
     bool linear_echo_estimate,
+    bool saturated_echo,
     const std::array<float, kFftLengthBy2Plus1>& echo,
     const std::array<float, kFftLengthBy2Plus1>& new_gain) {
   float max_inc;
@@ -374,7 +438,7 @@
     rate_dec = p.nonlinear.rate_dec;
     min_inc = p.nonlinear.min_inc;
     min_dec = p.nonlinear.min_dec;
-  } else if (initial_state_ && no_saturation_counter_ > 10) {
+  } else if (initial_state_ && !saturated_echo) {
     if (initial_state_change_counter_ > 0) {
       float change_factor =
           initial_state_change_counter_ * one_by_state_change_duration_blocks_;
@@ -404,7 +468,7 @@
     rate_dec = p.low_noise.rate_dec;
     min_inc = p.low_noise.min_inc;
     min_dec = p.low_noise.min_dec;
-  } else if (no_saturation_counter_ > 10) {
+  } else if (!saturated_echo) {
     max_inc = p.normal.max_inc;
     max_dec = p.normal.max_dec;
     rate_inc = p.normal.rate_inc;
diff --git a/modules/audio_processing/aec3/suppression_gain.h b/modules/audio_processing/aec3/suppression_gain.h
index 6624c1c..7b34d0a 100644
--- a/modules/audio_processing/aec3/suppression_gain.h
+++ b/modules/audio_processing/aec3/suppression_gain.h
@@ -17,6 +17,7 @@
 #include "api/audio/echo_canceller3_config.h"
 #include "modules/audio_processing/aec3/aec3_common.h"
 #include "modules/audio_processing/aec3/aec_state.h"
+#include "modules/audio_processing/aec3/coherence_gain.h"
 #include "modules/audio_processing/aec3/render_signal_analyzer.h"
 #include "rtc_base/constructormagic.h"
 
@@ -25,22 +26,27 @@
 class SuppressionGain {
  public:
   SuppressionGain(const EchoCanceller3Config& config,
-                  Aec3Optimization optimization);
-  void GetGain(const std::array<float, kFftLengthBy2Plus1>& nearend,
-               const std::array<float, kFftLengthBy2Plus1>& echo,
-               const std::array<float, kFftLengthBy2Plus1>& comfort_noise,
-               const RenderSignalAnalyzer& render_signal_analyzer,
-               const AecState& aec_state,
-               const std::vector<std::vector<float>>& render,
-               float* high_bands_gain,
-               std::array<float, kFftLengthBy2Plus1>* low_band_gain);
+                  Aec3Optimization optimization,
+                  int sample_rate_hz);
+  ~SuppressionGain();
+  void GetGain(
+      const std::array<float, kFftLengthBy2Plus1>& nearend_spectrum,
+      const std::array<float, kFftLengthBy2Plus1>& echo_spectrum,
+      const std::array<float, kFftLengthBy2Plus1>& comfort_noise_spectrum,
+      const FftData& linear_aec_fft,
+      const FftData& render_fft,
+      const FftData& capture_fft,
+      const RenderSignalAnalyzer& render_signal_analyzer,
+      const AecState& aec_state,
+      const std::vector<std::vector<float>>& render,
+      float* high_bands_gain,
+      std::array<float, kFftLengthBy2Plus1>* low_band_gain);
 
   // Toggles the usage of the initial state.
   void SetInitialState(bool state);
 
  private:
   void LowerBandGain(bool stationary_with_low_power,
-                     const rtc::Optional<int>& narrow_peak_band,
                      const AecState& aec_state,
                      const std::array<float, kFftLengthBy2Plus1>& nearend,
                      const std::array<float, kFftLengthBy2Plus1>& echo,
@@ -51,6 +57,7 @@
   void UpdateGainIncrease(
       bool low_noise_render,
       bool linear_echo_estimate,
+      bool saturated_echo,
       const std::array<float, kFftLengthBy2Plus1>& echo,
       const std::array<float, kFftLengthBy2Plus1>& new_gain);
 
@@ -62,6 +69,8 @@
     float average_power_ = 32768.f * 32768.f;
   };
 
+  static int instance_count_;
+  std::unique_ptr<ApmDataDumper> data_dumper_;
   const Aec3Optimization optimization_;
   const EchoCanceller3Config config_;
   const int state_change_duration_blocks_;
@@ -70,11 +79,11 @@
   std::array<float, kFftLengthBy2Plus1> last_masker_;
   std::array<float, kFftLengthBy2Plus1> gain_increase_;
   std::array<float, kFftLengthBy2Plus1> last_echo_;
-
   LowNoiseRenderDetector low_render_detector_;
-  size_t no_saturation_counter_ = 0;
   bool initial_state_ = true;
   int initial_state_change_counter_ = 0;
+  CoherenceGain coherence_gain_;
+
   RTC_DISALLOW_COPY_AND_ASSIGN(SuppressionGain);
 };
 
diff --git a/modules/audio_processing/aec3/suppression_gain_limiter.cc b/modules/audio_processing/aec3/suppression_gain_limiter.cc
index 643bb58..52218eb 100644
--- a/modules/audio_processing/aec3/suppression_gain_limiter.cc
+++ b/modules/audio_processing/aec3/suppression_gain_limiter.cc
@@ -38,7 +38,16 @@
   recent_reset_ = true;
 }
 
-void SuppressionGainUpperLimiter::Update(bool render_activity) {
+void SuppressionGainUpperLimiter::Update(bool render_activity,
+                                         bool transparent_mode) {
+  if (transparent_mode) {
+    active_render_seen_ = true;
+    call_startup_phase_ = false;
+    recent_reset_ = false;
+    suppressor_gain_limit_ = 1.f;
+    return;
+  }
+
   if (recent_reset_ && !call_startup_phase_) {
     // Only enforce 250 ms full suppression after in-call resets,
     constexpr int kMuteFramesAfterReset = kNumBlocksPerSecond / 4;
diff --git a/modules/audio_processing/aec3/suppression_gain_limiter.h b/modules/audio_processing/aec3/suppression_gain_limiter.h
index 7a3f228..e02f491 100644
--- a/modules/audio_processing/aec3/suppression_gain_limiter.h
+++ b/modules/audio_processing/aec3/suppression_gain_limiter.h
@@ -27,7 +27,7 @@
   void Reset();
 
   // Updates the limiting behavior for the current capture bloc.
-  void Update(bool render_activity);
+  void Update(bool render_activity, bool transparent_mode);
 
   // Returns the current suppressor gain limit.
   float Limit() const { return suppressor_gain_limit_; }
diff --git a/modules/audio_processing/aec3/suppression_gain_unittest.cc b/modules/audio_processing/aec3/suppression_gain_unittest.cc
index 0e48102..128c61e 100644
--- a/modules/audio_processing/aec3/suppression_gain_unittest.cc
+++ b/modules/audio_processing/aec3/suppression_gain_unittest.cc
@@ -29,15 +29,25 @@
   std::array<float, kFftLengthBy2Plus1> E2;
   std::array<float, kFftLengthBy2Plus1> R2;
   std::array<float, kFftLengthBy2Plus1> N2;
+  FftData E;
+  FftData X;
+  FftData Y;
   E2.fill(0.f);
   R2.fill(0.f);
   N2.fill(0.f);
+  E.re.fill(0.f);
+  E.im.fill(0.f);
+  X.re.fill(0.f);
+  X.im.fill(0.f);
+  Y.re.fill(0.f);
+  Y.im.fill(0.f);
+
   float high_bands_gain;
   AecState aec_state(EchoCanceller3Config{});
   EXPECT_DEATH(
-      SuppressionGain(EchoCanceller3Config{}, DetectOptimization())
-          .GetGain(E2, R2, N2, RenderSignalAnalyzer((EchoCanceller3Config{})),
-                   aec_state,
+      SuppressionGain(EchoCanceller3Config{}, DetectOptimization(), 16000)
+          .GetGain(E2, R2, N2, E, X, Y,
+                   RenderSignalAnalyzer((EchoCanceller3Config{})), aec_state,
                    std::vector<std::vector<float>>(
                        3, std::vector<float>(kBlockSize, 0.f)),
                    &high_bands_gain, nullptr),
@@ -48,8 +58,8 @@
 
 // Does a sanity check that the gains are correctly computed.
 TEST(SuppressionGain, BasicGainComputation) {
-  SuppressionGain suppression_gain(EchoCanceller3Config(),
-                                   DetectOptimization());
+  SuppressionGain suppression_gain(EchoCanceller3Config(), DetectOptimization(),
+                                   16000);
   RenderSignalAnalyzer analyzer(EchoCanceller3Config{});
   float high_bands_gain;
   std::array<float, kFftLengthBy2Plus1> E2;
@@ -58,6 +68,9 @@
   std::array<float, kFftLengthBy2Plus1> N2;
   std::array<float, kFftLengthBy2Plus1> g;
   std::array<float, kBlockSize> s;
+  FftData E;
+  FftData X;
+  FftData Y;
   std::vector<std::vector<float>> x(1, std::vector<float>(kBlockSize, 0.f));
   EchoCanceller3Config config;
   AecState aec_state(config);
@@ -73,21 +86,27 @@
   R2.fill(0.1f);
   N2.fill(100.f);
   s.fill(10.f);
+  E.re.fill(sqrtf(E2[0]));
+  E.im.fill(0.f);
+  X.re.fill(sqrtf(R2[0]));
+  X.im.fill(0.f);
+  Y.re.fill(sqrtf(Y2[0]));
+  Y.im.fill(0.f);
 
   // Ensure that the gain is no longer forced to zero.
   for (int k = 0; k <= kNumBlocksPerSecond / 5 + 1; ++k) {
     aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
                      subtractor.FilterImpulseResponse(),
-                     subtractor.ConvergedFilter(),
-                     *render_delay_buffer->GetRenderBuffer(), E2, Y2, s, false);
+                     subtractor.ConvergedFilter(), subtractor.DivergedFilter(),
+                     *render_delay_buffer->GetRenderBuffer(), E2, Y2, s);
   }
 
   for (int k = 0; k < 100; ++k) {
     aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
                      subtractor.FilterImpulseResponse(),
-                     subtractor.ConvergedFilter(),
-                     *render_delay_buffer->GetRenderBuffer(), E2, Y2, s, false);
-    suppression_gain.GetGain(E2, R2, N2, analyzer, aec_state, x,
+                     subtractor.ConvergedFilter(), subtractor.DivergedFilter(),
+                     *render_delay_buffer->GetRenderBuffer(), E2, Y2, s);
+    suppression_gain.GetGain(E2, R2, N2, E, X, Y, analyzer, aec_state, x,
                              &high_bands_gain, &g);
   }
   std::for_each(g.begin(), g.end(),
@@ -98,12 +117,16 @@
   Y2.fill(100.f);
   R2.fill(0.1f);
   N2.fill(0.f);
+  E.re.fill(sqrtf(E2[0]));
+  X.re.fill(sqrtf(R2[0]));
+  Y.re.fill(sqrtf(Y2[0]));
+
   for (int k = 0; k < 100; ++k) {
     aec_state.Update(delay_estimate, subtractor.FilterFrequencyResponse(),
                      subtractor.FilterImpulseResponse(),
-                     subtractor.ConvergedFilter(),
-                     *render_delay_buffer->GetRenderBuffer(), E2, Y2, s, false);
-    suppression_gain.GetGain(E2, R2, N2, analyzer, aec_state, x,
+                     subtractor.ConvergedFilter(), subtractor.DivergedFilter(),
+                     *render_delay_buffer->GetRenderBuffer(), E2, Y2, s);
+    suppression_gain.GetGain(E2, R2, N2, E, X, Y, analyzer, aec_state, x,
                              &high_bands_gain, &g);
   }
   std::for_each(g.begin(), g.end(),
@@ -112,9 +135,11 @@
   // Ensure that a strong echo is suppressed.
   E2.fill(1000000000.f);
   R2.fill(10000000000000.f);
-  N2.fill(0.f);
+  E.re.fill(sqrtf(E2[0]));
+  X.re.fill(sqrtf(R2[0]));
+
   for (int k = 0; k < 10; ++k) {
-    suppression_gain.GetGain(E2, R2, N2, analyzer, aec_state, x,
+    suppression_gain.GetGain(E2, R2, N2, E, X, Y, analyzer, aec_state, x,
                              &high_bands_gain, &g);
   }
   std::for_each(g.begin(), g.end(),
diff --git a/modules/audio_processing/aec_dump/BUILD.gn b/modules/audio_processing/aec_dump/BUILD.gn
index 7afaaf4..152c290 100644
--- a/modules/audio_processing/aec_dump/BUILD.gn
+++ b/modules/audio_processing/aec_dump/BUILD.gn
@@ -29,7 +29,6 @@
 
   deps = [
     "..:aec_dump_interface",
-    "../..:module_api",
     "../../../test:test_support",
   ]
 }
@@ -63,11 +62,12 @@
     deps = [
       ":aec_dump",
       "..:aec_dump_interface",
-      "../../../modules:module_api",
+      "../../../api/audio:audio_frame_api",
       "../../../rtc_base:checks",
       "../../../rtc_base:protobuf_utils",
       "../../../rtc_base:rtc_base_approved",
       "../../../rtc_base:rtc_task_queue",
+      "../../../rtc_base/system:file_wrapper",
       "../../../system_wrappers",
     ]
 
@@ -82,7 +82,6 @@
       ":aec_dump_impl",
       "..:aec_dump_interface",
       "..:audioproc_debug_proto",
-      "../../../modules:module_api",
       "../../../rtc_base:rtc_task_queue",
       "../../../test:fileutils",
       "../../../test:test_support",
diff --git a/modules/audio_processing/aec_dump/aec_dump_impl.cc b/modules/audio_processing/aec_dump/aec_dump_impl.cc
index 4deb192..ec35f0a 100644
--- a/modules/audio_processing/aec_dump/aec_dump_impl.cc
+++ b/modules/audio_processing/aec_dump/aec_dump_impl.cc
@@ -48,6 +48,10 @@
   pb_cfg->set_intelligibility_enhancer_enabled(
       config.intelligibility_enhancer_enabled);
 
+  pb_cfg->set_pre_amplifier_enabled(config.pre_amplifier_enabled);
+  pb_cfg->set_pre_amplifier_fixed_gain_factor(
+      config.pre_amplifier_fixed_gain_factor);
+
   pb_cfg->set_experiments_description(config.experiments_description);
 }
 
diff --git a/modules/audio_processing/aec_dump/aec_dump_impl.h b/modules/audio_processing/aec_dump/aec_dump_impl.h
index 36d72e9..0d88a7e 100644
--- a/modules/audio_processing/aec_dump/aec_dump_impl.h
+++ b/modules/audio_processing/aec_dump/aec_dump_impl.h
@@ -15,16 +15,16 @@
 #include <string>
 #include <vector>
 
+#include "api/audio/audio_frame.h"
 #include "modules/audio_processing/aec_dump/capture_stream_info.h"
 #include "modules/audio_processing/aec_dump/write_to_file_task.h"
 #include "modules/audio_processing/include/aec_dump.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/ignore_wundef.h"
 #include "rtc_base/platform_file.h"
 #include "rtc_base/race_checker.h"
+#include "rtc_base/system/file_wrapper.h"
 #include "rtc_base/task_queue.h"
 #include "rtc_base/thread_annotations.h"
-#include "system_wrappers/include/file_wrapper.h"
 
 // Files generated at build-time by the protobuf compiler.
 RTC_PUSH_IGNORING_WUNDEF()
diff --git a/modules/audio_processing/aec_dump/aec_dump_unittest.cc b/modules/audio_processing/aec_dump/aec_dump_unittest.cc
index 965ac03..98640b9 100644
--- a/modules/audio_processing/aec_dump/aec_dump_unittest.cc
+++ b/modules/audio_processing/aec_dump/aec_dump_unittest.cc
@@ -12,7 +12,6 @@
 
 #include "modules/audio_processing/aec_dump/aec_dump_factory.h"
 
-#include "modules/include/module_common_types.h"
 #include "rtc_base/task_queue.h"
 #include "test/gtest.h"
 #include "test/testsupport/fileutils.h"
diff --git a/modules/audio_processing/aec_dump/capture_stream_info.h b/modules/audio_processing/aec_dump/capture_stream_info.h
index 91bb1fa..da8fb58 100644
--- a/modules/audio_processing/aec_dump/capture_stream_info.h
+++ b/modules/audio_processing/aec_dump/capture_stream_info.h
@@ -15,9 +15,9 @@
 #include <utility>
 #include <vector>
 
+#include "api/audio/audio_frame.h"
 #include "modules/audio_processing/aec_dump/write_to_file_task.h"
 #include "modules/audio_processing/include/aec_dump.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/ignore_wundef.h"
 #include "rtc_base/logging.h"
diff --git a/modules/audio_processing/aec_dump/mock_aec_dump.h b/modules/audio_processing/aec_dump/mock_aec_dump.h
index 8cfabdd..c2088c0 100644
--- a/modules/audio_processing/aec_dump/mock_aec_dump.h
+++ b/modules/audio_processing/aec_dump/mock_aec_dump.h
@@ -14,7 +14,6 @@
 #include <memory>
 
 #include "modules/audio_processing/include/aec_dump.h"
-#include "modules/include/module_common_types.h"
 #include "test/gmock.h"
 
 namespace webrtc {
diff --git a/modules/audio_processing/aec_dump/write_to_file_task.h b/modules/audio_processing/aec_dump/write_to_file_task.h
index 7301473..711afb2 100644
--- a/modules/audio_processing/aec_dump/write_to_file_task.h
+++ b/modules/audio_processing/aec_dump/write_to_file_task.h
@@ -19,8 +19,8 @@
 #include "rtc_base/event.h"
 #include "rtc_base/ignore_wundef.h"
 #include "rtc_base/platform_file.h"
+#include "rtc_base/system/file_wrapper.h"
 #include "rtc_base/task_queue.h"
-#include "system_wrappers/include/file_wrapper.h"
 
 // Files generated at build-time by the protobuf compiler.
 RTC_PUSH_IGNORING_WUNDEF()
diff --git a/modules/audio_processing/agc/agc.cc b/modules/audio_processing/agc/agc.cc
index e161676..12c8cfb 100644
--- a/modules/audio_processing/agc/agc.cc
+++ b/modules/audio_processing/agc/agc.cc
@@ -18,7 +18,6 @@
 
 #include "modules/audio_processing/agc/loudness_histogram.h"
 #include "modules/audio_processing/agc/utility.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/checks.h"
 
 namespace webrtc {
diff --git a/modules/audio_processing/agc/agc_manager_direct.cc b/modules/audio_processing/agc/agc_manager_direct.cc
index 5ba5f4f..2d6ee81 100644
--- a/modules/audio_processing/agc/agc_manager_direct.cc
+++ b/modules/audio_processing/agc/agc_manager_direct.cc
@@ -18,7 +18,6 @@
 
 #include "modules/audio_processing/agc/gain_map_internal.h"
 #include "modules/audio_processing/gain_control_impl.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/numerics/safe_minmax.h"
diff --git a/modules/audio_processing/agc/legacy/gain_control.h b/modules/audio_processing/agc/legacy/gain_control.h
index 0f121b1..05b7ff6 100644
--- a/modules/audio_processing/agc/legacy/gain_control.h
+++ b/modules/audio_processing/agc/legacy/gain_control.h
@@ -208,7 +208,7 @@
  * This function creates and returns an AGC instance, which will contain the
  * state information for one (duplex) channel.
  */
-void* WebRtcAgc_Create();
+void* WebRtcAgc_Create(void);
 
 /*
  * This function frees the AGC instance created at the beginning.
diff --git a/modules/audio_processing/agc/loudness_histogram.cc b/modules/audio_processing/agc/loudness_histogram.cc
index 63d5f7c..0ed5850 100644
--- a/modules/audio_processing/agc/loudness_histogram.cc
+++ b/modules/audio_processing/agc/loudness_histogram.cc
@@ -13,7 +13,6 @@
 #include <cmath>
 #include <cstring>
 
-#include "modules/include/module_common_types.h"
 #include "rtc_base/checks.h"
 
 namespace webrtc {
diff --git a/modules/audio_processing/agc/mock_agc.h b/modules/audio_processing/agc/mock_agc.h
index b27d28c..cf2a859 100644
--- a/modules/audio_processing/agc/mock_agc.h
+++ b/modules/audio_processing/agc/mock_agc.h
@@ -13,7 +13,6 @@
 
 #include "modules/audio_processing/agc/agc.h"
 
-#include "modules/include/module_common_types.h"
 #include "test/gmock.h"
 
 namespace webrtc {
diff --git a/modules/audio_processing/agc2/BUILD.gn b/modules/audio_processing/agc2/BUILD.gn
index aca80d4..6f92f84 100644
--- a/modules/audio_processing/agc2/BUILD.gn
+++ b/modules/audio_processing/agc2/BUILD.gn
@@ -8,9 +8,47 @@
 
 import("../../../webrtc.gni")
 
-rtc_source_set("agc2") {
+group("agc2") {
+  deps = [
+    ":adaptive_digital",
+    ":fixed_digital",
+  ]
+}
+
+rtc_source_set("adaptive_digital") {
   sources = [
-    "agc2_common.h",
+    "adaptive_agc.cc",
+    "adaptive_agc.h",
+    "adaptive_digital_gain_applier.cc",
+    "adaptive_digital_gain_applier.h",
+    "adaptive_mode_level_estimator.cc",
+    "adaptive_mode_level_estimator.h",
+    "saturation_protector.cc",
+    "saturation_protector.h",
+  ]
+
+  configs += [ "..:apm_debug_dump" ]
+
+  deps = [
+    ":common",
+    ":gain_applier",
+    ":noise_level_estimator",
+    "..:aec_core",
+    "..:apm_logging",
+    "..:audio_frame_view",
+    "../../../api:array_view",
+    "../../../common_audio",
+    "../../../rtc_base:checks",
+    "../../../rtc_base:rtc_base_approved",
+    "../../../rtc_base:safe_minmax",
+    "../vad",
+    "../vad:vad_with_level",
+    "rnn_vad",
+  ]
+}
+
+rtc_source_set("fixed_digital") {
+  sources = [
     "fixed_digital_level_estimator.cc",
     "fixed_digital_level_estimator.h",
     "fixed_gain_controller.cc",
@@ -24,6 +62,7 @@
   configs += [ "..:apm_debug_dump" ]
 
   deps = [
+    ":common",
     "..:apm_logging",
     "..:audio_frame_view",
     "../../../api:array_view",
@@ -36,13 +75,74 @@
   ]
 }
 
+rtc_source_set("common") {
+  sources = [
+    "agc2_common.h",
+  ]
+  deps = [
+    "../../../rtc_base:rtc_base_approved",
+  ]
+}
+
+rtc_source_set("noise_level_estimator") {
+  sources = [
+    "biquad_filter.cc",
+    "biquad_filter.h",
+    "down_sampler.cc",
+    "down_sampler.h",
+    "noise_level_estimator.cc",
+    "noise_level_estimator.h",
+    "noise_spectrum_estimator.cc",
+    "noise_spectrum_estimator.h",
+    "signal_classifier.cc",
+    "signal_classifier.h",
+  ]
+  deps = [
+    "..:aec_core",
+    "..:apm_logging",
+    "..:audio_frame_view",
+    "../../../api:array_view",
+    "../../../common_audio",
+    "../../../rtc_base:checks",
+    "../../../rtc_base:macromagic",
+  ]
+
+  configs += [ "..:apm_debug_dump" ]
+}
+
+rtc_source_set("gain_applier") {
+  sources = [
+    "gain_applier.cc",
+    "gain_applier.h",
+  ]
+  deps = [
+    ":common",
+    "..:audio_frame_view",
+    "../../../rtc_base:safe_minmax",
+  ]
+}
+
+rtc_source_set("test_utils") {
+  testonly = true
+  visibility = [ ":*" ]
+  sources = [
+    "agc2_testing_common.cc",
+    "agc2_testing_common.h",
+    "vector_float_frame.cc",
+    "vector_float_frame.h",
+  ]
+  deps = [
+    "..:audio_frame_view",
+    "../../../rtc_base:checks",
+    "../../../rtc_base:rtc_base_approved",
+  ]
+}
+
 rtc_source_set("fixed_digital_unittests") {
   testonly = true
   configs += [ "..:apm_debug_dump" ]
 
   sources = [
-    "agc2_testing_common.cc",
-    "agc2_testing_common.h",
     "agc2_testing_common_unittest.cc",
     "compute_interpolated_gain_curve.cc",
     "compute_interpolated_gain_curve.h",
@@ -53,11 +153,11 @@
     "limiter.cc",
     "limiter.h",
     "limiter_unittest.cc",
-    "vector_float_frame.cc",
-    "vector_float_frame.h",
   ]
   deps = [
-    ":agc2",
+    ":common",
+    ":fixed_digital",
+    ":test_utils",
     "..:apm_logging",
     "..:audio_frame_view",
     "../../../api:array_view",
@@ -67,3 +167,49 @@
     "../../../rtc_base:rtc_base_tests_utils",
   ]
 }
+
+rtc_source_set("adaptive_digital_unittests") {
+  testonly = true
+  configs += [ "..:apm_debug_dump" ]
+
+  sources = [
+    "adaptive_digital_gain_applier_unittest.cc",
+    "adaptive_mode_level_estimator_unittest.cc",
+    "gain_applier_unittest.cc",
+    "saturation_protector_unittest.cc",
+  ]
+  deps = [
+    ":adaptive_digital",
+    ":common",
+    ":gain_applier",
+    ":test_utils",
+    "..:apm_logging",
+    "..:audio_frame_view",
+    "../../../api:array_view",
+    "../../../common_audio",
+    "../../../rtc_base:checks",
+    "../../../rtc_base:rtc_base_approved",
+    "../../../rtc_base:rtc_base_tests_utils",
+    "../vad:vad_with_level",
+  ]
+}
+
+rtc_source_set("noise_estimator_unittests") {
+  testonly = true
+  configs += [ "..:apm_debug_dump" ]
+
+  sources = [
+    "noise_level_estimator_unittest.cc",
+    "signal_classifier_unittest.cc",
+  ]
+  deps = [
+    ":noise_level_estimator",
+    ":test_utils",
+    "..:apm_logging",
+    "..:audio_frame_view",
+    "../../../api:array_view",
+    "../../../rtc_base:checks",
+    "../../../rtc_base:rtc_base_approved",
+    "../../../rtc_base:rtc_base_tests_utils",
+  ]
+}
diff --git a/modules/audio_processing/agc2/adaptive_agc.cc b/modules/audio_processing/agc2/adaptive_agc.cc
new file mode 100644
index 0000000..45e8853
--- /dev/null
+++ b/modules/audio_processing/agc2/adaptive_agc.cc
@@ -0,0 +1,64 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/adaptive_agc.h"
+
+#include <algorithm>
+#include <numeric>
+
+#include "common_audio/include/audio_util.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "modules/audio_processing/vad/voice_activity_detector.h"
+
+namespace webrtc {
+
+AdaptiveAgc::AdaptiveAgc(ApmDataDumper* apm_data_dumper)
+    : speech_level_estimator_(apm_data_dumper),
+      gain_applier_(apm_data_dumper),
+      apm_data_dumper_(apm_data_dumper),
+      noise_level_estimator_(apm_data_dumper) {
+  RTC_DCHECK(apm_data_dumper);
+}
+
+AdaptiveAgc::~AdaptiveAgc() = default;
+
+void AdaptiveAgc::Process(AudioFrameView<float> float_frame) {
+  // TODO(webrtc:7494): Remove this loop. Remove the vectors from
+  // VadWithData after we move to a VAD that outputs an estimate every
+  // kFrameDurationMs ms.
+  //
+  // Some VADs are 'bursty'. They return several estimates for some
+  // frames, and no estimates for other frames. We want to feed all to
+  // the level estimator, but only care about the last level it
+  // produces.
+  rtc::ArrayView<const VadWithLevel::LevelAndProbability> vad_results =
+      vad_.AnalyzeFrame(float_frame);
+  for (const auto& vad_result : vad_results) {
+    apm_data_dumper_->DumpRaw("agc2_vad_probability",
+                              vad_result.speech_probability);
+    apm_data_dumper_->DumpRaw("agc2_vad_rms_dbfs", vad_result.speech_rms_dbfs);
+
+    apm_data_dumper_->DumpRaw("agc2_vad_peak_dbfs",
+                              vad_result.speech_peak_dbfs);
+    speech_level_estimator_.UpdateEstimation(vad_result);
+  }
+
+  const float speech_level_dbfs = speech_level_estimator_.LatestLevelEstimate();
+
+  const float noise_level_dbfs = noise_level_estimator_.Analyze(float_frame);
+
+  apm_data_dumper_->DumpRaw("agc2_noise_estimate_dbfs", noise_level_dbfs);
+
+  // The gain applier applies the gain.
+  gain_applier_.Process(speech_level_dbfs, noise_level_dbfs, vad_results,
+                        float_frame);
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/adaptive_agc.h b/modules/audio_processing/agc2/adaptive_agc.h
new file mode 100644
index 0000000..a91aa2a
--- /dev/null
+++ b/modules/audio_processing/agc2/adaptive_agc.h
@@ -0,0 +1,41 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AGC2_ADAPTIVE_AGC_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_AGC_H_
+
+#include <memory>
+
+#include "modules/audio_processing/agc2/adaptive_digital_gain_applier.h"
+#include "modules/audio_processing/agc2/adaptive_mode_level_estimator.h"
+#include "modules/audio_processing/agc2/noise_level_estimator.h"
+#include "modules/audio_processing/include/audio_frame_view.h"
+#include "modules/audio_processing/vad/vad_with_level.h"
+
+namespace webrtc {
+class ApmDataDumper;
+
+class AdaptiveAgc {
+ public:
+  explicit AdaptiveAgc(ApmDataDumper* apm_data_dumper);
+  void Process(AudioFrameView<float> float_frame);
+  ~AdaptiveAgc();
+
+ private:
+  AdaptiveModeLevelEstimator speech_level_estimator_;
+  VadWithLevel vad_;
+  AdaptiveDigitalGainApplier gain_applier_;
+  ApmDataDumper* const apm_data_dumper_;
+  NoiseLevelEstimator noise_level_estimator_;
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_AGC_H_
diff --git a/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc b/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc
new file mode 100644
index 0000000..20b5a27
--- /dev/null
+++ b/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc
@@ -0,0 +1,124 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/adaptive_digital_gain_applier.h"
+
+#include <algorithm>
+
+#include "common_audio/include/audio_util.h"
+#include "modules/audio_processing/agc2/agc2_common.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/numerics/safe_minmax.h"
+
+namespace webrtc {
+namespace {
+
+// This function maps input level to desired applied gain. We want to
+// boost the signal so that peaks are at -kHeadroomDbfs. We can't
+// apply more than kMaxGainDb gain.
+float ComputeGainDb(float input_level_dbfs) {
+  // If the level is very low, boost it as much as we can.
+  if (input_level_dbfs < -(kHeadroomDbfs + kMaxGainDb)) {
+    return kMaxGainDb;
+  }
+
+  // We expect to end up here most of the time: the level is below
+  // -headroom, but we can boost it to -headroom.
+  if (input_level_dbfs < -kHeadroomDbfs) {
+    return -kHeadroomDbfs - input_level_dbfs;
+  }
+
+  // Otherwise, the level is too high and we can't boost. The
+  // LevelEstimator is responsible for not reporting bogus gain
+  // values.
+  RTC_DCHECK_LE(input_level_dbfs, 0.f);
+  return 0.f;
+}
+
+// We require 'gain + noise_level <= kMaxNoiseLevelDbfs'.
+float LimitGainByNoise(float target_gain,
+                       float input_noise_level_dbfs,
+                       ApmDataDumper* apm_data_dumper) {
+  const float noise_headroom_db = kMaxNoiseLevelDbfs - input_noise_level_dbfs;
+  apm_data_dumper->DumpRaw("agc2_noise_headroom_db", noise_headroom_db);
+  return std::min(target_gain, std::max(noise_headroom_db, 0.f));
+}
+
+// Computes how the gain should change during this frame.
+// Return the gain difference in db to 'last_gain_db'.
+float ComputeGainChangeThisFrameDb(float target_gain_db,
+                                   float last_gain_db,
+                                   bool gain_increase_allowed) {
+  float target_gain_difference_db = target_gain_db - last_gain_db;
+  if (!gain_increase_allowed) {
+    target_gain_difference_db = std::min(target_gain_difference_db, 0.f);
+  }
+
+  return rtc::SafeClamp(target_gain_difference_db, -kMaxGainChangePerFrameDb,
+                        kMaxGainChangePerFrameDb);
+}
+}  // namespace
+
+AdaptiveDigitalGainApplier::AdaptiveDigitalGainApplier(
+    ApmDataDumper* apm_data_dumper)
+    : gain_applier_(false, DbToRatio(last_gain_db_)),
+      apm_data_dumper_(apm_data_dumper) {}
+
+void AdaptiveDigitalGainApplier::Process(
+    float input_level_dbfs,
+    float input_noise_level_dbfs,
+    rtc::ArrayView<const VadWithLevel::LevelAndProbability> vad_results,
+    AudioFrameView<float> float_frame) {
+  RTC_DCHECK_GE(input_level_dbfs, -150.f);
+  RTC_DCHECK_LE(input_level_dbfs, 0.f);
+  RTC_DCHECK_GE(float_frame.num_channels(), 1);
+  RTC_DCHECK_GE(float_frame.samples_per_channel(), 1);
+
+  const float target_gain_db =
+      LimitGainByNoise(ComputeGainDb(input_level_dbfs), input_noise_level_dbfs,
+                       apm_data_dumper_);
+
+  // TODO(webrtc:7494): Remove this construct. Remove the vectors from
+  // VadWithData after we move to a VAD that outputs an estimate every
+  // kFrameDurationMs ms.
+  //
+  // Forbid increasing the gain when there is no speech. For some
+  // VADs, 'vad_results' has either many or 0 results. If there are 0
+  // results, keep the old flag. If there are many results, and at
+  // least one is confident speech, we allow attenuation.
+  if (!vad_results.empty()) {
+    gain_increase_allowed_ = std::all_of(
+        vad_results.begin(), vad_results.end(),
+        [](const VadWithLevel::LevelAndProbability& vad_result) {
+          return vad_result.speech_probability > kVadConfidenceThreshold;
+        });
+  }
+
+  const float gain_change_this_frame_db = ComputeGainChangeThisFrameDb(
+      target_gain_db, last_gain_db_, gain_increase_allowed_);
+
+  apm_data_dumper_->DumpRaw("agc2_want_to_change_by_db",
+                            target_gain_db - last_gain_db_);
+  apm_data_dumper_->DumpRaw("agc2_will_change_by_db",
+                            gain_change_this_frame_db);
+
+  // Optimization: avoid calling math functions if gain does not
+  // change.
+  if (gain_change_this_frame_db != 0.f) {
+    gain_applier_.SetGainFactor(
+        DbToRatio(last_gain_db_ + gain_change_this_frame_db));
+  }
+  gain_applier_.ApplyGain(float_frame);
+
+  // Remember that the gain has changed for the next iteration.
+  last_gain_db_ = last_gain_db_ + gain_change_this_frame_db;
+  apm_data_dumper_->DumpRaw("agc2_applied_gain_db", last_gain_db_);
+}
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/adaptive_digital_gain_applier.h b/modules/audio_processing/agc2/adaptive_digital_gain_applier.h
new file mode 100644
index 0000000..b06c65b
--- /dev/null
+++ b/modules/audio_processing/agc2/adaptive_digital_gain_applier.h
@@ -0,0 +1,46 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AGC2_ADAPTIVE_DIGITAL_GAIN_APPLIER_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_DIGITAL_GAIN_APPLIER_H_
+
+#include "modules/audio_processing/agc2/agc2_common.h"
+#include "modules/audio_processing/agc2/gain_applier.h"
+#include "modules/audio_processing/include/audio_frame_view.h"
+#include "modules/audio_processing/vad/vad_with_level.h"
+
+namespace webrtc {
+
+class ApmDataDumper;
+
+class AdaptiveDigitalGainApplier {
+ public:
+  explicit AdaptiveDigitalGainApplier(ApmDataDumper* apm_data_dumper);
+  // Decide what gain to apply.
+  void Process(
+      float input_level_dbfs,
+      float input_noise_level_dbfs,
+      rtc::ArrayView<const VadWithLevel::LevelAndProbability> vad_results,
+      AudioFrameView<float> float_frame);
+
+ private:
+  float last_gain_db_ = kInitialAdaptiveDigitalGainDb;
+  GainApplier gain_applier_;
+
+  // For some combinations of noise and speech probability, increasing
+  // the level is not allowed. Since we may get VAD results in bursts,
+  // we keep track of this variable until the next VAD results come
+  // in.
+  bool gain_increase_allowed_ = true;
+  ApmDataDumper* apm_data_dumper_ = nullptr;
+};
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_DIGITAL_GAIN_APPLIER_H_
diff --git a/modules/audio_processing/agc2/adaptive_digital_gain_applier_unittest.cc b/modules/audio_processing/agc2/adaptive_digital_gain_applier_unittest.cc
new file mode 100644
index 0000000..ebb040e
--- /dev/null
+++ b/modules/audio_processing/agc2/adaptive_digital_gain_applier_unittest.cc
@@ -0,0 +1,190 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/adaptive_digital_gain_applier.h"
+
+#include <algorithm>
+
+#include "common_audio/include/audio_util.h"
+#include "modules/audio_processing/agc2/agc2_common.h"
+#include "modules/audio_processing/agc2/vector_float_frame.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/gunit.h"
+
+namespace webrtc {
+namespace {
+// Constants used in place of estimated noise levels.
+constexpr float kNoNoiseDbfs = -90.f;
+constexpr float kWithNoiseDbfs = -20.f;
+
+// Runs gain applier and returns the applied gain in linear scale.
+float RunOnConstantLevel(int num_iterations,
+                         VadWithLevel::LevelAndProbability vad_data,
+                         float input_level_dbfs,
+                         AdaptiveDigitalGainApplier* gain_applier) {
+  float gain_linear = 0.f;
+
+  for (int i = 0; i < num_iterations; ++i) {
+    VectorFloatFrame fake_audio(1, 1, 1.f);
+    gain_applier->Process(
+        input_level_dbfs, kNoNoiseDbfs,
+        rtc::ArrayView<const VadWithLevel::LevelAndProbability>(&vad_data, 1),
+        fake_audio.float_frame_view());
+    gain_linear = fake_audio.float_frame_view().channel(0)[0];
+  }
+  return gain_linear;
+}
+
+constexpr VadWithLevel::LevelAndProbability kVadSpeech(1.f, -20.f, 0.f);
+}  // namespace
+
+TEST(AutomaticGainController2AdaptiveGainApplier, GainApplierShouldNotCrash) {
+  static_assert(
+      std::is_trivially_destructible<VadWithLevel::LevelAndProbability>::value,
+      "");
+  ApmDataDumper apm_data_dumper(0);
+  AdaptiveDigitalGainApplier gain_applier(&apm_data_dumper);
+
+  // Make one call with reasonable audio level values and settings.
+  VectorFloatFrame fake_audio(2, 480, 10000.f);
+  gain_applier.Process(
+      -5.0, kNoNoiseDbfs,
+      rtc::ArrayView<const VadWithLevel::LevelAndProbability>(&kVadSpeech, 1),
+      fake_audio.float_frame_view());
+}
+
+// Check that the output is -kHeadroom dBFS.
+TEST(AutomaticGainController2AdaptiveGainApplier, TargetLevelIsReached) {
+  ApmDataDumper apm_data_dumper(0);
+  AdaptiveDigitalGainApplier gain_applier(&apm_data_dumper);
+
+  constexpr float initial_level_dbfs = -5.f;
+
+  const float applied_gain =
+      RunOnConstantLevel(200, kVadSpeech, initial_level_dbfs, &gain_applier);
+
+  EXPECT_NEAR(applied_gain, DbToRatio(-kHeadroomDbfs - initial_level_dbfs),
+              0.1f);
+}
+
+// Check that the output is -kHeadroom dBFS
+TEST(AutomaticGainController2AdaptiveGainApplier, GainApproachesMaxGain) {
+  ApmDataDumper apm_data_dumper(0);
+  AdaptiveDigitalGainApplier gain_applier(&apm_data_dumper);
+
+  constexpr float initial_level_dbfs = -kHeadroomDbfs - kMaxGainDb - 10.f;
+  // A few extra frames for safety.
+  constexpr int kNumFramesToAdapt =
+      static_cast<int>(kMaxGainDb / kMaxGainChangePerFrameDb) + 10;
+
+  const float applied_gain = RunOnConstantLevel(
+      kNumFramesToAdapt, kVadSpeech, initial_level_dbfs, &gain_applier);
+  EXPECT_NEAR(applied_gain, DbToRatio(kMaxGainDb), 0.1f);
+
+  const float applied_gain_db = 20.f * std::log10(applied_gain);
+  EXPECT_NEAR(applied_gain_db, kMaxGainDb, 0.1f);
+}
+
+TEST(AutomaticGainController2AdaptiveGainApplier, GainDoesNotChangeFast) {
+  ApmDataDumper apm_data_dumper(0);
+  AdaptiveDigitalGainApplier gain_applier(&apm_data_dumper);
+
+  constexpr float initial_level_dbfs = -25.f;
+  // A few extra frames for safety.
+  constexpr int kNumFramesToAdapt =
+      static_cast<int>(initial_level_dbfs / kMaxGainChangePerFrameDb) + 10;
+
+  const float kMaxChangePerFrameLinear = DbToRatio(kMaxGainChangePerFrameDb);
+
+  float last_gain_linear = 1.f;
+  for (int i = 0; i < kNumFramesToAdapt; ++i) {
+    SCOPED_TRACE(i);
+    VectorFloatFrame fake_audio(1, 1, 1.f);
+    gain_applier.Process(
+        initial_level_dbfs, kNoNoiseDbfs,
+        rtc::ArrayView<const VadWithLevel::LevelAndProbability>(&kVadSpeech, 1),
+        fake_audio.float_frame_view());
+    float current_gain_linear = fake_audio.float_frame_view().channel(0)[0];
+    EXPECT_LE(std::abs(current_gain_linear - last_gain_linear),
+              kMaxChangePerFrameLinear);
+    last_gain_linear = current_gain_linear;
+  }
+
+  // Check that the same is true when gain decreases as well.
+  for (int i = 0; i < kNumFramesToAdapt; ++i) {
+    SCOPED_TRACE(i);
+    VectorFloatFrame fake_audio(1, 1, 1.f);
+    gain_applier.Process(
+        0.f, kNoNoiseDbfs,
+        rtc::ArrayView<const VadWithLevel::LevelAndProbability>(&kVadSpeech, 1),
+        fake_audio.float_frame_view());
+    float current_gain_linear = fake_audio.float_frame_view().channel(0)[0];
+    EXPECT_LE(std::abs(current_gain_linear - last_gain_linear),
+              kMaxChangePerFrameLinear);
+    last_gain_linear = current_gain_linear;
+  }
+}
+
+TEST(AutomaticGainController2AdaptiveGainApplier, GainIsRampedInAFrame) {
+  ApmDataDumper apm_data_dumper(0);
+  AdaptiveDigitalGainApplier gain_applier(&apm_data_dumper);
+
+  constexpr float initial_level_dbfs = -25.f;
+  constexpr int num_samples = 480;
+
+  VectorFloatFrame fake_audio(1, num_samples, 1.f);
+  gain_applier.Process(
+      initial_level_dbfs, kNoNoiseDbfs,
+      rtc::ArrayView<const VadWithLevel::LevelAndProbability>(&kVadSpeech, 1),
+      fake_audio.float_frame_view());
+  float maximal_difference = 0.f;
+  float current_value = 1.f * DbToRatio(kInitialAdaptiveDigitalGainDb);
+  for (const auto& x : fake_audio.float_frame_view().channel(0)) {
+    const float difference = std::abs(x - current_value);
+    maximal_difference = std::max(maximal_difference, difference);
+    current_value = x;
+  }
+
+  const float kMaxChangePerFrameLinear = DbToRatio(kMaxGainChangePerFrameDb);
+  const float kMaxChangePerSample = kMaxChangePerFrameLinear / num_samples;
+
+  EXPECT_LE(maximal_difference, kMaxChangePerSample);
+}
+
+TEST(AutomaticGainController2AdaptiveGainApplier, NoiseLimitsGain) {
+  ApmDataDumper apm_data_dumper(0);
+  AdaptiveDigitalGainApplier gain_applier(&apm_data_dumper);
+
+  constexpr float initial_level_dbfs = -25.f;
+  constexpr int num_samples = 480;
+  constexpr int num_initial_frames =
+      kInitialAdaptiveDigitalGainDb / kMaxGainChangePerFrameDb;
+  constexpr int num_frames = 50;
+
+  ASSERT_GT(kWithNoiseDbfs, kMaxNoiseLevelDbfs) << "kWithNoiseDbfs is too low";
+
+  for (int i = 0; i < num_initial_frames + num_frames; ++i) {
+    VectorFloatFrame fake_audio(1, num_samples, 1.f);
+    gain_applier.Process(
+        initial_level_dbfs, kWithNoiseDbfs,
+        rtc::ArrayView<const VadWithLevel::LevelAndProbability>(&kVadSpeech, 1),
+        fake_audio.float_frame_view());
+
+    // Wait so that the adaptive gain applier has time to lower the gain.
+    if (i > num_initial_frames) {
+      const float maximal_ratio =
+          *std::max_element(fake_audio.float_frame_view().channel(0).begin(),
+                            fake_audio.float_frame_view().channel(0).end());
+
+      EXPECT_NEAR(maximal_ratio, 1.f, 0.001f);
+    }
+  }
+}
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc b/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc
new file mode 100644
index 0000000..6aa2e91
--- /dev/null
+++ b/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc
@@ -0,0 +1,69 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/adaptive_mode_level_estimator.h"
+
+#include "modules/audio_processing/agc2/agc2_common.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/numerics/safe_minmax.h"
+
+namespace webrtc {
+
+AdaptiveModeLevelEstimator::AdaptiveModeLevelEstimator(
+    ApmDataDumper* apm_data_dumper)
+    : saturation_protector_(apm_data_dumper),
+      apm_data_dumper_(apm_data_dumper) {}
+
+void AdaptiveModeLevelEstimator::UpdateEstimation(
+    const VadWithLevel::LevelAndProbability& vad_data) {
+  RTC_DCHECK_GT(vad_data.speech_rms_dbfs, -150.f);
+  RTC_DCHECK_LT(vad_data.speech_rms_dbfs, 50.f);
+  RTC_DCHECK_GT(vad_data.speech_peak_dbfs, -150.f);
+  RTC_DCHECK_LT(vad_data.speech_peak_dbfs, 50.f);
+  RTC_DCHECK_GE(vad_data.speech_probability, 0.f);
+  RTC_DCHECK_LE(vad_data.speech_probability, 1.f);
+
+  if (vad_data.speech_probability < kVadConfidenceThreshold) {
+    DebugDumpEstimate();
+    return;
+  }
+
+  const bool buffer_is_full = buffer_size_ms_ >= kFullBufferSizeMs;
+  if (!buffer_is_full) {
+    buffer_size_ms_ += kFrameDurationMs;
+  }
+
+  const float leak_factor = buffer_is_full ? kFullBufferLeakFactor : 1.f;
+
+  estimate_numerator_ = estimate_numerator_ * leak_factor +
+                        vad_data.speech_rms_dbfs * vad_data.speech_probability;
+  estimate_denominator_ =
+      estimate_denominator_ * leak_factor + vad_data.speech_probability;
+
+  last_estimate_with_offset_dbfs_ = estimate_numerator_ / estimate_denominator_;
+
+  saturation_protector_.UpdateMargin(vad_data, last_estimate_with_offset_dbfs_);
+  DebugDumpEstimate();
+}
+
+float AdaptiveModeLevelEstimator::LatestLevelEstimate() const {
+  return rtc::SafeClamp<float>(
+      last_estimate_with_offset_dbfs_ + saturation_protector_.LastMargin(),
+      -90.f, 0.f);
+}
+
+void AdaptiveModeLevelEstimator::DebugDumpEstimate() {
+  apm_data_dumper_->DumpRaw("agc2_adaptive_level_estimate_with_offset_dbfs",
+                            last_estimate_with_offset_dbfs_);
+  apm_data_dumper_->DumpRaw("agc2_adaptive_level_estimate_dbfs",
+                            LatestLevelEstimate());
+  saturation_protector_.DebugDumpEstimate();
+}
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/adaptive_mode_level_estimator.h b/modules/audio_processing/agc2/adaptive_mode_level_estimator.h
new file mode 100644
index 0000000..9762f1f
--- /dev/null
+++ b/modules/audio_processing/agc2/adaptive_mode_level_estimator.h
@@ -0,0 +1,39 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AGC2_ADAPTIVE_MODE_LEVEL_ESTIMATOR_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_MODE_LEVEL_ESTIMATOR_H_
+
+#include "modules/audio_processing/agc2/saturation_protector.h"
+#include "modules/audio_processing/vad/vad_with_level.h"
+
+namespace webrtc {
+class ApmDataDumper;
+
+class AdaptiveModeLevelEstimator {
+ public:
+  explicit AdaptiveModeLevelEstimator(ApmDataDumper* apm_data_dumper);
+  void UpdateEstimation(const VadWithLevel::LevelAndProbability& vad_data);
+  float LatestLevelEstimate() const;
+
+ private:
+  void DebugDumpEstimate();
+
+  size_t buffer_size_ms_ = 0;
+  float last_estimate_with_offset_dbfs_ = kInitialSpeechLevelEstimateDbfs;
+  float estimate_numerator_ = 0.f;
+  float estimate_denominator_ = 0.f;
+  SaturationProtector saturation_protector_;
+  ApmDataDumper* const apm_data_dumper_;
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_ADAPTIVE_MODE_LEVEL_ESTIMATOR_H_
diff --git a/modules/audio_processing/agc2/adaptive_mode_level_estimator_unittest.cc b/modules/audio_processing/agc2/adaptive_mode_level_estimator_unittest.cc
new file mode 100644
index 0000000..71909d0
--- /dev/null
+++ b/modules/audio_processing/agc2/adaptive_mode_level_estimator_unittest.cc
@@ -0,0 +1,115 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/adaptive_mode_level_estimator.h"
+
+#include "modules/audio_processing/agc2/agc2_common.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/gunit.h"
+
+namespace webrtc {
+namespace {
+void RunOnConstantLevel(int num_iterations,
+                        VadWithLevel::LevelAndProbability vad_data,
+                        AdaptiveModeLevelEstimator* level_estimator) {
+  for (int i = 0; i < num_iterations; ++i) {
+    level_estimator->UpdateEstimation(vad_data);  // By copy
+  }
+}
+}  // namespace
+
+TEST(AutomaticGainController2AdaptiveModeLevelEstimator,
+     EstimatorShouldNotCrash) {
+  ApmDataDumper apm_data_dumper(0);
+  AdaptiveModeLevelEstimator level_estimator(&apm_data_dumper);
+
+  VadWithLevel::LevelAndProbability vad_data(1.f, -20.f, -10.f);
+  level_estimator.UpdateEstimation(vad_data);
+  static_cast<void>(level_estimator.LatestLevelEstimate());
+}
+
+TEST(AutomaticGainController2AdaptiveModeLevelEstimator, LevelShouldStabilize) {
+  ApmDataDumper apm_data_dumper(0);
+  AdaptiveModeLevelEstimator level_estimator(&apm_data_dumper);
+
+  constexpr float kSpeechRmsDbfs = -15.f;
+  RunOnConstantLevel(
+      100,
+      VadWithLevel::LevelAndProbability(
+          1.f, kSpeechRmsDbfs - kInitialSaturationMarginDb, kSpeechRmsDbfs),
+      &level_estimator);
+
+  EXPECT_NEAR(level_estimator.LatestLevelEstimate(), kSpeechRmsDbfs, 0.1f);
+}
+
+TEST(AutomaticGainController2AdaptiveModeLevelEstimator,
+     EstimatorIgnoresZeroProbabilityFrames) {
+  ApmDataDumper apm_data_dumper(0);
+  AdaptiveModeLevelEstimator level_estimator(&apm_data_dumper);
+
+  // Run for one second of fake audio.
+  constexpr float kSpeechRmsDbfs = -25.f;
+  RunOnConstantLevel(
+      100,
+      VadWithLevel::LevelAndProbability(
+          1.f, kSpeechRmsDbfs - kInitialSaturationMarginDb, kSpeechRmsDbfs),
+      &level_estimator);
+
+  // Run for one more second, but mark as not speech.
+  constexpr float kNoiseRmsDbfs = 0.f;
+  RunOnConstantLevel(
+      100, VadWithLevel::LevelAndProbability(0.f, kNoiseRmsDbfs, kNoiseRmsDbfs),
+      &level_estimator);
+
+  // Level should not have changed.
+  EXPECT_NEAR(level_estimator.LatestLevelEstimate(), kSpeechRmsDbfs, 0.1f);
+}
+
+TEST(AutomaticGainController2AdaptiveModeLevelEstimator, TimeToAdapt) {
+  ApmDataDumper apm_data_dumper(0);
+  AdaptiveModeLevelEstimator level_estimator(&apm_data_dumper);
+
+  // Run for one 'window size' interval
+  constexpr float kInitialSpeechRmsDbfs = -30.f;
+  RunOnConstantLevel(
+      kFullBufferSizeMs / kFrameDurationMs,
+      VadWithLevel::LevelAndProbability(
+          1.f, kInitialSpeechRmsDbfs - kInitialSaturationMarginDb,
+          kInitialSpeechRmsDbfs),
+      &level_estimator);
+
+  // Run for one half 'window size' interval. This should not be enough to
+  // adapt.
+  constexpr float kDifferentSpeechRmsDbfs = -10.f;
+  // It should at most differ by 25% after one 'window size' interval.
+  const float kMaxDifferenceDb =
+      0.25 * std::abs(kDifferentSpeechRmsDbfs - kInitialSpeechRmsDbfs);
+  RunOnConstantLevel(
+      static_cast<int>(kFullBufferSizeMs / kFrameDurationMs / 2),
+      VadWithLevel::LevelAndProbability(
+          1.f, kDifferentSpeechRmsDbfs - kInitialSaturationMarginDb,
+          kDifferentSpeechRmsDbfs),
+      &level_estimator);
+  EXPECT_GT(
+      std::abs(kDifferentSpeechRmsDbfs - level_estimator.LatestLevelEstimate()),
+      kMaxDifferenceDb);
+
+  // Run for some more time. Afterwards, we should have adapted.
+  RunOnConstantLevel(
+      static_cast<int>(3 * kFullBufferSizeMs / kFrameDurationMs),
+      VadWithLevel::LevelAndProbability(
+          1.f, kDifferentSpeechRmsDbfs - kInitialSaturationMarginDb,
+          kDifferentSpeechRmsDbfs),
+      &level_estimator);
+  EXPECT_NEAR(level_estimator.LatestLevelEstimate(), kDifferentSpeechRmsDbfs,
+              kMaxDifferenceDb);
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/agc2_common.h b/modules/audio_processing/agc2/agc2_common.h
index ad0ab4e..3a499d9 100644
--- a/modules/audio_processing/agc2/agc2_common.h
+++ b/modules/audio_processing/agc2/agc2_common.h
@@ -19,7 +19,7 @@
 
 constexpr float kMinFloatS16Value = -32768.f;
 constexpr float kMaxFloatS16Value = 32767.f;
-constexpr double kMaxAbsFloatS16Value = 32768.0;
+constexpr float kMaxAbsFloatS16Value = 32768.0f;
 
 constexpr size_t kFrameDurationMs = 10;
 constexpr size_t kSubFramesInFrame = 20;
@@ -27,6 +27,44 @@
 
 constexpr float kAttackFilterConstant = 0.f;
 
+// Adaptive digital gain applier settings below.
+constexpr float kMaxGainChangePerSecondDb = 3.f;
+constexpr float kMaxGainChangePerFrameDb =
+    kMaxGainChangePerSecondDb * kFrameDurationMs / 1000.f;
+constexpr float kHeadroomDbfs = 1.f;
+constexpr float kMaxGainDb = 30.f;
+constexpr float kInitialAdaptiveDigitalGainDb = 8.f;
+
+// This parameter must be tuned together with the noise estimator.
+constexpr float kMaxNoiseLevelDbfs = -50.f;
+
+// Used in the Level Estimator for deciding when to update the speech
+// level estimate. Also used in the adaptive digital gain applier to
+// decide when to allow target gain reduction.
+constexpr float kVadConfidenceThreshold = 0.9f;
+
+// The amount of 'memory' of the Level Estimator. Decides leak factors.
+constexpr size_t kFullBufferSizeMs = 1000;
+constexpr float kFullBufferLeakFactor = 1.f - 1.f / kFullBufferSizeMs;
+
+constexpr float kInitialSpeechLevelEstimateDbfs = -30.f;
+
+// Saturation Protector settings.
+constexpr float kInitialSaturationMarginDb = 17.f;
+
+constexpr size_t kPeakEnveloperSuperFrameLengthMs = 500;
+
+constexpr size_t kPeakEnveloperBufferSize =
+    kFullBufferSizeMs / kPeakEnveloperSuperFrameLengthMs + 1;
+
+// This value is 10 ** (-1/20 * frame_size_ms / satproc_attack_ms),
+// where satproc_attack_ms is 5000.
+constexpr float kSaturationProtectorAttackConstant = 0.9988493699365052f;
+
+// This value is 10 ** (-1/20 * frame_size_ms / satproc_decay_ms),
+// where satproc_decay_ms is 1000.
+constexpr float kSaturationProtectorDecayConstant = 0.9997697679981565f;
+
 // This is computed from kDecayMs by
 // 10 ** (-1/20 * subframe_duration / kDecayMs).
 // |subframe_duration| is |kFrameDurationMs / kSubFramesInFrame|.
diff --git a/modules/audio_processing/agc2/agc2_testing_common.h b/modules/audio_processing/agc2/agc2_testing_common.h
index a176282..8c4f400 100644
--- a/modules/audio_processing/agc2/agc2_testing_common.h
+++ b/modules/audio_processing/agc2/agc2_testing_common.h
@@ -11,9 +11,13 @@
 #ifndef MODULES_AUDIO_PROCESSING_AGC2_AGC2_TESTING_COMMON_H_
 #define MODULES_AUDIO_PROCESSING_AGC2_AGC2_TESTING_COMMON_H_
 
+#include <math.h>
+
+#include <limits>
 #include <vector>
 
 #include "rtc_base/basictypes.h"
+#include "rtc_base/checks.h"
 
 namespace webrtc {
 
@@ -26,8 +30,49 @@
 constexpr float kLimiterMaxInputLevelDbFs = 1.f;
 constexpr float kLimiterKneeSmoothnessDb = 1.f;
 constexpr float kLimiterCompressionRatio = 5.f;
+constexpr float kPi = 3.1415926536f;
 
 std::vector<double> LinSpace(const double l, const double r, size_t num_points);
+
+class SineGenerator {
+ public:
+  SineGenerator(float frequency, int rate)
+      : frequency_(frequency), rate_(rate) {}
+  float operator()() {
+    x_radians_ += frequency_ / rate_ * 2 * kPi;
+    if (x_radians_ > 2 * kPi) {
+      x_radians_ -= 2 * kPi;
+    }
+    return 1000.f * sinf(x_radians_);
+  }
+
+ private:
+  float frequency_;
+  int rate_;
+  float x_radians_ = 0.f;
+};
+
+class PulseGenerator {
+ public:
+  PulseGenerator(float frequency, int rate)
+      : samples_period_(
+            static_cast<int>(static_cast<float>(rate) / frequency)) {
+    RTC_DCHECK_GT(rate, frequency);
+  }
+  float operator()() {
+    sample_counter_++;
+    if (sample_counter_ >= samples_period_) {
+      sample_counter_ -= samples_period_;
+    }
+    return static_cast<float>(
+        sample_counter_ == 0 ? std::numeric_limits<int16_t>::max() : 10.f);
+  }
+
+ private:
+  int samples_period_;
+  int sample_counter_ = 0;
+};
+
 }  // namespace test
 }  // namespace webrtc
 
diff --git a/modules/audio_processing/agc2/biquad_filter.cc b/modules/audio_processing/agc2/biquad_filter.cc
new file mode 100644
index 0000000..c15c644
--- /dev/null
+++ b/modules/audio_processing/agc2/biquad_filter.cc
@@ -0,0 +1,35 @@
+/*
+ *  Copyright (c) 2016 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_processing/agc2/biquad_filter.h"
+
+namespace webrtc {
+
+// This method applies a biquad filter to an input signal x to produce an
+// output signal y. The biquad coefficients are specified at the construction
+// of the object.
+void BiQuadFilter::Process(rtc::ArrayView<const float> x,
+                           rtc::ArrayView<float> y) {
+  for (size_t k = 0; k < x.size(); ++k) {
+    // Use temporary variable for x[k] to allow in-place function call
+    // (that x and y refer to the same array).
+    const float tmp = x[k];
+    y[k] = coefficients_.b[0] * tmp + coefficients_.b[1] * biquad_state_.b[0] +
+           coefficients_.b[2] * biquad_state_.b[1] -
+           coefficients_.a[0] * biquad_state_.a[0] -
+           coefficients_.a[1] * biquad_state_.a[1];
+    biquad_state_.b[1] = biquad_state_.b[0];
+    biquad_state_.b[0] = tmp;
+    biquad_state_.a[1] = biquad_state_.a[0];
+    biquad_state_.a[0] = y[k];
+  }
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/biquad_filter.h b/modules/audio_processing/agc2/biquad_filter.h
new file mode 100644
index 0000000..4fd5e2e
--- /dev/null
+++ b/modules/audio_processing/agc2/biquad_filter.h
@@ -0,0 +1,56 @@
+/*
+ *  Copyright (c) 2016 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_PROCESSING_AGC2_BIQUAD_FILTER_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_BIQUAD_FILTER_H_
+
+#include "api/array_view.h"
+#include "rtc_base/arraysize.h"
+#include "rtc_base/constructormagic.h"
+
+namespace webrtc {
+
+class BiQuadFilter {
+ public:
+  struct BiQuadCoefficients {
+    float b[3];
+    float a[2];
+  };
+
+  BiQuadFilter() = default;
+
+  void Initialize(const BiQuadCoefficients& coefficients) {
+    coefficients_ = coefficients;
+  }
+
+  // Produces a filtered output y of the input x. Both x and y need to
+  // have the same length.
+  void Process(rtc::ArrayView<const float> x, rtc::ArrayView<float> y);
+
+ private:
+  struct BiQuadState {
+    BiQuadState() {
+      std::fill(b, b + arraysize(b), 0.f);
+      std::fill(a, a + arraysize(a), 0.f);
+    }
+
+    float b[2];
+    float a[2];
+  };
+
+  BiQuadState biquad_state_;
+  BiQuadCoefficients coefficients_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(BiQuadFilter);
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_BIQUAD_FILTER_H_
diff --git a/modules/audio_processing/agc2/down_sampler.cc b/modules/audio_processing/agc2/down_sampler.cc
new file mode 100644
index 0000000..50486e0
--- /dev/null
+++ b/modules/audio_processing/agc2/down_sampler.cc
@@ -0,0 +1,98 @@
+/*
+ *  Copyright (c) 2016 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_processing/agc2/down_sampler.h"
+
+#include <string.h>
+#include <algorithm>
+
+#include "modules/audio_processing/agc2/biquad_filter.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace {
+
+constexpr int kChunkSizeMs = 10;
+constexpr int kSampleRate8kHz = 8000;
+constexpr int kSampleRate16kHz = 16000;
+constexpr int kSampleRate32kHz = 32000;
+constexpr int kSampleRate48kHz = 48000;
+
+// Bandlimiter coefficients computed based on that only
+// the first 40 bins of the spectrum for the downsampled
+// signal are used.
+// [B,A] = butter(2,(41/64*4000)/8000)
+const BiQuadFilter::BiQuadCoefficients kLowPassFilterCoefficients_16kHz = {
+    {0.1455f, 0.2911f, 0.1455f},
+    {-0.6698f, 0.2520f}};
+
+// [B,A] = butter(2,(41/64*4000)/16000)
+const BiQuadFilter::BiQuadCoefficients kLowPassFilterCoefficients_32kHz = {
+    {0.0462f, 0.0924f, 0.0462f},
+    {-1.3066f, 0.4915f}};
+
+// [B,A] = butter(2,(41/64*4000)/24000)
+const BiQuadFilter::BiQuadCoefficients kLowPassFilterCoefficients_48kHz = {
+    {0.0226f, 0.0452f, 0.0226f},
+    {-1.5320f, 0.6224f}};
+
+}  // namespace
+
+DownSampler::DownSampler(ApmDataDumper* data_dumper)
+    : data_dumper_(data_dumper) {
+  Initialize(48000);
+}
+void DownSampler::Initialize(int sample_rate_hz) {
+  RTC_DCHECK(
+      sample_rate_hz == kSampleRate8kHz || sample_rate_hz == kSampleRate16kHz ||
+      sample_rate_hz == kSampleRate32kHz || sample_rate_hz == kSampleRate48kHz);
+
+  sample_rate_hz_ = sample_rate_hz;
+  down_sampling_factor_ = rtc::CheckedDivExact(sample_rate_hz_, 8000);
+
+  /// Note that the down sampling filter is not used if the sample rate is 8
+  /// kHz.
+  if (sample_rate_hz_ == kSampleRate16kHz) {
+    low_pass_filter_.Initialize(kLowPassFilterCoefficients_16kHz);
+  } else if (sample_rate_hz_ == kSampleRate32kHz) {
+    low_pass_filter_.Initialize(kLowPassFilterCoefficients_32kHz);
+  } else if (sample_rate_hz_ == kSampleRate48kHz) {
+    low_pass_filter_.Initialize(kLowPassFilterCoefficients_48kHz);
+  }
+}
+
+void DownSampler::DownSample(rtc::ArrayView<const float> in,
+                             rtc::ArrayView<float> out) {
+  data_dumper_->DumpWav("lc_down_sampler_input", in, sample_rate_hz_, 1);
+  RTC_DCHECK_EQ(sample_rate_hz_ * kChunkSizeMs / 1000, in.size());
+  RTC_DCHECK_EQ(kSampleRate8kHz * kChunkSizeMs / 1000, out.size());
+  const size_t kMaxNumFrames = kSampleRate48kHz * kChunkSizeMs / 1000;
+  float x[kMaxNumFrames];
+
+  // Band-limit the signal to 4 kHz.
+  if (sample_rate_hz_ != kSampleRate8kHz) {
+    low_pass_filter_.Process(in, rtc::ArrayView<float>(x, in.size()));
+
+    // Downsample the signal.
+    size_t k = 0;
+    for (size_t j = 0; j < out.size(); ++j) {
+      RTC_DCHECK_GT(kMaxNumFrames, k);
+      out[j] = x[k];
+      k += down_sampling_factor_;
+    }
+  } else {
+    std::copy(in.data(), in.data() + in.size(), out.data());
+  }
+
+  data_dumper_->DumpWav("lc_down_sampler_output", out, kSampleRate8kHz, 1);
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/down_sampler.h b/modules/audio_processing/agc2/down_sampler.h
new file mode 100644
index 0000000..a609ea8
--- /dev/null
+++ b/modules/audio_processing/agc2/down_sampler.h
@@ -0,0 +1,40 @@
+/*
+ *  Copyright (c) 2016 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_PROCESSING_AGC2_DOWN_SAMPLER_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_DOWN_SAMPLER_H_
+
+#include "api/array_view.h"
+#include "modules/audio_processing/agc2/biquad_filter.h"
+#include "rtc_base/constructormagic.h"
+
+namespace webrtc {
+
+class ApmDataDumper;
+
+class DownSampler {
+ public:
+  explicit DownSampler(ApmDataDumper* data_dumper);
+  void Initialize(int sample_rate_hz);
+
+  void DownSample(rtc::ArrayView<const float> in, rtc::ArrayView<float> out);
+
+ private:
+  ApmDataDumper* data_dumper_;
+  int sample_rate_hz_;
+  int down_sampling_factor_;
+  BiQuadFilter low_pass_filter_;
+
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(DownSampler);
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_DOWN_SAMPLER_H_
diff --git a/modules/audio_processing/agc2/fixed_gain_controller.cc b/modules/audio_processing/agc2/fixed_gain_controller.cc
index a565613..e59ae34 100644
--- a/modules/audio_processing/agc2/fixed_gain_controller.cc
+++ b/modules/audio_processing/agc2/fixed_gain_controller.cc
@@ -54,12 +54,8 @@
   gain_curve_applier_.SetSampleRate(sample_rate_hz);
 }
 
-void FixedGainController::EnableLimiter(bool enable_limiter) {
-  enable_limiter_ = enable_limiter;
-}
-
 void FixedGainController::Process(AudioFrameView<float> signal) {
-  // Apply fixed digital gain; interpolate if necessary. One of the
+  // Apply fixed digital gain. One of the
   // planned usages of the FGC is to only use the limiter. In that
   // case, the gain would be 1.0. Not doing the multiplications speeds
   // it up considerably. Hence the check.
@@ -72,16 +68,13 @@
     }
   }
 
-  // Use the limiter (if configured to).
-  if (enable_limiter_) {
-    gain_curve_applier_.Process(signal);
+  // Use the limiter.
+  gain_curve_applier_.Process(signal);
 
-    // Dump data for debug.
-    const auto channel_view = signal.channel(0);
-    apm_data_dumper_->DumpRaw("agc2_fixed_digital_gain_curve_applier",
-                              channel_view.size(), channel_view.data());
-  }
-
+  // Dump data for debug.
+  const auto channel_view = signal.channel(0);
+  apm_data_dumper_->DumpRaw("agc2_fixed_digital_gain_curve_applier",
+                            channel_view.size(), channel_view.data());
   // Hard-clipping.
   for (size_t k = 0; k < signal.num_channels(); ++k) {
     rtc::ArrayView<float> channel_view = signal.channel(k);
diff --git a/modules/audio_processing/agc2/fixed_gain_controller.h b/modules/audio_processing/agc2/fixed_gain_controller.h
index fd80348..2b92cfc 100644
--- a/modules/audio_processing/agc2/fixed_gain_controller.h
+++ b/modules/audio_processing/agc2/fixed_gain_controller.h
@@ -27,13 +27,11 @@
   // with any other method call).
   void SetGain(float gain_to_apply_db);
   void SetSampleRate(size_t sample_rate_hz);
-  void EnableLimiter(bool enable_limiter);
 
  private:
   float gain_to_apply_ = 1.f;
   ApmDataDumper* apm_data_dumper_ = nullptr;
   GainCurveApplier gain_curve_applier_;
-  bool enable_limiter_ = true;
 };
 
 }  // namespace webrtc
diff --git a/modules/audio_processing/agc2/fixed_gain_controller_unittest.cc b/modules/audio_processing/agc2/fixed_gain_controller_unittest.cc
index 1d6c2ae..d688f6a 100644
--- a/modules/audio_processing/agc2/fixed_gain_controller_unittest.cc
+++ b/modules/audio_processing/agc2/fixed_gain_controller_unittest.cc
@@ -49,34 +49,20 @@
 
 std::unique_ptr<FixedGainController> CreateFixedGainController(
     float gain_to_apply,
-    size_t rate,
-    bool enable_limiter) {
+    size_t rate) {
   std::unique_ptr<FixedGainController> fgc =
       rtc::MakeUnique<FixedGainController>(&test_data_dumper);
   fgc->SetGain(gain_to_apply);
   fgc->SetSampleRate(rate);
-  fgc->EnableLimiter(enable_limiter);
   return fgc;
 }
 
 }  // namespace
 
-TEST(AutomaticGainController2FixedDigital, CreateUseWithoutLimiter) {
-  const int kSampleRate = 48000;
-  std::unique_ptr<FixedGainController> fixed_gc =
-      CreateFixedGainController(kGainToApplyDb, kSampleRate, false);
-  VectorFloatFrame vectors_with_float_frame(
-      1, rtc::CheckedDivExact(kSampleRate, 100), kInputLevelLinear);
-  auto float_frame = vectors_with_float_frame.float_frame_view();
-  fixed_gc->Process(float_frame);
-  const auto channel = float_frame.channel(0);
-  EXPECT_LT(kInputLevelLinear, channel[0]);
-}
-
-TEST(AutomaticGainController2FixedDigital, CreateUseWithLimiter) {
+TEST(AutomaticGainController2FixedDigital, CreateUse) {
   const int kSampleRate = 44000;
   std::unique_ptr<FixedGainController> fixed_gc =
-      CreateFixedGainController(kGainToApplyDb, kSampleRate, true);
+      CreateFixedGainController(kGainToApplyDb, kSampleRate);
   VectorFloatFrame vectors_with_float_frame(
       1, rtc::CheckedDivExact(kSampleRate, 100), kInputLevelLinear);
   auto float_frame = vectors_with_float_frame.float_frame_view();
@@ -96,7 +82,7 @@
     // Since |test::kLimiterMaxInputLevelDbFs| > |gain_db|, the
     // limiter will not saturate the signal.
     std::unique_ptr<FixedGainController> fixed_gc_no_saturation =
-        CreateFixedGainController(gain_db, kSampleRate, true);
+        CreateFixedGainController(gain_db, kSampleRate);
 
     // Saturation not expected.
     SCOPED_TRACE(std::to_string(gain_db));
@@ -112,7 +98,7 @@
     // Since |test::kLimiterMaxInputLevelDbFs| < |gain|, the limiter
     // will saturate the signal.
     std::unique_ptr<FixedGainController> fixed_gc_saturation =
-        CreateFixedGainController(gain_db, kSampleRate, true);
+        CreateFixedGainController(gain_db, kSampleRate);
 
     // Saturation expected.
     SCOPED_TRACE(std::to_string(gain_db));
@@ -135,7 +121,7 @@
     // Since |gain| > |test::kLimiterMaxInputLevelDbFs|, the limiter will
     // not saturate the signal.
     std::unique_ptr<FixedGainController> fixed_gc_no_saturation =
-        CreateFixedGainController(gain_db, kSampleRate, true);
+        CreateFixedGainController(gain_db, kSampleRate);
 
     // Saturation not expected.
     SCOPED_TRACE(std::to_string(gain_db));
@@ -151,7 +137,7 @@
     // Singe |gain| < |test::kLimiterMaxInputLevelDbFs|, the limiter will
     // saturate the signal.
     std::unique_ptr<FixedGainController> fixed_gc_saturation =
-        CreateFixedGainController(gain_db, kSampleRate, true);
+        CreateFixedGainController(gain_db, kSampleRate);
 
     // Saturation expected.
     SCOPED_TRACE(std::to_string(gain_db));
@@ -170,7 +156,7 @@
   constexpr float kGainDbFactor10 = 20.f;
 
   std::unique_ptr<FixedGainController> fixed_gc_no_saturation =
-      CreateFixedGainController(kGainDbNoChange, kSampleRate, false);
+      CreateFixedGainController(kGainDbNoChange, kSampleRate);
 
   // Signal level is unchanged with 0 db gain.
   EXPECT_FLOAT_EQ(
diff --git a/modules/audio_processing/agc2/gain_applier.cc b/modules/audio_processing/agc2/gain_applier.cc
new file mode 100644
index 0000000..38eb1de
--- /dev/null
+++ b/modules/audio_processing/agc2/gain_applier.cc
@@ -0,0 +1,101 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/gain_applier.h"
+
+#include "modules/audio_processing/agc2/agc2_common.h"
+#include "rtc_base/numerics/safe_minmax.h"
+
+namespace webrtc {
+namespace {
+
+// Returns true when the gain factor is so close to 1 that it would
+// not affect int16 samples.
+bool GainCloseToOne(float gain_factor) {
+  return 1.f - 1.f / kMaxFloatS16Value <= gain_factor &&
+         gain_factor <= 1.f + 1.f / kMaxFloatS16Value;
+}
+
+void ClipSignal(AudioFrameView<float> signal) {
+  for (size_t k = 0; k < signal.num_channels(); ++k) {
+    rtc::ArrayView<float> channel_view = signal.channel(k);
+    for (auto& sample : channel_view) {
+      sample = rtc::SafeClamp(sample, kMinFloatS16Value, kMaxFloatS16Value);
+    }
+  }
+}
+
+void ApplyGainWithRamping(float last_gain_linear,
+                          float gain_at_end_of_frame_linear,
+                          float inverse_samples_per_channel,
+                          AudioFrameView<float> float_frame) {
+  // Do not modify the signal.
+  if (last_gain_linear == gain_at_end_of_frame_linear &&
+      GainCloseToOne(gain_at_end_of_frame_linear)) {
+    return;
+  }
+
+  // Gain is constant and different from 1.
+  if (last_gain_linear == gain_at_end_of_frame_linear) {
+    for (size_t k = 0; k < float_frame.num_channels(); ++k) {
+      rtc::ArrayView<float> channel_view = float_frame.channel(k);
+      for (auto& sample : channel_view) {
+        sample *= gain_at_end_of_frame_linear;
+      }
+    }
+    return;
+  }
+
+  // The gain changes. We have to change slowly to avoid discontinuities.
+  const float increment = (gain_at_end_of_frame_linear - last_gain_linear) *
+                          inverse_samples_per_channel;
+  float gain = last_gain_linear;
+  for (size_t i = 0; i < float_frame.samples_per_channel(); ++i) {
+    for (size_t ch = 0; ch < float_frame.num_channels(); ++ch) {
+      float_frame.channel(ch)[i] *= gain;
+    }
+    gain += increment;
+  }
+}
+
+}  // namespace
+
+GainApplier::GainApplier(bool hard_clip_samples, float initial_gain_factor)
+    : hard_clip_samples_(hard_clip_samples),
+      last_gain_factor_(initial_gain_factor),
+      current_gain_factor_(initial_gain_factor) {}
+
+void GainApplier::ApplyGain(AudioFrameView<float> signal) {
+  if (static_cast<int>(signal.samples_per_channel()) != samples_per_channel_) {
+    Initialize(signal.samples_per_channel());
+  }
+
+  ApplyGainWithRamping(last_gain_factor_, current_gain_factor_,
+                       inverse_samples_per_channel_, signal);
+
+  last_gain_factor_ = current_gain_factor_;
+
+  if (hard_clip_samples_) {
+    ClipSignal(signal);
+  }
+}
+
+void GainApplier::SetGainFactor(float gain_factor) {
+  RTC_DCHECK_GT(gain_factor, 0.f);
+  current_gain_factor_ = gain_factor;
+}
+
+void GainApplier::Initialize(size_t samples_per_channel) {
+  RTC_DCHECK_GT(samples_per_channel, 0);
+  samples_per_channel_ = static_cast<int>(samples_per_channel);
+  inverse_samples_per_channel_ = 1.f / samples_per_channel_;
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/gain_applier.h b/modules/audio_processing/agc2/gain_applier.h
new file mode 100644
index 0000000..a4f56ce
--- /dev/null
+++ b/modules/audio_processing/agc2/gain_applier.h
@@ -0,0 +1,41 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AGC2_GAIN_APPLIER_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_GAIN_APPLIER_H_
+
+#include "modules/audio_processing/include/audio_frame_view.h"
+
+namespace webrtc {
+class GainApplier {
+ public:
+  GainApplier(bool hard_clip_samples, float initial_gain_factor);
+
+  void ApplyGain(AudioFrameView<float> signal);
+  void SetGainFactor(float gain_factor);
+
+ private:
+  void Initialize(size_t samples_per_channel);
+
+  // Whether to clip samples after gain is applied. If 'true', result
+  // will fit in FloatS16 range.
+  const bool hard_clip_samples_;
+  float last_gain_factor_;
+
+  // If this value is not equal to 'last_gain_factor', gain will be
+  // ramped from 'last_gain_factor_' to this value during the next
+  // 'ApplyGain'.
+  float current_gain_factor_;
+  int samples_per_channel_ = -1;
+  float inverse_samples_per_channel_ = -1.f;
+};
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_GAIN_APPLIER_H_
diff --git a/modules/audio_processing/agc2/gain_applier_unittest.cc b/modules/audio_processing/agc2/gain_applier_unittest.cc
new file mode 100644
index 0000000..bbf3a85
--- /dev/null
+++ b/modules/audio_processing/agc2/gain_applier_unittest.cc
@@ -0,0 +1,94 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/gain_applier.h"
+
+#include <math.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "modules/audio_processing/agc2/vector_float_frame.h"
+#include "rtc_base/gunit.h"
+
+namespace webrtc {
+TEST(AutomaticGainController2GainApplier, InitialGainIsRespected) {
+  constexpr float initial_signal_level = 123.f;
+  constexpr float gain_factor = 10.f;
+  VectorFloatFrame fake_audio(1, 1, initial_signal_level);
+  GainApplier gain_applier(true, gain_factor);
+
+  gain_applier.ApplyGain(fake_audio.float_frame_view());
+  EXPECT_NEAR(fake_audio.float_frame_view().channel(0)[0],
+              initial_signal_level * gain_factor, 0.1f);
+}
+
+TEST(AutomaticGainController2GainApplier, ClippingIsDone) {
+  constexpr float initial_signal_level = 30000.f;
+  constexpr float gain_factor = 10.f;
+  VectorFloatFrame fake_audio(1, 1, initial_signal_level);
+  GainApplier gain_applier(true, gain_factor);
+
+  gain_applier.ApplyGain(fake_audio.float_frame_view());
+  EXPECT_NEAR(fake_audio.float_frame_view().channel(0)[0],
+              std::numeric_limits<int16_t>::max(), 0.1f);
+}
+
+TEST(AutomaticGainController2GainApplier, ClippingIsNotDone) {
+  constexpr float initial_signal_level = 30000.f;
+  constexpr float gain_factor = 10.f;
+  VectorFloatFrame fake_audio(1, 1, initial_signal_level);
+  GainApplier gain_applier(false, gain_factor);
+
+  gain_applier.ApplyGain(fake_audio.float_frame_view());
+
+  EXPECT_NEAR(fake_audio.float_frame_view().channel(0)[0],
+              initial_signal_level * gain_factor, 0.1f);
+}
+
+TEST(AutomaticGainController2GainApplier, RampingIsDone) {
+  constexpr float initial_signal_level = 30000.f;
+  constexpr float initial_gain_factor = 1.f;
+  constexpr float target_gain_factor = 0.5f;
+  constexpr int num_channels = 3;
+  constexpr int samples_per_channel = 4;
+  VectorFloatFrame fake_audio(num_channels, samples_per_channel,
+                              initial_signal_level);
+  GainApplier gain_applier(false, initial_gain_factor);
+
+  gain_applier.SetGainFactor(target_gain_factor);
+  gain_applier.ApplyGain(fake_audio.float_frame_view());
+
+  // The maximal gain change should be close to that in linear interpolation.
+  for (size_t channel = 0; channel < num_channels; ++channel) {
+    float max_signal_change = 0.f;
+    float last_signal_level = initial_signal_level;
+    for (const auto sample : fake_audio.float_frame_view().channel(channel)) {
+      const float current_change = fabs(last_signal_level - sample);
+      max_signal_change =
+          std::max(max_signal_change, current_change);
+      last_signal_level = sample;
+    }
+    const float total_gain_change =
+        fabs((initial_gain_factor - target_gain_factor) * initial_signal_level);
+    EXPECT_NEAR(max_signal_change, total_gain_change / samples_per_channel,
+                0.1f);
+  }
+
+  // Next frame should have the desired level.
+  VectorFloatFrame next_fake_audio_frame(num_channels, samples_per_channel,
+                                         initial_signal_level);
+  gain_applier.ApplyGain(next_fake_audio_frame.float_frame_view());
+
+  // The last sample should have the new gain.
+  EXPECT_NEAR(next_fake_audio_frame.float_frame_view().channel(0)[0],
+              initial_signal_level * target_gain_factor, 0.1f);
+}
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/interpolated_gain_curve.cc b/modules/audio_processing/agc2/interpolated_gain_curve.cc
index 69602b5..7f1f16c 100644
--- a/modules/audio_processing/agc2/interpolated_gain_curve.cc
+++ b/modules/audio_processing/agc2/interpolated_gain_curve.cc
@@ -20,19 +20,33 @@
 namespace {
 void LogRegionStats(const InterpolatedGainCurve::Stats& stats) {
   using Region = InterpolatedGainCurve::GainCurveRegion;
+  const int duration_s =
+      stats.region_duration_frames / (1000 / kFrameDurationMs);
 
-  std::string histogram_name = "WebRTC.Audio.AGC2.FixedDigitalGainCurveRegion.";
-  if (stats.region == Region::kIdentity) {
-    histogram_name += "Identity";
-  } else if (stats.region == Region::kKnee) {
-    histogram_name += "Knee";
-  } else if (stats.region == Region::kLimiter) {
-    histogram_name += "Limiter";
-  } else {
-    histogram_name += "Saturation";
+  switch (stats.region) {
+    case Region::kIdentity: {
+      RTC_HISTOGRAM_COUNTS_10000(
+          "WebRTC.Audio.Agc2.FixedDigitalGainCurveRegion.Identity", duration_s);
+      break;
+    }
+    case Region::kKnee: {
+      RTC_HISTOGRAM_COUNTS_10000(
+          "WebRTC.Audio.Agc2.FixedDigitalGainCurveRegion.Knee", duration_s);
+      break;
+    }
+    case Region::kLimiter: {
+      RTC_HISTOGRAM_COUNTS_10000(
+          "WebRTC.Audio.Agc2.FixedDigitalGainCurveRegion.Limiter", duration_s);
+      break;
+    }
+    case Region::kSaturation: {
+      RTC_HISTOGRAM_COUNTS_10000(
+          "WebRTC.Audio.Agc2.FixedDigitalGainCurveRegion.Saturation",
+          duration_s);
+      break;
+    }
+    default: { RTC_NOTREACHED(); }
   }
-  RTC_HISTOGRAM_COUNTS_10000(histogram_name,
-                             stats.region_duration_frames / 100);
 }
 }  // namespace
 
diff --git a/modules/audio_processing/agc2/noise_level_estimator.cc b/modules/audio_processing/agc2/noise_level_estimator.cc
new file mode 100644
index 0000000..d9aaf1f
--- /dev/null
+++ b/modules/audio_processing/agc2/noise_level_estimator.cc
@@ -0,0 +1,111 @@
+/*
+ *  Copyright (c) 2016 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_processing/agc2/noise_level_estimator.h"
+
+#include <math.h>
+
+#include <algorithm>
+#include <numeric>
+
+#include "common_audio/include/audio_util.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+
+namespace webrtc {
+
+namespace {
+constexpr int kFramesPerSecond = 100;
+
+float FrameEnergy(const AudioFrameView<const float>& audio) {
+  float energy = 0.f;
+  for (size_t k = 0; k < audio.num_channels(); ++k) {
+    float channel_energy =
+        std::accumulate(audio.channel(k).begin(), audio.channel(k).end(), 0.f,
+                        [](float a, float b) -> float { return a + b * b; });
+    energy = std::max(channel_energy, energy);
+  }
+  return energy;
+}
+
+float EnergyToDbfs(float signal_energy, size_t num_samples) {
+  const float rms = std::sqrt(signal_energy / num_samples);
+  return FloatS16ToDbfs(rms);
+}
+}  // namespace
+
+NoiseLevelEstimator::NoiseLevelEstimator(ApmDataDumper* data_dumper)
+    : signal_classifier_(data_dumper) {
+  Initialize(48000);
+}
+
+NoiseLevelEstimator::~NoiseLevelEstimator() {}
+
+void NoiseLevelEstimator::Initialize(int sample_rate_hz) {
+  sample_rate_hz_ = sample_rate_hz;
+  noise_energy_ = 1.f;
+  first_update_ = true;
+  min_noise_energy_ = sample_rate_hz * 2.f * 2.f / kFramesPerSecond;
+  noise_energy_hold_counter_ = 0;
+  signal_classifier_.Initialize(sample_rate_hz);
+}
+
+float NoiseLevelEstimator::Analyze(const AudioFrameView<const float>& frame) {
+  const int rate =
+      static_cast<int>(frame.samples_per_channel() * kFramesPerSecond);
+  if (rate != sample_rate_hz_) {
+    Initialize(rate);
+  }
+  const float frame_energy = FrameEnergy(frame);
+  if (frame_energy <= 0.f) {
+    RTC_DCHECK_GE(frame_energy, 0.f);
+    return EnergyToDbfs(noise_energy_, frame.samples_per_channel());
+  }
+
+  if (first_update_) {
+    // Initialize the noise energy to the frame energy.
+    first_update_ = false;
+    return EnergyToDbfs(
+        noise_energy_ = std::max(frame_energy, min_noise_energy_),
+        frame.samples_per_channel());
+  }
+
+  const SignalClassifier::SignalType signal_type =
+      signal_classifier_.Analyze(frame.channel(0));
+
+  // Update the noise estimate in a minimum statistics-type manner.
+  if (signal_type == SignalClassifier::SignalType::kStationary) {
+    if (frame_energy > noise_energy_) {
+      // Leak the estimate upwards towards the frame energy if no recent
+      // downward update.
+      noise_energy_hold_counter_ = std::max(noise_energy_hold_counter_ - 1, 0);
+
+      if (noise_energy_hold_counter_ == 0) {
+        noise_energy_ = std::min(noise_energy_ * 1.01f, frame_energy);
+      }
+    } else {
+      // Update smoothly downwards with a limited maximum update magnitude.
+      noise_energy_ =
+          std::max(noise_energy_ * 0.9f,
+                   noise_energy_ + 0.05f * (frame_energy - noise_energy_));
+      noise_energy_hold_counter_ = 1000;
+    }
+  } else {
+    // For a non-stationary signal, leak the estimate downwards in order to
+    // avoid estimate locking due to incorrect signal classification.
+    noise_energy_ = noise_energy_ * 0.99f;
+  }
+
+  // Ensure a minimum of the estimate.
+  return EnergyToDbfs(
+      noise_energy_ = std::max(noise_energy_, min_noise_energy_),
+      frame.samples_per_channel());
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/noise_level_estimator.h b/modules/audio_processing/agc2/noise_level_estimator.h
new file mode 100644
index 0000000..24067a1
--- /dev/null
+++ b/modules/audio_processing/agc2/noise_level_estimator.h
@@ -0,0 +1,43 @@
+/*
+ *  Copyright (c) 2016 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_PROCESSING_AGC2_NOISE_LEVEL_ESTIMATOR_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_NOISE_LEVEL_ESTIMATOR_H_
+
+#include "modules/audio_processing/agc2/signal_classifier.h"
+#include "modules/audio_processing/include/audio_frame_view.h"
+#include "rtc_base/constructormagic.h"
+
+namespace webrtc {
+class ApmDataDumper;
+
+class NoiseLevelEstimator {
+ public:
+  NoiseLevelEstimator(ApmDataDumper* data_dumper);
+  ~NoiseLevelEstimator();
+  // Returns the estimated noise level in dBFS.
+  float Analyze(const AudioFrameView<const float>& frame);
+
+ private:
+  void Initialize(int sample_rate_hz);
+
+  int sample_rate_hz_;
+  float min_noise_energy_;
+  bool first_update_;
+  float noise_energy_;
+  int noise_energy_hold_counter_;
+  SignalClassifier signal_classifier_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(NoiseLevelEstimator);
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_NOISE_LEVEL_ESTIMATOR_H_
diff --git a/modules/audio_processing/agc2/noise_level_estimator_unittest.cc b/modules/audio_processing/agc2/noise_level_estimator_unittest.cc
new file mode 100644
index 0000000..c4fd33b
--- /dev/null
+++ b/modules/audio_processing/agc2/noise_level_estimator_unittest.cc
@@ -0,0 +1,83 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/noise_level_estimator.h"
+
+#include <array>
+#include <functional>
+#include <limits>
+
+#include "modules/audio_processing/agc2/agc2_testing_common.h"
+#include "modules/audio_processing/agc2/vector_float_frame.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/gunit.h"
+#include "rtc_base/random.h"
+
+namespace webrtc {
+namespace {
+Random rand_gen(42);
+ApmDataDumper data_dumper(0);
+constexpr int kNumIterations = 200;
+constexpr int kFramesPerSecond = 100;
+
+// Runs the noise estimator on audio generated by 'sample_generator'
+// for kNumIterations. Returns the last noise level estimate.
+float RunEstimator(std::function<float()> sample_generator, int rate) {
+  NoiseLevelEstimator estimator(&data_dumper);
+  const size_t samples_per_channel =
+      rtc::CheckedDivExact(rate, kFramesPerSecond);
+  VectorFloatFrame signal(1, static_cast<int>(samples_per_channel), 0.f);
+
+  for (int i = 0; i < kNumIterations; ++i) {
+    AudioFrameView<float> frame_view = signal.float_frame_view();
+    for (size_t j = 0; j < samples_per_channel; ++j) {
+      frame_view.channel(0)[j] = sample_generator();
+    }
+    estimator.Analyze(frame_view);
+  }
+  return estimator.Analyze(signal.float_frame_view());
+}
+
+float WhiteNoiseGenerator() {
+  return static_cast<float>(rand_gen.Rand(std::numeric_limits<int16_t>::min(),
+                                          std::numeric_limits<int16_t>::max()));
+}
+}  // namespace
+
+// White random noise is stationary, but does not trigger the detector
+// every frame due to the randomness.
+TEST(AutomaticGainController2NoiseEstimator, RandomNoise) {
+  for (const auto rate : {8000, 16000, 32000, 48000}) {
+    const float noise_level = RunEstimator(WhiteNoiseGenerator, rate);
+    EXPECT_NEAR(noise_level, -5.f, 1.f);
+  }
+}
+
+// Sine curves are (very) stationary. They trigger the detector all
+// the time. Except for a few initial frames.
+TEST(AutomaticGainController2NoiseEstimator, SineTone) {
+  for (const auto rate : {8000, 16000, 32000, 48000}) {
+    test::SineGenerator gen(600.f, rate);
+    const float noise_level = RunEstimator(gen, rate);
+    EXPECT_NEAR(noise_level, -33.f, 1.f);
+  }
+}
+
+// Pulses are transient if they are far enough apart. They shouldn't
+// trigger the noise detector.
+TEST(AutomaticGainController2NoiseEstimator, PulseTone) {
+  for (const auto rate : {8000, 16000, 32000, 48000}) {
+    test::PulseGenerator gen(20.f, rate);
+    const int noise_level = RunEstimator(gen, rate);
+    EXPECT_NEAR(noise_level, -79.f, 1.f);
+  }
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/noise_spectrum_estimator.cc b/modules/audio_processing/agc2/noise_spectrum_estimator.cc
new file mode 100644
index 0000000..9e08126
--- /dev/null
+++ b/modules/audio_processing/agc2/noise_spectrum_estimator.cc
@@ -0,0 +1,68 @@
+/*
+ *  Copyright (c) 2016 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_processing/agc2/noise_spectrum_estimator.h"
+
+#include <string.h>
+#include <algorithm>
+
+#include "api/array_view.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/arraysize.h"
+
+namespace webrtc {
+namespace {
+constexpr float kMinNoisePower = 100.f;
+}  // namespace
+
+NoiseSpectrumEstimator::NoiseSpectrumEstimator(ApmDataDumper* data_dumper)
+    : data_dumper_(data_dumper) {
+  Initialize();
+}
+
+void NoiseSpectrumEstimator::Initialize() {
+  std::fill(noise_spectrum_, noise_spectrum_ + arraysize(noise_spectrum_),
+            kMinNoisePower);
+}
+
+void NoiseSpectrumEstimator::Update(rtc::ArrayView<const float> spectrum,
+                                    bool first_update) {
+  RTC_DCHECK_EQ(65, spectrum.size());
+
+  if (first_update) {
+    // Initialize the noise spectral estimate with the signal spectrum.
+    std::copy(spectrum.data(), spectrum.data() + spectrum.size(),
+              noise_spectrum_);
+  } else {
+    // Smoothly update the noise spectral estimate towards the signal spectrum
+    // such that the magnitude of the updates are limited.
+    for (size_t k = 0; k < spectrum.size(); ++k) {
+      if (noise_spectrum_[k] < spectrum[k]) {
+        noise_spectrum_[k] = std::min(
+            1.01f * noise_spectrum_[k],
+            noise_spectrum_[k] + 0.05f * (spectrum[k] - noise_spectrum_[k]));
+      } else {
+        noise_spectrum_[k] = std::max(
+            0.99f * noise_spectrum_[k],
+            noise_spectrum_[k] + 0.05f * (spectrum[k] - noise_spectrum_[k]));
+      }
+    }
+  }
+
+  // Ensure that the noise spectal estimate does not become too low.
+  for (auto& v : noise_spectrum_) {
+    v = std::max(v, kMinNoisePower);
+  }
+
+  data_dumper_->DumpRaw("lc_noise_spectrum", 65, noise_spectrum_);
+  data_dumper_->DumpRaw("lc_signal_spectrum", spectrum);
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/noise_spectrum_estimator.h b/modules/audio_processing/agc2/noise_spectrum_estimator.h
new file mode 100644
index 0000000..fd1cc13
--- /dev/null
+++ b/modules/audio_processing/agc2/noise_spectrum_estimator.h
@@ -0,0 +1,40 @@
+/*
+ *  Copyright (c) 2016 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_PROCESSING_AGC2_NOISE_SPECTRUM_ESTIMATOR_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_NOISE_SPECTRUM_ESTIMATOR_H_
+
+#include "api/array_view.h"
+#include "rtc_base/constructormagic.h"
+
+namespace webrtc {
+
+class ApmDataDumper;
+
+class NoiseSpectrumEstimator {
+ public:
+  explicit NoiseSpectrumEstimator(ApmDataDumper* data_dumper);
+  void Initialize();
+  void Update(rtc::ArrayView<const float> spectrum, bool first_update);
+
+  rtc::ArrayView<const float> GetNoiseSpectrum() const {
+    return rtc::ArrayView<const float>(noise_spectrum_);
+  }
+
+ private:
+  ApmDataDumper* data_dumper_;
+  float noise_spectrum_[65];
+
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(NoiseSpectrumEstimator);
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_NOISE_SPECTRUM_ESTIMATOR_H_
diff --git a/modules/audio_processing/agc2/rnn_vad/BUILD.gn b/modules/audio_processing/agc2/rnn_vad/BUILD.gn
new file mode 100644
index 0000000..e05dcab
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/BUILD.gn
@@ -0,0 +1,103 @@
+# Copyright (c) 2018 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.
+
+import("../../../../webrtc.gni")
+
+group("rnn_vad") {
+  deps = [
+    ":lib",
+  ]
+}
+
+source_set("lib") {
+  sources = [
+    "common.h",
+    "lp_residual.cc",
+    "lp_residual.h",
+    "pitch_info.h",
+    "pitch_search.cc",
+    "pitch_search.h",
+    "pitch_search_internal.cc",
+    "pitch_search_internal.h",
+    "ring_buffer.h",
+    "sequence_buffer.h",
+    "symmetric_matrix_buffer.h",
+  ]
+  deps = [
+    "../../../../api:array_view",
+    "../../../../rtc_base:checks",
+  ]
+}
+
+if (rtc_include_tests) {
+  source_set("lib_test") {
+    testonly = true
+    sources = [
+      "test_utils.cc",
+      "test_utils.h",
+    ]
+    deps = [
+      "../../../../api:array_view",
+      "../../../../rtc_base:checks",
+      "../../../../rtc_base:ptr_util",
+      "../../../../test:fileutils",
+      "../../../../test:test_support",
+    ]
+  }
+
+  unittest_resources = [
+    "../../../../resources/audio_processing/agc2/rnn_vad/pitch_buf_24k.dat",
+    "../../../../resources/audio_processing/agc2/rnn_vad/pitch_lp_res.dat",
+  ]
+
+  if (is_ios) {
+    bundle_data("unittests_bundle_data") {
+      testonly = true
+      sources = unittest_resources
+      outputs = [
+        "{{bundle_resources_dir}}/{{source_file_part}}",
+      ]
+    }
+  }
+
+  rtc_source_set("unittests") {
+    testonly = true
+    sources = [
+      "lp_residual_unittest.cc",
+      "pitch_search_internal_unittest.cc",
+      "pitch_search_unittest.cc",
+      "ring_buffer_unittest.cc",
+      "sequence_buffer_unittest.cc",
+      "symmetric_matrix_buffer_unittest.cc",
+    ]
+    deps = [
+      ":lib",
+      ":lib_test",
+      "../../../../api:array_view",
+      "../../../../test:test_support",
+    ]
+    data = unittest_resources
+    if (is_ios) {
+      deps += [ ":unittests_bundle_data" ]
+    }
+  }
+
+  rtc_executable("rnn_vad_tool") {
+    testonly = true
+    sources = [
+      "rnn_vad_tool.cc",
+    ]
+    deps = [
+      ":lib",
+      "../../../../api:array_view",
+      "../../../../common_audio:common_audio",
+      "../../../../rtc_base:rtc_base_approved",
+      "../../../../test:test_support",
+    ]
+  }
+}
diff --git a/modules/audio_processing/agc2/rnn_vad/common.h b/modules/audio_processing/agc2/rnn_vad/common.h
new file mode 100644
index 0000000..252bf84
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/common.h
@@ -0,0 +1,49 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AGC2_RNN_VAD_COMMON_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_COMMON_H_
+
+namespace webrtc {
+namespace rnn_vad {
+
+constexpr size_t kSampleRate24kHz = 24000;
+constexpr size_t kFrameSize10ms24kHz = kSampleRate24kHz / 100;
+constexpr size_t kFrameSize20ms24kHz = kFrameSize10ms24kHz * 2;
+
+// Pitch analysis params.
+constexpr size_t kMinPitch24kHz = kSampleRate24kHz / 800;   // 0.00125 s.
+constexpr size_t kMaxPitch24kHz = kSampleRate24kHz / 62.5;  // 0.016 s.
+constexpr size_t kBufSize24kHz = kMaxPitch24kHz + kFrameSize20ms24kHz;
+static_assert((kBufSize24kHz & 1) == 0, "The buffer size must be even.");
+
+// Define a higher minimum pitch period for the initial search. This is used to
+// avoid searching for very short periods, for which a refinement step is
+// responsible.
+constexpr size_t kInitialMinPitch24kHz = 3 * kMinPitch24kHz;
+static_assert(kMinPitch24kHz < kInitialMinPitch24kHz, "");
+static_assert(kInitialMinPitch24kHz < kMaxPitch24kHz, "");
+
+// 12 kHz analysis.
+constexpr size_t kSampleRate12kHz = 12000;
+constexpr size_t kFrameSize10ms12kHz = kSampleRate12kHz / 100;
+constexpr size_t kFrameSize20ms12kHz = kFrameSize10ms12kHz * 2;
+constexpr size_t kBufSize12kHz = kBufSize24kHz / 2;
+constexpr size_t kInitialMinPitch12kHz = kInitialMinPitch24kHz / 2;
+constexpr size_t kMaxPitch12kHz = kMaxPitch24kHz / 2;
+
+// 48 kHz constants.
+constexpr size_t kMinPitch48kHz = kMinPitch24kHz * 2;
+constexpr size_t kMaxPitch48kHz = kMaxPitch24kHz * 2;
+
+}  // namespace rnn_vad
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_COMMON_H_
diff --git a/modules/audio_processing/agc2/rnn_vad/lp_residual.cc b/modules/audio_processing/agc2/rnn_vad/lp_residual.cc
new file mode 100644
index 0000000..483336d
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/lp_residual.cc
@@ -0,0 +1,128 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/rnn_vad/lp_residual.h"
+
+#include <algorithm>
+#include <array>
+#include <numeric>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace rnn_vad {
+namespace {
+
+// Computes cross-correlation coefficients between |x| and |y| and writes them
+// in |x_corr|. The lag values are in {0, ..., max_lag - 1}, where max_lag
+// equals the size of |x_corr|.
+// The |x| and |y| sub-arrays used to compute a cross-correlation coefficients
+// for a lag l have both size "size of |x| - l" - i.e., the longest sub-array is
+// used. |x| and |y| must have the same size.
+void ComputeCrossCorrelation(
+    rtc::ArrayView<const float> x,
+    rtc::ArrayView<const float> y,
+    rtc::ArrayView<float, kNumLpcCoefficients> x_corr) {
+  constexpr size_t max_lag = x_corr.size();
+  RTC_DCHECK_EQ(x.size(), y.size());
+  RTC_DCHECK_LT(max_lag, x.size());
+  for (size_t lag = 0; lag < max_lag; ++lag)
+    x_corr[lag] =
+        std::inner_product(x.begin(), x.end() - lag, y.begin() + lag, 0.f);
+}
+
+// Applies denoising to the auto-correlation coefficients.
+void DenoiseAutoCorrelation(
+    rtc::ArrayView<float, kNumLpcCoefficients> auto_corr) {
+  // Assume -40 dB white noise floor.
+  auto_corr[0] *= 1.0001f;
+  for (size_t i = 1; i < kNumLpcCoefficients; ++i)
+    auto_corr[i] -= auto_corr[i] * (0.008f * i) * (0.008f * i);
+}
+
+// Computes the initial inverse filter coefficients given the auto-correlation
+// coefficients of an input frame.
+void ComputeInitialInverseFilterCoefficients(
+    rtc::ArrayView<const float, kNumLpcCoefficients> auto_corr,
+    rtc::ArrayView<float, kNumLpcCoefficients - 1> lpc_coeffs) {
+  float error = auto_corr[0];
+  for (size_t i = 0; i < kNumLpcCoefficients - 1; ++i) {
+    float reflection_coeff = 0.f;
+    for (size_t j = 0; j < i; ++j)
+      reflection_coeff += lpc_coeffs[j] * auto_corr[i - j];
+    reflection_coeff += auto_corr[i + 1];
+    reflection_coeff /= -error;
+    // Update LPC coefficients and total error.
+    lpc_coeffs[i] = reflection_coeff;
+    for (size_t j = 0; j<(i + 1)>> 1; ++j) {
+      const float tmp1 = lpc_coeffs[j];
+      const float tmp2 = lpc_coeffs[i - 1 - j];
+      lpc_coeffs[j] = tmp1 + reflection_coeff * tmp2;
+      lpc_coeffs[i - 1 - j] = tmp2 + reflection_coeff * tmp1;
+    }
+    error -= reflection_coeff * reflection_coeff * error;
+    if (error < 0.001f * auto_corr[0])
+      break;
+  }
+}
+
+}  // namespace
+
+void ComputeAndPostProcessLpcCoefficients(
+    rtc::ArrayView<const float> x,
+    rtc::ArrayView<float, kNumLpcCoefficients> lpc_coeffs) {
+  std::array<float, kNumLpcCoefficients> auto_corr;
+  ComputeCrossCorrelation(x, x, {auto_corr.data(), auto_corr.size()});
+  if (auto_corr[0] == 0.f) {  // Empty frame.
+    std::fill(lpc_coeffs.begin(), lpc_coeffs.end(), 0);
+    return;
+  }
+  DenoiseAutoCorrelation({auto_corr.data(), auto_corr.size()});
+  std::array<float, kNumLpcCoefficients - 1> lpc_coeffs_pre{};
+  ComputeInitialInverseFilterCoefficients(
+      {auto_corr.data(), auto_corr.size()},
+      {lpc_coeffs_pre.data(), lpc_coeffs_pre.size()});
+  // LPC coefficients post-processing.
+  // TODO(bugs.webrtc.org/9076): Consider removing these steps.
+  float c1 = 1.f;
+  for (size_t i = 0; i < kNumLpcCoefficients - 1; ++i) {
+    c1 *= 0.9f;
+    lpc_coeffs_pre[i] *= c1;
+  }
+  const float c2 = 0.8f;
+  lpc_coeffs[0] = lpc_coeffs_pre[0] + c2;
+  lpc_coeffs[1] = lpc_coeffs_pre[1] + c2 * lpc_coeffs_pre[0];
+  lpc_coeffs[2] = lpc_coeffs_pre[2] + c2 * lpc_coeffs_pre[1];
+  lpc_coeffs[3] = lpc_coeffs_pre[3] + c2 * lpc_coeffs_pre[2];
+  lpc_coeffs[4] = c2 * lpc_coeffs_pre[3];
+}
+
+void ComputeLpResidual(
+    rtc::ArrayView<const float, kNumLpcCoefficients> lpc_coeffs,
+    rtc::ArrayView<const float> x,
+    rtc::ArrayView<float> y) {
+  RTC_DCHECK_LT(kNumLpcCoefficients, x.size());
+  RTC_DCHECK_EQ(x.size(), y.size());
+  std::array<float, kNumLpcCoefficients> input_chunk;
+  input_chunk.fill(0.f);
+  for (size_t i = 0; i < y.size(); ++i) {
+    const float sum = std::inner_product(input_chunk.begin(), input_chunk.end(),
+                                         lpc_coeffs.begin(), x[i]);
+    // Circular shift and add a new sample.
+    for (size_t j = kNumLpcCoefficients - 1; j > 0; --j)
+      input_chunk[j] = input_chunk[j - 1];
+    input_chunk[0] = x[i];
+    // Copy result.
+    y[i] = sum;
+  }
+}
+
+}  // namespace rnn_vad
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/rnn_vad/lp_residual.h b/modules/audio_processing/agc2/rnn_vad/lp_residual.h
new file mode 100644
index 0000000..bffafd2
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/lp_residual.h
@@ -0,0 +1,39 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AGC2_RNN_VAD_LP_RESIDUAL_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_LP_RESIDUAL_H_
+
+#include "api/array_view.h"
+
+namespace webrtc {
+namespace rnn_vad {
+
+// LPC inverse filter length.
+constexpr size_t kNumLpcCoefficients = 5;
+
+// Given a frame |x|, computes a post-processed version of LPC coefficients
+// tailored for pitch estimation.
+void ComputeAndPostProcessLpcCoefficients(
+    rtc::ArrayView<const float> x,
+    rtc::ArrayView<float, kNumLpcCoefficients> lpc_coeffs);
+
+// Computes the LP residual for the input frame |x| and the LPC coefficients
+// |lpc_coeffs|. |y| and |x| can point to the same array for in-place
+// computation.
+void ComputeLpResidual(
+    rtc::ArrayView<const float, kNumLpcCoefficients> lpc_coeffs,
+    rtc::ArrayView<const float> x,
+    rtc::ArrayView<float> y);
+
+}  // namespace rnn_vad
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_LP_RESIDUAL_H_
diff --git a/modules/audio_processing/agc2/rnn_vad/lp_residual_unittest.cc b/modules/audio_processing/agc2/rnn_vad/lp_residual_unittest.cc
new file mode 100644
index 0000000..23f1e14
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/lp_residual_unittest.cc
@@ -0,0 +1,90 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/rnn_vad/lp_residual.h"
+
+#include <array>
+
+#include "modules/audio_processing/agc2/rnn_vad/common.h"
+#include "modules/audio_processing/agc2/rnn_vad/test_utils.h"
+// TODO(bugs.webrtc.org/8948): Add when the issue is fixed.
+// #include "test/fpe_observer.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace rnn_vad {
+namespace test {
+
+TEST(RnnVadTest, LpResidualOfEmptyFrame) {
+  // TODO(bugs.webrtc.org/8948): Add when the issue is fixed.
+  // FloatingPointExceptionObserver fpe_observer;
+
+  // Input frame (empty, i.e., all samples set to 0).
+  std::array<float, kFrameSize10ms24kHz> empty_frame;
+  empty_frame.fill(0.f);
+  // Compute inverse filter coefficients.
+  std::array<float, kNumLpcCoefficients> lpc_coeffs;
+  ComputeAndPostProcessLpcCoefficients({empty_frame},
+                                       {lpc_coeffs.data(), lpc_coeffs.size()});
+  // Compute LP residual.
+  std::array<float, kFrameSize10ms24kHz> lp_residual;
+  ComputeLpResidual({lpc_coeffs.data(), lpc_coeffs.size()}, {empty_frame},
+                    {lp_residual});
+}
+
+// TODO(bugs.webrtc.org/9076): Remove when the issue is fixed.
+TEST(RnnVadTest, LpResidualPipelineBitExactness) {
+  // Pitch buffer 24 kHz data reader.
+  auto pitch_buf_24kHz_reader = CreatePitchBuffer24kHzReader();
+  const size_t num_frames = pitch_buf_24kHz_reader.second;
+  std::array<float, kBufSize24kHz> pitch_buf_data;
+  rtc::ArrayView<float, kBufSize24kHz> pitch_buf_data_view(
+      pitch_buf_data.data(), pitch_buf_data.size());
+  // Read ground-truth.
+  auto lp_residual_reader = CreateLpResidualAndPitchPeriodGainReader();
+  ASSERT_EQ(num_frames, lp_residual_reader.second);
+  std::array<float, kBufSize24kHz> expected_lp_residual;
+  rtc::ArrayView<float, kBufSize24kHz> expected_lp_residual_view(
+      expected_lp_residual.data(), expected_lp_residual.size());
+  // Init pipeline.
+  std::array<float, kNumLpcCoefficients> lpc_coeffs;
+  rtc::ArrayView<float, kNumLpcCoefficients> lpc_coeffs_view(
+      lpc_coeffs.data(), kNumLpcCoefficients);
+  std::array<float, kBufSize24kHz> computed_lp_residual;
+  rtc::ArrayView<float, kBufSize24kHz> computed_lp_residual_view(
+      computed_lp_residual.data(), computed_lp_residual.size());
+  {
+    // TODO(bugs.webrtc.org/8948): Add when the issue is fixed.
+    // FloatingPointExceptionObserver fpe_observer;
+
+    for (size_t i = 0; i < num_frames; ++i) {
+      SCOPED_TRACE(i);
+      // Read input and expected output.
+      pitch_buf_24kHz_reader.first->ReadChunk(pitch_buf_data_view);
+      lp_residual_reader.first->ReadChunk(expected_lp_residual_view);
+      // Skip pitch gain and period.
+      float unused;
+      lp_residual_reader.first->ReadValue(&unused);
+      lp_residual_reader.first->ReadValue(&unused);
+      // Run pipeline.
+      ComputeAndPostProcessLpcCoefficients(pitch_buf_data_view,
+                                           lpc_coeffs_view);
+      ComputeLpResidual(lpc_coeffs_view, pitch_buf_data_view,
+                        computed_lp_residual_view);
+      // Compare.
+      ExpectNearAbsolute(expected_lp_residual_view, computed_lp_residual_view,
+                         kFloatMin);
+    }
+  }
+}
+
+}  // namespace test
+}  // namespace rnn_vad
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/rnn_vad/pitch_info.h b/modules/audio_processing/agc2/rnn_vad/pitch_info.h
new file mode 100644
index 0000000..f0998d1
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/pitch_info.h
@@ -0,0 +1,29 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AGC2_RNN_VAD_PITCH_INFO_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_INFO_H_
+
+namespace webrtc {
+namespace rnn_vad {
+
+// Stores pitch period and gain information. The pitch gain measures the
+// strength of the pitch (the higher, the stronger).
+struct PitchInfo {
+  PitchInfo() : period(0), gain(0.f) {}
+  PitchInfo(size_t p, float g) : period(p), gain(g) {}
+  size_t period;
+  float gain;
+};
+
+}  // namespace rnn_vad
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_INFO_H_
diff --git a/modules/audio_processing/agc2/rnn_vad/pitch_search.cc b/modules/audio_processing/agc2/rnn_vad/pitch_search.cc
new file mode 100644
index 0000000..4d83588
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/pitch_search.cc
@@ -0,0 +1,49 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/rnn_vad/pitch_search.h"
+#include "modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h"
+
+namespace webrtc {
+namespace rnn_vad {
+
+PitchInfo PitchSearch(rtc::ArrayView<const float, kBufSize24kHz> pitch_buf,
+                      PitchInfo prev_pitch_48kHz) {
+  // Perform the initial pitch search at 12 kHz.
+  std::array<float, kBufSize12kHz> pitch_buf_decimated;
+  Decimate2x(pitch_buf,
+             {pitch_buf_decimated.data(), pitch_buf_decimated.size()});
+  // Compute auto-correlation terms.
+  std::array<float, kNumInvertedLags12kHz> auto_corr;
+  ComputePitchAutoCorrelation(
+      {pitch_buf_decimated.data(), pitch_buf_decimated.size()}, kMaxPitch12kHz,
+      {auto_corr.data(), auto_corr.size()});
+  // Search for pitch at 12 kHz.
+  std::array<size_t, 2> pitch_candidates_inv_lags = FindBestPitchPeriods(
+      {auto_corr.data(), auto_corr.size()},
+      {pitch_buf_decimated.data(), pitch_buf_decimated.size()}, kMaxPitch12kHz);
+
+  // Refine the pitch period estimation.
+  // The refinement is done using the pitch buffer that contains 24 kHz samples.
+  // Therefore, adapt the inverted lags in |pitch_candidates_inv_lags| from 12
+  // to 24 kHz.
+  for (size_t i = 0; i < pitch_candidates_inv_lags.size(); ++i)
+    pitch_candidates_inv_lags[i] *= 2;
+  size_t pitch_inv_lag_48kHz = RefinePitchPeriod48kHz(
+      pitch_buf,
+      {pitch_candidates_inv_lags.data(), pitch_candidates_inv_lags.size()});
+  // Look for stronger harmonics to find the final pitch period and its gain.
+  RTC_DCHECK_LT(pitch_inv_lag_48kHz, kMaxPitch48kHz);
+  return CheckLowerPitchPeriodsAndComputePitchGain(
+      pitch_buf, kMaxPitch48kHz - pitch_inv_lag_48kHz, prev_pitch_48kHz);
+}
+
+}  // namespace rnn_vad
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/rnn_vad/pitch_search.h b/modules/audio_processing/agc2/rnn_vad/pitch_search.h
new file mode 100644
index 0000000..a0af0eb
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/pitch_search.h
@@ -0,0 +1,29 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_H_
+
+#include "api/array_view.h"
+#include "modules/audio_processing/agc2/rnn_vad/common.h"
+#include "modules/audio_processing/agc2/rnn_vad/pitch_info.h"
+
+namespace webrtc {
+namespace rnn_vad {
+
+// Searches the pitch period and gain. Return the pitch estimation data for
+// 48 kHz.
+PitchInfo PitchSearch(rtc::ArrayView<const float, kBufSize24kHz> pitch_buf,
+                      PitchInfo prev_pitch_48kHz);
+
+}  // namespace rnn_vad
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_H_
diff --git a/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc b/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc
new file mode 100644
index 0000000..1ff4621
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc
@@ -0,0 +1,407 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/rnn_vad/pitch_search_internal.h"
+
+#include <algorithm>
+#include <cmath>
+#include <numeric>
+#include <utility>
+
+#include "modules/audio_processing/agc2/rnn_vad/common.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace rnn_vad {
+namespace {
+
+// Converts a lag to an inverted lag (only for 24kHz).
+size_t GetInvertedLag(size_t lag) {
+  RTC_DCHECK_LE(lag, kMaxPitch24kHz);
+  return kMaxPitch24kHz - lag;
+}
+
+float ComputeAutoCorrelationCoeff(rtc::ArrayView<const float> pitch_buf,
+                                  size_t inv_lag,
+                                  size_t max_pitch_period) {
+  RTC_DCHECK_LT(inv_lag, pitch_buf.size());
+  RTC_DCHECK_LT(max_pitch_period, pitch_buf.size());
+  RTC_DCHECK_LE(inv_lag, max_pitch_period);
+  // TODO(bugs.webrtc.org/9076): Maybe optimize using vectorization.
+  return std::inner_product(pitch_buf.begin() + max_pitch_period,
+                            pitch_buf.end(), pitch_buf.begin() + inv_lag, 0.f);
+}
+
+// Computes a pseudo-interpolation offset for an estimated pitch period |lag| by
+// looking at the auto-correlation coefficients in the neighborhood of |lag|.
+// (namely, |prev_auto_corr|, |lag_auto_corr| and |next_auto_corr|). The output
+// is a lag in {-1, 0, +1}.
+// TODO(bugs.webrtc.org/9076): Consider removing pseudo-i since it
+// is relevant only if the spectral analysis works at a sample rate that is
+// twice as that of the pitch buffer (not so important instead for the estimated
+// pitch period feature fed into the RNN).
+int GetPitchPseudoInterpolationOffset(size_t lag,
+                                      float prev_auto_corr,
+                                      float lag_auto_corr,
+                                      float next_auto_corr) {
+  const float& a = prev_auto_corr;
+  const float& b = lag_auto_corr;
+  const float& c = next_auto_corr;
+
+  int offset = 0;
+  if ((c - a) > 0.7f * (b - a)) {
+    offset = 1;  // |c| is the largest auto-correlation coefficient.
+  } else if ((a - c) > 0.7f * (b - c)) {
+    offset = -1;  // |a| is the largest auto-correlation coefficient.
+  }
+  return offset;
+}
+
+// Refines a pitch period |lag| encoded as lag with pseudo-interpolation. The
+// output sample rate is twice as that of |lag|.
+size_t PitchPseudoInterpolationLagPitchBuf(
+    size_t lag,
+    rtc::ArrayView<const float, kBufSize24kHz> pitch_buf) {
+  int offset = 0;
+  // Cannot apply pseudo-interpolation at the boundaries.
+  if (lag > 0 && lag < kMaxPitch24kHz) {
+    offset = GetPitchPseudoInterpolationOffset(
+        lag,
+        ComputeAutoCorrelationCoeff(pitch_buf, GetInvertedLag(lag - 1),
+                                    kMaxPitch24kHz),
+        ComputeAutoCorrelationCoeff(pitch_buf, GetInvertedLag(lag),
+                                    kMaxPitch24kHz),
+        ComputeAutoCorrelationCoeff(pitch_buf, GetInvertedLag(lag + 1),
+                                    kMaxPitch24kHz));
+  }
+  return 2 * lag + offset;
+}
+
+// Refines a pitch period |inv_lag| encoded as inverted lag with
+// pseudo-interpolation. The output sample rate is twice as that of
+// |inv_lag|.
+size_t PitchPseudoInterpolationInvLagAutoCorr(
+    size_t inv_lag,
+    rtc::ArrayView<const float> auto_corr) {
+  int offset = 0;
+  // Cannot apply pseudo-interpolation at the boundaries.
+  if (inv_lag > 0 && inv_lag < auto_corr.size() - 1) {
+    offset = GetPitchPseudoInterpolationOffset(inv_lag, auto_corr[inv_lag + 1],
+                                               auto_corr[inv_lag],
+                                               auto_corr[inv_lag - 1]);
+  }
+  // TODO(bugs.webrtc.org/9076): When retraining, check if |offset| below should
+  // be subtracted since |inv_lag| is an inverted lag but offset is a lag.
+  return 2 * inv_lag + offset;
+}
+
+// Integer multipliers used in CheckLowerPitchPeriodsAndComputePitchGain() when
+// looking for sub-harmonics.
+// The values have been chosen to serve the following algorithm. Given the
+// initial pitch period T, we examine whether one of its harmonics is the true
+// fundamental frequency. We consider T/k with k in {2, ..., 15}. For each of
+// these harmonics, in addition to the pitch gain of itself, we choose one
+// multiple of its pitch period, n*T/k, to validate it (by averaging their pitch
+// gains). The multiplier n is chosen so that n*T/k is used only one time over
+// all k. When for example k = 4, we should also expect a peak at 3*T/4. When
+// k = 8 instead we don't want to look at 2*T/8, since we have already checked
+// T/4 before. Instead, we look at T*3/8.
+// The array can be generate in Python as follows:
+//   from fractions import Fraction
+//   # Smallest positive integer not in X.
+//   def mex(X):
+//     for i in range(1, int(max(X)+2)):
+//       if i not in X:
+//         return i
+//   # Visited multiples of the period.
+//   S = {1}
+//   for n in range(2, 16):
+//     sn = mex({n * i for i in S} | {1})
+//     S = S | {Fraction(1, n), Fraction(sn, n)}
+//     print(sn, end=', ')
+constexpr std::array<size_t, 14> kSubHarmonicMultipliers = {
+    {3, 2, 3, 2, 5, 2, 3, 2, 3, 2, 5, 2, 3, 2}};
+
+// Initial pitch period candidate thresholds for ComputePitchGainThreshold() for
+// a sample rate of 24 kHz. Computed as [5*k*k for k in range(16)].
+constexpr std::array<size_t, 14> kInitialPitchPeriodThresholds = {
+    {20, 45, 80, 125, 180, 245, 320, 405, 500, 605, 720, 845, 980, 1125}};
+
+}  // namespace
+
+void Decimate2x(rtc::ArrayView<const float, kBufSize24kHz> src,
+                rtc::ArrayView<float, kBufSize12kHz> dst) {
+  // TODO(bugs.webrtc.org/9076): Consider adding anti-aliasing filter.
+  static_assert(2 * dst.size() == src.size(), "");
+  for (size_t i = 0; i < dst.size(); ++i)
+    dst[i] = src[2 * i];
+}
+
+float ComputePitchGainThreshold(size_t candidate_pitch_period,
+                                size_t pitch_period_ratio,
+                                size_t initial_pitch_period,
+                                float initial_pitch_gain,
+                                size_t prev_pitch_period,
+                                size_t prev_pitch_gain) {
+  // Map arguments to more compact aliases.
+  const size_t& t1 = candidate_pitch_period;
+  const size_t& k = pitch_period_ratio;
+  const size_t& t0 = initial_pitch_period;
+  const float& g0 = initial_pitch_gain;
+  const size_t& t_prev = prev_pitch_period;
+  const size_t& g_prev = prev_pitch_gain;
+
+  // Validate input.
+  RTC_DCHECK_GE(k, 2);
+
+  // Compute a term that lowers the threshold when |t1| is close to the last
+  // estimated period |t_prev| - i.e., pitch tracking.
+  float lower_threshold_term = 0;
+  if (abs(static_cast<int>(t1) - static_cast<int>(t_prev)) <= 1) {
+    // The candidate pitch period is within 1 sample from the previous one.
+    // Make the candidate at |t1| very easy to be accepted.
+    lower_threshold_term = g_prev;
+  } else if (abs(static_cast<int>(t1) - static_cast<int>(t_prev)) == 2 &&
+             t0 > kInitialPitchPeriodThresholds[k - 2]) {
+    // The candidate pitch period is 2 samples far from the previous one and the
+    // period |t0| (from which |t1| has been derived) is greater than a
+    // threshold. Make |t1| easy to be accepted.
+    lower_threshold_term = 0.5f * g_prev;
+  }
+  // Set the threshold based on the gain of the initial estimate |t0|. Also
+  // reduce the chance of false positives caused by a bias towards high
+  // frequencies (originating from short-term correlations).
+  float threshold = std::max(0.3f, 0.7f * g0 - lower_threshold_term);
+  if (t1 < 3 * kMinPitch24kHz) {  // High frequency.
+    threshold = std::max(0.4f, 0.85f * g0 - lower_threshold_term);
+  } else if (t1 < 2 * kMinPitch24kHz) {  // Even higher frequency.
+    threshold = std::max(0.5f, 0.9f * g0 - lower_threshold_term);
+  }
+  return threshold;
+}
+
+void ComputeSlidingFrameSquareEnergies(
+    rtc::ArrayView<const float, kBufSize24kHz> pitch_buf,
+    rtc::ArrayView<float, kMaxPitch24kHz + 1> yy_values) {
+  float yy =
+      ComputeAutoCorrelationCoeff(pitch_buf, kMaxPitch24kHz, kMaxPitch24kHz);
+  yy_values[0] = yy;
+  for (size_t i = 1; i < yy_values.size(); ++i) {
+    RTC_DCHECK_LE(i, kMaxPitch24kHz + kFrameSize20ms24kHz);
+    RTC_DCHECK_LE(i, kMaxPitch24kHz);
+    const float old_coeff = pitch_buf[kMaxPitch24kHz + kFrameSize20ms24kHz - i];
+    const float new_coeff = pitch_buf[kMaxPitch24kHz - i];
+    yy -= old_coeff * old_coeff;
+    yy += new_coeff * new_coeff;
+    yy = std::max(0.f, yy);
+    yy_values[i] = yy;
+  }
+}
+
+// TODO(bugs.webrtc.org/9076): Optimize using FFT and/or vectorization.
+void ComputePitchAutoCorrelation(
+    rtc::ArrayView<const float, kBufSize12kHz> pitch_buf,
+    size_t max_pitch_period,
+    rtc::ArrayView<float, kNumInvertedLags12kHz> auto_corr) {
+  RTC_DCHECK_GT(max_pitch_period, auto_corr.size());
+  RTC_DCHECK_LT(max_pitch_period, pitch_buf.size());
+  // Compute auto-correlation coefficients.
+  for (size_t inv_lag = 0; inv_lag < auto_corr.size(); ++inv_lag) {
+    auto_corr[inv_lag] =
+        ComputeAutoCorrelationCoeff(pitch_buf, inv_lag, max_pitch_period);
+  }
+}
+
+std::array<size_t, 2> FindBestPitchPeriods(
+    rtc::ArrayView<const float> auto_corr,
+    rtc::ArrayView<const float> pitch_buf,
+    size_t max_pitch_period) {
+  // Stores a pitch candidate period and strength information.
+  struct PitchCandidate {
+    // Pitch period encoded as inverted lag.
+    size_t period_inverted_lag = 0;
+    // Pitch strength encoded as a ratio.
+    float strength_numerator = -1.f;
+    float strength_denominator = 0.f;
+    // Compare the strength of two pitch candidates.
+    bool HasStrongerPitchThan(const PitchCandidate& b) const {
+      // Comparing the numerator/denominator ratios without using divisions.
+      return strength_numerator * b.strength_denominator >
+             b.strength_numerator * strength_denominator;
+    }
+  };
+
+  RTC_DCHECK_GT(max_pitch_period, auto_corr.size());
+  RTC_DCHECK_LT(max_pitch_period, pitch_buf.size());
+  const size_t frame_size = pitch_buf.size() - max_pitch_period;
+  // TODO(bugs.webrtc.org/9076): Maybe optimize using vectorization.
+  float yy =
+      std::inner_product(pitch_buf.begin(), pitch_buf.begin() + frame_size + 1,
+                         pitch_buf.begin(), 1.f);
+  // Search best and second best pitches by looking at the scaled
+  // auto-correlation.
+  PitchCandidate candidate;
+  PitchCandidate best;
+  PitchCandidate second_best;
+  second_best.period_inverted_lag = 1;
+  for (size_t inv_lag = 0; inv_lag < auto_corr.size(); ++inv_lag) {
+    // A pitch candidate must have positive correlation.
+    if (auto_corr[inv_lag] > 0) {
+      candidate.period_inverted_lag = inv_lag;
+      candidate.strength_numerator = auto_corr[inv_lag] * auto_corr[inv_lag];
+      candidate.strength_denominator = yy;
+      if (candidate.HasStrongerPitchThan(second_best)) {
+        if (candidate.HasStrongerPitchThan(best)) {
+          second_best = best;
+          best = candidate;
+        } else {
+          second_best = candidate;
+        }
+      }
+    }
+    // Update |squared_energy_y| for the next inverted lag.
+    const float old_coeff = pitch_buf[inv_lag];
+    const float new_coeff = pitch_buf[inv_lag + frame_size];
+    yy -= old_coeff * old_coeff;
+    yy += new_coeff * new_coeff;
+    yy = std::max(0.f, yy);
+  }
+  return {{best.period_inverted_lag, second_best.period_inverted_lag}};
+}
+
+size_t RefinePitchPeriod48kHz(
+    rtc::ArrayView<const float, kBufSize24kHz> pitch_buf,
+    rtc::ArrayView<const size_t, 2> inv_lags) {
+  // Compute the auto-correlation terms only for neighbors of the given pitch
+  // candidates (similar to what is done in ComputePitchAutoCorrelation(), but
+  // for a few lag values).
+  std::array<float, kNumInvertedLags24kHz> auto_corr;
+  auto_corr.fill(0.f);  // Zeros become ignored lags in FindBestPitchPeriods().
+  auto is_neighbor = [](size_t i, size_t j) {
+    return ((i > j) ? (i - j) : (j - i)) <= 2;
+  };
+  for (size_t inv_lag = 0; inv_lag < auto_corr.size(); ++inv_lag) {
+    if (is_neighbor(inv_lag, inv_lags[0]) || is_neighbor(inv_lag, inv_lags[1]))
+      auto_corr[inv_lag] =
+          ComputeAutoCorrelationCoeff(pitch_buf, inv_lag, kMaxPitch24kHz);
+  }
+  // Find best pitch at 24 kHz.
+  const auto pitch_candidates_inv_lags = FindBestPitchPeriods(
+      {auto_corr.data(), auto_corr.size()},
+      {pitch_buf.data(), pitch_buf.size()}, kMaxPitch24kHz);
+  const auto inv_lag = pitch_candidates_inv_lags[0];  // Refine the best.
+  // Pseudo-interpolation.
+  return PitchPseudoInterpolationInvLagAutoCorr(
+      inv_lag, {auto_corr.data(), auto_corr.size()});
+}
+
+PitchInfo CheckLowerPitchPeriodsAndComputePitchGain(
+    rtc::ArrayView<const float, kBufSize24kHz> pitch_buf,
+    size_t initial_pitch_period_48kHz,
+    PitchInfo prev_pitch_48kHz) {
+  RTC_DCHECK_LE(kMinPitch48kHz, initial_pitch_period_48kHz);
+  RTC_DCHECK_LE(initial_pitch_period_48kHz, kMaxPitch48kHz);
+  // Stores information for a refined pitch candidate.
+  struct RefinedPitchCandidate {
+    RefinedPitchCandidate() {}
+    RefinedPitchCandidate(size_t period_24kHz, float gain, float xy, float yy)
+        : period_24kHz(period_24kHz), gain(gain), xy(xy), yy(yy) {}
+    size_t period_24kHz;
+    // Pitch strength information.
+    float gain;
+    // Additional pitch strength information used for the final estimation of
+    // pitch gain.
+    float xy;  // Cross-correlation.
+    float yy;  // Auto-correlation.
+  };
+
+  // Initialize.
+  std::array<float, kMaxPitch24kHz + 1> yy_values;
+  ComputeSlidingFrameSquareEnergies(pitch_buf,
+                                    {yy_values.data(), yy_values.size()});
+  const float xx = yy_values[0];
+  // Helper lambdas.
+  const auto pitch_gain = [](float xy, float yy, float xx) {
+    RTC_DCHECK_LE(0.f, xx * yy);
+    return xy / std::sqrt(1.f + xx * yy);
+  };
+  // Initial pitch candidate gain.
+  RefinedPitchCandidate best_pitch;
+  best_pitch.period_24kHz =
+      std::min(initial_pitch_period_48kHz / 2, kMaxPitch24kHz - 1);
+  best_pitch.xy = ComputeAutoCorrelationCoeff(
+      pitch_buf, GetInvertedLag(best_pitch.period_24kHz), kMaxPitch24kHz);
+  best_pitch.yy = yy_values[best_pitch.period_24kHz];
+  best_pitch.gain = pitch_gain(best_pitch.xy, best_pitch.yy, xx);
+
+  // Store the initial pitch period information.
+  const size_t initial_pitch_period = best_pitch.period_24kHz;
+  const float initial_pitch_gain = best_pitch.gain;
+
+  // Given the initial pitch estimation, check lower periods (i.e., harmonics).
+  const auto alternative_period = [](size_t period, size_t k,
+                                     size_t n) -> size_t {
+    RTC_DCHECK_LT(0, k);
+    return (2 * n * period + k) / (2 * k);  // Same as round(n*period/k).
+  };
+  for (size_t k = 2; k < kSubHarmonicMultipliers.size() + 2; ++k) {
+    size_t candidate_pitch_period =
+        alternative_period(initial_pitch_period, k, 1);
+    if (candidate_pitch_period < kMinPitch24kHz)
+      break;
+    // When looking at |candidate_pitch_period|, we also look at one of its
+    // sub-harmonics. |kSubHarmonicMultipliers| is used to know where to look.
+    // |k| == 2 is a special case since |candidate_pitch_secondary_period| might
+    // be greater than the maximum pitch period.
+    size_t candidate_pitch_secondary_period = alternative_period(
+        initial_pitch_period, k, kSubHarmonicMultipliers[k - 2]);
+    if (k == 2 && candidate_pitch_secondary_period > kMaxPitch24kHz)
+      candidate_pitch_secondary_period = initial_pitch_period;
+    RTC_DCHECK_NE(candidate_pitch_period, candidate_pitch_secondary_period)
+        << "The lower pitch period and the additional sub-harmonic must not "
+        << "coincide.";
+    // Compute an auto-correlation score for the primary pitch candidate
+    // |candidate_pitch_period| by also looking at its possible sub-harmonic
+    // |candidate_pitch_secondary_period|.
+    float xy_primary_period = ComputeAutoCorrelationCoeff(
+        pitch_buf, GetInvertedLag(candidate_pitch_period), kMaxPitch24kHz);
+    float xy_secondary_period = ComputeAutoCorrelationCoeff(
+        pitch_buf, GetInvertedLag(candidate_pitch_secondary_period),
+        kMaxPitch24kHz);
+    float xy = 0.5f * (xy_primary_period + xy_secondary_period);
+    float yy = 0.5f * (yy_values[candidate_pitch_period] +
+                       yy_values[candidate_pitch_secondary_period]);
+    float candidate_pitch_gain = pitch_gain(xy, yy, xx);
+
+    // Maybe update best period.
+    float threshold = ComputePitchGainThreshold(
+        candidate_pitch_period, k, initial_pitch_period, initial_pitch_gain,
+        prev_pitch_48kHz.period / 2, prev_pitch_48kHz.gain);
+    if (candidate_pitch_gain > threshold) {
+      best_pitch = {candidate_pitch_period, candidate_pitch_gain, xy, yy};
+    }
+  }
+
+  // Final pitch gain and period.
+  best_pitch.xy = std::max(0.f, best_pitch.xy);
+  RTC_DCHECK_LE(0.f, best_pitch.yy);
+  float final_pitch_gain = (best_pitch.yy <= best_pitch.xy)
+                               ? 1.f
+                               : best_pitch.xy / (best_pitch.yy + 1.f);
+  final_pitch_gain = std::min(best_pitch.gain, final_pitch_gain);
+  size_t final_pitch_period_48kHz = std::max(
+      kMinPitch48kHz,
+      PitchPseudoInterpolationLagPitchBuf(best_pitch.period_24kHz, pitch_buf));
+
+  return {final_pitch_period_48kHz, final_pitch_gain};
+}
+
+}  // namespace rnn_vad
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h b/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h
new file mode 100644
index 0000000..dfe1b35
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h
@@ -0,0 +1,100 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_INTERNAL_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_INTERNAL_H_
+
+#include <array>
+
+#include "api/array_view.h"
+#include "modules/audio_processing/agc2/rnn_vad/common.h"
+#include "modules/audio_processing/agc2/rnn_vad/pitch_info.h"
+
+namespace webrtc {
+namespace rnn_vad {
+
+// The inverted lags for the pitch interval [|kInitialMinPitch12kHz|,
+// |kMaxPitch12kHz|] are in the range [0, |kNumInvertedLags|].
+static_assert(kMaxPitch12kHz > kInitialMinPitch12kHz, "");
+static_assert(kMaxPitch24kHz > kInitialMinPitch24kHz, "");
+constexpr size_t kNumInvertedLags12kHz = kMaxPitch12kHz - kInitialMinPitch12kHz;
+constexpr size_t kNumInvertedLags24kHz = kMaxPitch24kHz - kInitialMinPitch24kHz;
+
+// Performs 2x decimation without any anti-aliasing filter.
+void Decimate2x(rtc::ArrayView<const float, kBufSize24kHz> src,
+                rtc::ArrayView<float, kBufSize12kHz> dst);
+
+// Computes a gain threshold for a candidate pitch period given the initial and
+// the previous pitch period and gain estimates and the pitch period ratio used
+// to derive the candidate pitch period from the initial period.
+float ComputePitchGainThreshold(size_t candidate_pitch_period,
+                                size_t pitch_period_ratio,
+                                size_t initial_pitch_period,
+                                float initial_pitch_gain,
+                                size_t prev_pitch_period,
+                                size_t prev_pitch_gain);
+
+// Computes the sum of squared samples for every sliding frame in the pitch
+// buffer. |yy_values| indexes are lags.
+//
+// The pitch buffer is structured as depicted below:
+// |.........|...........|
+//      a          b
+// The part on the left, named "a" contains the oldest samples, whereas "b" the
+// most recent ones. The size of "a" corresponds to the maximum pitch period,
+// that of "b" to the frame size (e.g., 16 ms and 20 ms respectively).
+void ComputeSlidingFrameSquareEnergies(
+    rtc::ArrayView<const float, kBufSize24kHz> pitch_buf,
+    rtc::ArrayView<float, kMaxPitch24kHz + 1> yy_values);
+
+// Computes the auto-correlation coefficients for a given pitch interval.
+// |auto_corr| indexes are inverted lags.
+//
+// The auto-correlations coefficients are computed as follows:
+// |.........|...........|  <- pitch buffer
+//           [ x (fixed) ]
+// [   y_0   ]
+//         [ y_{m-1} ]
+// x and y are sub-array of equal length; x is never moved, whereas y slides.
+// The cross-correlation between y_0 and x corresponds to the auto-correlation
+// for the maximum pitch period. Hence, the first value in |auto_corr| has an
+// inverted lag equal to 0 that corresponds to a lag equal to the maximum pitch
+// period.
+void ComputePitchAutoCorrelation(
+    rtc::ArrayView<const float, kBufSize12kHz> pitch_buf,
+    size_t max_pitch_period,
+    rtc::ArrayView<float, kNumInvertedLags12kHz> auto_corr);
+
+// Given the auto-correlation coefficients stored according to
+// ComputePitchAutoCorrelation() (i.e., using inverted lags), returns the best
+// and the second best pitch periods.
+std::array<size_t, 2> FindBestPitchPeriods(
+    rtc::ArrayView<const float> auto_corr,
+    rtc::ArrayView<const float> pitch_buf,
+    size_t max_pitch_period);
+
+// Refines the pitch period estimation given the pitch buffer |pitch_buf| and
+// the initial pitch period estimation |inv_lags|. Returns an inverted lag at
+// 48 kHz.
+size_t RefinePitchPeriod48kHz(
+    rtc::ArrayView<const float, kBufSize24kHz> pitch_buf,
+    rtc::ArrayView<const size_t, 2> inv_lags);
+
+// Refines the pitch period estimation and compute the pitch gain. Returns the
+// refined pitch estimation data at 48 kHz.
+PitchInfo CheckLowerPitchPeriodsAndComputePitchGain(
+    rtc::ArrayView<const float, kBufSize24kHz> pitch_buf,
+    size_t initial_pitch_period_48kHz,
+    PitchInfo prev_pitch_48kHz);
+
+}  // namespace rnn_vad
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_PITCH_SEARCH_INTERNAL_H_
diff --git a/modules/audio_processing/agc2/rnn_vad/pitch_search_internal_unittest.cc b/modules/audio_processing/agc2/rnn_vad/pitch_search_internal_unittest.cc
new file mode 100644
index 0000000..9a6a267
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/pitch_search_internal_unittest.cc
@@ -0,0 +1,531 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/rnn_vad/pitch_search_internal.h"
+
+#include <array>
+#include <tuple>
+
+#include "modules/audio_processing/agc2/rnn_vad/test_utils.h"
+// TODO(bugs.webrtc.org/8948): Add when the issue is fixed.
+// #include "test/fpe_observer.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace rnn_vad {
+namespace test {
+namespace {
+
+// TODO(bugs.webrtc.org/9076): Move to resource file.
+constexpr std::array<float, kBufSize24kHz> kPitchBufferData = {
+    -35.248100281f, -25.836528778f, 5.682674408f,  2.880297661f,  -1.648161888f,
+    -4.094896793f,  -3.500580072f,  -0.896141529f, -2.989939451f, -4.608089447f,
+    -3.721750736f,  -2.290785789f,  -3.326566458f, -4.370154381f, -3.221047878f,
+    -4.049056530f,  -2.846302271f,  -1.805017233f, -1.547624588f, -0.809937477f,
+    -1.446955442f,  -3.258146763f,  -1.849959373f, 0.005283833f,  -0.571619749f,
+    -0.630573988f,  -0.162780523f,  -2.699024916f, -0.856231451f, 2.748089552f,
+    2.026614428f,   -0.474685907f,  -0.571918726f, 1.186420918f,  1.770769954f,
+    2.017296791f,   1.154794335f,   1.082345366f,  1.954892635f,  2.249727726f,
+    2.643483400f,   1.857815385f,   0.064472735f,  0.015978813f,  0.301099658f,
+    0.478950322f,   -0.669701457f,  -0.654453993f, 1.338572979f,  -0.493052602f,
+    -1.763812065f,  0.524392128f,   0.010438919f,  -1.726593733f, -2.866710663f,
+    -2.065258503f,  -3.010460854f,  -3.994765282f, -4.102010250f, -3.135548830f,
+    -2.597487926f,  -2.255330563f,  -1.002008915f, 0.523116589f,  1.430158496f,
+    -1.655169368f,  -2.263641357f,  0.766040802f,  1.166070461f,  0.002490997f,
+    0.401043415f,   -0.158550858f,  -0.572042346f, 1.365390539f,  -1.397871614f,
+    -2.020734787f,  -1.979169965f,  -1.025816441f, 0.012545407f,  -1.042758584f,
+    -1.206598401f,  -1.140330791f,  -3.060853720f, -3.530077934f, -1.774474382f,
+    -1.342000484f,  -3.171817064f,  -2.489153862f, -1.593364000f, -2.552185535f,
+    -2.899760723f,  -4.698278427f,  -4.123534203f, -2.613421679f, -2.061793327f,
+    -4.113687515f,  -3.174087524f,  -2.367874622f, -4.523970604f, -4.250762939f,
+    -2.752931118f,  -1.547106743f,  -4.109455109f, -3.893044472f, -2.348384857f,
+    -3.194510698f,  -3.502159357f,  -2.785978794f, -1.981978416f, -3.279178143f,
+    -3.007923365f,  -1.801304340f,  -1.839247227f, -1.003675938f, -0.985928297f,
+    -1.647925615f,  -2.166392088f,  -1.947163343f, 0.488545895f,  1.567199469f,
+    -1.179960012f,  -2.710370064f,  -2.613196850f, -3.205850124f, -2.796218395f,
+    -0.715085745f,  1.406243801f,   -0.779834270f, -2.075612307f, -0.922246933f,
+    -1.849850416f,  0.979040504f,   3.570628166f,  0.945924520f,  -2.821768284f,
+    -6.262358189f,  -6.154916763f,  -0.567943573f, 2.386518955f,  1.673806906f,
+    -3.676584721f,  -7.129202843f,  -3.311969519f, 1.126702785f,  3.218248606f,
+    1.600885630f,   -1.709451079f,  -6.822564125f, -6.011950970f, -0.671678543f,
+    1.080205441f,   -1.342422366f,  -3.589303732f, -3.586701870f, -3.425134897f,
+    -1.078015327f,  2.556719542f,   0.469867468f,  0.139251709f,  -0.118916273f,
+    -1.284181952f,  0.941113472f,   0.550188303f,  -1.767568469f, -5.429461956f,
+    -5.065113068f,  -2.111886740f,  -3.606999397f, -2.410579205f, 1.013466120f,
+    1.057218194f,   0.305267453f,   2.898609161f,  5.776575565f,  4.792305946f,
+    -0.863526106f,  -2.439013481f,  -0.825202525f, -2.297998428f, -0.520106375f,
+    -0.653605103f,  -3.204111576f,  -2.455038786f, -2.160304308f, 0.622359931f,
+    3.803062916f,   4.340928555f,   2.390868664f,  1.645600080f,  0.405841053f,
+    -0.153203994f,  3.438643217f,   4.752261162f,  1.552502871f,  1.947945356f,
+    0.856451511f,   -0.606808305f,  -1.223945618f, -1.845071912f, -0.204472303f,
+    1.750840783f,   2.435559034f,   -1.253612280f, -2.675215721f, 1.614801407f,
+    3.002861023f,   1.743503809f,   3.409059286f,  4.303173542f,  2.441751957f,
+    1.752274275f,   1.874113560f,   2.070837736f,  1.401355743f,  -0.330647945f,
+    -0.664121151f,  1.196543574f,   1.506967187f,  0.985752344f,  -1.265938520f,
+    -1.433794141f,  0.380195618f,   0.061504841f,  1.079771042f,  1.773771763f,
+    3.226663589f,   4.170571804f,   4.220288277f,  3.619904041f,  2.316211224f,
+    2.012817860f,   0.370972633f,   0.517094851f,  1.869508862f,  0.357770681f,
+    -2.991472483f,  -3.216646433f,  0.232109070f,  1.803660274f,  2.928784370f,
+    4.909455776f,   5.913621426f,   4.653719902f,  4.387111187f,  4.793289661f,
+    4.744520187f,   5.214610100f,   3.996322632f,  2.619040728f,  0.758128643f,
+    -0.092789888f,  0.070066452f,   0.704165459f,  2.042234898f,  2.768569231f,
+    3.340583324f,   3.212181091f,   2.748130322f,  3.077554941f,  2.189792156f,
+    2.646749735f,   2.817450523f,   1.611892223f,  1.981805444f,  -1.088236094f,
+    -2.187484741f,  -0.654897690f,  -0.900939941f, 0.148309708f,  1.498139143f,
+    -0.261296749f,  -3.220157146f,  -1.727450609f, 0.807144105f,  -0.809251904f,
+    -2.361308336f,  -1.421746969f,  -0.793132067f, -0.313778281f, -0.641793191f,
+    -0.999286890f,  0.219423503f,   0.976444781f,  0.152786255f,  -0.405437022f,
+    0.120257735f,   -0.392024517f,  -0.019678771f, 1.492373466f,  0.926774263f,
+    0.566291928f,   1.307234287f,   1.496955752f,  1.448441863f,  2.212901354f,
+    1.314700723f,   0.213681281f,   1.011370897f,  1.827155828f,  0.250772655f,
+    -0.429592669f,  0.435638547f,   1.506532907f,  1.350761652f,  -0.387142301f,
+    -1.770648122f,  -2.690037489f,  -1.788924456f, -2.023291588f, -2.354584694f,
+    -2.587521076f,  -2.002159595f,  -0.355855435f, 0.825611115f,  3.075081587f,
+    2.687968254f,   0.074088633f,   0.439936757f,  1.214704275f,  2.670343399f,
+    1.567362547f,   -1.573154926f,  -3.216549397f, -3.596383333f, -3.893716335f,
+    -2.456265688f,  -4.313135624f,  -5.783064842f, -5.344826221f, -3.484399319f,
+    -2.235594273f,  -3.568959475f,  -2.447141886f, -0.755384564f, -1.178364277f,
+    1.034289122f,   1.746821165f,   -1.159413576f, -2.569937706f, -1.742212296f,
+    -0.270784855f,  1.886857986f,   0.831889153f,  0.636521816f,  -0.067433357f,
+    -0.256595969f,  0.907287478f,   1.575596929f,  0.393882513f,  -0.510042071f,
+    0.507258415f,   0.059408009f,   1.776192427f,  1.664948106f,  -0.341539711f,
+    -0.072047889f,  -0.795555651f,  0.704908550f,  2.127685547f,  1.486027241f,
+    1.973046541f,   2.456688404f,   2.871328354f,  4.989626408f,  5.076294422f,
+    4.262395859f,   3.622689009f,   3.241683960f,  4.222597599f,  3.575423479f,
+    1.997965097f,   1.391216874f,   2.329971790f,  2.898612261f,  3.871258736f,
+    2.857767582f,   2.960238218f,   3.047467470f,  2.790968180f,  2.183730364f,
+    1.991029263f,   2.727865934f,   1.561259747f,  0.787606239f,  3.036532879f,
+    2.430759192f,   1.475822210f,   2.307994127f,  1.857011318f,  1.538355589f,
+    2.320549965f,   3.305005074f,   2.554165363f,  2.630100727f,  3.506094217f,
+    4.454113483f,   2.894124269f,   4.061129570f,  4.425602436f,  3.218537807f,
+    2.712452173f,   5.546891212f,   6.138017654f,  5.897895813f,  5.698192596f,
+    4.096743584f,   2.661385298f,   3.646550655f,  4.626225948f,  5.025664330f,
+    3.861543894f,   4.374861717f,   5.388185978f,  3.376737356f,  2.751175404f,
+    3.299628258f,   2.025987387f,   1.094563961f,  0.128147125f,  -4.321690559f,
+    -6.165239811f,  -4.245608330f,  -2.974690914f, -5.110438824f, -6.619713306f,
+    -6.594148636f,  -7.972207069f,  -8.034727097f, -7.296438217f, -6.822746754f,
+    -6.375267029f,  -7.629575729f,  -8.404177666f, -5.002337456f, -7.024040699f,
+    -7.799823761f,  -5.423873901f,  -4.861459732f, -2.772324085f, 0.002551556f,
+    -1.445306778f,  -1.726813316f,  0.889497757f,  1.760663986f,  2.722227097f,
+    4.755805969f,   4.188167572f,   1.547533512f,  2.444593906f,  1.612852097f,
+    -0.508655310f,  0.046535015f,   1.720140934f,  1.265070438f,  0.976964772f,
+    2.446830273f,   6.308787823f,   7.798269272f,  5.347163200f,  3.540414810f,
+    3.510186911f,   4.305843830f,   5.957427025f,  7.200410843f,  7.049768448f,
+    7.179680824f,   8.508881569f,   9.094768524f,  12.307214737f, 14.215225220f,
+    11.316717148f,  8.660657883f,   7.528784275f,  7.616339207f,  6.968524933f,
+    4.246424198f,   0.214603424f,   0.449179649f,  1.695000648f,  0.110423088f,
+    -0.304885864f,  -2.038585663f,  -5.223299980f, -5.486608505f, -5.728059292f,
+    -4.866038799f,  -2.678806305f,  -3.464673519f, -3.407086372f, -2.490849733f,
+    -0.161162257f,  0.118952155f,   0.312392950f,  -0.341049194f, 0.013419867f,
+    3.722306252f,   3.901551247f,   1.781876802f,  2.446551561f,  3.659160852f,
+    2.530288696f,   3.577404499f,   3.201550961f,  0.281389952f,  -0.291333675f,
+    1.386508465f,   2.181721210f,   -2.802821159f, -1.531007886f, 1.608560324f,
+    -0.523656845f,  -0.281057000f,  0.571323991f,  0.668095112f,  -1.637194037f,
+    -2.756963253f,  -1.340666890f,  -2.180127621f, -1.874165773f, 0.660111070f,
+    0.197176635f,   0.781580091f,   1.749967933f,  0.674724638f,  -2.082683325f,
+    -3.159717083f,  -2.898023844f,  -4.691623211f, -5.614190102f, -6.157790661f,
+    -7.776132584f,  -8.029224396f,  -6.940879345f, -7.065263271f, -7.003522396f,
+    -5.691181183f,  -7.872379780f,  -7.614178658f, -5.778759003f, -4.605045319f,
+    -4.695390224f,  -5.865473270f,  -5.825413227f, -4.648111820f, -2.193091869f,
+    -0.172003269f,  1.482686043f,   -0.915655136f, -2.626194954f, 1.852293015f,
+    4.184171677f,   4.083235264f,   1.048256874f,  -1.361350536f, 0.438748837f,
+    1.716395378f,   2.916294813f,   2.639499664f,  0.059617281f,  -1.883811951f,
+    2.136622429f,   6.641947269f,   5.951328754f,  3.875293493f,  3.003573895f,
+    2.687273264f,   4.843512535f,   6.420391560f,  6.014624596f,  3.444208860f,
+    0.717782736f,   2.659932613f,   5.204012871f,  5.516477585f,  3.315031528f,
+    0.454023123f,   -0.026421070f,  0.802503586f,  2.606507778f,  1.679640770f,
+    -1.917723656f,  -3.348850250f,  -2.580049515f, -1.783200264f, -0.810425520f,
+    -0.374402523f,  -3.705567360f,  -5.367071629f, -4.344952106f, -0.968293428f,
+    1.147591949f,   -1.240655184f,  -2.621209621f, -2.452539444f, -1.543132067f,
+    0.422753096f,   1.026433110f,   0.858573675f,  -0.695377707f, -0.242624998f,
+    3.892488956f,   4.100893021f,   3.498974323f,  1.744507313f,  -0.912925899f,
+    0.929271877f,   3.531583786f,   4.938030243f,  4.081199646f,  0.061933577f,
+    -2.232783318f,  -1.356980443f,  1.794556737f,  3.510458231f,  1.323192716f,
+    -0.505770206f,  2.126557350f,   2.507567406f,  2.232018232f,  1.872283101f,
+    1.265762568f,   0.577634692f,   0.021484375f,  3.114191532f,  1.579384208f,
+    0.930754900f,   0.308351398f,   -0.425426602f, 3.359810352f,  2.437057972f,
+    1.210662127f,   0.708607912f,   -1.576705575f, 0.007833481f,  -0.178357601f,
+    -0.880272985f,  0.078738928f,   0.339336634f,  -0.763550043f, -1.669098496f,
+    -2.083987713f,  -1.946106076f,  -0.953974366f, -0.856883168f, -1.282670021f,
+    -1.551425457f,  -2.249363184f,  -2.555188894f, -1.254808664f, -1.368662596f,
+    -1.839509130f,  -0.839046180f,  -0.452676475f, 0.721064806f,  1.988085508f,
+    0.456556678f,   -0.255003691f,  0.384676337f,  1.075410485f,  0.617453933f,
+    1.470067143f,   1.493275523f,   0.954153359f,  1.027234554f,  -0.434967309f,
+    -0.694453120f,  0.477285773f,   0.436861426f,  1.486879349f,  -0.158989906f,
+    0.361879885f,   3.234876394f,   1.105287671f,  -0.982552111f, 1.514200211f,
+    0.821707547f,   -1.142312169f,  1.845819831f,  3.934516191f,  2.251807690f,
+    0.530044913f,   -1.043874860f,  -0.891365111f, -0.264675498f, 0.288083673f,
+    0.606682122f,   -1.132072091f,  -3.530973911f, -2.005296707f, 0.335011721f,
+    -0.240332901f,  -2.763209343f,  -2.148519516f, -1.864180326f, -0.814615071f,
+    -1.589591861f,  -2.455522776f,  -0.756391644f, 0.689822078f,  0.171640277f,
+    -0.225937843f,  0.363246441f,   0.098157287f,  -1.638891220f, -0.400456548f,
+    1.076233864f,   2.288599968f,   2.716089964f,  1.585703373f,  0.846301913f,
+    0.887506902f,   -0.439320147f,  -0.823126972f, 0.712436378f,  1.027045608f,
+    0.360925227f,   -2.289939404f,  -1.035227180f, 0.931313038f,  -0.133454978f,
+    0.160856903f,   0.700653732f,   0.817580283f,  -0.223383546f, 0.713623106f,
+    1.327106714f,   1.558022618f,   1.346337557f,  -0.661301017f, 0.707845926f,
+    2.435726643f,   0.763329387f,   0.485213757f,  2.295393229f,  4.525130272f,
+    2.354229450f,   -0.043517172f,  1.635316610f,  1.651852608f,  1.240020633f,
+    0.320237398f,   -0.571269870f,  -0.686546564f, -1.796948791f, -0.966899753f,
+    -0.404109240f,  -1.295783877f,  -2.058131218f, -2.279026985f, -2.183017731f,
+    -2.516988277f,  -0.276667058f,  -0.475267202f, -2.645681143f, -0.504431605f,
+    -1.031255722f,  -3.401877880f,  -1.075011969f, -0.667404234f, -2.419279575f,
+    -1.230643749f,  1.151491284f,   0.374734998f,  -2.004124880f, -1.923788905f,
+    -0.767004371f,  0.512374282f,   2.254727125f,  1.373157024f,  0.633022547f,
+    0.194831967f,   0.226476192f,   1.294842482f,  0.838023365f,  1.291390896f,
+    0.128176212f,   -1.109287858f,  0.166733295f,  0.847469866f,  -0.662097514f,
+    -0.489783406f,  1.523754478f,   1.903803706f,  -0.748670340f, 0.721136212f,
+    1.627746105f,   -0.731291413f,  0.646574259f,  1.722917080f,  0.372141778f,
+    -0.063563704f,  0.916404963f,   2.092662811f,  1.699481010f,  0.181074798f,
+    -1.361395121f,  0.581034362f,   1.451567292f,  0.526586652f,  1.206429839f,
+    -1.041464567f,  -2.891606331f,  0.638695598f,  1.198848009f,  -0.771047413f,
+    -1.074250221f,  -0.500067651f,  0.308775485f,  0.552724898f,  1.083443999f,
+    1.371356130f,   0.360372365f,   3.391613960f,  2.896605730f,  0.799045980f,
+    0.922905385f,   3.240214348f,   4.740911484f,  2.945639610f,  2.544054747f,
+    3.048654795f,   3.541822433f,   4.390746117f,  5.632675171f,  7.721554756f,
+    6.390114784f,   5.962307930f,   5.873732567f,  5.625522137f,  4.857854843f,
+    3.148367405f,   3.966898203f,   4.309705257f,  3.543770313f,  2.427399397f,
+    0.324177742f,   -1.809771061f,  -2.191485405f, 0.006873131f,  -0.876847267f,
+    -0.928904057f,  0.889565945f,   -0.127671242f, -1.695463657f, -1.193793774f,
+    -1.452976227f,  -3.406696558f,  -2.564189196f, -2.136555195f, -2.374645710f,
+    -3.230790854f,  -3.076714516f,  -3.245117664f, -2.254387617f, -0.245034039f,
+    -1.072510719f,  -1.887740970f,  0.431427240f,  1.132410765f,  -1.015120149f,
+    -0.274977922f,  -1.910447717f,  -2.865208864f, -0.131696820f};
+
+// TODO(bugs.webrtc.org/9076): Move to resource file.
+constexpr std::array<float, 385> kPitchBufferFrameSquareEnergies = {
+    5150.291992188f, 5150.894531250f, 5145.122558594f, 5148.914062500f,
+    5152.802734375f, 5156.541015625f, 5163.048339844f, 5172.149414062f,
+    5177.349121094f, 5184.365722656f, 5199.292480469f, 5202.612304688f,
+    5197.510253906f, 5189.979492188f, 5183.533203125f, 5190.677734375f,
+    5203.943359375f, 5207.876464844f, 5209.395019531f, 5225.451660156f,
+    5249.794921875f, 5271.816894531f, 5280.045410156f, 5285.289062500f,
+    5288.319335938f, 5289.758789062f, 5294.285644531f, 5289.979980469f,
+    5287.337402344f, 5287.237792969f, 5281.462402344f, 5271.676269531f,
+    5256.257324219f, 5240.524414062f, 5230.869628906f, 5207.531250000f,
+    5176.040039062f, 5144.021484375f, 5109.295410156f, 5068.527832031f,
+    5008.909667969f, 4977.587890625f, 4959.000976562f, 4950.016601562f,
+    4940.795410156f, 4937.358398438f, 4935.286132812f, 4914.154296875f,
+    4906.706542969f, 4906.924804688f, 4907.674804688f, 4899.855468750f,
+    4894.340820312f, 4906.948242188f, 4910.065429688f, 4921.032714844f,
+    4949.294433594f, 4982.643066406f, 5000.996093750f, 5005.875488281f,
+    5020.441894531f, 5031.938964844f, 5041.877441406f, 5035.990722656f,
+    5037.362792969f, 5043.038085938f, 5044.236328125f, 5042.322753906f,
+    5041.990722656f, 5047.362304688f, 5056.785644531f, 5054.579101562f,
+    5050.326171875f, 5053.495117188f, 5060.186523438f, 5065.591796875f,
+    5066.717285156f, 5069.499511719f, 5076.201171875f, 5076.687011719f,
+    5076.316894531f, 5077.581054688f, 5076.226074219f, 5074.094238281f,
+    5074.039062500f, 5073.663574219f, 5076.283691406f, 5077.278808594f,
+    5076.094238281f, 5077.806152344f, 5081.035644531f, 5082.431640625f,
+    5082.995605469f, 5084.653320312f, 5084.936035156f, 5085.394042969f,
+    5085.735351562f, 5080.651855469f, 5080.542968750f, 5079.969238281f,
+    5076.432617188f, 5072.439453125f, 5073.252441406f, 5071.974609375f,
+    5071.458496094f, 5066.017578125f, 5065.670898438f, 5065.144042969f,
+    5055.592773438f, 5060.104980469f, 5060.505371094f, 5054.157226562f,
+    5056.915039062f, 5067.208007812f, 5060.940917969f, 5058.419921875f,
+    5053.248046875f, 5049.823730469f, 5048.573242188f, 5053.195312500f,
+    5053.444335938f, 5054.143066406f, 5056.270019531f, 5063.881835938f,
+    5070.784667969f, 5074.042480469f, 5080.785156250f, 5085.663085938f,
+    5095.979003906f, 5101.596191406f, 5088.784667969f, 5087.686523438f,
+    5087.946777344f, 5087.369140625f, 5081.445312500f, 5081.519042969f,
+    5087.940917969f, 5102.099121094f, 5126.864257812f, 5147.613281250f,
+    5170.079589844f, 5189.276367188f, 5210.265136719f, 5244.745117188f,
+    5268.821777344f, 5277.381835938f, 5279.768066406f, 5278.750000000f,
+    5283.853027344f, 5292.671386719f, 5291.744628906f, 5294.732421875f,
+    5294.322265625f, 5294.267089844f, 5297.530761719f, 5302.179199219f,
+    5312.768066406f, 5323.202148438f, 5335.357910156f, 5344.610839844f,
+    5347.597167969f, 5346.077148438f, 5346.071289062f, 5346.083984375f,
+    5348.088378906f, 5349.661621094f, 5350.157226562f, 5351.855957031f,
+    5347.257812500f, 5345.171875000f, 5344.617675781f, 5343.106445312f,
+    5342.778808594f, 5338.655761719f, 5341.668457031f, 5347.518066406f,
+    5362.014160156f, 5361.167968750f, 5362.926269531f, 5371.575195312f,
+    5374.099609375f, 5381.186523438f, 5381.963867188f, 5386.806152344f,
+    5389.590820312f, 5384.562011719f, 5372.485839844f, 5370.576660156f,
+    5369.640136719f, 5369.698242188f, 5371.199707031f, 5372.644531250f,
+    5394.006835938f, 5395.366699219f, 5395.259277344f, 5395.398437500f,
+    5395.895507812f, 5401.420898438f, 5420.036621094f, 5434.017578125f,
+    5434.215820312f, 5437.827636719f, 5442.944335938f, 5450.980468750f,
+    5449.246582031f, 5449.135742188f, 5453.259765625f, 5453.792968750f,
+    5459.676757812f, 5460.213867188f, 5479.227539062f, 5512.076171875f,
+    5520.272949219f, 5519.662109375f, 5517.395996094f, 5516.550292969f,
+    5520.786621094f, 5527.268066406f, 5526.668457031f, 5549.916992188f,
+    5577.750976562f, 5580.141113281f, 5579.533691406f, 5576.632324219f,
+    5573.938476562f, 5571.166503906f, 5570.603027344f, 5570.708496094f,
+    5577.238769531f, 5577.625976562f, 5589.325683594f, 5602.189941406f,
+    5612.587402344f, 5613.887695312f, 5613.588867188f, 5608.100585938f,
+    5632.956054688f, 5679.322265625f, 5682.149414062f, 5683.846191406f,
+    5691.708496094f, 5683.279785156f, 5694.248535156f, 5744.740722656f,
+    5756.655761719f, 5755.952148438f, 5756.665527344f, 5750.700195312f,
+    5784.060546875f, 5823.021972656f, 5829.233398438f, 5817.804687500f,
+    5827.333984375f, 5826.451171875f, 5824.887695312f, 5825.734375000f,
+    5813.386230469f, 5789.609863281f, 5779.115234375f, 5778.762695312f,
+    5785.748046875f, 5792.981933594f, 5787.567871094f, 5778.096679688f,
+    5764.337402344f, 5766.734375000f, 5766.489746094f, 5769.543945312f,
+    5773.183593750f, 5775.720703125f, 5774.311523438f, 5769.303710938f,
+    5765.815917969f, 5767.521484375f, 5775.251953125f, 5785.067382812f,
+    5770.117187500f, 5749.073242188f, 5747.606933594f, 5757.671875000f,
+    5762.530273438f, 5774.506347656f, 5784.737304688f, 5775.916015625f,
+    5779.816894531f, 5795.064453125f, 5808.736816406f, 5813.699707031f,
+    5823.773925781f, 5840.490234375f, 5833.751953125f, 5810.150390625f,
+    5800.072265625f, 5815.070800781f, 5822.964355469f, 5817.615234375f,
+    5783.978027344f, 5748.952636719f, 5735.553710938f, 5730.132812500f,
+    5724.260253906f, 5721.703613281f, 5695.653808594f, 5652.838867188f,
+    5649.729980469f, 5647.268554688f, 5647.265136719f, 5641.350585938f,
+    5636.762695312f, 5637.900390625f, 5639.662109375f, 5639.672851562f,
+    5638.901367188f, 5622.253417969f, 5604.906738281f, 5601.475585938f,
+    5595.938476562f, 5595.687011719f, 5598.612792969f, 5601.322753906f,
+    5598.558593750f, 5577.227050781f, 5544.295410156f, 5514.978027344f,
+    5499.678222656f, 5488.303222656f, 5471.735839844f, 5429.718261719f,
+    5376.806640625f, 5348.682128906f, 5307.851074219f, 5260.914062500f,
+    5212.738281250f, 5148.544921875f, 5091.187500000f, 5053.512207031f,
+    5023.785156250f, 5002.202148438f, 4994.252441406f, 4984.498046875f,
+    4980.251464844f, 4979.796875000f, 4976.738769531f, 4979.579589844f,
+    4986.528320312f, 4991.153808594f, 4991.462890625f, 4987.881347656f,
+    4987.417480469f, 4983.885742188f, 4984.341308594f, 4985.302734375f,
+    4985.303710938f, 4985.449707031f, 4989.282226562f, 4994.246582031f,
+    4992.635742188f, 4992.064453125f, 4987.331054688f, 4985.806152344f,
+    4986.047851562f, 4985.968750000f, 4979.141113281f, 4976.958984375f,
+    4972.650390625f, 4959.916503906f, 4956.325683594f, 4956.408691406f,
+    4949.288085938f, 4951.827636719f, 4962.202636719f, 4981.184570312f,
+    4992.152832031f, 4997.386230469f, 5011.211914062f, 5026.242187500f,
+    5023.573730469f, 5012.373046875f, 5017.451171875f, 5010.541015625f,
+    4980.446777344f, 4958.639648438f, 4963.649902344f, 5627.020507812f,
+    6869.356445312f};
+
+// TODO(bugs.webrtc.org/9076): Move to resource file.
+constexpr std::array<float, 147> kPitchBufferAutoCorrCoeffs = {
+    -423.526794434f, -260.724456787f, -173.558380127f, -71.720344543f,
+    -1.149698257f,   71.451370239f,   71.455848694f,   149.755233765f,
+    199.401885986f,  243.961334229f,  269.339721680f,  243.776992798f,
+    294.753814697f,  209.465484619f,  139.224700928f,  131.474136353f,
+    42.872886658f,   -32.431114197f,  -90.191261292f,  -94.912338257f,
+    -172.627227783f, -138.089843750f, -89.236648560f,  -69.348426819f,
+    25.044368744f,   44.184486389f,   61.602676392f,   150.157394409f,
+    185.254760742f,  233.352676392f,  296.255371094f,  292.464141846f,
+    256.903472900f,  250.926574707f,  174.207122803f,  130.214172363f,
+    65.655899048f,   -68.448402405f,  -147.239669800f, -230.553405762f,
+    -311.217895508f, -447.173889160f, -509.306060791f, -551.155822754f,
+    -580.678405762f, -658.902709961f, -697.141967773f, -751.233032227f,
+    -690.860351562f, -571.689575195f, -521.124572754f, -429.477294922f,
+    -375.685913086f, -277.387329102f, -154.100753784f, -105.723197937f,
+    117.502632141f,  219.290512085f,  255.376770020f,  444.264831543f,
+    470.727416992f,  460.139129639f,  494.179931641f,  389.801116943f,
+    357.082763672f,  222.748138428f,  179.100601196f,  -26.893497467f,
+    -85.033767700f,  -223.577529907f, -247.136367798f, -223.011428833f,
+    -292.724914551f, -246.538131714f, -247.388458252f, -228.452484131f,
+    -30.476575851f,  4.652336121f,    64.730491638f,   156.081161499f,
+    177.569305420f,  261.671569824f,  336.274414062f,  424.203369141f,
+    564.190734863f,  608.841796875f,  671.252136230f,  712.249877930f,
+    623.135498047f,  564.775695801f,  576.405639648f,  380.181854248f,
+    306.687164307f,  180.344757080f,  -41.317466736f,  -183.548736572f,
+    -223.835021973f, -273.299652100f, -235.727813721f, -276.899627686f,
+    -302.224975586f, -349.227142334f, -370.935058594f, -364.022613525f,
+    -287.682952881f, -273.828704834f, -156.869720459f, -88.654510498f,
+    14.299798012f,   137.048034668f,  260.182342529f,  423.380767822f,
+    591.277282715f,  581.151306152f,  643.898864746f,  547.919006348f,
+    355.534271240f,  238.222915649f,  4.463035583f,    -193.763305664f,
+    -281.212432861f, -546.399353027f, -615.602600098f, -574.225891113f,
+    -726.701843262f, -564.840942383f, -588.488037109f, -651.052551270f,
+    -453.769104004f, -502.886627197f, -463.373016357f, -291.709564209f,
+    -288.857421875f, -152.114242554f, 105.401855469f,  211.479980469f,
+    468.501983643f,  796.984985352f,  880.254089355f,  1114.614379883f,
+    1219.664794922f, 1093.687377930f, 1125.042602539f, 1020.942382812f,
+    794.315246582f,  772.126831055f,  447.410736084f};
+
+constexpr std::array<size_t, 2> kTestPitchPeriods = {
+    3 * kMinPitch48kHz / 2, (3 * kMinPitch48kHz + kMaxPitch48kHz) / 2,
+};
+constexpr std::array<float, 2> kTestPitchGains = {0.35f, 0.75f};
+
+}  // namespace
+
+class ComputePitchGainThresholdTest
+    : public testing::Test,
+      public ::testing::WithParamInterface<
+          std::tuple<size_t, size_t, size_t, float, size_t, float, float>> {};
+
+TEST_P(ComputePitchGainThresholdTest, BitExactness) {
+  const auto params = GetParam();
+  const size_t candidate_pitch_period = std::get<0>(params);
+  const size_t pitch_period_ratio = std::get<1>(params);
+  const size_t initial_pitch_period = std::get<2>(params);
+  const float initial_pitch_gain = std::get<3>(params);
+  const size_t prev_pitch_period = std::get<4>(params);
+  const size_t prev_pitch_gain = std::get<5>(params);
+  const float threshold = std::get<6>(params);
+
+  {
+    // TODO(bugs.webrtc.org/8948): Add when the issue is fixed.
+    // FloatingPointExceptionObserver fpe_observer;
+
+    EXPECT_NEAR(
+        threshold,
+        ComputePitchGainThreshold(candidate_pitch_period, pitch_period_ratio,
+                                  initial_pitch_period, initial_pitch_gain,
+                                  prev_pitch_period, prev_pitch_gain),
+        3e-6f);
+  }
+}
+
+INSTANTIATE_TEST_CASE_P(
+    RnnVadTest,
+    ComputePitchGainThresholdTest,
+    ::testing::Values(
+        std::make_tuple(31, 7, 219, 0.45649201f, 199, 0.604747f, 0.40000001f),
+        std::make_tuple(113,
+                        2,
+                        226,
+                        0.20967799f,
+                        219,
+                        0.40392199f,
+                        0.30000001f),
+        std::make_tuple(63, 2, 126, 0.210788f, 364, 0.098519f, 0.40000001f),
+        std::make_tuple(30, 5, 152, 0.82356697f, 149, 0.55535901f, 0.700032f),
+        std::make_tuple(76, 2, 151, 0.79522997f, 151, 0.82356697f, 0.675946f),
+        std::make_tuple(31, 5, 153, 0.85069299f, 150, 0.79073799f, 0.72308898f),
+        std::make_tuple(78, 2, 156, 0.72750503f, 153, 0.85069299f, 0.618379f)));
+
+TEST(RnnVadTest, ComputeSlidingFrameSquareEnergiesBitExactness) {
+  std::array<float, kPitchBufferFrameSquareEnergies.size()> computed_output;
+  {
+    // TODO(bugs.webrtc.org/8948): Add when the issue is fixed.
+    // FloatingPointExceptionObserver fpe_observer;
+
+    ComputeSlidingFrameSquareEnergies(
+        {kPitchBufferData.data(), kPitchBufferData.size()},
+        {computed_output.data(), computed_output.size()});
+  }
+  ExpectNearAbsolute({kPitchBufferFrameSquareEnergies.data(),
+                      kPitchBufferFrameSquareEnergies.size()},
+                     {computed_output.data(), computed_output.size()}, 3e-2f);
+}
+
+TEST(RnnVadTest, ComputePitchAutoCorrelationBitExactness) {
+  std::array<float, kBufSize12kHz> pitch_buf_decimated;
+  Decimate2x({kPitchBufferData.data(), kPitchBufferData.size()},
+             {pitch_buf_decimated.data(), pitch_buf_decimated.size()});
+  std::array<float, kPitchBufferAutoCorrCoeffs.size()> computed_output;
+  {
+    // TODO(bugs.webrtc.org/8948): Add when the issue is fixed.
+    // FloatingPointExceptionObserver fpe_observer;
+
+    ComputePitchAutoCorrelation(
+        {pitch_buf_decimated.data(), pitch_buf_decimated.size()},
+        kMaxPitch12kHz, {computed_output.data(), computed_output.size()});
+  }
+  ExpectNearAbsolute(
+      {kPitchBufferAutoCorrCoeffs.data(), kPitchBufferAutoCorrCoeffs.size()},
+      {computed_output.data(), computed_output.size()}, 3e-3f);
+}
+
+TEST(RnnVadTest, FindBestPitchPeriodsBitExactness) {
+  std::array<float, kBufSize12kHz> pitch_buf_decimated;
+  Decimate2x({kPitchBufferData.data(), kPitchBufferData.size()},
+             {pitch_buf_decimated.data(), pitch_buf_decimated.size()});
+  std::array<size_t, 2> pitch_candidates_inv_lags;
+  {
+    // TODO(bugs.webrtc.org/8948): Add when the issue is fixed.
+    // FloatingPointExceptionObserver fpe_observer;
+
+    pitch_candidates_inv_lags = FindBestPitchPeriods(
+        {kPitchBufferAutoCorrCoeffs}, {pitch_buf_decimated}, kMaxPitch12kHz);
+  }
+  const std::array<size_t, 2> expected_output = {140, 142};
+  EXPECT_EQ(expected_output, pitch_candidates_inv_lags);
+}
+
+TEST(RnnVadTest, RefinePitchPeriod48kHzBitExactness) {
+  std::array<float, kBufSize12kHz> pitch_buf_decimated;
+  Decimate2x({kPitchBufferData.data(), kPitchBufferData.size()},
+             {pitch_buf_decimated.data(), pitch_buf_decimated.size()});
+  size_t pitch_inv_lag;
+  {
+    // TODO(bugs.webrtc.org/8948): Add when the issue is fixed.
+    // FloatingPointExceptionObserver fpe_observer;
+
+    const std::array<size_t, 2> pitch_candidates_inv_lags = {280, 284};
+    pitch_inv_lag = RefinePitchPeriod48kHz(
+        {kPitchBufferData.data(), kPitchBufferData.size()},
+        {pitch_candidates_inv_lags.data(), pitch_candidates_inv_lags.size()});
+  }
+  EXPECT_EQ(560u, pitch_inv_lag);
+}
+
+class CheckLowerPitchPeriodsAndComputePitchGainTest
+    : public testing::Test,
+      public ::testing::WithParamInterface<
+          std::tuple<size_t, size_t, float, size_t, float>> {};
+
+TEST_P(CheckLowerPitchPeriodsAndComputePitchGainTest, BitExactness) {
+  const auto params = GetParam();
+  const size_t initial_pitch_period = std::get<0>(params);
+  const size_t prev_pitch_period = std::get<1>(params);
+  const float prev_pitch_gain = std::get<2>(params);
+  const size_t expected_pitch_period = std::get<3>(params);
+  const float expected_pitch_gain = std::get<4>(params);
+
+  {
+    // TODO(bugs.webrtc.org/8948): Add when the issue is fixed.
+    // FloatingPointExceptionObserver fpe_observer;
+
+    const auto computed_output = CheckLowerPitchPeriodsAndComputePitchGain(
+        {kPitchBufferData.data(), kPitchBufferData.size()},
+        initial_pitch_period, {prev_pitch_period, prev_pitch_gain});
+    EXPECT_EQ(expected_pitch_period, computed_output.period);
+    EXPECT_NEAR(expected_pitch_gain, computed_output.gain, 1e-6f);
+  }
+}
+
+INSTANTIATE_TEST_CASE_P(RnnVadTest,
+                        CheckLowerPitchPeriodsAndComputePitchGainTest,
+                        ::testing::Values(std::make_tuple(kTestPitchPeriods[0],
+                                                          kTestPitchPeriods[0],
+                                                          kTestPitchGains[0],
+                                                          91,
+                                                          -0.0188608f),
+                                          std::make_tuple(kTestPitchPeriods[0],
+                                                          kTestPitchPeriods[0],
+                                                          kTestPitchGains[1],
+                                                          91,
+                                                          -0.0188608f),
+                                          std::make_tuple(kTestPitchPeriods[0],
+                                                          kTestPitchPeriods[1],
+                                                          kTestPitchGains[0],
+                                                          91,
+                                                          -0.0188608f),
+                                          std::make_tuple(kTestPitchPeriods[0],
+                                                          kTestPitchPeriods[1],
+                                                          kTestPitchGains[1],
+                                                          91,
+                                                          -0.0188608f),
+                                          std::make_tuple(kTestPitchPeriods[1],
+                                                          kTestPitchPeriods[0],
+                                                          kTestPitchGains[0],
+                                                          475,
+                                                          -0.0904344f),
+                                          std::make_tuple(kTestPitchPeriods[1],
+                                                          kTestPitchPeriods[0],
+                                                          kTestPitchGains[1],
+                                                          475,
+                                                          -0.0904344f),
+                                          std::make_tuple(kTestPitchPeriods[1],
+                                                          kTestPitchPeriods[1],
+                                                          kTestPitchGains[0],
+                                                          475,
+                                                          -0.0904344f),
+                                          std::make_tuple(kTestPitchPeriods[1],
+                                                          kTestPitchPeriods[1],
+                                                          kTestPitchGains[1],
+                                                          475,
+                                                          -0.0904344f)));
+
+}  // namespace test
+}  // namespace rnn_vad
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/rnn_vad/pitch_search_unittest.cc b/modules/audio_processing/agc2/rnn_vad/pitch_search_unittest.cc
new file mode 100644
index 0000000..4417764
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/pitch_search_unittest.cc
@@ -0,0 +1,51 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/rnn_vad/pitch_search.h"
+
+#include <array>
+
+#include "modules/audio_processing/agc2/rnn_vad/test_utils.h"
+// TODO(bugs.webrtc.org/8948): Add when the issue is fixed.
+// #include "test/fpe_observer.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace rnn_vad {
+namespace test {
+
+// TODO(bugs.webrtc.org/9076): Remove when the issue is fixed.
+TEST(RnnVadTest, PitchSearchBitExactness) {
+  auto lp_residual_reader = CreateLpResidualAndPitchPeriodGainReader();
+  const size_t num_frames = lp_residual_reader.second;
+  std::array<float, 864> lp_residual;
+  float expected_pitch_period, expected_pitch_gain;
+  PitchInfo last_pitch;
+  {
+    // TODO(bugs.webrtc.org/8948): Add when the issue is fixed.
+    // FloatingPointExceptionObserver fpe_observer;
+
+    for (size_t i = 0; i < num_frames; ++i) {
+      SCOPED_TRACE(i);
+      lp_residual_reader.first->ReadChunk(
+          {lp_residual.data(), lp_residual.size()});
+      lp_residual_reader.first->ReadValue(&expected_pitch_period);
+      lp_residual_reader.first->ReadValue(&expected_pitch_gain);
+      last_pitch =
+          PitchSearch({lp_residual.data(), lp_residual.size()}, last_pitch);
+      EXPECT_EQ(static_cast<size_t>(expected_pitch_period), last_pitch.period);
+      EXPECT_NEAR(expected_pitch_gain, last_pitch.gain, 1e-5f);
+    }
+  }
+}
+
+}  // namespace test
+}  // namespace rnn_vad
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/rnn_vad/ring_buffer.h b/modules/audio_processing/agc2/rnn_vad/ring_buffer.h
new file mode 100644
index 0000000..294b0c0
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/ring_buffer.h
@@ -0,0 +1,66 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AGC2_RNN_VAD_RING_BUFFER_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RING_BUFFER_H_
+
+#include <array>
+#include <cstring>
+#include <type_traits>
+
+#include "api/array_view.h"
+
+namespace webrtc {
+namespace rnn_vad {
+
+// Ring buffer for N arrays of type T each one with size S.
+template <typename T, size_t S, size_t N>
+class RingBuffer {
+  static_assert(S > 0, "");
+  static_assert(N > 0, "");
+  static_assert(std::is_arithmetic<T>::value,
+                "Integral or floating point required.");
+
+ public:
+  RingBuffer() : tail_(0) {}
+  RingBuffer(const RingBuffer&) = delete;
+  RingBuffer& operator=(const RingBuffer&) = delete;
+  ~RingBuffer() = default;
+  // Set the ring buffer values to zero.
+  void Reset() { buffer_.fill(0); }
+  // Replace the least recently pushed array in the buffer with |new_values|.
+  void Push(rtc::ArrayView<const T, S> new_values) {
+    std::memcpy(buffer_.data() + S * tail_, new_values.data(), S * sizeof(T));
+    tail_ += 1;
+    if (tail_ == N)
+      tail_ = 0;
+  }
+  // Return an array view onto the array with a given delay. A view on the last
+  // and least recently push array is returned when |delay| is 0 and N - 1
+  // respectively.
+  rtc::ArrayView<const T, S> GetArrayView(size_t delay) const {
+    const int delay_int = static_cast<int>(delay);
+    RTC_DCHECK_LE(0, delay_int);
+    RTC_DCHECK_LT(delay_int, N);
+    int offset = tail_ - 1 - delay_int;
+    if (offset < 0)
+      offset += N;
+    return {buffer_.data() + S * offset, S};
+  }
+
+ private:
+  int tail_;  // Index of the least recently pushed sub-array.
+  std::array<T, S * N> buffer_{};
+};
+
+}  // namespace rnn_vad
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_RING_BUFFER_H_
diff --git a/modules/audio_processing/agc2/rnn_vad/ring_buffer_unittest.cc b/modules/audio_processing/agc2/rnn_vad/ring_buffer_unittest.cc
new file mode 100644
index 0000000..91383d1
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/ring_buffer_unittest.cc
@@ -0,0 +1,115 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/rnn_vad/ring_buffer.h"
+
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace rnn_vad {
+namespace test {
+namespace {
+
+// Compare the elements of two given array views.
+template <typename T, std::ptrdiff_t S>
+void ExpectEq(rtc::ArrayView<const T, S> a, rtc::ArrayView<const T, S> b) {
+  for (size_t i = 0; i < S; ++i) {
+    SCOPED_TRACE(i);
+    EXPECT_EQ(a[i], b[i]);
+  }
+}
+
+// Test push/read sequences.
+template <typename T, size_t S, size_t N>
+void TestRingBuffer() {
+  SCOPED_TRACE(N);
+  SCOPED_TRACE(S);
+  std::array<T, S> prev_pushed_array;
+  std::array<T, S> pushed_array;
+  rtc::ArrayView<const T, S> pushed_array_view(pushed_array.data(), S);
+
+  // Init.
+  RingBuffer<T, S, N> ring_buf;
+  ring_buf.GetArrayView(0);
+  pushed_array.fill(0);
+  ring_buf.Push(pushed_array_view);
+  ExpectEq(pushed_array_view, ring_buf.GetArrayView(0));
+
+  // Push N times and check most recent and second most recent.
+  for (T v = 1; v <= static_cast<T>(N); ++v) {
+    SCOPED_TRACE(v);
+    prev_pushed_array = pushed_array;
+    pushed_array.fill(v);
+    ring_buf.Push(pushed_array_view);
+    ExpectEq(pushed_array_view, ring_buf.GetArrayView(0));
+    if (N > 1) {
+      pushed_array.fill(v - 1);
+      ExpectEq(pushed_array_view, ring_buf.GetArrayView(1));
+    }
+  }
+
+  // Check buffer.
+  for (size_t delay = 2; delay < N; ++delay) {
+    SCOPED_TRACE(delay);
+    T expected_value = N - static_cast<T>(delay);
+    pushed_array.fill(expected_value);
+    ExpectEq(pushed_array_view, ring_buf.GetArrayView(delay));
+  }
+}
+
+}  // namespace
+
+// Check that for different delays, different views are returned.
+TEST(RnnVadTest, RingBufferArrayViews) {
+  constexpr size_t s = 3;
+  constexpr size_t n = 4;
+  RingBuffer<int, s, n> ring_buf;
+  std::array<int, s> pushed_array;
+  pushed_array.fill(1);
+  for (size_t k = 0; k <= n; ++k) {  // Push data n + 1 times.
+    SCOPED_TRACE(k);
+    // Check array views.
+    for (size_t i = 0; i < n; ++i) {
+      SCOPED_TRACE(i);
+      auto view_i = ring_buf.GetArrayView(i);
+      for (size_t j = i + 1; j < n; ++j) {
+        SCOPED_TRACE(j);
+        auto view_j = ring_buf.GetArrayView(j);
+        EXPECT_NE(view_i, view_j);
+      }
+    }
+    ring_buf.Push({pushed_array.data(), pushed_array.size()});
+  }
+}
+
+TEST(RnnVadTest, RingBufferUnsigned) {
+  TestRingBuffer<uint8_t, 1, 1>();
+  TestRingBuffer<uint8_t, 2, 5>();
+  TestRingBuffer<uint8_t, 5, 2>();
+  TestRingBuffer<uint8_t, 5, 5>();
+}
+
+TEST(RnnVadTest, RingBufferSigned) {
+  TestRingBuffer<int, 1, 1>();
+  TestRingBuffer<int, 2, 5>();
+  TestRingBuffer<int, 5, 2>();
+  TestRingBuffer<int, 5, 5>();
+}
+
+TEST(RnnVadTest, RingBufferFloating) {
+  TestRingBuffer<float, 1, 1>();
+  TestRingBuffer<float, 2, 5>();
+  TestRingBuffer<float, 5, 2>();
+  TestRingBuffer<float, 5, 5>();
+}
+
+}  // namespace test
+}  // namespace rnn_vad
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/rnn_vad/rnn_vad_tool.cc b/modules/audio_processing/agc2/rnn_vad/rnn_vad_tool.cc
new file mode 100644
index 0000000..6ab932c
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/rnn_vad_tool.cc
@@ -0,0 +1,134 @@
+/*
+ *  Copyright (c) 2018 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 <array>
+#include <string>
+#include <vector>
+
+#include "common_audio/resampler/push_sinc_resampler.h"
+#include "common_audio/wav_file.h"
+#include "modules/audio_processing/agc2/rnn_vad/common.h"
+#include "rtc_base/flags.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace test {
+namespace {
+
+using rnn_vad::kFrameSize10ms24kHz;
+
+DEFINE_string(i, "", "Path to the input wav file");
+std::string InputWavFile() {
+  return static_cast<std::string>(FLAG_i);
+}
+
+DEFINE_string(f, "", "Path to the output features file");
+std::string OutputFeaturesFile() {
+  return static_cast<std::string>(FLAG_f);
+}
+
+DEFINE_string(o, "", "Path to the output VAD probabilities file");
+std::string OutputVadProbsFile() {
+  return static_cast<std::string>(FLAG_o);
+}
+
+DEFINE_bool(help, false, "Prints this message");
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  rtc::LogMessage::LogToDebug(rtc::LS_INFO);
+  rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
+  if (FLAG_help) {
+    rtc::FlagList::Print(nullptr, false);
+    return 0;
+  }
+
+  // Open wav input file and check properties.
+  WavReader wav_reader(InputWavFile());
+  if (wav_reader.num_channels() != 1) {
+    RTC_LOG(LS_ERROR) << "Only mono wav files are supported";
+    return 1;
+  }
+  if (wav_reader.sample_rate() % 100 != 0) {
+    RTC_LOG(LS_ERROR) << "The sample rate rate must allow 10 ms frames.";
+    return 1;
+  }
+  RTC_LOG(LS_INFO) << "Input sample rate: " << wav_reader.sample_rate();
+
+  // Init output files.
+  FILE* vad_probs_file = fopen(OutputVadProbsFile().c_str(), "wb");
+  FILE* features_file = nullptr;
+  const std::string output_feature_file = OutputFeaturesFile();
+  if (!output_feature_file.empty()) {
+    features_file = fopen(output_feature_file.c_str(), "wb");
+  }
+
+  // Init resampling.
+  const size_t frame_size_10ms =
+      rtc::CheckedDivExact(wav_reader.sample_rate(), 100);
+  std::vector<float> samples_10ms;
+  samples_10ms.resize(frame_size_10ms);
+  std::array<float, kFrameSize10ms24kHz> samples_10ms_24kHz;
+  PushSincResampler resampler(frame_size_10ms, kFrameSize10ms24kHz);
+
+  // TODO(bugs.webrtc.org/9076): Init feature extractor and RNN-based VAD.
+
+  // Compute VAD probabilities.
+  while (true) {
+    // Read frame at the input sample rate.
+    const auto read_samples =
+        wav_reader.ReadSamples(frame_size_10ms, samples_10ms.data());
+    if (read_samples < frame_size_10ms) {
+      break;  // EOF.
+    }
+    // Resample input.
+    resampler.Resample(samples_10ms.data(), samples_10ms.size(),
+                       samples_10ms_24kHz.data(), samples_10ms_24kHz.size());
+
+    // TODO(bugs.webrtc.org/9076): Extract features.
+    float vad_probability;
+    bool is_silence = true;
+
+    // Write features.
+    if (features_file) {
+      const float float_is_silence = is_silence ? 1.f : 0.f;
+      fwrite(&float_is_silence, sizeof(float), 1, features_file);
+      // TODO(bugs.webrtc.org/9076): Write feature vector.
+    }
+
+    // Compute VAD probability.
+    if (is_silence) {
+      vad_probability = 0.f;
+      // TODO(bugs.webrtc.org/9076): Reset VAD.
+    } else {
+      // TODO(bugs.webrtc.org/9076): Compute VAD probability.
+    }
+    RTC_DCHECK_GE(vad_probability, 0.f);
+    RTC_DCHECK_GE(1.f, vad_probability);
+    fwrite(&vad_probability, sizeof(float), 1, vad_probs_file);
+  }
+  // Close output file(s).
+  fclose(vad_probs_file);
+  RTC_LOG(LS_INFO) << "VAD probabilities written to " << FLAG_o;
+  if (features_file) {
+    fclose(features_file);
+    RTC_LOG(LS_INFO) << "features written to " << FLAG_f;
+  }
+
+  return 0;
+}
+
+}  // namespace test
+}  // namespace webrtc
+
+int main(int argc, char* argv[]) {
+  return webrtc::test::main(argc, argv);
+}
diff --git a/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h b/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h
new file mode 100644
index 0000000..7ae2f95
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h
@@ -0,0 +1,81 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AGC2_RNN_VAD_SEQUENCE_BUFFER_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SEQUENCE_BUFFER_H_
+
+#include <array>
+#include <cstring>
+#include <type_traits>
+
+#include "api/array_view.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace rnn_vad {
+
+// Linear buffer implementation to (i) push fixed size chunks of sequential data
+// and (ii) view contiguous parts of the buffer. The buffer and the pushed
+// chunks have size S and N respectively. For instance, when S = 2N the first
+// half of the sequence buffer is replaced with its second half, and the new N
+// values are written at the end of the buffer.
+template <typename T, size_t S, size_t N>
+class SequenceBuffer {
+  static_assert(S >= N,
+                "The new chunk size is larger than the sequence buffer size.");
+  static_assert(std::is_arithmetic<T>::value,
+                "Integral or floating point required.");
+
+ public:
+  SequenceBuffer() { buffer_.fill(0); }
+  SequenceBuffer(const SequenceBuffer&) = delete;
+  SequenceBuffer& operator=(const SequenceBuffer&) = delete;
+  ~SequenceBuffer() = default;
+  size_t size() const { return S; }
+  size_t chunks_size() const { return N; }
+  // Sets the sequence buffer values to zero.
+  void Reset() { buffer_.fill(0); }
+  // Returns a view on the whole buffer.
+  rtc::ArrayView<const T, S> GetBufferView() const {
+    return {buffer_.data(), S};
+  }
+  // Returns a view on part of the buffer; the first element starts at the given
+  // offset and the last one is the last one in the buffer.
+  rtc::ArrayView<const T> GetBufferView(int offset) const {
+    RTC_DCHECK_LE(0, offset);
+    RTC_DCHECK_LT(offset, S);
+    return {buffer_.data() + offset, S - offset};
+  }
+  // Returns a view on part of the buffer; the first element starts at the given
+  // offset and the size of the view is |size|.
+  rtc::ArrayView<const T> GetBufferView(int offset, size_t size) const {
+    RTC_DCHECK_LE(0, offset);
+    RTC_DCHECK_LT(offset, S);
+    RTC_DCHECK_LT(0, size);
+    RTC_DCHECK_LE(size, S - offset);
+    return {buffer_.data() + offset, size};
+  }
+  // Shifts left the buffer by N items and add new N items at the end.
+  void Push(rtc::ArrayView<const T, N> new_values) {
+    // Make space for the new values.
+    if (S > N)
+      std::memmove(buffer_.data(), buffer_.data() + N, (S - N) * sizeof(T));
+    // Copy the new values at the end of the buffer.
+    std::memcpy(buffer_.data() + S - N, new_values.data(), N * sizeof(T));
+  }
+
+ private:
+  std::array<T, S> buffer_;
+};
+
+}  // namespace rnn_vad
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SEQUENCE_BUFFER_H_
diff --git a/modules/audio_processing/agc2/rnn_vad/sequence_buffer_unittest.cc b/modules/audio_processing/agc2/rnn_vad/sequence_buffer_unittest.cc
new file mode 100644
index 0000000..7628c17
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/sequence_buffer_unittest.cc
@@ -0,0 +1,105 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/rnn_vad/sequence_buffer.h"
+
+#include <algorithm>
+
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace rnn_vad {
+namespace test {
+namespace {
+
+template <typename T, size_t S, size_t N>
+void TestSequenceBufferPushOp() {
+  SCOPED_TRACE(S);
+  SCOPED_TRACE(N);
+  SequenceBuffer<T, S, N> seq_buf;
+  auto seq_buf_view = seq_buf.GetBufferView();
+  std::array<T, N> chunk;
+  rtc::ArrayView<T, N> chunk_view(chunk.data(), chunk.size());
+
+  // Check that a chunk is fully gone after ceil(S / N) push ops.
+  chunk.fill(1);
+  seq_buf.Push(chunk_view);
+  chunk.fill(0);
+  constexpr size_t required_push_ops = (S % N) ? S / N + 1 : S / N;
+  for (size_t i = 0; i < required_push_ops - 1; ++i) {
+    SCOPED_TRACE(i);
+    seq_buf.Push(chunk_view);
+    // Still in the buffer.
+    const auto* m = std::max_element(seq_buf_view.begin(), seq_buf_view.end());
+    EXPECT_EQ(1, *m);
+  }
+  // Gone after another push.
+  seq_buf.Push(chunk_view);
+  const auto* m = std::max_element(seq_buf_view.begin(), seq_buf_view.end());
+  EXPECT_EQ(0, *m);
+
+  // Check that the last item moves left by N positions after a push op.
+  if (S > N) {
+    // Fill in with non-zero values.
+    for (size_t i = 0; i < N; ++i)
+      chunk[i] = static_cast<T>(i + 1);
+    seq_buf.Push(chunk_view);
+    // With the next Push(), |last| will be moved left by N positions.
+    const T last = chunk[N - 1];
+    for (size_t i = 0; i < N; ++i)
+      chunk[i] = static_cast<T>(last + i + 1);
+    seq_buf.Push(chunk_view);
+    EXPECT_EQ(last, seq_buf_view[S - N - 1]);
+  }
+}
+
+}  // namespace
+
+TEST(RnnVadTest, SequenceBufferGetters) {
+  constexpr size_t buffer_size = 8;
+  constexpr size_t chunk_size = 8;
+  SequenceBuffer<int, buffer_size, chunk_size> seq_buf;
+  EXPECT_EQ(buffer_size, seq_buf.size());
+  EXPECT_EQ(chunk_size, seq_buf.chunks_size());
+  // Test view.
+  auto seq_buf_view = seq_buf.GetBufferView();
+  EXPECT_EQ(0, seq_buf_view[0]);
+  EXPECT_EQ(0, seq_buf_view[seq_buf_view.size() - 1]);
+  constexpr std::array<int, chunk_size> chunk = {10, 20, 30, 40,
+                                                 50, 60, 70, 80};
+  seq_buf.Push({chunk.data(), chunk_size});
+  EXPECT_EQ(10, *seq_buf_view.begin());
+  EXPECT_EQ(80, *(seq_buf_view.end() - 1));
+}
+
+TEST(RnnVadTest, SequenceBufferPushOpsUnsigned) {
+  TestSequenceBufferPushOp<uint8_t, 32, 8>();   // Chunk size: 25%.
+  TestSequenceBufferPushOp<uint8_t, 32, 16>();  // Chunk size: 50%.
+  TestSequenceBufferPushOp<uint8_t, 32, 32>();  // Chunk size: 100%.
+  TestSequenceBufferPushOp<uint8_t, 23, 7>();   // Non-integer ratio.
+}
+
+TEST(RnnVadTest, SequenceBufferPushOpsSigned) {
+  TestSequenceBufferPushOp<int, 32, 8>();   // Chunk size: 25%.
+  TestSequenceBufferPushOp<int, 32, 16>();  // Chunk size: 50%.
+  TestSequenceBufferPushOp<int, 32, 32>();  // Chunk size: 100%.
+  TestSequenceBufferPushOp<int, 23, 7>();   // Non-integer ratio.
+}
+
+TEST(RnnVadTest, SequenceBufferPushOpsFloating) {
+  TestSequenceBufferPushOp<float, 32, 8>();   // Chunk size: 25%.
+  TestSequenceBufferPushOp<float, 32, 16>();  // Chunk size: 50%.
+  TestSequenceBufferPushOp<float, 32, 32>();  // Chunk size: 100%.
+  TestSequenceBufferPushOp<float, 23, 7>();   // Non-integer ratio.
+}
+
+}  // namespace test
+}  // namespace rnn_vad
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h b/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h
new file mode 100644
index 0000000..f0282aa
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h
@@ -0,0 +1,94 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AGC2_RNN_VAD_SYMMETRIC_MATRIX_BUFFER_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SYMMETRIC_MATRIX_BUFFER_H_
+
+#include <algorithm>
+#include <array>
+#include <cstring>
+#include <utility>
+
+#include "api/array_view.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace rnn_vad {
+
+// Data structure to buffer the results of pair-wise comparisons between items
+// stored in a ring buffer. Every time that the oldest item is replaced in the
+// ring buffer, the new one is compared to the remaining items in the ring
+// buffer. The results of such comparisons need to be buffered and automatically
+// removed when one of the two corresponding items that have been compared is
+// removed from the ring buffer. It is assumed that the comparison is symmetric
+// and that comparing an item with itself is not needed.
+template <typename T, size_t S>
+class SymmetricMatrixBuffer {
+  static_assert(S > 2, "");
+
+ public:
+  SymmetricMatrixBuffer() = default;
+  SymmetricMatrixBuffer(const SymmetricMatrixBuffer&) = delete;
+  SymmetricMatrixBuffer& operator=(const SymmetricMatrixBuffer&) = delete;
+  ~SymmetricMatrixBuffer() = default;
+  // Sets the buffer values to zero.
+  void Reset() {
+    static_assert(std::is_arithmetic<T>::value,
+                  "Integral or floating point required.");
+    buf_.fill(0);
+  }
+  // Pushes the results from the comparison between the most recent item and
+  // those that are still in the ring buffer. The first element in |values| must
+  // correspond to the comparison between the most recent item and the second
+  // most recent one in the ring buffer, whereas the last element in |values|
+  // must correspond to the comparison between the most recent item and the
+  // oldest one in the ring buffer.
+  void Push(rtc::ArrayView<T, S - 1> values) {
+    // Move the lower-right sub-matrix of size (S-2) x (S-2) one row up and one
+    // column left.
+    std::memmove(buf_.data(), buf_.data() + S, (buf_.size() - S) * sizeof(T));
+    // Copy new values in the last column in the right order.
+    for (size_t i = 0; i < values.size(); ++i) {
+      const size_t index = (S - 1 - i) * (S - 1) - 1;
+      RTC_DCHECK_LE(static_cast<size_t>(0), index);
+      RTC_DCHECK_LT(index, buf_.size());
+      buf_[index] = values[i];
+    }
+  }
+  // Reads the value that corresponds to comparison of two items in the ring
+  // buffer having delay |delay1| and |delay2|. The two arguments must not be
+  // equal and both must be in {0, ..., S - 1}.
+  T GetValue(size_t delay1, size_t delay2) const {
+    int row = S - 1 - static_cast<int>(delay1);
+    int col = S - 1 - static_cast<int>(delay2);
+    RTC_DCHECK_NE(row, col) << "The diagonal cannot be accessed.";
+    if (row > col)
+      std::swap(row, col);  // Swap to access the upper-right triangular part.
+    RTC_DCHECK_LE(0, row);
+    RTC_DCHECK_LT(row, S - 1) << "Not enforcing row < col and row != col.";
+    RTC_DCHECK_LE(1, col) << "Not enforcing row < col and row != col.";
+    RTC_DCHECK_LT(col, S);
+    const int index = row * (S - 1) + (col - 1);
+    RTC_DCHECK_LE(0, index);
+    RTC_DCHECK_LT(index, buf_.size());
+    return buf_[index];
+  }
+
+ private:
+  // Encode an upper-right triangular matrix (excluding its diagonal) using a
+  // square matrix. This allows to move the data in Push() with one single
+  // operation.
+  std::array<T, (S - 1) * (S - 1)> buf_{};
+};
+
+}  // namespace rnn_vad
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_SYMMETRIC_MATRIX_BUFFER_H_
diff --git a/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer_unittest.cc b/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer_unittest.cc
new file mode 100644
index 0000000..a1b8007
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer_unittest.cc
@@ -0,0 +1,110 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/rnn_vad/symmetric_matrix_buffer.h"
+
+#include "modules/audio_processing/agc2/rnn_vad/ring_buffer.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace rnn_vad {
+namespace test {
+namespace {
+
+template <typename T, size_t S>
+void CheckSymmetry(const SymmetricMatrixBuffer<T, S>* sym_matrix_buf) {
+  for (size_t row = 0; row < S - 1; ++row)
+    for (size_t col = row + 1; col < S; ++col)
+      EXPECT_EQ(sym_matrix_buf->GetValue(row, col),
+                sym_matrix_buf->GetValue(col, row));
+}
+
+using PairType = std::pair<int, int>;
+
+// Checks that the symmetric matrix buffer contains any pair with a value equal
+// to the given one.
+template <size_t S>
+bool CheckPairsWithValueExist(
+    const SymmetricMatrixBuffer<PairType, S>* sym_matrix_buf,
+    const int value) {
+  for (size_t row = 0; row < S - 1; ++row) {
+    for (size_t col = row + 1; col < S; ++col) {
+      auto p = sym_matrix_buf->GetValue(row, col);
+      if (p.first == value || p.second == value)
+        return true;
+    }
+  }
+  return false;
+}
+
+}  // namespace
+
+// Test that shows how to combine RingBuffer and SymmetricMatrixBuffer to
+// efficiently compute pair-wise scores. This test verifies that the evolution
+// of a SymmetricMatrixBuffer instance follows that of RingBuffer.
+TEST(RnnVadTest, SymmetricMatrixBufferUseCase) {
+  // Instance a ring buffer which will be fed with a series of integer values.
+  constexpr int kRingBufSize = 10;
+  RingBuffer<int, 1, static_cast<size_t>(kRingBufSize)> ring_buf;
+  // Instance a symmetric matrix buffer for the ring buffer above. It stores
+  // pairs of integers with which this test can easily check that the evolution
+  // of RingBuffer and SymmetricMatrixBuffer match.
+  SymmetricMatrixBuffer<PairType, kRingBufSize> sym_matrix_buf;
+  for (int t = 1; t <= 100; ++t) {  // Evolution steps.
+    SCOPED_TRACE(t);
+    const int t_removed = ring_buf.GetArrayView(kRingBufSize - 1)[0];
+    ring_buf.Push({&t, 1});
+    // The head of the ring buffer is |t|.
+    ASSERT_EQ(t, ring_buf.GetArrayView(0)[0]);
+    // Create the comparisons between |t| and the older elements in the ring
+    // buffer.
+    std::array<PairType, kRingBufSize - 1> new_comparions;
+    for (int i = 0; i < kRingBufSize - 1; ++i) {
+      // Start comparing |t| to the second newest element in the ring buffer.
+      const int delay = i + 1;
+      const auto t_prev = ring_buf.GetArrayView(delay)[0];
+      ASSERT_EQ(std::max(0, t - delay), t_prev);
+      // Compare the last element |t| with |t_prev|.
+      new_comparions[i].first = t_prev;
+      new_comparions[i].second = t;
+    }
+    // Push the new comparisons in the symmetric matrix buffer.
+    sym_matrix_buf.Push({new_comparions.data(), new_comparions.size()});
+    // Tests.
+    CheckSymmetry(&sym_matrix_buf);
+    // Check that the pairs resulting from the content in the ring buffer are
+    // in the right position.
+    for (size_t delay1 = 0; delay1 < kRingBufSize - 1; ++delay1) {
+      for (size_t delay2 = delay1 + 1; delay2 < kRingBufSize; ++delay2) {
+        const auto t1 = ring_buf.GetArrayView(delay1)[0];
+        const auto t2 = ring_buf.GetArrayView(delay2)[0];
+        ASSERT_LE(t2, t1);
+        const auto p = sym_matrix_buf.GetValue(delay1, delay2);
+        EXPECT_EQ(p.first, t2);
+        EXPECT_EQ(p.second, t1);
+      }
+    }
+    // Check that every older element in the ring buffer still has a
+    // corresponding pair in the symmetric matrix buffer.
+    for (size_t delay = 1; delay < kRingBufSize; ++delay) {
+      const auto t_prev = ring_buf.GetArrayView(delay)[0];
+      EXPECT_TRUE(CheckPairsWithValueExist(&sym_matrix_buf, t_prev));
+    }
+    // Check that the element removed from the ring buffer has no corresponding
+    // pairs in the symmetric matrix buffer.
+    if (t > kRingBufSize - 1) {
+      EXPECT_FALSE(CheckPairsWithValueExist(&sym_matrix_buf, t_removed));
+    }
+  }
+}
+
+}  // namespace test
+}  // namespace rnn_vad
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/rnn_vad/test_utils.cc b/modules/audio_processing/agc2/rnn_vad/test_utils.cc
new file mode 100644
index 0000000..c6cf21e
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/test_utils.cc
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/rnn_vad/test_utils.h"
+
+#include "rtc_base/checks.h"
+#include "rtc_base/ptr_util.h"
+#include "test/gtest.h"
+#include "test/testsupport/fileutils.h"
+
+namespace webrtc {
+namespace rnn_vad {
+namespace test {
+namespace {
+
+using ReaderPairType =
+    std::pair<std::unique_ptr<BinaryFileReader<float>>, const size_t>;
+
+}  // namespace
+
+using webrtc::test::ResourcePath;
+
+void ExpectNearAbsolute(rtc::ArrayView<const float> expected,
+                        rtc::ArrayView<const float> computed,
+                        float tolerance) {
+  ASSERT_EQ(expected.size(), computed.size());
+  for (size_t i = 0; i < expected.size(); ++i) {
+    SCOPED_TRACE(i);
+    EXPECT_NEAR(expected[i], computed[i], tolerance);
+  }
+}
+
+ReaderPairType CreatePitchBuffer24kHzReader() {
+  auto ptr = rtc::MakeUnique<BinaryFileReader<float>>(
+      ResourcePath("audio_processing/agc2/rnn_vad/pitch_buf_24k", "dat"), 864);
+  return {std::move(ptr),
+          rtc::CheckedDivExact(ptr->data_length(), static_cast<size_t>(864))};
+}
+
+ReaderPairType CreateLpResidualAndPitchPeriodGainReader() {
+  constexpr size_t num_lp_residual_coeffs = 864;
+  auto ptr = rtc::MakeUnique<BinaryFileReader<float>>(
+      ResourcePath("audio_processing/agc2/rnn_vad/pitch_lp_res", "dat"),
+      num_lp_residual_coeffs);
+  return {std::move(ptr),
+          rtc::CheckedDivExact(ptr->data_length(), 2 + num_lp_residual_coeffs)};
+}
+
+}  // namespace test
+}  // namespace rnn_vad
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/rnn_vad/test_utils.h b/modules/audio_processing/agc2/rnn_vad/test_utils.h
new file mode 100644
index 0000000..3f580ab
--- /dev/null
+++ b/modules/audio_processing/agc2/rnn_vad/test_utils.h
@@ -0,0 +1,103 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AGC2_RNN_VAD_TEST_UTILS_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_TEST_UTILS_H_
+
+#include <algorithm>
+#include <fstream>
+#include <limits>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "api/array_view.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace rnn_vad {
+namespace test {
+
+constexpr float kFloatMin = std::numeric_limits<float>::min();
+
+// Fail for every pair from two equally sized rtc::ArrayView<float> views such
+// that their absolute error is above a given threshold.
+void ExpectNearAbsolute(rtc::ArrayView<const float> expected,
+                        rtc::ArrayView<const float> computed,
+                        float tolerance);
+
+// Reader for binary files consisting of an arbitrary long sequence of elements
+// having type T. It is possible to read and cast to another type D at once.
+template <typename T, typename D = T>
+class BinaryFileReader {
+ public:
+  explicit BinaryFileReader(const std::string& file_path, size_t chunk_size = 1)
+      : is_(file_path, std::ios::binary | std::ios::ate),
+        data_length_(is_.tellg() / sizeof(T)),
+        chunk_size_(chunk_size) {
+    RTC_CHECK_LT(0, chunk_size_);
+    RTC_CHECK(is_);
+    SeekBeginning();
+    buf_.resize(chunk_size_);
+  }
+  BinaryFileReader(const BinaryFileReader&) = delete;
+  BinaryFileReader& operator=(const BinaryFileReader&) = delete;
+  ~BinaryFileReader() = default;
+  size_t data_length() const { return data_length_; }
+  bool ReadValue(D* dst) {
+    if (std::is_same<T, D>::value) {
+      is_.read(reinterpret_cast<char*>(dst), sizeof(T));
+    } else {
+      T v;
+      is_.read(reinterpret_cast<char*>(&v), sizeof(T));
+      *dst = static_cast<D>(v);
+    }
+    return is_.gcount() == sizeof(T);
+  }
+  bool ReadChunk(rtc::ArrayView<D> dst) {
+    RTC_DCHECK_EQ(chunk_size_, dst.size());
+    const std::streamsize bytes_to_read = chunk_size_ * sizeof(T);
+    if (std::is_same<T, D>::value) {
+      is_.read(reinterpret_cast<char*>(dst.data()), bytes_to_read);
+    } else {
+      is_.read(reinterpret_cast<char*>(buf_.data()), bytes_to_read);
+      std::transform(buf_.begin(), buf_.end(), dst.begin(),
+                     [](const T& v) -> D { return static_cast<D>(v); });
+    }
+    return is_.gcount() == bytes_to_read;
+  }
+  void SeekForward(size_t items) { is_.seekg(items * sizeof(T), is_.cur); }
+  void SeekBeginning() { is_.seekg(0, is_.beg); }
+
+ private:
+  std::ifstream is_;
+  const size_t data_length_;
+  const size_t chunk_size_;
+  std::vector<T> buf_;
+};
+
+// Factories for resource file readers; the functions below return a pair where
+// the first item is a reader unique pointer and the second the number of chunks
+// that can be read from the file.
+
+// Creates a reader for the pitch buffer content at 24 kHz.
+std::pair<std::unique_ptr<BinaryFileReader<float>>, const size_t>
+CreatePitchBuffer24kHzReader();
+// Creates a reader for the the LP residual coefficients and the pitch period
+// and gain values.
+std::pair<std::unique_ptr<BinaryFileReader<float>>, const size_t>
+CreateLpResidualAndPitchPeriodGainReader();
+
+}  // namespace test
+}  // namespace rnn_vad
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_RNN_VAD_TEST_UTILS_H_
diff --git a/modules/audio_processing/agc2/saturation_protector.cc b/modules/audio_processing/agc2/saturation_protector.cc
new file mode 100644
index 0000000..216e1b6
--- /dev/null
+++ b/modules/audio_processing/agc2/saturation_protector.cc
@@ -0,0 +1,90 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/saturation_protector.h"
+
+#include <algorithm>
+
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/numerics/safe_minmax.h"
+
+namespace webrtc {
+
+namespace {
+void ShiftBuffer(std::array<float, kPeakEnveloperBufferSize>* buffer_) {
+  // Move everything one element back.
+  std::copy(buffer_->begin() + 1, buffer_->end(), buffer_->begin());
+}
+}  // namespace
+
+SaturationProtector::PeakEnveloper::PeakEnveloper() = default;
+
+void SaturationProtector::PeakEnveloper::Process(float frame_peak_dbfs) {
+  // Update the delayed buffer and the current superframe peak.
+  current_superframe_peak_dbfs_ =
+      std::max(current_superframe_peak_dbfs_, frame_peak_dbfs);
+  speech_time_in_estimate_ms_ += kFrameDurationMs;
+  if (speech_time_in_estimate_ms_ > kPeakEnveloperSuperFrameLengthMs) {
+    speech_time_in_estimate_ms_ = 0;
+    const bool buffer_full = elements_in_buffer_ == kPeakEnveloperBufferSize;
+    if (buffer_full) {
+      ShiftBuffer(&peak_delay_buffer_);
+      *peak_delay_buffer_.rbegin() = current_superframe_peak_dbfs_;
+    } else {
+      peak_delay_buffer_[elements_in_buffer_] = current_superframe_peak_dbfs_;
+      elements_in_buffer_++;
+    }
+    current_superframe_peak_dbfs_ = -90.f;
+  }
+}
+
+float SaturationProtector::PeakEnveloper::Query() const {
+  float result;
+  if (elements_in_buffer_ > 0) {
+    result = peak_delay_buffer_[0];
+  } else {
+    result = current_superframe_peak_dbfs_;
+  }
+  return result;
+}
+
+SaturationProtector::SaturationProtector(ApmDataDumper* apm_data_dumper)
+    : apm_data_dumper_(apm_data_dumper) {}
+
+void SaturationProtector::UpdateMargin(
+    const VadWithLevel::LevelAndProbability& vad_data,
+    float last_speech_level_estimate) {
+  peak_enveloper_.Process(vad_data.speech_peak_dbfs);
+  const float delayed_peak_dbfs = peak_enveloper_.Query();
+  const float difference_db = delayed_peak_dbfs - last_speech_level_estimate;
+
+  if (last_margin_ < difference_db) {
+    last_margin_ = last_margin_ * kSaturationProtectorAttackConstant +
+                   difference_db * (1.f - kSaturationProtectorAttackConstant);
+  } else {
+    last_margin_ = last_margin_ * kSaturationProtectorDecayConstant +
+                   difference_db * (1.f - kSaturationProtectorDecayConstant);
+  }
+
+  last_margin_ = rtc::SafeClamp<float>(last_margin_, 12.f, 25.f);
+}
+
+float SaturationProtector::LastMargin() const {
+  return last_margin_;
+}
+
+void SaturationProtector::DebugDumpEstimate() const {
+  apm_data_dumper_->DumpRaw(
+      "agc2_adaptive_saturation_protector_delayed_peak_dbfs",
+      peak_enveloper_.Query());
+  apm_data_dumper_->DumpRaw("agc2_adaptive_saturation_margin_db", last_margin_);
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/saturation_protector.h b/modules/audio_processing/agc2/saturation_protector.h
new file mode 100644
index 0000000..d330c15
--- /dev/null
+++ b/modules/audio_processing/agc2/saturation_protector.h
@@ -0,0 +1,64 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_AGC2_SATURATION_PROTECTOR_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_SATURATION_PROTECTOR_H_
+
+#include <array>
+
+#include "modules/audio_processing/agc2/agc2_common.h"
+#include "modules/audio_processing/vad/vad_with_level.h"
+
+namespace webrtc {
+
+class ApmDataDumper;
+
+class SaturationProtector {
+ public:
+  explicit SaturationProtector(ApmDataDumper* apm_data_dumper);
+
+  // Update and return margin estimate. This method should be called
+  // whenever a frame is reliably classified as 'speech'.
+  //
+  // Returned value is in DB scale.
+  void UpdateMargin(const VadWithLevel::LevelAndProbability& vad_data,
+                    float last_speech_level_estimate_dbfs);
+
+  // Returns latest computed margin. Used in cases when speech is not
+  // detected.
+  float LastMargin() const;
+
+  void DebugDumpEstimate() const;
+
+ private:
+  // Computes a delayed envelope of peaks.
+  class PeakEnveloper {
+   public:
+    PeakEnveloper();
+    void Process(float frame_peak_dbfs);
+
+    float Query() const;
+
+   private:
+    size_t speech_time_in_estimate_ms_ = 0;
+    float current_superframe_peak_dbfs_ = -90.f;
+    size_t elements_in_buffer_ = 0;
+    std::array<float, kPeakEnveloperBufferSize> peak_delay_buffer_ = {};
+  };
+
+  ApmDataDumper* apm_data_dumper_;
+
+  float last_margin_ = kInitialSaturationMarginDb;
+  PeakEnveloper peak_enveloper_;
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_SATURATION_PROTECTOR_H_
diff --git a/modules/audio_processing/agc2/saturation_protector_unittest.cc b/modules/audio_processing/agc2/saturation_protector_unittest.cc
new file mode 100644
index 0000000..88da2a2
--- /dev/null
+++ b/modules/audio_processing/agc2/saturation_protector_unittest.cc
@@ -0,0 +1,137 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/saturation_protector.h"
+
+#include <algorithm>
+
+#include "modules/audio_processing/agc2/agc2_common.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/gunit.h"
+
+namespace webrtc {
+namespace {
+float RunOnConstantLevel(int num_iterations,
+                         VadWithLevel::LevelAndProbability vad_data,
+                         float estimated_level_dbfs,
+                         SaturationProtector* saturation_protector) {
+  float last_margin = saturation_protector->LastMargin();
+  float max_difference = 0.f;
+  for (int i = 0; i < num_iterations; ++i) {
+    saturation_protector->UpdateMargin(vad_data, estimated_level_dbfs);
+    const float new_margin = saturation_protector->LastMargin();
+    max_difference =
+        std::max(max_difference, std::abs(new_margin - last_margin));
+    last_margin = new_margin;
+  }
+  return max_difference;
+}
+}  // namespace
+
+TEST(AutomaticGainController2SaturationProtector, ProtectorShouldNotCrash) {
+  ApmDataDumper apm_data_dumper(0);
+  SaturationProtector saturation_protector(&apm_data_dumper);
+  VadWithLevel::LevelAndProbability vad_data(1.f, -20.f, -10.f);
+
+  saturation_protector.UpdateMargin(vad_data, -20.f);
+  static_cast<void>(saturation_protector.LastMargin());
+  saturation_protector.DebugDumpEstimate();
+}
+
+// Check that the estimate converges to the ratio between peaks and
+// level estimator values after a while.
+TEST(AutomaticGainController2SaturationProtector,
+     ProtectorEstimatesCrestRatio) {
+  ApmDataDumper apm_data_dumper(0);
+  SaturationProtector saturation_protector(&apm_data_dumper);
+
+  constexpr float kPeakLevel = -20.f;
+  constexpr float kCrestFactor = kInitialSaturationMarginDb + 1.f;
+  constexpr float kSpeechLevel = kPeakLevel - kCrestFactor;
+  const float kMaxDifference =
+      0.5 * std::abs(kInitialSaturationMarginDb - kCrestFactor);
+
+  static_cast<void>(RunOnConstantLevel(
+      2000, VadWithLevel::LevelAndProbability(1.f, -90.f, kPeakLevel),
+      kSpeechLevel, &saturation_protector));
+
+  EXPECT_NEAR(saturation_protector.LastMargin(), kCrestFactor, kMaxDifference);
+}
+
+TEST(AutomaticGainController2SaturationProtector, ProtectorChangesSlowly) {
+  ApmDataDumper apm_data_dumper(0);
+  SaturationProtector saturation_protector(&apm_data_dumper);
+
+  constexpr float kPeakLevel = -20.f;
+  constexpr float kCrestFactor = kInitialSaturationMarginDb - 5.f;
+  constexpr float kOtherCrestFactor = kInitialSaturationMarginDb;
+  constexpr float kSpeechLevel = kPeakLevel - kCrestFactor;
+  constexpr float kOtherSpeechLevel = kPeakLevel - kOtherCrestFactor;
+
+  constexpr int kNumIterations = 1000;
+  float max_difference = RunOnConstantLevel(
+      kNumIterations, VadWithLevel::LevelAndProbability(1.f, -90.f, kPeakLevel),
+      kSpeechLevel, &saturation_protector);
+
+  max_difference =
+      std::max(RunOnConstantLevel(
+                   kNumIterations,
+                   VadWithLevel::LevelAndProbability(1.f, -90.f, kPeakLevel),
+                   kOtherSpeechLevel, &saturation_protector),
+               max_difference);
+
+  constexpr float kMaxChangeSpeedDbPerSecond = 0.5;  // 1 db / 2 seconds.
+
+  EXPECT_LE(max_difference,
+            kMaxChangeSpeedDbPerSecond / 1000 * kFrameDurationMs);
+}
+
+TEST(AutomaticGainController2SaturationProtector,
+     ProtectorAdaptsToDelayedChanges) {
+  ApmDataDumper apm_data_dumper(0);
+  SaturationProtector saturation_protector(&apm_data_dumper);
+
+  constexpr int kDelayIterations = kFullBufferSizeMs / kFrameDurationMs;
+  constexpr float kInitialSpeechLevelDbfs = -30;
+  constexpr float kLaterSpeechLevelDbfs = -15;
+
+  // First run on initial level.
+  float max_difference = RunOnConstantLevel(
+      kDelayIterations,
+      VadWithLevel::LevelAndProbability(
+          1.f, -90.f, kInitialSpeechLevelDbfs + kInitialSaturationMarginDb),
+      kInitialSpeechLevelDbfs, &saturation_protector);
+
+  // Then peak changes, but not RMS.
+  max_difference = std::max(
+      RunOnConstantLevel(
+          kDelayIterations,
+          VadWithLevel::LevelAndProbability(
+              1.f, -90.f, kLaterSpeechLevelDbfs + kInitialSaturationMarginDb),
+          kInitialSpeechLevelDbfs, &saturation_protector),
+      max_difference);
+
+  // Then both change.
+  max_difference = std::max(
+      RunOnConstantLevel(
+          kDelayIterations,
+          VadWithLevel::LevelAndProbability(
+              1.f, -90.f, kLaterSpeechLevelDbfs + kInitialSaturationMarginDb),
+          kLaterSpeechLevelDbfs, &saturation_protector),
+      max_difference);
+
+  const float total_difference =
+      std::abs(saturation_protector.LastMargin() - kInitialSaturationMarginDb);
+
+  EXPECT_LE(total_difference, 0.05f);
+  EXPECT_LE(max_difference, 0.01f);
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/signal_classifier.cc b/modules/audio_processing/agc2/signal_classifier.cc
new file mode 100644
index 0000000..0ec3414
--- /dev/null
+++ b/modules/audio_processing/agc2/signal_classifier.cc
@@ -0,0 +1,167 @@
+/*
+ *  Copyright (c) 2016 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_processing/agc2/signal_classifier.h"
+
+#include <algorithm>
+#include <numeric>
+#include <vector>
+
+#include "api/array_view.h"
+#include "modules/audio_processing/agc2/down_sampler.h"
+#include "modules/audio_processing/agc2/noise_spectrum_estimator.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/constructormagic.h"
+
+namespace webrtc {
+namespace {
+
+void RemoveDcLevel(rtc::ArrayView<float> x) {
+  RTC_DCHECK_LT(0, x.size());
+  float mean = std::accumulate(x.data(), x.data() + x.size(), 0.f);
+  mean /= x.size();
+
+  for (float& v : x) {
+    v -= mean;
+  }
+}
+
+void PowerSpectrum(const OouraFft* ooura_fft,
+                   rtc::ArrayView<const float> x,
+                   rtc::ArrayView<float> spectrum) {
+  RTC_DCHECK_EQ(65, spectrum.size());
+  RTC_DCHECK_EQ(128, x.size());
+  float X[128];
+  std::copy(x.data(), x.data() + x.size(), X);
+  ooura_fft->Fft(X);
+
+  float* X_p = X;
+  RTC_DCHECK_EQ(X_p, &X[0]);
+  spectrum[0] = (*X_p) * (*X_p);
+  ++X_p;
+  RTC_DCHECK_EQ(X_p, &X[1]);
+  spectrum[64] = (*X_p) * (*X_p);
+  for (int k = 1; k < 64; ++k) {
+    ++X_p;
+    RTC_DCHECK_EQ(X_p, &X[2 * k]);
+    spectrum[k] = (*X_p) * (*X_p);
+    ++X_p;
+    RTC_DCHECK_EQ(X_p, &X[2 * k + 1]);
+    spectrum[k] += (*X_p) * (*X_p);
+  }
+}
+
+webrtc::SignalClassifier::SignalType ClassifySignal(
+    rtc::ArrayView<const float> signal_spectrum,
+    rtc::ArrayView<const float> noise_spectrum,
+    ApmDataDumper* data_dumper) {
+  int num_stationary_bands = 0;
+  int num_highly_nonstationary_bands = 0;
+
+  // Detect stationary and highly nonstationary bands.
+  for (size_t k = 1; k < 40; k++) {
+    if (signal_spectrum[k] < 3 * noise_spectrum[k] &&
+        signal_spectrum[k] * 3 > noise_spectrum[k]) {
+      ++num_stationary_bands;
+    } else if (signal_spectrum[k] > 9 * noise_spectrum[k]) {
+      ++num_highly_nonstationary_bands;
+    }
+  }
+
+  data_dumper->DumpRaw("lc_num_stationary_bands", 1, &num_stationary_bands);
+  data_dumper->DumpRaw("lc_num_highly_nonstationary_bands", 1,
+                       &num_highly_nonstationary_bands);
+
+  // Use the detected number of bands to classify the overall signal
+  // stationarity.
+  if (num_stationary_bands > 15) {
+    return SignalClassifier::SignalType::kStationary;
+  } else {
+    return SignalClassifier::SignalType::kNonStationary;
+  }
+}
+
+}  // namespace
+
+SignalClassifier::FrameExtender::FrameExtender(size_t frame_size,
+                                               size_t extended_frame_size)
+    : x_old_(extended_frame_size - frame_size, 0.f) {}
+
+SignalClassifier::FrameExtender::~FrameExtender() = default;
+
+void SignalClassifier::FrameExtender::ExtendFrame(
+    rtc::ArrayView<const float> x,
+    rtc::ArrayView<float> x_extended) {
+  RTC_DCHECK_EQ(x_old_.size() + x.size(), x_extended.size());
+  std::copy(x_old_.data(), x_old_.data() + x_old_.size(), x_extended.data());
+  std::copy(x.data(), x.data() + x.size(), x_extended.data() + x_old_.size());
+  std::copy(x_extended.data() + x_extended.size() - x_old_.size(),
+            x_extended.data() + x_extended.size(), x_old_.data());
+}
+
+SignalClassifier::SignalClassifier(ApmDataDumper* data_dumper)
+    : data_dumper_(data_dumper),
+      down_sampler_(data_dumper_),
+      noise_spectrum_estimator_(data_dumper_) {
+  Initialize(48000);
+}
+SignalClassifier::~SignalClassifier() {}
+
+void SignalClassifier::Initialize(int sample_rate_hz) {
+  down_sampler_.Initialize(sample_rate_hz);
+  noise_spectrum_estimator_.Initialize();
+  frame_extender_.reset(new FrameExtender(80, 128));
+  sample_rate_hz_ = sample_rate_hz;
+  initialization_frames_left_ = 2;
+  consistent_classification_counter_ = 3;
+  last_signal_type_ = SignalClassifier::SignalType::kNonStationary;
+}
+
+SignalClassifier::SignalType SignalClassifier::Analyze(
+    rtc::ArrayView<const float> signal) {
+  RTC_DCHECK_EQ(signal.size(), sample_rate_hz_ / 100);
+
+  // Compute the signal power spectrum.
+  float downsampled_frame[80];
+  down_sampler_.DownSample(signal, downsampled_frame);
+  float extended_frame[128];
+  frame_extender_->ExtendFrame(downsampled_frame, extended_frame);
+  RemoveDcLevel(extended_frame);
+  float signal_spectrum[65];
+  PowerSpectrum(&ooura_fft_, extended_frame, signal_spectrum);
+
+  // Classify the signal based on the estimate of the noise spectrum and the
+  // signal spectrum estimate.
+  const SignalType signal_type = ClassifySignal(
+      signal_spectrum, noise_spectrum_estimator_.GetNoiseSpectrum(),
+      data_dumper_);
+
+  // Update the noise spectrum based on the signal spectrum.
+  noise_spectrum_estimator_.Update(signal_spectrum,
+                                   initialization_frames_left_ > 0);
+
+  // Update the number of frames until a reliable signal spectrum is achieved.
+  initialization_frames_left_ = std::max(0, initialization_frames_left_ - 1);
+
+  if (last_signal_type_ == signal_type) {
+    consistent_classification_counter_ =
+        std::max(0, consistent_classification_counter_ - 1);
+  } else {
+    last_signal_type_ = signal_type;
+    consistent_classification_counter_ = 3;
+  }
+
+  if (consistent_classification_counter_ > 0) {
+    return SignalClassifier::SignalType::kNonStationary;
+  }
+  return signal_type;
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/signal_classifier.h b/modules/audio_processing/agc2/signal_classifier.h
new file mode 100644
index 0000000..23fe315
--- /dev/null
+++ b/modules/audio_processing/agc2/signal_classifier.h
@@ -0,0 +1,67 @@
+/*
+ *  Copyright (c) 2016 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_PROCESSING_AGC2_SIGNAL_CLASSIFIER_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_SIGNAL_CLASSIFIER_H_
+
+#include <memory>
+#include <vector>
+
+#include "api/array_view.h"
+#include "modules/audio_processing/agc2/down_sampler.h"
+#include "modules/audio_processing/agc2/noise_spectrum_estimator.h"
+#include "modules/audio_processing/utility/ooura_fft.h"
+#include "rtc_base/constructormagic.h"
+
+namespace webrtc {
+
+class ApmDataDumper;
+class AudioBuffer;
+
+class SignalClassifier {
+ public:
+  enum class SignalType { kNonStationary, kStationary };
+
+  explicit SignalClassifier(ApmDataDumper* data_dumper);
+  ~SignalClassifier();
+
+  void Initialize(int sample_rate_hz);
+  SignalType Analyze(rtc::ArrayView<const float> signal);
+
+ private:
+  class FrameExtender {
+   public:
+    FrameExtender(size_t frame_size, size_t extended_frame_size);
+    ~FrameExtender();
+
+    void ExtendFrame(rtc::ArrayView<const float> x,
+                     rtc::ArrayView<float> x_extended);
+
+   private:
+    std::vector<float> x_old_;
+
+    RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(FrameExtender);
+  };
+
+  ApmDataDumper* const data_dumper_;
+  DownSampler down_sampler_;
+  std::unique_ptr<FrameExtender> frame_extender_;
+  NoiseSpectrumEstimator noise_spectrum_estimator_;
+  int sample_rate_hz_;
+  int initialization_frames_left_;
+  int consistent_classification_counter_;
+  SignalType last_signal_type_;
+  const OouraFft ooura_fft_;
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(SignalClassifier);
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_SIGNAL_CLASSIFIER_H_
diff --git a/modules/audio_processing/agc2/signal_classifier_unittest.cc b/modules/audio_processing/agc2/signal_classifier_unittest.cc
new file mode 100644
index 0000000..62171b3
--- /dev/null
+++ b/modules/audio_processing/agc2/signal_classifier_unittest.cc
@@ -0,0 +1,82 @@
+/*
+ *  Copyright (c) 2018 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_processing/agc2/signal_classifier.h"
+
+#include <array>
+#include <functional>
+#include <limits>
+
+#include "modules/audio_processing/agc2/agc2_testing_common.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/gunit.h"
+#include "rtc_base/random.h"
+
+namespace webrtc {
+
+namespace {
+Random rand_gen(42);
+ApmDataDumper data_dumper(0);
+constexpr int kNumIterations = 100;
+
+// Runs the signal classifier on audio generated by 'sample_generator'
+// for kNumIterations. Returns the number of frames classified as noise.
+int RunClassifier(std::function<float()> sample_generator, int rate) {
+  SignalClassifier classifier(&data_dumper);
+  std::array<float, 480> signal;
+  classifier.Initialize(rate);
+  const size_t samples_per_channel = rtc::CheckedDivExact(rate, 100);
+  int number_of_noise_frames = 0;
+  for (int i = 0; i < kNumIterations; ++i) {
+    for (size_t j = 0; j < samples_per_channel; ++j) {
+      signal[j] = sample_generator();
+    }
+    number_of_noise_frames +=
+        classifier.Analyze({&signal[0], samples_per_channel}) ==
+        SignalClassifier::SignalType::kStationary;
+  }
+  return number_of_noise_frames;
+}
+
+float WhiteNoiseGenerator() {
+  return static_cast<float>(rand_gen.Rand(std::numeric_limits<int16_t>::min(),
+                                          std::numeric_limits<int16_t>::max()));
+}
+}  // namespace
+
+// White random noise is stationary, but does not trigger the detector
+// every frame due to the randomness.
+TEST(AutomaticGainController2SignalClassifier, WhiteNoise) {
+  for (const auto rate : {8000, 16000, 32000, 48000}) {
+    const int number_of_noise_frames = RunClassifier(WhiteNoiseGenerator, rate);
+    EXPECT_GT(number_of_noise_frames, kNumIterations / 2);
+  }
+}
+
+// Sine curves are (very) stationary. They trigger the detector all
+// the time. Except for a few initial frames.
+TEST(AutomaticGainController2SignalClassifier, SineTone) {
+  for (const auto rate : {8000, 16000, 32000, 48000}) {
+    test::SineGenerator gen(600.f, rate);
+    const int number_of_noise_frames = RunClassifier(gen, rate);
+    EXPECT_GE(number_of_noise_frames, kNumIterations - 5);
+  }
+}
+
+// Pulses are transient if they are far enough apart. They shouldn't
+// trigger the noise detector.
+TEST(AutomaticGainController2SignalClassifier, PulseTone) {
+  for (const auto rate : {8000, 16000, 32000, 48000}) {
+    test::PulseGenerator gen(30.f, rate);
+    const int number_of_noise_frames = RunClassifier(gen, rate);
+    EXPECT_EQ(number_of_noise_frames, 0);
+  }
+}
+}  // namespace webrtc
diff --git a/modules/audio_processing/audio_buffer.h b/modules/audio_processing/audio_buffer.h
index 8451bde..508f96f 100644
--- a/modules/audio_processing/audio_buffer.h
+++ b/modules/audio_processing/audio_buffer.h
@@ -14,10 +14,10 @@
 #include <memory>
 #include <vector>
 
+#include "api/audio/audio_frame.h"
 #include "common_audio/channel_buffer.h"
 #include "modules/audio_processing/include/audio_processing.h"
 #include "modules/audio_processing/splitting_filter.h"
-#include "modules/include/module_common_types.h"
 #include "typedefs.h"  // NOLINT(build/include)
 
 namespace webrtc {
diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc
index 554dead..8d1ccee 100644
--- a/modules/audio_processing/audio_processing_impl.cc
+++ b/modules/audio_processing/audio_processing_impl.cc
@@ -20,6 +20,7 @@
 #include "common_audio/signal_processing/include/signal_processing_library.h"
 #include "modules/audio_processing/aec/aec_core.h"
 #include "modules/audio_processing/agc/agc_manager_direct.h"
+#include "modules/audio_processing/agc2/gain_applier.h"
 #include "modules/audio_processing/audio_buffer.h"
 #include "modules/audio_processing/beamformer/nonlinear_beamformer.h"
 #include "modules/audio_processing/common.h"
@@ -43,9 +44,7 @@
 #include "modules/audio_processing/residual_echo_detector.h"
 #include "modules/audio_processing/transient/transient_suppressor.h"
 #include "modules/audio_processing/voice_detection_impl.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/atomicops.h"
-#include "system_wrappers/include/file_wrapper.h"
 #include "system_wrappers/include/metrics.h"
 
 // Check to verify that the define for the intelligibility enhancer is properly
@@ -187,6 +186,7 @@
     bool beamformer_enabled,
     bool adaptive_gain_controller_enabled,
     bool gain_controller2_enabled,
+    bool pre_amplifier_enabled,
     bool echo_controller_enabled,
     bool voice_activity_detector_enabled,
     bool level_estimator_enabled,
@@ -206,6 +206,7 @@
       (adaptive_gain_controller_enabled != adaptive_gain_controller_enabled_);
   changed |=
       (gain_controller2_enabled != gain_controller2_enabled_);
+  changed |= (pre_amplifier_enabled_ != pre_amplifier_enabled);
   changed |= (echo_controller_enabled != echo_controller_enabled_);
   changed |= (level_estimator_enabled != level_estimator_enabled_);
   changed |=
@@ -221,6 +222,7 @@
     beamformer_enabled_ = beamformer_enabled;
     adaptive_gain_controller_enabled_ = adaptive_gain_controller_enabled;
     gain_controller2_enabled_ = gain_controller2_enabled;
+    pre_amplifier_enabled_ = pre_amplifier_enabled;
     echo_controller_enabled_ = echo_controller_enabled;
     level_estimator_enabled_ = level_estimator_enabled;
     voice_activity_detector_enabled_ = voice_activity_detector_enabled;
@@ -252,7 +254,8 @@
 
 bool AudioProcessingImpl::ApmSubmoduleStates::CaptureFullBandProcessingActive()
     const {
-  return gain_controller2_enabled_ || capture_post_processor_enabled_;
+  return gain_controller2_enabled_ || capture_post_processor_enabled_ ||
+         pre_amplifier_enabled_;
 }
 
 bool AudioProcessingImpl::ApmSubmoduleStates::RenderMultiBandSubModulesActive()
@@ -313,6 +316,7 @@
   std::unique_ptr<EchoControl> echo_controller;
   std::unique_ptr<CustomProcessing> capture_post_processor;
   std::unique_ptr<CustomProcessing> render_pre_processor;
+  std::unique_ptr<GainApplier> pre_amplifier;
 };
 
 AudioProcessingBuilder::AudioProcessingBuilder() = default;
@@ -380,6 +384,8 @@
     NonlinearBeamformer* beamformer)
     : data_dumper_(
           new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
+      runtime_settings_(100),
+      runtime_settings_enqueuer_(&runtime_settings_),
       high_pass_filter_impl_(new HighPassFilterImpl(this)),
       echo_control_factory_(std::move(echo_control_factory)),
       submodule_states_(!!capture_post_processor, !!render_pre_processor),
@@ -713,9 +719,12 @@
     config_.gain_controller2 = AudioProcessing::Config::GainController2();
   }
   InitializeGainController2();
+  InitializePreAmplifier();
   private_submodules_->gain_controller2->ApplyConfig(config_.gain_controller2);
   RTC_LOG(LS_INFO) << "Gain Controller 2 activated: "
                    << config_.gain_controller2.enabled;
+  RTC_LOG(LS_INFO) << "Pre-amplifier activated: "
+                   << config_.pre_amplifier.enabled;
 }
 
 void AudioProcessingImpl::SetExtraOptions(const webrtc::Config& config) {
@@ -796,6 +805,32 @@
   }
 }
 
+void AudioProcessingImpl::SetRuntimeSetting(RuntimeSetting setting) {
+  RTC_DCHECK(setting.type() != RuntimeSetting::Type::kNotSpecified);
+  runtime_settings_enqueuer_.Enqueue(setting);
+}
+
+AudioProcessingImpl::RuntimeSettingEnqueuer::RuntimeSettingEnqueuer(
+    SwapQueue<RuntimeSetting>* runtime_settings)
+    : runtime_settings_(*runtime_settings) {
+  RTC_DCHECK(runtime_settings);
+}
+
+AudioProcessingImpl::RuntimeSettingEnqueuer::~RuntimeSettingEnqueuer() =
+    default;
+
+void AudioProcessingImpl::RuntimeSettingEnqueuer::Enqueue(
+    RuntimeSetting setting) {
+  size_t remaining_attempts = 10;
+  while (!runtime_settings_.Insert(&setting) && remaining_attempts-- > 0) {
+    RuntimeSetting setting_to_discard;
+    if (runtime_settings_.Remove(&setting_to_discard))
+      RTC_LOG(LS_ERROR)
+          << "The runtime settings queue is full. Oldest setting discarded.";
+  }
+  if (remaining_attempts == 0)
+    RTC_LOG(LS_ERROR) << "Cannot enqueue a new runtime setting.";
+}
 
 int AudioProcessingImpl::ProcessStream(const float* const* src,
                                        size_t samples_per_channel,
@@ -878,6 +913,25 @@
   return kNoError;
 }
 
+void AudioProcessingImpl::HandleRuntimeSettings() {
+  RuntimeSetting setting;
+  while (runtime_settings_.Remove(&setting)) {
+    switch (setting.type()) {
+      case RuntimeSetting::Type::kCapturePreGain:
+        if (config_.pre_amplifier.enabled) {
+          float value;
+          setting.GetFloat(&value);
+          private_submodules_->pre_amplifier->SetGainFactor(value);
+        }
+        // TODO(bugs.chromium.org/9138): Log setting handling by Aec Dump.
+        break;
+      case RuntimeSetting::Type::kNotSpecified:
+        RTC_NOTREACHED();
+        break;
+    }
+  }
+}
+
 void AudioProcessingImpl::QueueBandedRenderAudio(AudioBuffer* audio) {
   EchoCancellationImpl::PackRenderAudioBuffer(audio, num_output_channels(),
                                               num_reverse_channels(),
@@ -1132,6 +1186,8 @@
 }
 
 int AudioProcessingImpl::ProcessCaptureStreamLocked() {
+  HandleRuntimeSettings();
+
   // Ensure that not both the AEC and AECM are active at the same time.
   // TODO(peah): Simplify once the public API Enable functions for these
   // are moved to APM.
@@ -1142,6 +1198,12 @@
 
   AudioBuffer* capture_buffer = capture_.capture_audio.get();  // For brevity.
 
+  if (private_submodules_->pre_amplifier) {
+    private_submodules_->pre_amplifier->ApplyGain(AudioFrameView<float>(
+        capture_buffer->channels_f(), capture_buffer->num_channels(),
+        capture_buffer->num_frames()));
+  }
+
   capture_input_rms_.Analyze(rtc::ArrayView<const int16_t>(
       capture_buffer->channels_const()[0],
       capture_nonlocked_.capture_processing_format.num_frames()));
@@ -1209,6 +1271,11 @@
   if (private_submodules_->echo_controller) {
     data_dumper_->DumpRaw("stream_delay", stream_delay_ms());
 
+    if (was_stream_delay_set()) {
+      private_submodules_->echo_controller->SetAudioBufferDelay(
+          stream_delay_ms());
+    }
+
     private_submodules_->echo_controller->ProcessCapture(
         capture_buffer, capture_.echo_path_gain_change);
   } else {
@@ -1375,8 +1442,8 @@
   processing_config.reverse_output_stream() = output_config;
 
   RETURN_ON_ERR(MaybeInitializeRender(processing_config));
-  assert(input_config.num_frames() ==
-         formats_.api_format.reverse_input_stream().num_frames());
+  RTC_DCHECK_EQ(input_config.num_frames(),
+                formats_.api_format.reverse_input_stream().num_frames());
 
   if (aec_dump_) {
     const size_t channel_size =
@@ -1724,7 +1791,7 @@
       capture_nonlocked_.intelligibility_enabled,
       capture_nonlocked_.beamformer_enabled,
       public_submodules_->gain_control->is_enabled(),
-      config_.gain_controller2.enabled,
+      config_.gain_controller2.enabled, config_.pre_amplifier.enabled,
       capture_nonlocked_.echo_controller_enabled,
       public_submodules_->voice_detection->is_enabled(),
       public_submodules_->level_estimator->is_enabled(),
@@ -1790,12 +1857,20 @@
   }
 }
 
+void AudioProcessingImpl::InitializePreAmplifier() {
+  if (config_.pre_amplifier.enabled) {
+    private_submodules_->pre_amplifier.reset(
+        new GainApplier(true, config_.pre_amplifier.fixed_gain_factor));
+  } else {
+    private_submodules_->pre_amplifier.reset();
+  }
+}
+
 void AudioProcessingImpl::InitializeResidualEchoDetector() {
   RTC_DCHECK(private_submodules_->echo_detector);
   private_submodules_->echo_detector->Initialize(
-      proc_sample_rate_hz(), num_proc_channels(),
-      formats_.render_processing_format.sample_rate_hz(),
-      formats_.render_processing_format.num_channels());
+      proc_sample_rate_hz(), 1,
+      formats_.render_processing_format.sample_rate_hz(), 1);
 }
 
 void AudioProcessingImpl::InitializePostProcessor() {
@@ -1941,6 +2016,9 @@
   apm_config.intelligibility_enhancer_enabled =
       capture_nonlocked_.intelligibility_enabled;
   apm_config.experiments_description = experiments_description;
+  apm_config.pre_amplifier_enabled = config_.pre_amplifier.enabled;
+  apm_config.pre_amplifier_fixed_gain_factor =
+      config_.pre_amplifier.fixed_gain_factor;
 
   if (!forced && apm_config == apm_config_for_aec_dump_) {
     return;
diff --git a/modules/audio_processing/audio_processing_impl.h b/modules/audio_processing/audio_processing_impl.h
index 55c47ac..a49924d 100644
--- a/modules/audio_processing/audio_processing_impl.h
+++ b/modules/audio_processing/audio_processing_impl.h
@@ -24,10 +24,8 @@
 #include "rtc_base/function_view.h"
 #include "rtc_base/gtest_prod_util.h"
 #include "rtc_base/ignore_wundef.h"
-#include "rtc_base/protobuf_utils.h"
 #include "rtc_base/swap_queue.h"
 #include "rtc_base/thread_annotations.h"
-#include "system_wrappers/include/file_wrapper.h"
 
 namespace webrtc {
 
@@ -66,6 +64,8 @@
       std::unique_ptr<AudioGenerator> audio_generator) override;
   void DetachPlayoutAudioGenerator() override;
 
+  void SetRuntimeSetting(RuntimeSetting setting) override;
+
   // Capture-side exclusive methods possibly running APM in a
   // multi-threaded manner. Acquire the capture lock.
   int ProcessStream(AudioFrame* frame) override;
@@ -149,6 +149,21 @@
   std::unique_ptr<ApmDataDumper> data_dumper_;
   static int instance_count_;
 
+  SwapQueue<RuntimeSetting> runtime_settings_;
+
+  // Class providing thread-safe message pipe functionality for
+  // |runtime_settings_|.
+  class RuntimeSettingEnqueuer {
+   public:
+    explicit RuntimeSettingEnqueuer(
+        SwapQueue<RuntimeSetting>* runtime_settings);
+    ~RuntimeSettingEnqueuer();
+    void Enqueue(RuntimeSetting setting);
+
+   private:
+    SwapQueue<RuntimeSetting>& runtime_settings_;
+  } runtime_settings_enqueuer_;
+
   // Submodule interface implementations.
   std::unique_ptr<HighPassFilter> high_pass_filter_impl_;
 
@@ -169,6 +184,7 @@
                 bool beamformer_enabled,
                 bool adaptive_gain_controller_enabled,
                 bool gain_controller2_enabled,
+                bool pre_amplifier_enabled,
                 bool echo_controller_enabled,
                 bool voice_activity_detector_enabled,
                 bool level_estimator_enabled,
@@ -192,6 +208,7 @@
     bool beamformer_enabled_ = false;
     bool adaptive_gain_controller_enabled_ = false;
     bool gain_controller2_enabled_ = false;
+    bool pre_amplifier_enabled_ = false;
     bool echo_controller_enabled_ = false;
     bool level_estimator_enabled_ = false;
     bool voice_activity_detector_enabled_ = false;
@@ -236,9 +253,13 @@
   void InitializeLowCutFilter() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
   void InitializeEchoController() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
   void InitializeGainController2() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
+  void InitializePreAmplifier() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
   void InitializePostProcessor() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
   void InitializePreProcessor() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_render_);
 
+  // Handle all the runtime settings in the queue.
+  void HandleRuntimeSettings() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
+
   void EmptyQueuedRenderAudio();
   void AllocateRenderQueue()
       RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_render_, crit_capture_);
diff --git a/modules/audio_processing/audio_processing_impl_locking_unittest.cc b/modules/audio_processing/audio_processing_impl_locking_unittest.cc
index d4cff45..39f8b8b 100644
--- a/modules/audio_processing/audio_processing_impl_locking_unittest.cc
+++ b/modules/audio_processing/audio_processing_impl_locking_unittest.cc
@@ -16,7 +16,6 @@
 
 #include "api/array_view.h"
 #include "modules/audio_processing/test/test_utils.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/criticalsection.h"
 #include "rtc_base/event.h"
 #include "rtc_base/platform_thread.h"
diff --git a/modules/audio_processing/audio_processing_impl_unittest.cc b/modules/audio_processing/audio_processing_impl_unittest.cc
index e152bef..2ea0159 100644
--- a/modules/audio_processing/audio_processing_impl_unittest.cc
+++ b/modules/audio_processing/audio_processing_impl_unittest.cc
@@ -11,7 +11,6 @@
 #include "modules/audio_processing/audio_processing_impl.h"
 
 #include "modules/audio_processing/test/test_utils.h"
-#include "modules/include/module_common_types.h"
 #include "test/gmock.h"
 #include "test/gtest.h"
 
@@ -34,6 +33,24 @@
   MOCK_CONST_METHOD0(Release, rtc::RefCountReleaseStatus());
 };
 
+void GenerateFixedFrame(int16_t audio_level,
+                        size_t input_rate,
+                        size_t num_channels,
+                        AudioFrame* fixed_frame) {
+  const size_t samples_per_input_channel = rtc::CheckedDivExact(
+      input_rate, static_cast<size_t>(rtc::CheckedDivExact(
+                      1000, AudioProcessing::kChunkSizeMs)));
+  fixed_frame->samples_per_channel_ = samples_per_input_channel;
+  fixed_frame->sample_rate_hz_ = input_rate;
+  fixed_frame->num_channels_ = num_channels;
+
+  RTC_DCHECK_LE(samples_per_input_channel * num_channels,
+                AudioFrame::kMaxDataSizeSamples);
+  for (size_t i = 0; i < samples_per_input_channel * num_channels; ++i) {
+    fixed_frame->mutable_data()[i] = audio_level;
+  }
+}
+
 }  // namespace
 
 TEST(AudioProcessingImplTest, AudioParameterChangeTriggersInit) {
@@ -75,4 +92,34 @@
   EXPECT_NOERR(mock.ProcessReverseStream(&frame));
 }
 
+TEST(AudioProcessingImplTest, UpdateCapturePreGainRuntimeSetting) {
+  std::unique_ptr<AudioProcessing> apm(AudioProcessingBuilder().Create());
+  webrtc::AudioProcessing::Config apm_config;
+  apm_config.pre_amplifier.enabled = true;
+  apm_config.pre_amplifier.fixed_gain_factor = 1.f;
+  apm->ApplyConfig(apm_config);
+
+  AudioFrame frame;
+  constexpr int16_t audio_level = 10000;
+  constexpr size_t input_rate = 48000;
+  constexpr size_t num_channels = 2;
+
+  GenerateFixedFrame(audio_level, input_rate, num_channels, &frame);
+  apm->ProcessStream(&frame);
+  EXPECT_EQ(frame.data()[100], audio_level)
+      << "With factor 1, frame shouldn't be modified.";
+
+  constexpr float gain_factor = 2.f;
+  apm->SetRuntimeSetting(
+      AudioProcessing::RuntimeSetting::CreateCapturePreGain(gain_factor));
+
+  // Process for two frames to have time to ramp up gain.
+  for (int i = 0; i < 2; ++i) {
+    GenerateFixedFrame(audio_level, input_rate, num_channels, &frame);
+    apm->ProcessStream(&frame);
+  }
+  EXPECT_EQ(frame.data()[100], gain_factor * audio_level)
+      << "Frame should be amplified.";
+}
+
 }  // namespace webrtc
diff --git a/modules/audio_processing/audio_processing_performance_unittest.cc b/modules/audio_processing/audio_processing_performance_unittest.cc
index 8dd81b2..d312137 100644
--- a/modules/audio_processing/audio_processing_performance_unittest.cc
+++ b/modules/audio_processing/audio_processing_performance_unittest.cc
@@ -17,7 +17,6 @@
 
 #include "api/array_view.h"
 #include "modules/audio_processing/test/test_utils.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/atomicops.h"
 #include "rtc_base/numerics/safe_conversions.h"
 #include "rtc_base/platform_thread.h"
diff --git a/modules/audio_processing/audio_processing_unittest.cc b/modules/audio_processing/audio_processing_unittest.cc
index 89d6cb9..fbd81a1 100644
--- a/modules/audio_processing/audio_processing_unittest.cc
+++ b/modules/audio_processing/audio_processing_unittest.cc
@@ -27,7 +27,6 @@
 #include "modules/audio_processing/include/mock_audio_processing.h"
 #include "modules/audio_processing/test/protobuf_utils.h"
 #include "modules/audio_processing/test/test_utils.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/arraysize.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/gtest_prod_util.h"
@@ -36,6 +35,7 @@
 #include "rtc_base/numerics/safe_minmax.h"
 #include "rtc_base/protobuf_utils.h"
 #include "rtc_base/refcountedobject.h"
+#include "rtc_base/swap_queue.h"
 #include "rtc_base/task_queue.h"
 #include "rtc_base/thread.h"
 #include "system_wrappers/include/event_wrapper.h"
@@ -2820,6 +2820,34 @@
 
 }  // namespace
 
+TEST(RuntimeSettingTest, TestDefaultCtor) {
+  auto s = AudioProcessing::RuntimeSetting();
+  EXPECT_EQ(AudioProcessing::RuntimeSetting::Type::kNotSpecified, s.type());
+}
+
+TEST(RuntimeSettingTest, TestCapturePreGain) {
+  using Type = AudioProcessing::RuntimeSetting::Type;
+  {
+    auto s = AudioProcessing::RuntimeSetting::CreateCapturePreGain(1.25f);
+    EXPECT_EQ(Type::kCapturePreGain, s.type());
+    float v;
+    s.GetFloat(&v);
+    EXPECT_EQ(1.25f, v);
+  }
+
+#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+  EXPECT_DEATH(AudioProcessing::RuntimeSetting::CreateCapturePreGain(0.1f), "");
+#endif
+}
+
+TEST(RuntimeSettingTest, TestUsageWithSwapQueue) {
+  SwapQueue<AudioProcessing::RuntimeSetting> q(1);
+  auto s = AudioProcessing::RuntimeSetting();
+  ASSERT_TRUE(q.Insert(&s));
+  ASSERT_TRUE(q.Remove(&s));
+  EXPECT_EQ(AudioProcessing::RuntimeSetting::Type::kNotSpecified, s.type());
+}
+
 TEST(ApmConfiguration, EnablePostProcessing) {
   // Verify that apm uses a capture post processing module if one is provided.
   webrtc::Config webrtc_config;
diff --git a/modules/audio_processing/debug.proto b/modules/audio_processing/debug.proto
index 4417773..f7f8d10 100644
--- a/modules/audio_processing/debug.proto
+++ b/modules/audio_processing/debug.proto
@@ -72,8 +72,11 @@
   // Semicolon-separated string containing experimental feature
   // descriptions.
   optional string experiments_description = 17;
-  // Intelligibility Enhancer
+  // Intelligibility Enhancer.
   optional bool intelligibility_enhancer_enabled = 18;
+  // Pre amplifier.
+  optional bool pre_amplifier_enabled = 19;
+  optional float pre_amplifier_fixed_gain_factor = 20;
 }
 
 message Event {
diff --git a/modules/audio_processing/gain_controller2.cc b/modules/audio_processing/gain_controller2.cc
index aa866c1..6c0149a 100644
--- a/modules/audio_processing/gain_controller2.cc
+++ b/modules/audio_processing/gain_controller2.cc
@@ -23,7 +23,8 @@
 GainController2::GainController2()
     : data_dumper_(
           new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
-      gain_controller_(data_dumper_.get()) {}
+      fixed_gain_controller_(data_dumper_.get()),
+      adaptive_agc_(data_dumper_.get()) {}
 
 GainController2::~GainController2() = default;
 
@@ -32,7 +33,7 @@
              sample_rate_hz == AudioProcessing::kSampleRate16kHz ||
              sample_rate_hz == AudioProcessing::kSampleRate32kHz ||
              sample_rate_hz == AudioProcessing::kSampleRate48kHz);
-  gain_controller_.SetSampleRate(sample_rate_hz);
+  fixed_gain_controller_.SetSampleRate(sample_rate_hz);
   data_dumper_->InitiateNewSetOfRecordings();
   data_dumper_->DumpRaw("sample_rate_hz", sample_rate_hz);
 }
@@ -40,15 +41,15 @@
 void GainController2::Process(AudioBuffer* audio) {
   AudioFrameView<float> float_frame(audio->channels_f(), audio->num_channels(),
                                     audio->num_frames());
-  gain_controller_.Process(float_frame);
+  adaptive_agc_.Process(float_frame);
+  fixed_gain_controller_.Process(float_frame);
 }
 
 void GainController2::ApplyConfig(
     const AudioProcessing::Config::GainController2& config) {
   RTC_DCHECK(Validate(config));
   config_ = config;
-  gain_controller_.SetGain(config_.fixed_gain_db);
-  gain_controller_.EnableLimiter(config_.enable_limiter);
+  fixed_gain_controller_.SetGain(config_.fixed_gain_db);
 }
 
 bool GainController2::Validate(
@@ -60,8 +61,7 @@
     const AudioProcessing::Config::GainController2& config) {
   std::stringstream ss;
   ss << "{enabled: " << (config.enabled ? "true" : "false") << ", "
-     << "fixed_gain_dB: " << config.fixed_gain_db << ", "
-     << "enable_limiter: " << (config.enable_limiter ? "true" : "false") << "}";
+     << "fixed_gain_dB: " << config.fixed_gain_db << "}";
   return ss.str();
 }
 
diff --git a/modules/audio_processing/gain_controller2.h b/modules/audio_processing/gain_controller2.h
index 6de4564..e2ca05e 100644
--- a/modules/audio_processing/gain_controller2.h
+++ b/modules/audio_processing/gain_controller2.h
@@ -14,6 +14,7 @@
 #include <memory>
 #include <string>
 
+#include "modules/audio_processing/agc2/adaptive_agc.h"
 #include "modules/audio_processing/agc2/fixed_gain_controller.h"
 #include "modules/audio_processing/include/audio_processing.h"
 #include "rtc_base/constructormagic.h"
@@ -41,8 +42,9 @@
  private:
   static int instance_count_;
   std::unique_ptr<ApmDataDumper> data_dumper_;
-  FixedGainController gain_controller_;
+  FixedGainController fixed_gain_controller_;
   AudioProcessing::Config::GainController2 config_;
+  AdaptiveAgc adaptive_agc_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(GainController2);
 };
diff --git a/modules/audio_processing/gain_controller2_unittest.cc b/modules/audio_processing/gain_controller2_unittest.cc
index 0c3b383..5bedccd 100644
--- a/modules/audio_processing/gain_controller2_unittest.cc
+++ b/modules/audio_processing/gain_controller2_unittest.cc
@@ -61,12 +61,11 @@
   config.fixed_gain_db = 5.f;
 
   config.enabled = false;
-  config.enable_limiter = true;
-  EXPECT_EQ("{enabled: false, fixed_gain_dB: 5, enable_limiter: true}",
+  EXPECT_EQ("{enabled: false, fixed_gain_dB: 5}",
             GainController2::ToString(config));
 
   config.enabled = true;
-  EXPECT_EQ("{enabled: true, fixed_gain_dB: 5, enable_limiter: true}",
+  EXPECT_EQ("{enabled: true, fixed_gain_dB: 5}",
             GainController2::ToString(config));
 }
 
@@ -81,12 +80,6 @@
   SetAudioBufferSamples(sample_value, &ab);
   AudioProcessing::Config::GainController2 config;
 
-  // Check that samples are not modified when the fixed gain is 0 dB.
-  config.fixed_gain_db = 0.f;
-  gain_controller2->ApplyConfig(config);
-  gain_controller2->Process(&ab);
-  EXPECT_EQ(ab.channels_f()[0][0], sample_value);
-
   // Check that samples are amplified when the fixed gain is greater than 0 dB.
   config.fixed_gain_db = 5.f;
   gain_controller2->ApplyConfig(config);
diff --git a/modules/audio_processing/include/aec_dump.cc b/modules/audio_processing/include/aec_dump.cc
index 365d015..c243b52 100644
--- a/modules/audio_processing/include/aec_dump.cc
+++ b/modules/audio_processing/include/aec_dump.cc
@@ -35,6 +35,9 @@
          intelligibility_enhancer_enabled ==
              other.intelligibility_enhancer_enabled &&
          noise_robust_agc_enabled == other.noise_robust_agc_enabled &&
+         pre_amplifier_enabled == other.pre_amplifier_enabled &&
+         pre_amplifier_fixed_gain_factor ==
+             other.pre_amplifier_fixed_gain_factor &&
          experiments_description == other.experiments_description;
 }
 }  // namespace webrtc
diff --git a/modules/audio_processing/include/aec_dump.h b/modules/audio_processing/include/aec_dump.h
index 2035bf4..e824892 100644
--- a/modules/audio_processing/include/aec_dump.h
+++ b/modules/audio_processing/include/aec_dump.h
@@ -16,12 +16,11 @@
 #include <vector>
 
 #include "api/array_view.h"
+#include "api/audio/audio_frame.h"
 #include "modules/audio_processing/include/audio_frame_view.h"
 
 namespace webrtc {
 
-class AudioFrame;
-
 // Struct for passing current config from APM without having to
 // include protobuf headers.
 struct InternalAPMConfig {
@@ -51,6 +50,8 @@
   bool transient_suppression_enabled = false;
   bool intelligibility_enhancer_enabled = false;
   bool noise_robust_agc_enabled = false;
+  bool pre_amplifier_enabled = false;
+  float pre_amplifier_fixed_gain_factor = 1.f;
   std::string experiments_description = "";
 };
 
diff --git a/modules/audio_processing/include/audio_processing.h b/modules/audio_processing/include/audio_processing.h
index d31174a..379a664 100644
--- a/modules/audio_processing/include/audio_processing.h
+++ b/modules/audio_processing/include/audio_processing.h
@@ -270,14 +270,20 @@
       bool enabled = false;
     } high_pass_filter;
 
-    // Enables the next generation AGC functionality. This feature replaces the
-    // standard methods of gain control in the previous AGC.
-    // The functionality is not yet activated in the code and turning this on
-    // does not yet have the desired behavior.
+    // Enabled the pre-amplifier. It amplifies the capture signal
+    // before any other processing is done.
+    struct PreAmplifier {
+      bool enabled = false;
+      float fixed_gain_factor = 1.f;
+    } pre_amplifier;
+
+    // Enables the next generation AGC functionality. This feature
+    // replaces the standard methods of gain control in the previous
+    // AGC. This functionality is currently only partially
+    // implemented.
     struct GainController2 {
       bool enabled = false;
       float fixed_gain_db = 0.f;
-      bool enable_limiter = true;
     } gain_controller2;
 
     // Explicit copy assignment implementation to avoid issues with memory
@@ -303,6 +309,32 @@
     kStereoAndKeyboard
   };
 
+  // Specifies the properties of a setting to be passed to AudioProcessing at
+  // runtime.
+  class RuntimeSetting {
+   public:
+    enum class Type { kNotSpecified, kCapturePreGain };
+
+    RuntimeSetting() : type_(Type::kNotSpecified), value_(0.f) {}
+    ~RuntimeSetting() = default;
+
+    static RuntimeSetting CreateCapturePreGain(float gain) {
+      RTC_DCHECK_GE(gain, 1.f) << "Attenuation is not allowed.";
+      return {Type::kCapturePreGain, gain};
+    }
+
+    Type type() const { return type_; }
+    void GetFloat(float* value) const {
+      RTC_DCHECK(value);
+      *value = value_;
+    }
+
+   private:
+    RuntimeSetting(Type id, float value) : type_(id), value_(value) {}
+    Type type_;
+    float value_;
+  };
+
   ~AudioProcessing() override {}
 
   // Initializes internal states, while retaining all user settings. This
@@ -360,6 +392,9 @@
   // Default false.
   virtual void set_output_will_be_muted(bool muted) = 0;
 
+  // Enqueue a runtime setting.
+  virtual void SetRuntimeSetting(RuntimeSetting setting) = 0;
+
   // Processes a 10 ms |frame| of the primary audio stream. On the client-side,
   // this is the near-end (or captured) audio.
   //
diff --git a/modules/audio_processing/include/mock_audio_processing.h b/modules/audio_processing/include/mock_audio_processing.h
index 96a04ef..5fa3b1b 100644
--- a/modules/audio_processing/include/mock_audio_processing.h
+++ b/modules/audio_processing/include/mock_audio_processing.h
@@ -121,6 +121,7 @@
   MOCK_METHOD2(ProcessCapture,
                void(AudioBuffer* capture, bool echo_path_change));
   MOCK_CONST_METHOD0(GetMetrics, Metrics());
+  MOCK_METHOD1(SetAudioBufferDelay, void(size_t delay_ms));
 };
 
 class MockVoiceDetection : public VoiceDetection {
@@ -167,6 +168,7 @@
   MOCK_CONST_METHOD0(num_output_channels, size_t());
   MOCK_CONST_METHOD0(num_reverse_channels, size_t());
   MOCK_METHOD1(set_output_will_be_muted, void(bool muted));
+  MOCK_METHOD1(SetRuntimeSetting, void(RuntimeSetting setting));
   MOCK_METHOD1(ProcessStream, int(AudioFrame* frame));
   MOCK_METHOD7(ProcessStream, int(const float* const* src,
                                   size_t samples_per_channel,
diff --git a/modules/audio_processing/logging/apm_data_dumper.cc b/modules/audio_processing/logging/apm_data_dumper.cc
index 2f6a6d6..e2e8602 100644
--- a/modules/audio_processing/logging/apm_data_dumper.cc
+++ b/modules/audio_processing/logging/apm_data_dumper.cc
@@ -10,8 +10,7 @@
 
 #include "modules/audio_processing/logging/apm_data_dumper.h"
 
-#include <sstream>
-
+#include "rtc_base/strings/string_builder.h"
 #include "rtc_base/stringutils.h"
 
 // Check to verify that the define is properly set.
@@ -29,7 +28,8 @@
                          int instance_index,
                          int reinit_index,
                          const std::string& suffix) {
-  std::stringstream ss;
+  char buf[1024];
+  rtc::SimpleStringBuilder ss(buf);
   ss << name << "_" << instance_index << "-" << reinit_index << suffix;
   return ss.str();
 }
diff --git a/modules/audio_processing/module.mk b/modules/audio_processing/module.mk
index 7f39204..a6c8dad 100644
--- a/modules/audio_processing/module.mk
+++ b/modules/audio_processing/module.mk
@@ -97,9 +97,11 @@
 	modules/audio_processing/aec3/block_processor.o \
 	modules/audio_processing/aec3/block_processor_metrics.o \
 	modules/audio_processing/aec3/cascaded_biquad_filter.o \
+	modules/audio_processing/aec3/coherence_gain.o \
 	modules/audio_processing/aec3/comfort_noise_generator.o \
 	modules/audio_processing/aec3/decimator.o \
 	modules/audio_processing/aec3/downsampled_render_buffer.o \
+	modules/audio_processing/aec3/echo_audibility.o \
 	modules/audio_processing/aec3/echo_canceller3.o \
 	modules/audio_processing/aec3/echo_path_delay_estimator.o \
 	modules/audio_processing/aec3/echo_path_variability.o \
@@ -108,12 +110,12 @@
 	modules/audio_processing/aec3/erl_estimator.o \
 	modules/audio_processing/aec3/erle_estimator.o \
 	modules/audio_processing/aec3/fft_buffer.o \
+	modules/audio_processing/aec3/filter_analyzer.o \
 	modules/audio_processing/aec3/frame_blocker.o \
 	modules/audio_processing/aec3/main_filter_update_gain.o \
 	modules/audio_processing/aec3/matched_filter.o \
 	modules/audio_processing/aec3/matched_filter_lag_aggregator.o \
 	modules/audio_processing/aec3/matrix_buffer.o \
-	modules/audio_processing/aec3/output_selector.o \
 	modules/audio_processing/aec3/render_buffer.o \
 	modules/audio_processing/aec3/render_delay_buffer.o \
 	modules/audio_processing/aec3/render_delay_controller.o \
@@ -122,6 +124,7 @@
 	modules/audio_processing/aec3/residual_echo_estimator.o \
 	modules/audio_processing/aec3/shadow_filter_update_gain.o \
 	modules/audio_processing/aec3/skew_estimator.o \
+	modules/audio_processing/aec3/stationarity_estimator.o \
 	modules/audio_processing/aec3/subtractor.o \
 	modules/audio_processing/aec3/suppression_filter.o \
 	modules/audio_processing/aec3/suppression_gain.o \
@@ -135,10 +138,23 @@
 	api/audio/echo_canceller3_config.o
 
 agc2_CXX_OBJECTS = \
+	modules/audio_processing/agc2/adaptive_agc.o \
+	modules/audio_processing/agc2/adaptive_digital_gain_applier.o \
+	modules/audio_processing/agc2/adaptive_mode_level_estimator.o \
+	modules/audio_processing/agc2/biquad_filter.o \
+	modules/audio_processing/agc2/compute_interpolated_gain_curve.o \
+	modules/audio_processing/agc2/down_sampler.o \
 	modules/audio_processing/agc2/fixed_digital_level_estimator.o \
 	modules/audio_processing/agc2/fixed_gain_controller.o \
+	modules/audio_processing/agc2/gain_applier.o \
 	modules/audio_processing/agc2/gain_curve_applier.o \
-	modules/audio_processing/agc2/interpolated_gain_curve.o
+	modules/audio_processing/agc2/interpolated_gain_curve.o \
+	modules/audio_processing/agc2/limiter.o \
+	modules/audio_processing/agc2/noise_level_estimator.o \
+	modules/audio_processing/agc2/noise_spectrum_estimator.o \
+	modules/audio_processing/agc2/saturation_protector.o \
+	modules/audio_processing/agc2/signal_classifier.o \
+	modules/audio_processing/agc2/vector_float_frame.o
 
 # Dependency of agc/agc.o
 vad_CXX_OBJECTS = \
diff --git a/modules/audio_processing/ns/noise_suppression.h b/modules/audio_processing/ns/noise_suppression.h
index a167142..fa5da70 100644
--- a/modules/audio_processing/ns/noise_suppression.h
+++ b/modules/audio_processing/ns/noise_suppression.h
@@ -24,7 +24,7 @@
 /*
  * This function creates an instance of the floating point Noise Suppression.
  */
-NsHandle* WebRtcNs_Create();
+NsHandle* WebRtcNs_Create(void);
 
 /*
  * This function frees the dynamic memory of a specified noise suppression
@@ -126,7 +126,7 @@
  *
  * Return value         : Number of frequency bins.
  */
-size_t WebRtcNs_num_freq();
+size_t WebRtcNs_num_freq(void);
 
 #ifdef __cplusplus
 }
diff --git a/modules/audio_processing/ns/noise_suppression_x.h b/modules/audio_processing/ns/noise_suppression_x.h
index 838861d..f25fb7a 100644
--- a/modules/audio_processing/ns/noise_suppression_x.h
+++ b/modules/audio_processing/ns/noise_suppression_x.h
@@ -24,7 +24,7 @@
 /*
  * This function creates an instance of the fixed point Noise Suppression.
  */
-NsxHandle* WebRtcNsx_Create();
+NsxHandle* WebRtcNsx_Create(void);
 
 /*
  * This function frees the dynamic memory of a specified Noise Suppression
@@ -104,7 +104,7 @@
  *
  * Return value         : Number of frequency bins.
  */
-size_t WebRtcNsx_num_freq();
+size_t WebRtcNsx_num_freq(void);
 
 #ifdef __cplusplus
 }
diff --git a/modules/audio_processing/transient/click_annotate.cc b/modules/audio_processing/transient/click_annotate.cc
index a8b4a30..1f01d01 100644
--- a/modules/audio_processing/transient/click_annotate.cc
+++ b/modules/audio_processing/transient/click_annotate.cc
@@ -14,9 +14,9 @@
 #include <memory>
 #include <vector>
 
-#include "modules/audio_processing/transient/transient_detector.h"
 #include "modules/audio_processing/transient/file_utils.h"
-#include "system_wrappers/include/file_wrapper.h"
+#include "modules/audio_processing/transient/transient_detector.h"
+#include "rtc_base/system/file_wrapper.h"
 
 using webrtc::FileWrapper;
 using webrtc::TransientDetector;
diff --git a/modules/audio_processing/transient/file_utils.cc b/modules/audio_processing/transient/file_utils.cc
index 7bf2e08..40732b9 100644
--- a/modules/audio_processing/transient/file_utils.cc
+++ b/modules/audio_processing/transient/file_utils.cc
@@ -12,7 +12,7 @@
 
 #include <memory>
 
-#include "system_wrappers/include/file_wrapper.h"
+#include "rtc_base/system/file_wrapper.h"
 #include "typedefs.h"  // NOLINT(build/include)
 
 namespace webrtc {
diff --git a/modules/audio_processing/transient/file_utils.h b/modules/audio_processing/transient/file_utils.h
index 3f05c1d..4b04fac 100644
--- a/modules/audio_processing/transient/file_utils.h
+++ b/modules/audio_processing/transient/file_utils.h
@@ -13,7 +13,7 @@
 
 #include <string.h>
 
-#include "system_wrappers/include/file_wrapper.h"
+#include "rtc_base/system/file_wrapper.h"
 #include "typedefs.h"  // NOLINT(build/include)
 
 namespace webrtc {
diff --git a/modules/audio_processing/transient/file_utils_unittest.cc b/modules/audio_processing/transient/file_utils_unittest.cc
index c5e0399..d9880f9 100644
--- a/modules/audio_processing/transient/file_utils_unittest.cc
+++ b/modules/audio_processing/transient/file_utils_unittest.cc
@@ -15,7 +15,7 @@
 #include <memory>
 #include <vector>
 
-#include "system_wrappers/include/file_wrapper.h"
+#include "rtc_base/system/file_wrapper.h"
 #include "test/gtest.h"
 #include "test/testsupport/fileutils.h"
 #include "typedefs.h"  // NOLINT(build/include)
diff --git a/modules/audio_processing/transient/transient_detector_unittest.cc b/modules/audio_processing/transient/transient_detector_unittest.cc
index 96af179..8f60954 100644
--- a/modules/audio_processing/transient/transient_detector_unittest.cc
+++ b/modules/audio_processing/transient/transient_detector_unittest.cc
@@ -16,7 +16,7 @@
 
 #include "modules/audio_processing/transient/common.h"
 #include "modules/audio_processing/transient/file_utils.h"
-#include "system_wrappers/include/file_wrapper.h"
+#include "rtc_base/system/file_wrapper.h"
 #include "test/gtest.h"
 #include "test/testsupport/fileutils.h"
 #include "typedefs.h"  // NOLINT(build/include)
diff --git a/modules/audio_processing/transient/transient_suppression_test.cc b/modules/audio_processing/transient/transient_suppression_test.cc
index 14fe4f8..8512e01 100644
--- a/modules/audio_processing/transient/transient_suppression_test.cc
+++ b/modules/audio_processing/transient/transient_suppression_test.cc
@@ -19,7 +19,6 @@
 
 #include "common_audio/include/audio_util.h"
 #include "modules/audio_processing/agc/agc.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/flags.h"
 #include "test/gtest.h"
 #include "test/testsupport/fileutils.h"
diff --git a/modules/audio_processing/transient/wpd_tree_unittest.cc b/modules/audio_processing/transient/wpd_tree_unittest.cc
index a90af77..02c7f51 100644
--- a/modules/audio_processing/transient/wpd_tree_unittest.cc
+++ b/modules/audio_processing/transient/wpd_tree_unittest.cc
@@ -16,7 +16,7 @@
 
 #include "modules/audio_processing/transient/daubechies_8_wavelet_coeffs.h"
 #include "modules/audio_processing/transient/file_utils.h"
-#include "system_wrappers/include/file_wrapper.h"
+#include "rtc_base/system/file_wrapper.h"
 #include "test/gtest.h"
 #include "test/testsupport/fileutils.h"
 
diff --git a/modules/audio_processing/typing_detection.h b/modules/audio_processing/typing_detection.h
index fe74a59..14dfe1d 100644
--- a/modules/audio_processing/typing_detection.h
+++ b/modules/audio_processing/typing_detection.h
@@ -11,7 +11,6 @@
 #ifndef MODULES_AUDIO_PROCESSING_TYPING_DETECTION_H_
 #define MODULES_AUDIO_PROCESSING_TYPING_DETECTION_H_
 
-#include "modules/include/module_common_types.h"
 #include "typedefs.h"  // NOLINT(build/include)
 
 namespace webrtc {
diff --git a/modules/audio_processing/vad/BUILD.gn b/modules/audio_processing/vad/BUILD.gn
index 9976b78..f9f5974 100644
--- a/modules/audio_processing/vad/BUILD.gn
+++ b/modules/audio_processing/vad/BUILD.gn
@@ -35,7 +35,6 @@
     "voice_gmm_tables.h",
   ]
   deps = [
-    "../..:module_api",
     "../../..:typedefs",
     "../../../audio/utility:audio_frame_operations",
     "../../../common_audio",
@@ -45,6 +44,16 @@
   ]
 }
 
+rtc_source_set("vad_with_level") {
+  sources = [
+    "vad_with_level.h",
+  ]
+  deps = [
+    "..:audio_frame_view",
+    "../../../api:array_view",
+  ]
+}
+
 if (rtc_include_tests) {
   rtc_static_library("vad_unittests") {
     testonly = true
@@ -60,7 +69,6 @@
     ]
     deps = [
       ":vad",
-      "../..:module_api",
       "../../../common_audio",
       "../../../test:fileutils",
       "../../../test:test_support",
diff --git a/modules/audio_processing/vad/pitch_based_vad.cc b/modules/audio_processing/vad/pitch_based_vad.cc
index bca2552..240ec63 100644
--- a/modules/audio_processing/vad/pitch_based_vad.cc
+++ b/modules/audio_processing/vad/pitch_based_vad.cc
@@ -17,7 +17,6 @@
 #include "modules/audio_processing/vad/common.h"
 #include "modules/audio_processing/vad/noise_gmm_tables.h"
 #include "modules/audio_processing/vad/voice_gmm_tables.h"
-#include "modules/include/module_common_types.h"
 
 namespace webrtc {
 
diff --git a/modules/audio_processing/vad/standalone_vad.cc b/modules/audio_processing/vad/standalone_vad.cc
index 004cefe..2640892 100644
--- a/modules/audio_processing/vad/standalone_vad.cc
+++ b/modules/audio_processing/vad/standalone_vad.cc
@@ -10,8 +10,9 @@
 
 #include "modules/audio_processing/vad/standalone_vad.h"
 
+#include <string.h>
+
 #include "audio/utility/audio_frame_operations.h"
-#include "modules/include/module_common_types.h"
 #include "rtc_base/checks.h"
 #include "typedefs.h"  // NOLINT(build/include)
 
diff --git a/modules/audio_processing/vad/standalone_vad_unittest.cc b/modules/audio_processing/vad/standalone_vad_unittest.cc
index 28d1349..512276e 100644
--- a/modules/audio_processing/vad/standalone_vad_unittest.cc
+++ b/modules/audio_processing/vad/standalone_vad_unittest.cc
@@ -14,7 +14,6 @@
 
 #include <memory>
 
-#include "modules/include/module_common_types.h"
 #include "test/gtest.h"
 #include "test/testsupport/fileutils.h"
 
diff --git a/modules/audio_processing/vad/vad_audio_proc.cc b/modules/audio_processing/vad/vad_audio_proc.cc
index b1841d0..6b559d1 100644
--- a/modules/audio_processing/vad/vad_audio_proc.cc
+++ b/modules/audio_processing/vad/vad_audio_proc.cc
@@ -12,6 +12,7 @@
 
 #include <math.h>
 #include <stdio.h>
+#include <string.h>
 
 #include "common_audio/fft4g.h"
 #include "modules/audio_processing/vad/pitch_internal.h"
@@ -24,7 +25,6 @@
 #include "modules/audio_coding/codecs/isac/main/source/pitch_estimator.h"
 #include "modules/audio_coding/codecs/isac/main/source/structs.h"
 }
-#include "modules/include/module_common_types.h"
 
 namespace webrtc {
 
diff --git a/modules/audio_processing/vad/vad_audio_proc_unittest.cc b/modules/audio_processing/vad/vad_audio_proc_unittest.cc
index c520257..5b96be6 100644
--- a/modules/audio_processing/vad/vad_audio_proc_unittest.cc
+++ b/modules/audio_processing/vad/vad_audio_proc_unittest.cc
@@ -20,7 +20,6 @@
 #include <string>
 
 #include "modules/audio_processing/vad/common.h"
-#include "modules/include/module_common_types.h"
 #include "test/gtest.h"
 #include "test/testsupport/fileutils.h"
 
diff --git a/modules/audio_processing/vad/vad_with_level.h b/modules/audio_processing/vad/vad_with_level.h
new file mode 100644
index 0000000..9ad4d17
--- /dev/null
+++ b/modules/audio_processing/vad/vad_with_level.h
@@ -0,0 +1,40 @@
+/*
+ *  Copyright (c) 2018 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_PROCESSING_VAD_VAD_WITH_LEVEL_H_
+#define MODULES_AUDIO_PROCESSING_VAD_VAD_WITH_LEVEL_H_
+
+#include "api/array_view.h"
+#include "modules/audio_processing/include/audio_frame_view.h"
+
+namespace webrtc {
+class VadWithLevel {
+ public:
+  struct LevelAndProbability {
+    constexpr LevelAndProbability(float prob, float rms, float peak)
+        : speech_probability(prob),
+          speech_rms_dbfs(rms),
+          speech_peak_dbfs(peak) {}
+    LevelAndProbability() = default;
+    float speech_probability = 0;
+    float speech_rms_dbfs = 0;  // Root mean square in decibels to full-scale.
+    float speech_peak_dbfs = 0;
+  };
+
+  // TODO(webrtc:7494): This is a stub. Add implementation.
+  rtc::ArrayView<const LevelAndProbability> AnalyzeFrame(
+      AudioFrameView<const float> frame) {
+    return {nullptr, 0};
+  }
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_VAD_VAD_WITH_LEVEL_H_
diff --git a/modules/include/module_common_types.h b/modules/include/module_common_types.h
index 1290075..1c174a7 100644
--- a/modules/include/module_common_types.h
+++ b/modules/include/module_common_types.h
@@ -18,9 +18,6 @@
 #include <limits>
 
 #include "api/optional.h"
-// TODO(bugs.webrtc.org/7504): Included here because users of this header expect
-// it to declare AudioFrame. Delete as soon as all known users are updated.
-#include "api/audio/audio_frame.h"
 #include "api/rtp_headers.h"
 #include "api/video/video_rotation.h"
 #include "common_types.h"  // NOLINT(build/include)
diff --git a/modules/video_coding/codecs/h264/include/h264_globals.h b/modules/video_coding/codecs/h264/include/h264_globals.h
index cae270c..bfeba67 100644
--- a/modules/video_coding/codecs/h264/include/h264_globals.h
+++ b/modules/video_coding/codecs/h264/include/h264_globals.h
@@ -14,6 +14,8 @@
 #ifndef MODULES_VIDEO_CODING_CODECS_H264_INCLUDE_H264_GLOBALS_H_
 #define MODULES_VIDEO_CODING_CODECS_H264_INCLUDE_H264_GLOBALS_H_
 
+#include <string>
+
 namespace webrtc {
 
 // The packetization types that we support: single, aggregated, and fragmented.
@@ -40,17 +42,15 @@
 // This function is declared inline because it is not clear which
 // .cc file it should belong to.
 // TODO(hta): Refactor. https://bugs.webrtc.org/6842
-inline std::ostream& operator<<(std::ostream& stream,
-                                H264PacketizationMode mode) {
-  switch (mode) {
-    case H264PacketizationMode::NonInterleaved:
-      stream << "NonInterleaved";
-      break;
-    case H264PacketizationMode::SingleNalUnit:
-      stream << "SingleNalUnit";
-      break;
+// TODO(jonasolsson): Use absl::string_view instead when that's available.
+inline std::string ToString(H264PacketizationMode mode) {
+  if (mode == H264PacketizationMode::NonInterleaved) {
+    return "NonInterleaved";
+  } else if (mode == H264PacketizationMode::SingleNalUnit) {
+    return "SingleNalUnit";
   }
-  return stream;
+  RTC_NOTREACHED();
+  return "";
 }
 
 struct NaluInfo {
diff --git a/modules/video_coding/codecs/vp8/include/vp8.h b/modules/video_coding/codecs/vp8/include/vp8.h
index 00808e2..57be521 100644
--- a/modules/video_coding/codecs/vp8/include/vp8.h
+++ b/modules/video_coding/codecs/vp8/include/vp8.h
@@ -23,14 +23,14 @@
  public:
   static std::unique_ptr<VP8Encoder> Create();
 
-  virtual ~VP8Encoder() {}
+  ~VP8Encoder() override {}
 };  // end of VP8Encoder class
 
 class VP8Decoder : public VideoDecoder {
  public:
   static std::unique_ptr<VP8Decoder> Create();
 
-  virtual ~VP8Decoder() {}
+  ~VP8Decoder() override {}
 };  // end of VP8Decoder class
 }  // namespace webrtc
 
diff --git a/modules/video_coding/codecs/vp9/include/vp9.h b/modules/video_coding/codecs/vp9/include/vp9.h
index 172e69e..39dc138 100644
--- a/modules/video_coding/codecs/vp9/include/vp9.h
+++ b/modules/video_coding/codecs/vp9/include/vp9.h
@@ -23,7 +23,7 @@
   static bool IsSupported();
   static std::unique_ptr<VP9Encoder> Create();
 
-  virtual ~VP9Encoder() {}
+  ~VP9Encoder() override {}
 };
 
 class VP9Decoder : public VideoDecoder {
@@ -31,7 +31,7 @@
   static bool IsSupported();
   static std::unique_ptr<VP9Decoder> Create();
 
-  virtual ~VP9Decoder() {}
+  ~VP9Decoder() override {}
 };
 }  // namespace webrtc
 
diff --git a/modules/video_coding/codecs/vp9/include/vp9_globals.h b/modules/video_coding/codecs/vp9/include/vp9_globals.h
index 945eb0c..918e411 100644
--- a/modules/video_coding/codecs/vp9/include/vp9_globals.h
+++ b/modules/video_coding/codecs/vp9/include/vp9_globals.h
@@ -14,6 +14,8 @@
 #ifndef MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_GLOBALS_H_
 #define MODULES_VIDEO_CODING_CODECS_VP9_INCLUDE_VP9_GLOBALS_H_
 
+#include <assert.h>
+
 #include "modules/video_coding/codecs/interface/common_constants.h"
 
 namespace webrtc {
@@ -27,6 +29,9 @@
 const size_t kMaxVp9FramesInGof = 0xFF;  // 8 bits
 const size_t kMaxVp9NumberOfSpatialLayers = 8;
 
+const size_t kMinVp9SpatialLayerWidth = 320;
+const size_t kMinVp9SpatialLayerHeight = 180;
+
 enum TemporalStructureMode {
   kTemporalStructureMode1,  // 1 temporal layer structure - i.e., IPPP...
   kTemporalStructureMode2,  // 2 temporal layers 01...
@@ -157,6 +162,7 @@
     beginning_of_frame = false;
     end_of_frame = false;
     ss_data_available = false;
+    non_ref_for_inter_layer_pred = false;
     picture_id = kNoPictureId;
     max_picture_id = kMaxTwoBytePictureId;
     tl0_pic_idx = kNoTl0PicIdx;
@@ -167,6 +173,7 @@
     gof_idx = kNoGofIdx;
     num_ref_pics = 0;
     num_spatial_layers = 1;
+    end_of_picture = true;
   }
 
   bool inter_pic_predicted;  // This layer frame is dependent on previously
@@ -177,6 +184,8 @@
   bool end_of_frame;  // True if this packet is the last in a VP9 layer frame.
   bool ss_data_available;   // True if SS data is available in this payload
                             // descriptor.
+  bool non_ref_for_inter_layer_pred;  // True for frame which is not used as
+                                      // reference for inter-layer prediction.
   int16_t picture_id;       // PictureID index, 15 bits;
                             // kNoPictureId if PictureID does not exist.
   int16_t max_picture_id;   // Maximum picture ID index; either 0x7F or 0x7FFF;
@@ -203,6 +212,8 @@
   uint16_t width[kMaxVp9NumberOfSpatialLayers];
   uint16_t height[kMaxVp9NumberOfSpatialLayers];
   GofInfoVP9 gof;
+
+  bool end_of_picture;  // This frame is the last frame in picture.
 };
 
 }  // namespace webrtc
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
index 50e0b24..35c9da8 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
@@ -14,16 +14,12 @@
   import("//build/config/android/config.gni")
   import("//build/config/android/rules.gni")
 }
-if (is_win) {
-  import("//build/config/clang/clang.gni")
-}
 
 group("base") {
   public_deps = [
     ":rtc_base",
     ":rtc_base_approved",
     ":rtc_task_queue",
-    ":sequenced_task_checker",
     ":weak_ptr",
   ]
   if (is_android) {
@@ -85,13 +81,171 @@
 rtc_source_set("rtc_base_approved") {
   visibility = [ "*" ]
   public_deps = [
+    ":atomicops",
+    ":criticalsection",
+    ":logging",
+    ":macromagic",
+    ":platform_thread",
+    ":platform_thread_types",
+    ":ptr_util",
+    ":refcount",
     ":rtc_base_approved_generic",
+    ":rtc_event",
+    ":safe_conversions",
+    ":stringutils",
+    ":thread_checker",
+    ":timeutils",
   ]
   if (is_mac && !build_with_chromium) {
     public_deps += [ ":rtc_base_approved_objc" ]
   }
 }
 
+rtc_source_set("macromagic") {
+  sources = [
+    "arraysize.h",
+    "basictypes.h",
+    "constructormagic.h",
+    "format_macros.h",
+    "stringize_macros.h",
+    "thread_annotations.h",
+  ]
+}
+
+rtc_source_set("platform_thread_types") {
+  sources = [
+    "platform_thread_types.cc",
+    "platform_thread_types.h",
+  ]
+}
+
+rtc_source_set("ptr_util") {
+  sources = [
+    "ptr_util.h",
+    "scoped_ref_ptr.h",
+  ]
+}
+
+rtc_source_set("refcount") {
+  sources = [
+    "refcount.h",
+    "refcountedobject.h",
+    "refcounter.h",
+  ]
+  deps = [
+    ":atomicops",
+    ":macromagic",
+  ]
+}
+
+rtc_source_set("criticalsection") {
+  sources = [
+    "criticalsection.cc",
+    "criticalsection.h",
+  ]
+  deps = [
+    ":atomicops",
+    ":checks",
+    ":macromagic",
+    ":platform_thread_types",
+    "..:typedefs",
+  ]
+}
+
+rtc_source_set("platform_thread") {
+  visibility = [
+    ":rtc_base_approved",
+    ":rtc_base_approved_generic",
+    ":rtc_task_queue_libevent",
+    ":rtc_task_queue_win",
+    ":sequenced_task_checker",
+  ]
+  sources = [
+    "platform_thread.cc",
+    "platform_thread.h",
+  ]
+  deps = [
+    ":atomicops",
+    ":checks",
+    ":macromagic",
+    ":platform_thread_types",
+    ":rtc_event",
+    ":thread_checker",
+    ":timeutils",
+    "..:typedefs",
+  ]
+}
+
+rtc_source_set("rtc_event") {
+  deps = [
+    ":checks",
+    ":macromagic",
+  ]
+
+  if (build_with_chromium) {
+    # Dependency on chromium's waitable_event (in //base).
+    deps += [ "//base:base" ]
+    sources = [
+      "../../webrtc_overrides/rtc_base/event.cc",
+      "../../webrtc_overrides/rtc_base/event.h",
+    ]
+  } else {
+    sources = [
+      "event.cc",
+      "event.h",
+    ]
+  }
+}
+
+rtc_source_set("logging") {
+  deps = [
+    ":criticalsection",
+    ":macromagic",
+    ":platform_thread_types",
+    ":stringutils",
+    ":timeutils",
+  ]
+
+  if (build_with_chromium) {
+    # Dependency on chromium's logging (in //base).
+    deps += [ "//base:base" ]
+    sources = [
+      "../../webrtc_overrides/rtc_base/logging.cc",
+      "../../webrtc_overrides/rtc_base/logging.h",
+    ]
+  } else {
+    sources = [
+      "logging.cc",
+      "logging.h",
+    ]
+
+    # logging.h needs the deprecation header while downstream projects are
+    # removing code that depends on logging implementation details.
+    deps += [ ":deprecation" ]
+  }
+}
+
+rtc_source_set("thread_checker") {
+  sources = [
+    "thread_checker.h",
+    "thread_checker_impl.cc",
+    "thread_checker_impl.h",
+  ]
+  deps = [
+    ":checks",
+    ":criticalsection",
+    ":macromagic",
+    ":platform_thread_types",
+    "..:typedefs",
+  ]
+}
+
+rtc_source_set("atomicops") {
+  sources = [
+    "atomicops.h",
+  ]
+}
+
 rtc_source_set("checks") {
   sources = [
     "checks.cc",
@@ -140,8 +294,31 @@
   ]
 }
 
+rtc_source_set("safe_conversions") {
+  sources = [
+    "numerics/safe_conversions.h",
+    "numerics/safe_conversions_impl.h",
+  ]
+  deps = [
+    ":checks",
+  ]
+}
+
+rtc_source_set("timeutils") {
+  sources = [
+    "timeutils.cc",
+    "timeutils.h",
+  ]
+  deps = [
+    ":checks",
+    ":safe_conversions",
+  ]
+}
+
 rtc_source_set("stringutils") {
   sources = [
+    "stringencode.cc",
+    "stringencode.h",
     "strings/string_builder.cc",
     "strings/string_builder.h",
     "stringutils.cc",
@@ -154,6 +331,17 @@
   ]
 }
 
+rtc_source_set("audio_format_to_string") {
+  sources = [
+    "strings/audio_format_to_string.cc",
+    "strings/audio_format_to_string.h",
+  ]
+  deps = [
+    ":stringutils",
+    "../api/audio_codecs:audio_codecs_api",
+  ]
+}
+
 rtc_source_set("type_traits") {
   sources = [
     "type_traits.h",
@@ -170,27 +358,37 @@
 rtc_source_set("rtc_base_approved_generic") {
   visibility = [
     ":rtc_base_approved",
-    ":rtc_base_approved_objc",
     ":weak_ptr_unittests",
   ]
 
   cflags = []
   defines = []
   libs = []
+  data_deps = []
   deps = [
+    ":atomicops",
     ":checks",
+    ":criticalsection",
+    ":logging",
+    ":macromagic",
+    ":platform_thread",
+    ":platform_thread_types",
+    ":ptr_util",
+    ":refcount",
+    ":rtc_event",
+    ":rtc_task_queue",
     ":safe_compare",
+    ":safe_conversions",
     ":stringutils",
+    ":thread_checker",
+    ":timeutils",
     ":type_traits",
     "../:typedefs",
   ]
 
   sources = [
-    "arraysize.h",
-    "atomicops.h",
     "base64.cc",
     "base64.h",
-    "basictypes.h",
     "bind.h",
     "bitbuffer.cc",
     "bitbuffer.h",
@@ -202,18 +400,14 @@
     "bytebuffer.cc",
     "bytebuffer.h",
     "byteorder.h",
-    "constructormagic.h",
     "copyonwritebuffer.cc",
     "copyonwritebuffer.h",
-    "criticalsection.cc",
-    "criticalsection.h",
     "event_tracer.cc",
     "event_tracer.h",
     "file.cc",
     "file.h",
     "flags.cc",
     "flags.h",
-    "format_macros.h",
     "function_view.h",
     "ignore_wundef.h",
     "location.cc",
@@ -222,18 +416,11 @@
     "numerics/histogram_percentile_counter.h",
     "numerics/mod_ops.h",
     "numerics/moving_max_counter.h",
-    "numerics/safe_conversions.h",
-    "numerics/safe_conversions_impl.h",
     "onetimeevent.h",
     "pathutils.cc",
     "pathutils.h",
     "platform_file.cc",
     "platform_file.h",
-    "platform_thread.cc",
-    "platform_thread.h",
-    "platform_thread_types.cc",
-    "platform_thread_types.h",
-    "ptr_util.h",
     "race_checker.cc",
     "race_checker.h",
     "random.cc",
@@ -242,25 +429,12 @@
     "rate_statistics.h",
     "ratetracker.cc",
     "ratetracker.h",
-    "refcount.h",
-    "refcountedobject.h",
-    "refcounter.h",
-    "scoped_ref_ptr.h",
     "string_to_number.cc",
     "string_to_number.h",
-    "stringencode.cc",
-    "stringencode.h",
-    "stringize_macros.h",
     "swap_queue.h",
     "template_util.h",
-    "thread_annotations.h",
-    "thread_checker.h",
-    "thread_checker_impl.cc",
-    "thread_checker_impl.h",
     "timestampaligner.cc",
     "timestampaligner.h",
-    "timeutils.cc",
-    "timeutils.h",
     "trace_event.h",
     "zero_memory.cc",
     "zero_memory.h",
@@ -276,107 +450,15 @@
     libs += [ "log" ]
   }
 
-  if (is_posix) {
+  if (is_posix || is_fuchsia) {
     sources += [ "file_posix.cc" ]
   }
 
   if (is_win) {
     sources += [ "file_win.cc" ]
+    data_deps += [ "//build/win:runtime_libs" ]
   }
 
-  if (build_with_chromium) {
-    # Dependency on chromium's logging (in //base).
-    deps += [ "//base:base" ]
-    sources += [
-      "../../webrtc_overrides/rtc_base/event.cc",
-      "../../webrtc_overrides/rtc_base/event.h",
-      "../../webrtc_overrides/rtc_base/logging.cc",
-      "../../webrtc_overrides/rtc_base/logging.h",
-    ]
-  } else {
-    sources += [
-      "event.cc",
-      "event.h",
-      "logging.cc",
-      "logging.h",
-    ]
-
-    # logging.h needs the deprecation header while downstream projects are
-    # removing code that depends on logging implementation details.
-    deps += [ ":deprecation" ]
-  }
-  if (is_component_build && is_win) {
-    # Copy the VS runtime DLLs into the isolate so that they don't have to be
-    # preinstalled on the target machine. The debug runtimes have a "d" at
-    # the end.
-    # This is a copy of https://codereview.chromium.org/1783973002.
-    # TODO(ehmaldonado): We'd like Chromium to make this changes easier to use,
-    # so we don't have to copy their changes and risk breakages.
-    # See http://crbug.com/653569
-    if (is_debug) {
-      vcrt_suffix = "d"
-    } else {
-      vcrt_suffix = ""
-    }
-
-    # These runtime files are copied to the output directory by the
-    # vs_toolchain script that runs as part of toolchain configuration.
-    data = [
-      "$root_out_dir/msvcp140${vcrt_suffix}.dll",
-      "$root_out_dir/vccorlib140${vcrt_suffix}.dll",
-      "$root_out_dir/vcruntime140${vcrt_suffix}.dll",
-
-      # Universal Windows 10 CRT files
-      "$root_out_dir/api-ms-win-core-console-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-datetime-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-debug-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-errorhandling-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-file-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-file-l1-2-0.dll",
-      "$root_out_dir/api-ms-win-core-file-l2-1-0.dll",
-      "$root_out_dir/api-ms-win-core-handle-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-heap-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-interlocked-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-libraryloader-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-localization-l1-2-0.dll",
-      "$root_out_dir/api-ms-win-core-memory-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-namedpipe-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-processenvironment-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-processthreads-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-processthreads-l1-1-1.dll",
-      "$root_out_dir/api-ms-win-core-profile-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-rtlsupport-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-string-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-synch-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-synch-l1-2-0.dll",
-      "$root_out_dir/api-ms-win-core-sysinfo-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-timezone-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-core-util-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-crt-conio-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-crt-convert-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-crt-environment-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-crt-filesystem-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-crt-heap-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-crt-locale-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-crt-math-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-crt-multibyte-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-crt-private-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-crt-process-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-crt-runtime-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-crt-stdio-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-crt-string-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-crt-time-l1-1-0.dll",
-      "$root_out_dir/api-ms-win-crt-utility-l1-1-0.dll",
-      "$root_out_dir/ucrtbase${vcrt_suffix}.dll",
-    ]
-    if (is_asan) {
-      if (current_cpu == "x64") {
-        data += [ "$clang_base_path/lib/clang/$clang_version/lib/windows/clang_rt.asan_dynamic-x86_64.dll" ]
-      } else {
-        data += [ "$clang_base_path/lib/clang/$clang_version/lib/windows/clang_rt.asan_dynamic-i386.dll" ]
-      }
-    }
-  }
   if (is_nacl) {
     deps += [ "//native_client_sdk/src/libraries/nacl_io" ]
   }
@@ -395,16 +477,14 @@
       "logging_mac.mm",
     ]
     deps = [
-      ":rtc_base_approved_generic",
+      ":logging",
     ]
   }
 }
 
 rtc_source_set("rtc_task_queue") {
   visibility = [ "*" ]
-  deps = [
-    ":rtc_base_approved",
-  ]
+  deps = []
   public_deps = [
     ":rtc_task_queue_api",
   ]
@@ -421,47 +501,109 @@
 # provided implementation should be used. An external implementation should
 # depend on rtc_task_queue_api.
 rtc_source_set("rtc_task_queue_api") {
+  # The visibility list is commented out so that we won't break external
+  # implementations, but left here to manually test as well as for sake of what
+  # targets we expect to depend on rtc_task_queue_api.
+  # visibility = [
+  #   ":rtc_task_queue",
+  #   ":rtc_task_queue_impl",
+  #   ":sequenced_task_checker",
+  # ]
   sources = [
     "task_queue.h",
   ]
   deps = [
-    ":rtc_base_approved",
+    ":macromagic",
+    ":ptr_util",
   ]
 }
 
-rtc_source_set("rtc_task_queue_impl") {
-  visibility = [ "*" ]
-  deps = [
-    ":checks",
-    ":rtc_base_approved",
-    ":rtc_task_queue_api",
-  ]
-  if (rtc_build_libevent) {
-    deps += [ "//base/third_party/libevent" ]
-  }
-  if (rtc_enable_libevent) {
+if (rtc_enable_libevent) {
+  rtc_source_set("rtc_task_queue_libevent") {
+    visibility = [ ":rtc_task_queue_impl" ]
     sources = [
       "task_queue_libevent.cc",
       "task_queue_posix.cc",
       "task_queue_posix.h",
     ]
+    deps = [
+      ":checks",
+      ":criticalsection",
+      ":logging",
+      ":platform_thread",
+      ":ptr_util",
+      ":refcount",
+      ":rtc_task_queue_api",
+      ":safe_conversions",
+      ":timeutils",
+    ]
+    if (rtc_build_libevent) {
+      deps += [ "//base/third_party/libevent" ]
+    }
+  }
+}
+
+if (is_mac || is_ios) {
+  rtc_source_set("rtc_task_queue_gcd") {
+    visibility = [ ":rtc_task_queue_impl" ]
+    sources = [
+      "task_queue_gcd.cc",
+      "task_queue_posix.cc",
+      "task_queue_posix.h",
+    ]
+    deps = [
+      ":checks",
+      ":logging",
+      ":ptr_util",
+      ":refcount",
+      ":rtc_task_queue_api",
+    ]
+  }
+}
+
+if (is_win) {
+  rtc_source_set("rtc_task_queue_win") {
+    visibility = [ ":rtc_task_queue_impl" ]
+    sources = [
+      "task_queue_win.cc",
+    ]
+    deps = [
+      ":checks",
+      ":criticalsection",
+      ":logging",
+      ":macromagic",
+      ":platform_thread",
+      ":ptr_util",
+      ":refcount",
+      ":rtc_event",
+      ":rtc_task_queue_api",
+      ":safe_conversions",
+      ":timeutils",
+    ]
+  }
+}
+
+rtc_source_set("rtc_task_queue_impl") {
+  visibility = [ "*" ]
+  if (rtc_enable_libevent) {
+    deps = [
+      ":rtc_task_queue_libevent",
+    ]
   } else {
     if (is_mac || is_ios) {
-      sources = [
-        "task_queue_gcd.cc",
-        "task_queue_posix.cc",
-        "task_queue_posix.h",
+      deps = [
+        ":rtc_task_queue_gcd",
       ]
     }
     if (is_win) {
-      sources = [
-        "task_queue_win.cc",
+      deps = [
+        ":rtc_task_queue_win",
       ]
     }
   }
 }
 
-rtc_static_library("sequenced_task_checker") {
+rtc_source_set("sequenced_task_checker") {
   sources = [
     "sequenced_task_checker.h",
     "sequenced_task_checker_impl.cc",
@@ -469,8 +611,10 @@
   ]
   deps = [
     ":checks",
-    ":rtc_base_approved",
+    ":criticalsection",
+    ":macromagic",
     ":rtc_task_queue",
+    ":thread_checker",
   ]
 }
 
@@ -480,7 +624,8 @@
     "weak_ptr.h",
   ]
   deps = [
-    ":rtc_base_approved",
+    ":ptr_util",
+    ":refcount",
     ":sequenced_task_checker",
   ]
 }
@@ -637,17 +782,20 @@
     "openssl.h",
     "openssladapter.cc",
     "openssladapter.h",
+    "opensslcommon.cc",
+    "opensslcommon.h",
     "openssldigest.cc",
     "openssldigest.h",
     "opensslidentity.cc",
     "opensslidentity.h",
+    "opensslsessioncache.cc",
+    "opensslsessioncache.h",
     "opensslstreamadapter.cc",
     "opensslstreamadapter.h",
     "physicalsocketserver.cc",
     "physicalsocketserver.h",
     "proxyinfo.cc",
     "proxyinfo.h",
-    "ratelimiter.h",
     "rtccertificate.cc",
     "rtccertificate.h",
     "rtccertificategenerator.cc",
@@ -657,6 +805,7 @@
     "sigslot.cc",
     "sigslot.h",
     "sigslotrepeater.h",
+    "socket.cc",
     "socket.h",
     "socketadapters.cc",
     "socketadapters.h",
@@ -802,7 +951,7 @@
     defines += [ "_CRT_NONSTDC_NO_DEPRECATE" ]
   }
 
-  if (is_posix) {
+  if (is_posix || is_fuchsia) {
     sources += [
       "ifaddrs_converter.cc",
       "ifaddrs_converter.h",
@@ -869,7 +1018,6 @@
     "nattypes.h",
     "proxyserver.cc",
     "proxyserver.h",
-    "refcount.h",
     "sigslottester.h",
     "sigslottester.h.pump",
     "testbase64.h",
@@ -964,7 +1112,9 @@
 
   rtc_source_set("rtc_base_approved_unittests") {
     testonly = true
-
+    if (is_msan) {
+      cflags = [ "-fsanitize=memory" ]
+    }
     sources = [
       "atomicops_unittest.cc",
       "base64_unittest.cc",
@@ -990,12 +1140,14 @@
       "numerics/safe_minmax_unittest.cc",
       "onetimeevent_unittest.cc",
       "pathutils_unittest.cc",
+      "platform_file_unittest.cc",
       "platform_thread_unittest.cc",
       "random_unittest.cc",
       "rate_limiter_unittest.cc",
       "rate_statistics_unittest.cc",
       "ratetracker_unittest.cc",
       "refcountedobject_unittest.cc",
+      "sanitizer_unittest.cc",
       "string_to_number_unittest.cc",
       "stringencode_unittest.cc",
       "stringize_macros_unittest.cc",
@@ -1019,11 +1171,13 @@
       ":rtc_task_queue",
       ":safe_compare",
       ":safe_minmax",
+      ":sanitizer",
       ":stringutils",
       "../api:array_view",
       "../system_wrappers:system_wrappers",
       "../test:fileutils",
       "../test:test_support",
+      "memory:unittests",
     ]
   }
 
@@ -1070,6 +1224,7 @@
       ":rtc_base_approved_generic",
       ":rtc_base_tests_main",
       ":rtc_base_tests_utils",
+      ":rtc_event",
       ":rtc_task_queue",
       ":weak_ptr",
       "../test:test_support",
@@ -1134,9 +1289,11 @@
         "win32window_unittest.cc",
       ]
     }
-    if (is_posix) {
+    if (is_posix || is_fuchsia) {
       sources += [
         "openssladapter_unittest.cc",
+        "opensslcommon_unittest.cc",
+        "opensslsessioncache_unittest.cc",
         "ssladapter_unittest.cc",
         "sslidentity_unittest.cc",
         "sslstreamadapter_unittest.cc",
diff --git a/rtc_base/OWNERS b/rtc_base/OWNERS
index 9e8ef1f..f46838c 100644
--- a/rtc_base/OWNERS
+++ b/rtc_base/OWNERS
@@ -1,13 +1,12 @@
-henrikg@webrtc.org
+deadbeef@webrtc.org
 hta@webrtc.org
 juberti@webrtc.org
+kwiberg@webrtc.org
 mflodman@webrtc.org
 perkj@webrtc.org
 pthatcher@webrtc.org
 sergeyu@chromium.org
 tommi@webrtc.org
-deadbeef@webrtc.org
-kwiberg@webrtc.org
 
 # These are for the common case of adding or renaming files. If you're doing
 # structural changes, please get a review from a reviewer in this file.
diff --git a/rtc_base/asyncpacketsocket.cc b/rtc_base/asyncpacketsocket.cc
index d945039..e1b1eae 100644
--- a/rtc_base/asyncpacketsocket.cc
+++ b/rtc_base/asyncpacketsocket.cc
@@ -12,18 +12,31 @@
 
 namespace rtc {
 
-PacketTimeUpdateParams::PacketTimeUpdateParams()
-    : rtp_sendtime_extension_id(-1),
-      srtp_auth_tag_len(-1),
-      srtp_packet_index(-1) {
-}
+PacketTimeUpdateParams::PacketTimeUpdateParams() = default;
+
+PacketTimeUpdateParams::PacketTimeUpdateParams(
+    const PacketTimeUpdateParams& other) = default;
 
 PacketTimeUpdateParams::~PacketTimeUpdateParams() = default;
 
-AsyncPacketSocket::AsyncPacketSocket() {
-}
+PacketOptions::PacketOptions() = default;
+PacketOptions::PacketOptions(DiffServCodePoint dscp) : dscp(dscp) {}
+PacketOptions::PacketOptions(const PacketOptions& other) = default;
+PacketOptions::~PacketOptions() = default;
 
-AsyncPacketSocket::~AsyncPacketSocket() {
+AsyncPacketSocket::AsyncPacketSocket() = default;
+
+AsyncPacketSocket::~AsyncPacketSocket() = default;
+
+void CopySocketInformationToPacketInfo(size_t packet_size_bytes,
+                                       const AsyncPacketSocket& socket_from,
+                                       bool is_connectionless,
+                                       rtc::PacketInfo* info) {
+  info->packet_size_bytes = packet_size_bytes;
+  info->local_socket_address = socket_from.GetLocalAddress();
+  if (!is_connectionless) {
+    info->remote_socket_address = socket_from.GetRemoteAddress();
+  }
 }
 
 };  // namespace rtc
diff --git a/rtc_base/asyncpacketsocket.h b/rtc_base/asyncpacketsocket.h
index 16f4de0..ca59afb 100644
--- a/rtc_base/asyncpacketsocket.h
+++ b/rtc_base/asyncpacketsocket.h
@@ -24,23 +24,30 @@
 // after changing the value.
 struct PacketTimeUpdateParams {
   PacketTimeUpdateParams();
+  PacketTimeUpdateParams(const PacketTimeUpdateParams& other);
   ~PacketTimeUpdateParams();
 
-  int rtp_sendtime_extension_id;    // extension header id present in packet.
+  int rtp_sendtime_extension_id = -1;  // extension header id present in packet.
   std::vector<char> srtp_auth_key;  // Authentication key.
-  int srtp_auth_tag_len;            // Authentication tag length.
-  int64_t srtp_packet_index;        // Required for Rtp Packet authentication.
+  int srtp_auth_tag_len = -1;       // Authentication tag length.
+  int64_t srtp_packet_index = -1;   // Required for Rtp Packet authentication.
 };
 
 // This structure holds meta information for the packet which is about to send
 // over network.
 struct PacketOptions {
-  PacketOptions() : dscp(DSCP_NO_CHANGE), packet_id(-1) {}
-  explicit PacketOptions(DiffServCodePoint dscp) : dscp(dscp), packet_id(-1) {}
+  PacketOptions();
+  explicit PacketOptions(DiffServCodePoint dscp);
+  PacketOptions(const PacketOptions& other);
+  ~PacketOptions();
 
-  DiffServCodePoint dscp;
-  int packet_id;  // 16 bits, -1 represents "not set".
+  DiffServCodePoint dscp = DSCP_NO_CHANGE;
+  // When used with RTP packets (for example, webrtc::PacketOptions), the value
+  // should be 16 bits. A value of -1 represents "not set".
+  int64_t packet_id = -1;
   PacketTimeUpdateParams packet_time_params;
+  // PacketInfo is passed to SentPacket when signaling this packet is sent.
+  PacketInfo info_signaled_after_sent;
 };
 
 // This structure will have the information about when packet is actually
@@ -138,6 +145,11 @@
   RTC_DISALLOW_COPY_AND_ASSIGN(AsyncPacketSocket);
 };
 
+void CopySocketInformationToPacketInfo(size_t packet_size_bytes,
+                                       const AsyncPacketSocket& socket_from,
+                                       bool is_connectionless,
+                                       rtc::PacketInfo* info);
+
 }  // namespace rtc
 
 #endif  // RTC_BASE_ASYNCPACKETSOCKET_H_
diff --git a/rtc_base/asynctcpsocket.cc b/rtc_base/asynctcpsocket.cc
index 9e0589c..058d945 100644
--- a/rtc_base/asynctcpsocket.cc
+++ b/rtc_base/asynctcpsocket.cc
@@ -297,7 +297,9 @@
     return res;
   }
 
-  rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis());
+  rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis(),
+                              options.info_signaled_after_sent);
+  CopySocketInformationToPacketInfo(cb, *this, false, &sent_packet.info);
   SignalSentPacket(this, sent_packet);
 
   // We claim to have sent the whole thing, even if we only sent partial
diff --git a/rtc_base/asyncudpsocket.cc b/rtc_base/asyncudpsocket.cc
index 5a50ae3..d8e60b9 100644
--- a/rtc_base/asyncudpsocket.cc
+++ b/rtc_base/asyncudpsocket.cc
@@ -60,7 +60,9 @@
 
 int AsyncUDPSocket::Send(const void *pv, size_t cb,
                          const rtc::PacketOptions& options) {
-  rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis());
+  rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis(),
+                              options.info_signaled_after_sent);
+  CopySocketInformationToPacketInfo(cb, *this, false, &sent_packet.info);
   int ret = socket_->Send(pv, cb);
   SignalSentPacket(this, sent_packet);
   return ret;
@@ -69,7 +71,10 @@
 int AsyncUDPSocket::SendTo(const void *pv, size_t cb,
                            const SocketAddress& addr,
                            const rtc::PacketOptions& options) {
-  rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis());
+  rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis(),
+                              options.info_signaled_after_sent);
+  CopySocketInformationToPacketInfo(cb, *this, true, &sent_packet.info);
+  sent_packet.info.remote_socket_address = addr;
   int ret = socket_->SendTo(pv, cb, addr);
   SignalSentPacket(this, sent_packet);
   return ret;
diff --git a/rtc_base/criticalsection.cc b/rtc_base/criticalsection.cc
index f9168d8..b2bbd03 100644
--- a/rtc_base/criticalsection.cc
+++ b/rtc_base/criticalsection.cc
@@ -10,6 +10,7 @@
 
 #include "rtc_base/criticalsection.h"
 
+#include "rtc_base/atomicops.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/platform_thread_types.h"
 
diff --git a/rtc_base/criticalsection.h b/rtc_base/criticalsection.h
index 1ef9634..569e147 100644
--- a/rtc_base/criticalsection.h
+++ b/rtc_base/criticalsection.h
@@ -11,7 +11,6 @@
 #ifndef RTC_BASE_CRITICALSECTION_H_
 #define RTC_BASE_CRITICALSECTION_H_
 
-#include "rtc_base/atomicops.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/constructormagic.h"
 #include "rtc_base/platform_thread_types.h"
diff --git a/rtc_base/criticalsection_unittest.cc b/rtc_base/criticalsection_unittest.cc
index f5d6957..6792504 100644
--- a/rtc_base/criticalsection_unittest.cc
+++ b/rtc_base/criticalsection_unittest.cc
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include "rtc_base/arraysize.h"
+#include "rtc_base/atomicops.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/criticalsection.h"
 #include "rtc_base/event.h"
diff --git a/rtc_base/event_tracer.cc b/rtc_base/event_tracer.cc
index 9cbda97..3461ec6 100644
--- a/rtc_base/event_tracer.cc
+++ b/rtc_base/event_tracer.cc
@@ -14,6 +14,7 @@
 #include <string>
 #include <vector>
 
+#include "rtc_base/atomicops.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/criticalsection.h"
 #include "rtc_base/event.h"
diff --git a/rtc_base/experiments/BUILD.gn b/rtc_base/experiments/BUILD.gn
index 305ad24..1ba8827 100644
--- a/rtc_base/experiments/BUILD.gn
+++ b/rtc_base/experiments/BUILD.gn
@@ -19,3 +19,47 @@
     "../../system_wrappers:field_trial_api",
   ]
 }
+
+rtc_static_library("congestion_controller_experiment") {
+  sources = [
+    "congestion_controller_experiment.cc",
+    "congestion_controller_experiment.h",
+  ]
+  deps = [
+    "../:rtc_base_approved",
+    "../../api:optional",
+    "../../system_wrappers:field_trial_api",
+  ]
+}
+
+rtc_static_library("quality_scaling_experiment") {
+  sources = [
+    "quality_scaling_experiment.cc",
+    "quality_scaling_experiment.h",
+  ]
+  deps = [
+    "../:rtc_base_approved",
+    "../..:webrtc_common",
+    "../../api:optional",
+    "../../api/video_codecs:video_codecs_api",
+    "../../system_wrappers:field_trial_api",
+  ]
+}
+
+if (rtc_include_tests) {
+  rtc_source_set("experiments_unittests") {
+    testonly = true
+
+    sources = [
+      "congestion_controller_experiment_unittest.cc",
+      "quality_scaling_experiment_unittest.cc",
+    ]
+    deps = [
+      ":congestion_controller_experiment",
+      ":quality_scaling_experiment",
+      "../:rtc_base_tests_main",
+      "../:rtc_base_tests_utils",
+      "../../test:field_trial",
+    ]
+  }
+}
diff --git a/rtc_base/experiments/congestion_controller_experiment.cc b/rtc_base/experiments/congestion_controller_experiment.cc
new file mode 100644
index 0000000..5733fea
--- /dev/null
+++ b/rtc_base/experiments/congestion_controller_experiment.cc
@@ -0,0 +1,57 @@
+/*
+ *  Copyright 2018 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 "rtc_base/experiments/congestion_controller_experiment.h"
+
+#include <string>
+
+#include "system_wrappers/include/field_trial.h"
+
+namespace webrtc {
+namespace {
+
+const char kBbrControllerExperiment[] = "WebRTC-BweCongestionController";
+}  // namespace
+
+bool CongestionControllerExperiment::BbrControllerEnabled() {
+  std::string trial_string =
+      webrtc::field_trial::FindFullName(kBbrControllerExperiment);
+  return trial_string.find("Enabled,BBR") == 0;
+}
+
+rtc::Optional<CongestionControllerExperiment::BbrExperimentConfig>
+CongestionControllerExperiment::GetBbrExperimentConfig() {
+  if (!BbrControllerEnabled())
+    return rtc::nullopt;
+  std::string trial_string =
+      webrtc::field_trial::FindFullName(kBbrControllerExperiment);
+  BbrExperimentConfig config;
+  if (sscanf(
+          trial_string.c_str(),
+          "Enabled,BBR,"
+          "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,"
+          "%lf,%lf,%lf,%lf,%lf,%lf",
+          &config.exit_startup_on_loss, &config.exit_startup_rtt_threshold_ms,
+          &config.fully_drain_queue, &config.initial_conservation_in_startup,
+          &config.num_startup_rtts, &config.probe_rtt_based_on_bdp,
+          &config.probe_rtt_disabled_if_app_limited,
+          &config.probe_rtt_skipped_if_similar_rtt, &config.rate_based_recovery,
+          &config.rate_based_startup, &config.slower_startup,
+          &config.encoder_rate_gain, &config.encoder_rate_gain_in_probe_rtt,
+          &config.max_ack_height_window_multiplier,
+          &config.max_aggregation_bytes_multiplier,
+          &config.probe_bw_pacing_gain_offset,
+          &config.probe_rtt_congestion_window_gain) == 17) {
+    return config;
+  } else {
+    return rtc::nullopt;
+  }
+}
+
+}  // namespace webrtc
diff --git a/rtc_base/experiments/congestion_controller_experiment.h b/rtc_base/experiments/congestion_controller_experiment.h
new file mode 100644
index 0000000..478d106
--- /dev/null
+++ b/rtc_base/experiments/congestion_controller_experiment.h
@@ -0,0 +1,41 @@
+/*
+ *  Copyright 2018 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 RTC_BASE_EXPERIMENTS_CONGESTION_CONTROLLER_EXPERIMENT_H_
+#define RTC_BASE_EXPERIMENTS_CONGESTION_CONTROLLER_EXPERIMENT_H_
+#include <api/optional.h>
+namespace webrtc {
+class CongestionControllerExperiment {
+ public:
+  struct BbrExperimentConfig {
+    int exit_startup_on_loss;
+    int exit_startup_rtt_threshold_ms;
+    int fully_drain_queue;
+    int initial_conservation_in_startup;
+    int num_startup_rtts;
+    int probe_rtt_based_on_bdp;
+    int probe_rtt_disabled_if_app_limited;
+    int probe_rtt_skipped_if_similar_rtt;
+    int rate_based_recovery;
+    int rate_based_startup;
+    int slower_startup;
+    double encoder_rate_gain;
+    double encoder_rate_gain_in_probe_rtt;
+    double max_ack_height_window_multiplier;
+    double max_aggregation_bytes_multiplier;
+    double probe_bw_pacing_gain_offset;
+    double probe_rtt_congestion_window_gain;
+  };
+  static bool BbrControllerEnabled();
+  static rtc::Optional<BbrExperimentConfig> GetBbrExperimentConfig();
+};
+
+}  // namespace webrtc
+
+#endif  // RTC_BASE_EXPERIMENTS_CONGESTION_CONTROLLER_EXPERIMENT_H_
diff --git a/rtc_base/experiments/congestion_controller_experiment_unittest.cc b/rtc_base/experiments/congestion_controller_experiment_unittest.cc
new file mode 100644
index 0000000..b8e1fff
--- /dev/null
+++ b/rtc_base/experiments/congestion_controller_experiment_unittest.cc
@@ -0,0 +1,89 @@
+/*
+ *  Copyright 2018 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 "rtc_base/experiments/congestion_controller_experiment.h"
+#include "rtc_base/gunit.h"
+#include "test/field_trial.h"
+
+namespace webrtc {
+namespace {
+void ExpectEquals(CongestionControllerExperiment::BbrExperimentConfig a,
+                  CongestionControllerExperiment::BbrExperimentConfig b) {
+  EXPECT_EQ(a.exit_startup_on_loss, b.exit_startup_on_loss);
+  EXPECT_EQ(a.exit_startup_rtt_threshold_ms, b.exit_startup_rtt_threshold_ms);
+  EXPECT_EQ(a.fully_drain_queue, b.fully_drain_queue);
+  EXPECT_EQ(a.initial_conservation_in_startup,
+            b.initial_conservation_in_startup);
+  EXPECT_EQ(a.num_startup_rtts, b.num_startup_rtts);
+  EXPECT_EQ(a.probe_rtt_based_on_bdp, b.probe_rtt_based_on_bdp);
+  EXPECT_EQ(a.probe_rtt_disabled_if_app_limited,
+            b.probe_rtt_disabled_if_app_limited);
+  EXPECT_EQ(a.probe_rtt_skipped_if_similar_rtt,
+            b.probe_rtt_skipped_if_similar_rtt);
+  EXPECT_EQ(a.rate_based_recovery, b.rate_based_recovery);
+  EXPECT_EQ(a.rate_based_startup, b.rate_based_startup);
+  EXPECT_EQ(a.slower_startup, b.slower_startup);
+  EXPECT_EQ(a.encoder_rate_gain, b.encoder_rate_gain);
+  EXPECT_EQ(a.encoder_rate_gain_in_probe_rtt, b.encoder_rate_gain_in_probe_rtt);
+  EXPECT_EQ(a.max_ack_height_window_multiplier,
+            b.max_ack_height_window_multiplier);
+  EXPECT_EQ(a.max_aggregation_bytes_multiplier,
+            b.max_aggregation_bytes_multiplier);
+  EXPECT_EQ(a.probe_bw_pacing_gain_offset, b.probe_bw_pacing_gain_offset);
+  EXPECT_EQ(a.probe_rtt_congestion_window_gain,
+            b.probe_rtt_congestion_window_gain);
+}
+}  // namespace
+
+TEST(CongestionControllerExperimentTest, BbrDisabledByDefault) {
+  webrtc::test::ScopedFieldTrials field_trials("");
+  EXPECT_FALSE(CongestionControllerExperiment::BbrControllerEnabled());
+}
+
+TEST(CongestionControllerExperimentTest, BbrEnabledByFieldTrial) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-BweCongestionController/Enabled,BBR/");
+  EXPECT_TRUE(CongestionControllerExperiment::BbrControllerEnabled());
+}
+
+TEST(CongestionControllerExperimentTest, BbrBadParametersFails) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-BweCongestionController/Enabled,BBR,"
+      "garbage,here/");
+  EXPECT_FALSE(CongestionControllerExperiment::GetBbrExperimentConfig());
+}
+
+TEST(CongestionControllerExperimentTest, BbrZeroParametersParsed) {
+  CongestionControllerExperiment::BbrExperimentConfig truth = {
+      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-BweCongestionController/Enabled,BBR,"
+      "0,0,0,0,0,0,0,0,0,0,0,"
+      "0,0,0,0,0,0/");
+  auto config = CongestionControllerExperiment::GetBbrExperimentConfig();
+  EXPECT_TRUE(config);
+  if (config)
+    ExpectEquals(truth, *config);
+}
+
+TEST(CongestionControllerExperimentTest, BbrNonZeroParametersParsed) {
+  CongestionControllerExperiment::BbrExperimentConfig truth = {
+      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25};
+
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-BweCongestionController/Enabled,BBR,"
+      "1,1,1,1,1,1,1,1,1,1,1,"
+      "0.25,0.25,0.25,0.25,0.25,0.25/");
+  auto config = CongestionControllerExperiment::GetBbrExperimentConfig();
+  EXPECT_TRUE(config);
+  if (config)
+    ExpectEquals(truth, *config);
+}
+}  // namespace webrtc
diff --git a/rtc_base/experiments/quality_scaling_experiment.cc b/rtc_base/experiments/quality_scaling_experiment.cc
new file mode 100644
index 0000000..8c99954
--- /dev/null
+++ b/rtc_base/experiments/quality_scaling_experiment.cc
@@ -0,0 +1,97 @@
+/*
+ *  Copyright 2018 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 "rtc_base/experiments/quality_scaling_experiment.h"
+
+#include <string>
+
+#include "rtc_base/logging.h"
+#include "system_wrappers/include/field_trial.h"
+
+namespace webrtc {
+namespace {
+constexpr char kFieldTrial[] = "WebRTC-Video-QualityScaling";
+constexpr int kMinQp = 1;
+constexpr int kMaxVp8Qp = 127;
+constexpr int kMaxVp9Qp = 255;
+constexpr int kMaxH264Qp = 51;
+constexpr int kMaxGenericQp = 255;
+
+rtc::Optional<VideoEncoder::QpThresholds> GetThresholds(int low,
+                                                        int high,
+                                                        int max) {
+  if (low < kMinQp || high > max || high < low)
+    return rtc::nullopt;
+
+  RTC_LOG(LS_INFO) << "QP thresholds: low: " << low << ", high: " << high;
+  return rtc::Optional<VideoEncoder::QpThresholds>(
+      VideoEncoder::QpThresholds(low, high));
+}
+}  // namespace
+
+bool QualityScalingExperiment::Enabled() {
+  return webrtc::field_trial::IsEnabled(kFieldTrial);
+}
+
+rtc::Optional<QualityScalingExperiment::Settings>
+QualityScalingExperiment::ParseSettings() {
+  const std::string group = webrtc::field_trial::FindFullName(kFieldTrial);
+  if (group.empty())
+    return rtc::nullopt;
+
+  Settings s;
+  if (sscanf(group.c_str(), "Enabled-%d,%d,%d,%d,%d,%d,%d,%d,%f,%f,%d",
+             &s.vp8_low, &s.vp8_high, &s.vp9_low, &s.vp9_high, &s.h264_low,
+             &s.h264_high, &s.generic_low, &s.generic_high, &s.alpha_high,
+             &s.alpha_low, &s.drop) != 11) {
+    RTC_LOG(LS_WARNING) << "Invalid number of parameters provided.";
+    return rtc::nullopt;
+  }
+  return s;
+}
+
+rtc::Optional<VideoEncoder::QpThresholds>
+QualityScalingExperiment::GetQpThresholds(VideoCodecType codec_type) {
+  const auto settings = ParseSettings();
+  if (!settings)
+    return rtc::nullopt;
+
+  switch (codec_type) {
+    case kVideoCodecVP8:
+      return GetThresholds(settings->vp8_low, settings->vp8_high, kMaxVp8Qp);
+    case kVideoCodecVP9:
+      return GetThresholds(settings->vp9_low, settings->vp9_high, kMaxVp9Qp);
+    case kVideoCodecH264:
+      return GetThresholds(settings->h264_low, settings->h264_high, kMaxH264Qp);
+    case kVideoCodecGeneric:
+      return GetThresholds(settings->generic_low, settings->generic_high,
+                           kMaxGenericQp);
+    default:
+      return rtc::nullopt;
+  }
+}
+
+QualityScalingExperiment::Config QualityScalingExperiment::GetConfig() {
+  const auto settings = ParseSettings();
+  if (!settings)
+    return Config();
+
+  Config config;
+  config.use_all_drop_reasons = settings->drop > 0;
+
+  if (settings->alpha_high < 0 || settings->alpha_low < settings->alpha_high) {
+    RTC_LOG(LS_WARNING) << "Invalid alpha value provided, using default.";
+    return config;
+  }
+  config.alpha_high = settings->alpha_high;
+  config.alpha_low = settings->alpha_low;
+  return config;
+}
+
+}  // namespace webrtc
diff --git a/rtc_base/experiments/quality_scaling_experiment.h b/rtc_base/experiments/quality_scaling_experiment.h
new file mode 100644
index 0000000..6f24d6b
--- /dev/null
+++ b/rtc_base/experiments/quality_scaling_experiment.h
@@ -0,0 +1,59 @@
+/*
+ *  Copyright 2018 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 RTC_BASE_EXPERIMENTS_QUALITY_SCALING_EXPERIMENT_H_
+#define RTC_BASE_EXPERIMENTS_QUALITY_SCALING_EXPERIMENT_H_
+
+#include "api/optional.h"
+#include "api/video_codecs/video_encoder.h"
+#include "common_types.h"  // NOLINT(build/include)
+
+namespace webrtc {
+class QualityScalingExperiment {
+ public:
+  struct Settings {
+    int vp8_low;       // VP8: low QP threshold.
+    int vp8_high;      // VP8: high QP threshold.
+    int vp9_low;       // VP9: low QP threshold.
+    int vp9_high;      // VP9: high QP threshold.
+    int h264_low;      // H264: low QP threshold.
+    int h264_high;     // H264: high QP threshold.
+    int generic_low;   // Generic: low QP threshold.
+    int generic_high;  // Generic: high QP threshold.
+    float alpha_high;  // |alpha_| for ExpFilter used when checking high QP.
+    float alpha_low;   // |alpha_| for ExpFilter used when checking low QP.
+    int drop;          // >0 sets |use_all_drop_reasons| to true.
+  };
+
+  // Used by QualityScaler.
+  struct Config {
+    float alpha_high = 0.9995f;
+    float alpha_low = 0.9999f;
+    // If set, all type of dropped frames are used.
+    // Otherwise only dropped frames by MediaOptimization are used.
+    bool use_all_drop_reasons = false;
+  };
+
+  // Returns true if the experiment is enabled.
+  static bool Enabled();
+
+  // Returns settings from field trial.
+  static rtc::Optional<Settings> ParseSettings();
+
+  // Returns QpThresholds for the |codec_type|.
+  static rtc::Optional<VideoEncoder::QpThresholds> GetQpThresholds(
+      VideoCodecType codec_type);
+
+  // Returns parsed values. If the parsing fails, default values are returned.
+  static Config GetConfig();
+};
+
+}  // namespace webrtc
+
+#endif  // RTC_BASE_EXPERIMENTS_QUALITY_SCALING_EXPERIMENT_H_
diff --git a/rtc_base/experiments/quality_scaling_experiment_unittest.cc b/rtc_base/experiments/quality_scaling_experiment_unittest.cc
new file mode 100644
index 0000000..15aadd6
--- /dev/null
+++ b/rtc_base/experiments/quality_scaling_experiment_unittest.cc
@@ -0,0 +1,169 @@
+/*
+ *  Copyright 2018 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 "rtc_base/experiments/quality_scaling_experiment.h"
+
+#include "rtc_base/gunit.h"
+#include "test/field_trial.h"
+
+namespace webrtc {
+namespace {
+void ExpectEqualSettings(QualityScalingExperiment::Settings a,
+                         QualityScalingExperiment::Settings b) {
+  EXPECT_EQ(a.vp8_low, b.vp8_low);
+  EXPECT_EQ(a.vp8_high, b.vp8_high);
+  EXPECT_EQ(a.vp9_low, b.vp9_low);
+  EXPECT_EQ(a.vp9_high, b.vp9_high);
+  EXPECT_EQ(a.h264_low, b.h264_low);
+  EXPECT_EQ(a.h264_high, b.h264_high);
+  EXPECT_EQ(a.generic_low, b.generic_low);
+  EXPECT_EQ(a.generic_high, b.generic_high);
+  EXPECT_EQ(a.alpha_high, b.alpha_high);
+  EXPECT_EQ(a.alpha_low, b.alpha_low);
+  EXPECT_EQ(a.drop, b.drop);
+}
+
+void ExpectEqualConfig(QualityScalingExperiment::Config a,
+                       QualityScalingExperiment::Config b) {
+  EXPECT_EQ(a.alpha_high, b.alpha_high);
+  EXPECT_EQ(a.alpha_low, b.alpha_low);
+  EXPECT_EQ(a.use_all_drop_reasons, b.use_all_drop_reasons);
+}
+}  // namespace
+
+TEST(QualityScalingExperimentTest, DisabledWithoutFieldTrial) {
+  webrtc::test::ScopedFieldTrials field_trials("");
+  EXPECT_FALSE(QualityScalingExperiment::Enabled());
+}
+
+TEST(QualityScalingExperimentTest, EnabledWithFieldTrial) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-QualityScaling/Enabled/");
+  EXPECT_TRUE(QualityScalingExperiment::Enabled());
+}
+
+TEST(QualityScalingExperimentTest, ParseSettings) {
+  const QualityScalingExperiment::Settings kExpected = {1, 2, 3,    4,     5, 6,
+                                                        7, 8, 0.9f, 0.99f, 1};
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-QualityScaling/Enabled-1,2,3,4,5,6,7,8,0.9,0.99,1/");
+  const auto settings = QualityScalingExperiment::ParseSettings();
+  EXPECT_TRUE(settings);
+  ExpectEqualSettings(kExpected, *settings);
+}
+
+TEST(QualityScalingExperimentTest, ParseSettingsFailsWithoutFieldTrial) {
+  webrtc::test::ScopedFieldTrials field_trials("");
+  EXPECT_FALSE(QualityScalingExperiment::ParseSettings());
+}
+
+TEST(QualityScalingExperimentTest, ParseSettingsFailsWithInvalidFieldTrial) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-QualityScaling/Enabled-invalid/");
+  EXPECT_FALSE(QualityScalingExperiment::ParseSettings());
+}
+
+TEST(QualityScalingExperimentTest, GetConfig) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-QualityScaling/Enabled-1,2,3,4,5,6,7,8,0.9,0.99,0/");
+  const auto config = QualityScalingExperiment::GetConfig();
+  EXPECT_EQ(0.9f, config.alpha_high);
+  EXPECT_EQ(0.99f, config.alpha_low);
+  EXPECT_FALSE(config.use_all_drop_reasons);
+}
+
+TEST(QualityScalingExperimentTest, GetsDefaultConfigForInvalidFieldTrial) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-QualityScaling/Enabled-invalid/");
+  const auto config = QualityScalingExperiment::GetConfig();
+  ExpectEqualConfig(config, QualityScalingExperiment::Config());
+}
+
+TEST(QualityScalingExperimentTest, GetsDefaultAlphaForInvalidValue) {
+  QualityScalingExperiment::Config expected_config;
+  expected_config.use_all_drop_reasons = true;
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-QualityScaling/Enabled-1,2,3,4,5,6,7,8,0.99,0.9,1/");
+  const auto config = QualityScalingExperiment::GetConfig();
+  ExpectEqualConfig(config, expected_config);
+}
+
+TEST(QualityScalingExperimentTest, GetVp8Thresholds) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-QualityScaling/Enabled-1,2,3,4,5,6,0,0,0.9,0.99,1/");
+  const auto thresholds =
+      QualityScalingExperiment::GetQpThresholds(kVideoCodecVP8);
+  EXPECT_TRUE(thresholds);
+  EXPECT_EQ(1, thresholds->low);
+  EXPECT_EQ(2, thresholds->high);
+}
+
+TEST(QualityScalingExperimentTest, GetThresholdsFailsForInvalidVp8Value) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-QualityScaling/Enabled-0,0,3,4,5,6,7,8,0.9,0.99,1/");
+  const auto thresholds =
+      QualityScalingExperiment::GetQpThresholds(kVideoCodecVP8);
+  EXPECT_FALSE(thresholds);
+}
+
+TEST(QualityScalingExperimentTest, GetVp9Thresholds) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-QualityScaling/Enabled-1,2,3,4,5,6,0,0,0.9,0.99,1/");
+  const auto thresholds =
+      QualityScalingExperiment::GetQpThresholds(kVideoCodecVP9);
+  EXPECT_TRUE(thresholds);
+  EXPECT_EQ(3, thresholds->low);
+  EXPECT_EQ(4, thresholds->high);
+}
+
+TEST(QualityScalingExperimentTest, GetThresholdsFailsForInvalidVp9Value) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-QualityScaling/Enabled-1,2,0,0,5,6,7,8,0.9,0.99,1/");
+  const auto thresholds =
+      QualityScalingExperiment::GetQpThresholds(kVideoCodecVP9);
+  EXPECT_FALSE(thresholds);
+}
+
+TEST(QualityScalingExperimentTest, GetH264Thresholds) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-QualityScaling/Enabled-1,2,3,4,5,6,0,0,0.9,0.99,1/");
+  const auto thresholds =
+      QualityScalingExperiment::GetQpThresholds(kVideoCodecH264);
+  EXPECT_TRUE(thresholds);
+  EXPECT_EQ(5, thresholds->low);
+  EXPECT_EQ(6, thresholds->high);
+}
+
+TEST(QualityScalingExperimentTest, GetThresholdsFailsForInvalidH264Value) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-QualityScaling/Enabled-1,2,3,4,0,0,7,8,0.9,0.99,1/");
+  const auto thresholds =
+      QualityScalingExperiment::GetQpThresholds(kVideoCodecH264);
+  EXPECT_FALSE(thresholds);
+}
+
+TEST(QualityScalingExperimentTest, GetGenericThresholds) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-QualityScaling/Enabled-1,2,3,4,0,0,7,8,0.9,0.99,1/");
+  const auto thresholds =
+      QualityScalingExperiment::GetQpThresholds(kVideoCodecGeneric);
+  EXPECT_TRUE(thresholds);
+  EXPECT_EQ(7, thresholds->low);
+  EXPECT_EQ(8, thresholds->high);
+}
+
+TEST(QualityScalingExperimentTest, GetThresholdsFailsForInvalidGenericValue) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Video-QualityScaling/Enabled-1,2,3,4,5,6,0,0,0.9,0.99,1/");
+  const auto thresholds =
+      QualityScalingExperiment::GetQpThresholds(kVideoCodecGeneric);
+  EXPECT_FALSE(thresholds);
+}
+}  // namespace webrtc
diff --git a/rtc_base/format_macros.h b/rtc_base/format_macros.h
index d252a94..48127e1 100644
--- a/rtc_base/format_macros.h
+++ b/rtc_base/format_macros.h
@@ -24,8 +24,6 @@
 //   printf("xyz: %" PRIuS, size);
 // The "u" in the macro corresponds to %u, and S is for "size".
 
-#include "typedefs.h"  // NOLINT(build/include)
-
 #if defined(WEBRTC_POSIX)
 
 #if (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64)
diff --git a/rtc_base/ifaddrs-android.cc b/rtc_base/ifaddrs-android.cc
index 85a4497..b713c02 100644
--- a/rtc_base/ifaddrs-android.cc
+++ b/rtc_base/ifaddrs-android.cc
@@ -174,24 +174,24 @@
           rtattr* rta = IFA_RTA(address_msg);
           ssize_t payload_len = IFA_PAYLOAD(header);
           while (RTA_OK(rta, payload_len)) {
-            if (rta->rta_type == IFA_ADDRESS) {
-              int family = address_msg->ifa_family;
-              if (family == AF_INET || family == AF_INET6) {
-                ifaddrs* newest = new ifaddrs;
-                memset(newest, 0, sizeof(ifaddrs));
-                if (current) {
-                  current->ifa_next = newest;
-                } else {
-                  start = newest;
-                }
-                if (populate_ifaddrs(newest, address_msg, RTA_DATA(rta),
-                                     RTA_PAYLOAD(rta)) != 0) {
-                  freeifaddrs(start);
-                  *result = nullptr;
-                  return -1;
-                }
-                current = newest;
+            if ((address_msg->ifa_family == AF_INET &&
+                    rta->rta_type == IFA_LOCAL) ||
+                (address_msg->ifa_family == AF_INET6 &&
+                 rta->rta_type == IFA_ADDRESS)) {
+              ifaddrs* newest = new ifaddrs;
+              memset(newest, 0, sizeof(ifaddrs));
+              if (current) {
+                current->ifa_next = newest;
+              } else {
+                start = newest;
               }
+              if (populate_ifaddrs(newest, address_msg, RTA_DATA(rta),
+                                   RTA_PAYLOAD(rta)) != 0) {
+                freeifaddrs(start);
+                *result = nullptr;
+                return -1;
+              }
+              current = newest;
             }
             rta = RTA_NEXT(rta, payload_len);
           }
diff --git a/rtc_base/ifaddrs_converter.cc b/rtc_base/ifaddrs_converter.cc
index 2db99ef..586b4e9 100644
--- a/rtc_base/ifaddrs_converter.cc
+++ b/rtc_base/ifaddrs_converter.cc
@@ -22,8 +22,8 @@
     IPAddress* mask) {
   switch (interface->ifa_addr->sa_family) {
     case AF_INET: {
-      *ip = IPAddress(
-          reinterpret_cast<sockaddr_in*>(interface->ifa_addr)->sin_addr);
+      *ip = InterfaceAddress(IPAddress(
+          reinterpret_cast<sockaddr_in*>(interface->ifa_addr)->sin_addr));
       *mask = IPAddress(
           reinterpret_cast<sockaddr_in*>(interface->ifa_netmask)->sin_addr);
       return true;
diff --git a/rtc_base/ipaddress.cc b/rtc_base/ipaddress.cc
index d441f07..e6a02c4 100644
--- a/rtc_base/ipaddress.cc
+++ b/rtc_base/ipaddress.cc
@@ -120,11 +120,6 @@
   return false;
 }
 
-std::ostream& operator<<(std::ostream& os, const IPAddress& ip) {
-  os << ip.ToString();
-  return os;
-}
-
 in6_addr IPAddress::ipv6_address() const {
   return u_.ip6;
 }
@@ -216,13 +211,13 @@
   return *this;
 }
 
-std::ostream& operator<<(std::ostream& os, const InterfaceAddress& ip) {
-  os << static_cast<const IPAddress&>(ip);
+std::string InterfaceAddress::ToString() const {
+  std::string result = IPAddress::ToString();
 
-  if (ip.family() == AF_INET6)
-    os << "|flags:0x" << std::hex << ip.ipv6_flags();
+  if (family() == AF_INET6)
+    result += "|flags:0x" + rtc::ToHex(ipv6_flags());
 
-  return os;
+  return result;
 }
 
 static bool IPIsPrivateNetworkV4(const IPAddress& ip) {
diff --git a/rtc_base/ipaddress.h b/rtc_base/ipaddress.h
index 4ef7d08..c965cf1 100644
--- a/rtc_base/ipaddress.h
+++ b/rtc_base/ipaddress.h
@@ -83,7 +83,13 @@
   bool operator!=(const IPAddress& other) const;
   bool operator <(const IPAddress& other) const;
   bool operator >(const IPAddress& other) const;
-  friend std::ostream& operator<<(std::ostream& os, const IPAddress& addr);
+
+#ifdef UNIT_TEST
+  inline std::ostream& operator<<(  // no-presubmit-check TODO(webrtc:8982)
+      std::ostream& os) {           // no-presubmit-check TODO(webrtc:8982)
+    return os << ToString();
+  }
+#endif  // UNIT_TEST
 
   int family() const { return family_; }
   in_addr ipv4_address() const;
@@ -126,8 +132,8 @@
  public:
   InterfaceAddress() : ipv6_flags_(IPV6_ADDRESS_FLAG_NONE) {}
 
-  InterfaceAddress(IPAddress ip)
-    : IPAddress(ip), ipv6_flags_(IPV6_ADDRESS_FLAG_NONE) {}
+  explicit InterfaceAddress(IPAddress ip)
+      : IPAddress(ip), ipv6_flags_(IPV6_ADDRESS_FLAG_NONE) {}
 
   InterfaceAddress(IPAddress addr, int ipv6_flags)
     : IPAddress(addr), ipv6_flags_(ipv6_flags) {}
@@ -141,8 +147,8 @@
   bool operator!=(const InterfaceAddress& other) const;
 
   int ipv6_flags() const { return ipv6_flags_; }
-  friend std::ostream& operator<<(std::ostream& os,
-                                  const InterfaceAddress& addr);
+
+  std::string ToString() const;
 
  private:
   int ipv6_flags_;
diff --git a/rtc_base/ipaddress_unittest.cc b/rtc_base/ipaddress_unittest.cc
index 90c9559..3698d3f 100644
--- a/rtc_base/ipaddress_unittest.cc
+++ b/rtc_base/ipaddress_unittest.cc
@@ -288,39 +288,39 @@
   IPAddress addr(v4addr);
   IPAddress addr2(addr);
 
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   addr = IPAddress(INADDR_ANY);
   addr2 = IPAddress(addr);
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   addr = IPAddress(INADDR_LOOPBACK);
   addr2 = IPAddress(addr);
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   addr = IPAddress(kIPv4PublicAddr);
   addr2 = IPAddress(addr);
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   addr = IPAddress(kIPv4RFC1918Addr);
   addr2 = IPAddress(addr);
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   addr = IPAddress(in6addr_any);
   addr2 = IPAddress(addr);
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   addr = IPAddress(in6addr_loopback);
   addr2 = IPAddress(addr);
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   addr = IPAddress(kIPv6LinkLocalAddr);
   addr2 = IPAddress(addr);
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   addr = IPAddress(kIPv6PublicAddr);
   addr2 = IPAddress(addr);
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 }
 
 TEST(IPAddressTest, TestEquality) {
@@ -446,46 +446,46 @@
 
   EXPECT_TRUE(IPFromString(kIPv4AnyAddrString, &addr));
   EXPECT_EQ(addr.ToString(), kIPv4AnyAddrString);
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   addr2 = IPAddress(INADDR_LOOPBACK);
   EXPECT_TRUE(IPFromString(kIPv4LoopbackAddrString, &addr));
   EXPECT_EQ(addr.ToString(), kIPv4LoopbackAddrString);
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   addr2 = IPAddress(kIPv4RFC1918Addr);
   EXPECT_TRUE(IPFromString(kIPv4RFC1918AddrString, &addr));
   EXPECT_EQ(addr.ToString(), kIPv4RFC1918AddrString);
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   addr2 = IPAddress(kIPv4PublicAddr);
   EXPECT_TRUE(IPFromString(kIPv4PublicAddrString, &addr));
   EXPECT_EQ(addr.ToString(), kIPv4PublicAddrString);
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   addr2 = IPAddress(in6addr_any);
   EXPECT_TRUE(IPFromString(kIPv6AnyAddrString, &addr));
   EXPECT_EQ(addr.ToString(), kIPv6AnyAddrString);
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   addr2 = IPAddress(in6addr_loopback);
   EXPECT_TRUE(IPFromString(kIPv6LoopbackAddrString, &addr));
   EXPECT_EQ(addr.ToString(), kIPv6LoopbackAddrString);
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   addr2 = IPAddress(kIPv6LinkLocalAddr);
   EXPECT_TRUE(IPFromString(kIPv6LinkLocalAddrString, &addr));
   EXPECT_EQ(addr.ToString(), kIPv6LinkLocalAddrString);
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   addr2 = IPAddress(kIPv6PublicAddr);
   EXPECT_TRUE(IPFromString(kIPv6PublicAddrString, &addr));
   EXPECT_EQ(addr.ToString(), kIPv6PublicAddrString);
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   addr2 = IPAddress(kIPv4MappedRFC1918Addr);
   EXPECT_TRUE(IPFromString(kIPv4MappedV4StyleAddrString, &addr));
-  EXPECT_PRED2(AreEqual, addr, addr2);
+  EXPECT_TRUE(AreEqual(addr, addr2));
 
   // Broken cases, should set addr to AF_UNSPEC.
   EXPECT_PRED1(BrokenIPStringFails, kIPv4BrokenString1);
diff --git a/rtc_base/java/src/org/webrtc/ThreadUtils.java b/rtc_base/java/src/org/webrtc/ThreadUtils.java
index 3cc80d3..a403870 100644
--- a/rtc_base/java/src/org/webrtc/ThreadUtils.java
+++ b/rtc_base/java/src/org/webrtc/ThreadUtils.java
@@ -16,13 +16,14 @@
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import javax.annotation.Nullable;
 
 public class ThreadUtils {
   /**
    * Utility class to be used for checking that a method is called on the correct thread.
    */
   public static class ThreadChecker {
-    private Thread thread = Thread.currentThread();
+    @Nullable private Thread thread = Thread.currentThread();
 
     public void checkIsOnValidThread() {
       if (thread == null) {
diff --git a/rtc_base/memory/BUILD.gn b/rtc_base/memory/BUILD.gn
new file mode 100644
index 0000000..e24b8ce
--- /dev/null
+++ b/rtc_base/memory/BUILD.gn
@@ -0,0 +1,47 @@
+# Copyright (c) 2018 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.
+
+import("../../webrtc.gni")
+if (is_android) {
+  import("//build/config/android/config.gni")
+  import("//build/config/android/rules.gni")
+}
+
+rtc_source_set("aligned_array") {
+  sources = [
+    "aligned_array.h",
+  ]
+  deps = [
+    ":aligned_malloc",
+    "..:checks",
+  ]
+}
+
+rtc_source_set("aligned_malloc") {
+  sources = [
+    "aligned_malloc.cc",
+    "aligned_malloc.h",
+  ]
+  deps = [
+    "../..:typedefs",
+  ]
+}
+
+rtc_source_set("unittests") {
+  testonly = true
+  sources = [
+    "aligned_array_unittest.cc",
+    "aligned_malloc_unittest.cc",
+  ]
+  deps = [
+    ":aligned_array",
+    ":aligned_malloc",
+    "../..:typedefs",
+    "../../test:test_support",
+  ]
+}
diff --git a/system_wrappers/include/aligned_array.h b/rtc_base/memory/aligned_array.h
similarity index 88%
rename from system_wrappers/include/aligned_array.h
rename to rtc_base/memory/aligned_array.h
index 793c785..dcdef12 100644
--- a/system_wrappers/include/aligned_array.h
+++ b/rtc_base/memory/aligned_array.h
@@ -8,11 +8,11 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_SYSTEM_WRAPPERS_INCLUDE_ALIGNED_ARRAY_
-#define WEBRTC_SYSTEM_WRAPPERS_INCLUDE_ALIGNED_ARRAY_
+#ifndef RTC_BASE_MEMORY_ALIGNED_ARRAY_H_
+#define RTC_BASE_MEMORY_ALIGNED_ARRAY_H_
 
 #include "rtc_base/checks.h"
-#include "system_wrappers/include/aligned_malloc.h"
+#include "rtc_base/memory/aligned_malloc.h"
 
 namespace webrtc {
 
@@ -75,4 +75,4 @@
 
 }  // namespace webrtc
 
-#endif  // WEBRTC_SYSTEM_WRAPPERS_INCLUDE_ALIGNED_ARRAY_
+#endif  // RTC_BASE_MEMORY_ALIGNED_ARRAY_H_
diff --git a/system_wrappers/source/aligned_array_unittest.cc b/rtc_base/memory/aligned_array_unittest.cc
similarity index 96%
rename from system_wrappers/source/aligned_array_unittest.cc
rename to rtc_base/memory/aligned_array_unittest.cc
index e5a3c18..81fd468 100644
--- a/system_wrappers/source/aligned_array_unittest.cc
+++ b/rtc_base/memory/aligned_array_unittest.cc
@@ -8,7 +8,7 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "system_wrappers/include/aligned_array.h"
+#include "rtc_base/memory/aligned_array.h"
 
 #include <stdint.h>
 
diff --git a/system_wrappers/source/aligned_malloc.cc b/rtc_base/memory/aligned_malloc.cc
similarity index 97%
rename from system_wrappers/source/aligned_malloc.cc
rename to rtc_base/memory/aligned_malloc.cc
index 43ece9e..4e1e85c 100644
--- a/system_wrappers/source/aligned_malloc.cc
+++ b/rtc_base/memory/aligned_malloc.cc
@@ -8,7 +8,7 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "system_wrappers/include/aligned_malloc.h"
+#include "rtc_base/memory/aligned_malloc.h"
 
 #include <memory.h>
 #include <stdlib.h>
diff --git a/system_wrappers/include/aligned_malloc.h b/rtc_base/memory/aligned_malloc.h
similarity index 92%
rename from system_wrappers/include/aligned_malloc.h
rename to rtc_base/memory/aligned_malloc.h
index 33b23d2..42a6daa 100644
--- a/system_wrappers/include/aligned_malloc.h
+++ b/rtc_base/memory/aligned_malloc.h
@@ -8,8 +8,8 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef SYSTEM_WRAPPERS_INCLUDE_ALIGNED_MALLOC_H_
-#define SYSTEM_WRAPPERS_INCLUDE_ALIGNED_MALLOC_H_
+#ifndef RTC_BASE_MEMORY_ALIGNED_MALLOC_H_
+#define RTC_BASE_MEMORY_ALIGNED_MALLOC_H_
 
 // The functions declared here
 // 1) Allocates block of aligned memory.
@@ -54,4 +54,4 @@
 
 }  // namespace webrtc
 
-#endif  // SYSTEM_WRAPPERS_INCLUDE_ALIGNED_MALLOC_H_
+#endif  // RTC_BASE_MEMORY_ALIGNED_MALLOC_H_
diff --git a/system_wrappers/source/aligned_malloc_unittest.cc b/rtc_base/memory/aligned_malloc_unittest.cc
similarity index 97%
rename from system_wrappers/source/aligned_malloc_unittest.cc
rename to rtc_base/memory/aligned_malloc_unittest.cc
index 7afbf6d..742a772 100644
--- a/system_wrappers/source/aligned_malloc_unittest.cc
+++ b/rtc_base/memory/aligned_malloc_unittest.cc
@@ -8,7 +8,7 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "system_wrappers/include/aligned_malloc.h"
+#include "rtc_base/memory/aligned_malloc.h"
 
 #include <memory>
 
diff --git a/rtc_base/memory/module.mk b/rtc_base/memory/module.mk
new file mode 100644
index 0000000..37c23f4
--- /dev/null
+++ b/rtc_base/memory/module.mk
@@ -0,0 +1,5 @@
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+include common.mk
diff --git a/rtc_base/module.mk b/rtc_base/module.mk
index 8bf4804..0f8f8ca 100644
--- a/rtc_base/module.mk
+++ b/rtc_base/module.mk
@@ -42,6 +42,8 @@
 
 rtc_base_CXX_OBJECTS = \
 	rtc_base/checks.o \
+	rtc_base/memory/aligned_malloc.o \
+	rtc_base/system/file_wrapper.o \
 	$(rtc_base_approved_generic_CXX_OBJECTS) \
 	$(task_queue_impl_CXX_OBJECTS)
 
diff --git a/rtc_base/nat_unittest.cc b/rtc_base/nat_unittest.cc
index d68df1d..68f0f1c 100644
--- a/rtc_base/nat_unittest.cc
+++ b/rtc_base/nat_unittest.cc
@@ -232,11 +232,12 @@
     }
   }
   if (ext_addr2.IsNil()) {
-    RTC_LOG(LS_WARNING) << "No available IP of same family as " << int_addr;
+    RTC_LOG(LS_WARNING) << "No available IP of same family as "
+                        << int_addr.ToString();
     return;
   }
 
-  RTC_LOG(LS_INFO) << "selected ip " << ext_addr2.ipaddr();
+  RTC_LOG(LS_INFO) << "selected ip " << ext_addr2.ipaddr().ToString();
 
   SocketAddress ext_addrs[4] = {
       SocketAddress(ext_addr1),
diff --git a/rtc_base/network.cc b/rtc_base/network.cc
index 6d59888..6daa7c3 100644
--- a/rtc_base/network.cc
+++ b/rtc_base/network.cc
@@ -24,7 +24,7 @@
 
 #if defined(WEBRTC_WIN)
 #include "rtc_base/win32.h"
-#include <Iphlpapi.h>
+#include <iphlpapi.h>
 #elif !defined(__native_client__)
 #include "rtc_base/ifaddrs_converter.h"
 #endif
@@ -111,6 +111,24 @@
   }
 }
 
+uint16_t ComputeNetworkCostByType(int type) {
+  switch (type) {
+    case rtc::ADAPTER_TYPE_ETHERNET:
+    case rtc::ADAPTER_TYPE_LOOPBACK:
+      return kNetworkCostMin;
+    case rtc::ADAPTER_TYPE_WIFI:
+      return kNetworkCostLow;
+    case rtc::ADAPTER_TYPE_CELLULAR:
+      return kNetworkCostHigh;
+    case rtc::ADAPTER_TYPE_VPN:
+      // The cost of a VPN should be computed using its underlying network type.
+      RTC_NOTREACHED();
+      return kNetworkCostUnknown;
+    default:
+      return kNetworkCostUnknown;
+  }
+}
+
 #if !defined(__native_client__)
 bool IsIgnoredIPv6(const InterfaceAddress& ip) {
   if (ip.family() != AF_INET6) {
@@ -476,6 +494,7 @@
     }
 
     AdapterType adapter_type = ADAPTER_TYPE_UNKNOWN;
+    AdapterType vpn_underlying_adapter_type = ADAPTER_TYPE_UNKNOWN;
     if (cursor->ifa_flags & IFF_LOOPBACK) {
       adapter_type = ADAPTER_TYPE_LOOPBACK;
     } else {
@@ -488,6 +507,11 @@
         adapter_type = GetAdapterTypeFromName(cursor->ifa_name);
       }
     }
+
+    if (adapter_type == ADAPTER_TYPE_VPN && network_monitor_) {
+      vpn_underlying_adapter_type =
+          network_monitor_->GetVpnUnderlyingAdapterType(cursor->ifa_name);
+    }
     int prefix_length = CountIPMaskBits(mask);
     prefix = TruncateIP(ip, prefix_length);
     std::string key = MakeNetworkKey(std::string(cursor->ifa_name),
@@ -502,6 +526,7 @@
       network->set_scope_id(scope_id);
       network->AddIP(ip);
       network->set_ignored(IsIgnoredNetwork(*network));
+      network->set_underlying_type_for_vpn(vpn_underlying_adapter_type);
       if (include_ignored || !network->ignored()) {
         current_networks[key] = network.get();
         networks->push_back(network.release());
@@ -511,6 +536,8 @@
       existing_network->AddIP(ip);
       if (adapter_type != ADAPTER_TYPE_UNKNOWN) {
         existing_network->set_type(adapter_type);
+        existing_network->set_underlying_type_for_vpn(
+            vpn_underlying_adapter_type);
       }
     }
   }
@@ -633,7 +660,7 @@
               scope_id = v6_addr->sin6_scope_id;
               ip = IPAddress(v6_addr->sin6_addr);
 
-              if (IsIgnoredIPv6(ip)) {
+              if (IsIgnoredIPv6(InterfaceAddress(ip))) {
                 continue;
               }
 
@@ -971,13 +998,22 @@
   return static_cast<IPAddress>(selected_ip);
 }
 
+uint16_t Network::GetCost() const {
+  AdapterType type = IsVpn() ? underlying_type_for_vpn_ : type_;
+  return ComputeNetworkCostByType(type);
+}
+
 std::string Network::ToString() const {
   std::stringstream ss;
   // Print out the first space-terminated token of the network desc, plus
   // the IP address.
-  ss << "Net[" << description_.substr(0, description_.find(' '))
-     << ":" << prefix_.ToSensitiveString() << "/" << prefix_length_
-     << ":" << AdapterTypeToString(type_) << "]";
+  ss << "Net[" << description_.substr(0, description_.find(' ')) << ":"
+     << prefix_.ToSensitiveString() << "/" << prefix_length_ << ":"
+     << AdapterTypeToString(type_);
+  if (IsVpn()) {
+    ss << "/" << AdapterTypeToString(underlying_type_for_vpn_);
+  }
+  ss << "]";
   return ss.str();
 }
 
diff --git a/rtc_base/network.h b/rtc_base/network.h
index 49934cc..49f500c 100644
--- a/rtc_base/network.h
+++ b/rtc_base/network.h
@@ -293,7 +293,7 @@
           AdapterType type);
   Network(const Network&);
   ~Network();
-
+  // This signal is fired whenever type() or underlying_type_for_vpn() changes.
   sigslot::signal1<const Network*> SignalTypeChanged;
 
   const DefaultLocalAddressProvider* default_local_address_provider() {
@@ -346,6 +346,7 @@
 
   // Adds an active IP address to this network. Does not check for duplicates.
   void AddIP(const InterfaceAddress& ip) { ips_.push_back(ip); }
+  void AddIP(const IPAddress& ip) { ips_.push_back(rtc::InterfaceAddress(ip)); }
 
   // Sets the network's IP address list. Returns true if new IP addresses were
   // detected. Passing true to already_changed skips this check.
@@ -366,28 +367,37 @@
   void set_ignored(bool ignored) { ignored_ = ignored; }
 
   AdapterType type() const { return type_; }
+  // When type() is ADAPTER_TYPE_VPN, this returns the type of the underlying
+  // network interface used by the VPN, typically the preferred network type
+  // (see for example, the method setUnderlyingNetworks(android.net.Network[])
+  // on https://developer.android.com/reference/android/net/VpnService.html).
+  // When this information is unavailable from the OS, ADAPTER_TYPE_UNKNOWN is
+  // returned.
+  AdapterType underlying_type_for_vpn() const {
+    return underlying_type_for_vpn_;
+  }
   void set_type(AdapterType type) {
     if (type_ == type) {
       return;
     }
     type_ = type;
+    if (type != ADAPTER_TYPE_VPN) {
+      underlying_type_for_vpn_ = ADAPTER_TYPE_UNKNOWN;
+    }
     SignalTypeChanged(this);
   }
 
-  uint16_t GetCost() const {
-    switch (type_) {
-      case rtc::ADAPTER_TYPE_ETHERNET:
-      case rtc::ADAPTER_TYPE_LOOPBACK:
-        return kNetworkCostMin;
-      case rtc::ADAPTER_TYPE_WIFI:
-      case rtc::ADAPTER_TYPE_VPN:
-        return kNetworkCostLow;
-      case rtc::ADAPTER_TYPE_CELLULAR:
-        return kNetworkCostHigh;
-      default:
-        return kNetworkCostUnknown;
+  void set_underlying_type_for_vpn(AdapterType type) {
+    if (underlying_type_for_vpn_ == type) {
+      return;
     }
+    underlying_type_for_vpn_ = type;
+    SignalTypeChanged(this);
   }
+
+  bool IsVpn() const { return type_ == ADAPTER_TYPE_VPN; }
+
+  uint16_t GetCost() const;
   // A unique id assigned by the network manager, which may be signaled
   // to the remote side in the candidate.
   uint16_t id() const { return id_; }
@@ -420,6 +430,7 @@
   int scope_id_;
   bool ignored_;
   AdapterType type_;
+  AdapterType underlying_type_for_vpn_ = ADAPTER_TYPE_UNKNOWN;
   int preference_;
   bool active_ = true;
   uint16_t id_ = 0;
diff --git a/rtc_base/network_unittest.cc b/rtc_base/network_unittest.cc
index bf09c24..f4dcc6c 100644
--- a/rtc_base/network_unittest.cc
+++ b/rtc_base/network_unittest.cc
@@ -552,19 +552,16 @@
       // But with two addresses now.
       EXPECT_EQ(2U, (*it)->GetIPs().size());
       EXPECT_NE((*it)->GetIPs().end(),
-                std::find((*it)->GetIPs().begin(),
-                          (*it)->GetIPs().end(),
-                          check_ip));
+                std::find((*it)->GetIPs().begin(), (*it)->GetIPs().end(),
+                          InterfaceAddress(check_ip)));
       EXPECT_NE((*it)->GetIPs().end(),
-                std::find((*it)->GetIPs().begin(),
-                          (*it)->GetIPs().end(),
-                          ip));
+                std::find((*it)->GetIPs().begin(), (*it)->GetIPs().end(),
+                          InterfaceAddress(ip)));
     } else {
       // Check the IP didn't get added anywhere it wasn't supposed to.
       EXPECT_EQ((*it)->GetIPs().end(),
-                std::find((*it)->GetIPs().begin(),
-                          (*it)->GetIPs().end(),
-                          ip));
+                std::find((*it)->GetIPs().begin(), (*it)->GetIPs().end(),
+                          InterfaceAddress(ip)));
     }
   }
 }
@@ -606,9 +603,8 @@
     } else {
       // Check the IP didn't get added anywhere it wasn't supposed to.
       EXPECT_EQ((*it)->GetIPs().end(),
-                std::find((*it)->GetIPs().begin(),
-                          (*it)->GetIPs().end(),
-                          ip));
+                std::find((*it)->GetIPs().begin(), (*it)->GetIPs().end(),
+                          InterfaceAddress(ip)));
     }
   }
 }
@@ -962,8 +958,8 @@
   // IPAddresses.
   EXPECT_EQ(list2.size(), 1uL);
   EXPECT_EQ(list2[0]->GetIPs().size(), 2uL);
-  EXPECT_EQ(list2[0]->GetIPs()[0], ip1);
-  EXPECT_EQ(list2[0]->GetIPs()[1], ip2);
+  EXPECT_EQ(list2[0]->GetIPs()[0], InterfaceAddress(ip1));
+  EXPECT_EQ(list2[0]->GetIPs()[1], InterfaceAddress(ip2));
 }
 
 // Test that MergeNetworkList successfully detects the change if
diff --git a/rtc_base/networkmonitor.cc b/rtc_base/networkmonitor.cc
index 0272951..ad6805a 100644
--- a/rtc_base/networkmonitor.cc
+++ b/rtc_base/networkmonitor.cc
@@ -39,6 +39,11 @@
   SignalNetworksChanged();
 }
 
+AdapterType NetworkMonitorBase::GetVpnUnderlyingAdapterType(
+    const std::string& interface_name) {
+  return ADAPTER_TYPE_UNKNOWN;
+}
+
 NetworkMonitorFactory::NetworkMonitorFactory() {}
 NetworkMonitorFactory::~NetworkMonitorFactory() {}
 
diff --git a/rtc_base/networkmonitor.h b/rtc_base/networkmonitor.h
index 254b225..a174473 100644
--- a/rtc_base/networkmonitor.h
+++ b/rtc_base/networkmonitor.h
@@ -74,6 +74,8 @@
   virtual void OnNetworksChanged() = 0;
 
   virtual AdapterType GetAdapterType(const std::string& interface_name) = 0;
+  virtual AdapterType GetVpnUnderlyingAdapterType(
+      const std::string& interface_name) = 0;
 };
 
 class NetworkMonitorBase : public NetworkMonitorInterface,
@@ -87,6 +89,9 @@
 
   void OnMessage(Message* msg) override;
 
+  AdapterType GetVpnUnderlyingAdapterType(
+      const std::string& interface_name) override;
+
  protected:
   Thread* worker_thread() { return worker_thread_; }
 
diff --git a/rtc_base/openssl.h b/rtc_base/openssl.h
index dbbae05..eeed373 100644
--- a/rtc_base/openssl.h
+++ b/rtc_base/openssl.h
@@ -11,6 +11,11 @@
 #ifndef RTC_BASE_OPENSSL_H_
 #define RTC_BASE_OPENSSL_H_
 
+#if defined(WEBRTC_WIN)
+// Must be included first before openssl headers.
+#include "rtc_base/win32.h"  // NOLINT
+#endif                       // WEBRTC_WIN
+
 #include <openssl/ssl.h>
 
 #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
diff --git a/rtc_base/openssladapter.cc b/rtc_base/openssladapter.cc
index ce00469..03b3ca8 100644
--- a/rtc_base/openssladapter.cc
+++ b/rtc_base/openssladapter.cc
@@ -14,11 +14,6 @@
 #include <unistd.h>
 #endif
 
-#if defined(WEBRTC_WIN)
-// Must be included first before openssl headers.
-#include "rtc_base/win32.h"  // NOLINT
-#endif  // WEBRTC_WIN
-
 #include <openssl/bio.h>
 #include <openssl/crypto.h>
 #include <openssl/err.h>
@@ -26,12 +21,14 @@
 #include <openssl/rand.h>
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
+#include "rtc_base/openssl.h"
 
 #include "rtc_base/arraysize.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/numerics/safe_conversions.h"
-#include "rtc_base/openssl.h"
+#include "rtc_base/opensslcommon.h"
+#include "rtc_base/ptr_util.h"
 #include "rtc_base/sslroots.h"
 #include "rtc_base/stringencode.h"
 #include "rtc_base/stringutils.h"
@@ -205,9 +202,9 @@
 }
 
 OpenSSLAdapter::OpenSSLAdapter(AsyncSocket* socket,
-                               OpenSSLAdapterFactory* factory)
+                               OpenSSLSessionCache* ssl_session_cache)
     : SSLAdapter(socket),
-      factory_(factory),
+      ssl_session_cache_(ssl_session_cache),
       state_(SSL_NONE),
       role_(SSL_CLIENT),
       ssl_read_needs_write_(false),
@@ -221,8 +218,8 @@
   // If a factory is used, take a reference on the factory's SSL_CTX.
   // Otherwise, we'll create our own later.
   // Either way, we'll release our reference via SSL_CTX_free() in Cleanup().
-  if (factory_) {
-    ssl_ctx_ = factory_->ssl_ctx();
+  if (ssl_session_cache_ != nullptr) {
+    ssl_ctx_ = ssl_session_cache_->GetSSLContext();
     RTC_DCHECK(ssl_ctx_);
     // Note: if using OpenSSL, requires version 1.1.0 or later.
     SSL_CTX_up_ref(ssl_ctx_);
@@ -306,7 +303,7 @@
   // First set up the context. We should either have a factory, with its own
   // pre-existing context, or be running standalone, in which case we will
   // need to create one, and specify |false| to disable session caching.
-  if (!factory_) {
+  if (ssl_session_cache_ == nullptr) {
     RTC_DCHECK(!ssl_ctx_);
     ssl_ctx_ = CreateContext(ssl_mode_, false);
   }
@@ -351,8 +348,8 @@
     SSL_set_tlsext_host_name(ssl_, ssl_host_name_.c_str());
 
     // Enable session caching, if configured and a hostname is supplied.
-    if (factory_) {
-      SSL_SESSION* cached = factory_->LookupSession(ssl_host_name_);
+    if (ssl_session_cache_ != nullptr) {
+      SSL_SESSION* cached = ssl_session_cache_->LookupSession(ssl_host_name_);
       if (cached) {
         if (SSL_set_session(ssl_, cached) == 0) {
           RTC_LOG(LS_WARNING) << "Failed to apply SSL session from cache";
@@ -414,7 +411,7 @@
   int code = (role_ == SSL_CLIENT) ? SSL_connect(ssl_) : SSL_accept(ssl_);
   switch (SSL_get_error(ssl_, code)) {
   case SSL_ERROR_NONE:
-    if (!SSLPostConnectionCheck(ssl_, ssl_host_name_.c_str())) {
+    if (!SSLPostConnectionCheck(ssl_, ssl_host_name_)) {
       RTC_LOG(LS_ERROR) << "TLS post connection check failed";
       // make sure we close the socket
       Cleanup();
@@ -614,7 +611,6 @@
 
 int OpenSSLAdapter::Recv(void* pv, size_t cb, int64_t* timestamp) {
   switch (state_) {
-
   case SSL_NONE:
     return AsyncSocketAdapter::Recv(pv, cb, timestamp);
 
@@ -790,96 +786,18 @@
   AsyncSocketAdapter::OnCloseEvent(socket, err);
 }
 
-bool OpenSSLAdapter::VerifyServerName(SSL* ssl, const char* host,
-                                      bool ignore_bad_cert) {
-  if (!host)
-    return false;
+bool OpenSSLAdapter::SSLPostConnectionCheck(SSL* ssl, const std::string& host) {
+  bool is_valid_cert_name = openssl::VerifyPeerCertMatchesHost(ssl, host) &&
+                            (SSL_get_verify_result(ssl) == X509_V_OK ||
+                             custom_verification_succeeded_);
 
-  // Checking the return from SSL_get_peer_certificate here is not strictly
-  // necessary.  With our setup, it is not possible for it to return
-  // null.  However, it is good form to check the return.
-  X509* certificate = SSL_get_peer_certificate(ssl);
-  if (!certificate)
-    return false;
-
-  // Logging certificates is extremely verbose. So it is disabled by default.
-#ifdef LOG_CERTIFICATES
-  {
-    RTC_DLOG(LS_INFO) << "Certificate from server:";
-    BIO* mem = BIO_new(BIO_s_mem());
-    X509_print_ex(mem, certificate, XN_FLAG_SEP_CPLUS_SPC, X509_FLAG_NO_HEADER);
-    BIO_write(mem, "\0", 1);
-    char* buffer;
-    BIO_get_mem_data(mem, &buffer);
-    RTC_DLOG(LS_INFO) << buffer;
-    BIO_free(mem);
-
-    char* cipher_description =
-        SSL_CIPHER_description(SSL_get_current_cipher(ssl), nullptr, 128);
-    RTC_DLOG(LS_INFO) << "Cipher: " << cipher_description;
-    OPENSSL_free(cipher_description);
+  if (!is_valid_cert_name && ignore_bad_cert_) {
+    RTC_DLOG(LS_WARNING) << "Other TLS post connection checks failed."
+                            "ignore_bad_cert_ set to true. Overriding name "
+                            "verification failure!";
+    is_valid_cert_name = true;
   }
-#endif
-
-  bool ok = false;
-  GENERAL_NAMES* names = reinterpret_cast<GENERAL_NAMES*>(
-      X509_get_ext_d2i(certificate, NID_subject_alt_name, nullptr, nullptr));
-  if (names) {
-    for (size_t i = 0; i < static_cast<size_t>(sk_GENERAL_NAME_num(names));
-         i++) {
-      const GENERAL_NAME* name = sk_GENERAL_NAME_value(names, i);
-      if (name->type != GEN_DNS)
-        continue;
-      std::string value(
-          reinterpret_cast<const char*>(ASN1_STRING_data(name->d.dNSName)),
-          ASN1_STRING_length(name->d.dNSName));
-      // string_match takes NUL-terminated strings, so check for embedded NULs.
-      if (value.find('\0') != std::string::npos)
-        continue;
-      if (string_match(host, value.c_str())) {
-        ok = true;
-        break;
-      }
-    }
-    GENERAL_NAMES_free(names);
-  }
-
-  char data[256];
-  X509_NAME* subject;
-  if (!ok && ((subject = X509_get_subject_name(certificate)) != nullptr) &&
-      (X509_NAME_get_text_by_NID(subject, NID_commonName, data, sizeof(data)) >
-       0)) {
-    data[sizeof(data)-1] = 0;
-    if (_stricmp(data, host) == 0)
-      ok = true;
-  }
-
-  X509_free(certificate);
-
-  // This should only ever be turned on for debugging and development.
-  if (!ok && ignore_bad_cert) {
-    RTC_DLOG(LS_WARNING) << "TLS certificate check FAILED.  "
-                         << "Allowing connection anyway.";
-    ok = true;
-  }
-
-  return ok;
-}
-
-bool OpenSSLAdapter::SSLPostConnectionCheck(SSL* ssl, const char* host) {
-  bool ok = VerifyServerName(ssl, host, ignore_bad_cert_);
-
-  if (ok) {
-    ok = (SSL_get_verify_result(ssl) == X509_V_OK ||
-          custom_verification_succeeded_);
-  }
-
-  if (!ok && ignore_bad_cert_) {
-    RTC_DLOG(LS_INFO) << "Other TLS post connection checks failed.";
-    ok = true;
-  }
-
-  return ok;
+  return is_valid_cert_name;
 }
 
 #if !defined(NDEBUG)
@@ -960,9 +878,9 @@
 int OpenSSLAdapter::NewSSLSessionCallback(SSL* ssl, SSL_SESSION* session) {
   OpenSSLAdapter* stream =
       reinterpret_cast<OpenSSLAdapter*>(SSL_get_app_data(ssl));
-  RTC_DCHECK(stream->factory_);
+  RTC_DCHECK(stream->ssl_session_cache_);
   RTC_LOG(LS_INFO) << "Caching SSL session for " << stream->ssl_host_name_;
-  stream->factory_->AddSession(stream->ssl_host_name_, session);
+  stream->ssl_session_cache_->AddSession(stream->ssl_host_name_, session);
   return 1;  // We've taken ownership of the session; OpenSSL shouldn't free it.
 }
 
@@ -1061,43 +979,26 @@
 // OpenSSLAdapterFactory
 //////////////////////////////////////////////////////////////////////
 
-OpenSSLAdapterFactory::OpenSSLAdapterFactory()
-    : ssl_mode_(SSL_MODE_TLS), ssl_ctx_(nullptr) {}
-
-OpenSSLAdapterFactory::~OpenSSLAdapterFactory() {
-  for (auto it : sessions_) {
-    SSL_SESSION_free(it.second);
-  }
-  SSL_CTX_free(ssl_ctx_);
-}
+OpenSSLAdapterFactory::OpenSSLAdapterFactory() = default;
+OpenSSLAdapterFactory::~OpenSSLAdapterFactory() = default;
 
 void OpenSSLAdapterFactory::SetMode(SSLMode mode) {
-  RTC_DCHECK(!ssl_ctx_);
+  RTC_DCHECK(!ssl_session_cache_);
   ssl_mode_ = mode;
 }
 
 OpenSSLAdapter* OpenSSLAdapterFactory::CreateAdapter(AsyncSocket* socket) {
-  if (!ssl_ctx_) {
-    bool enable_cache = true;
-    ssl_ctx_ = OpenSSLAdapter::CreateContext(ssl_mode_, enable_cache);
-    if (!ssl_ctx_) {
+  if (ssl_session_cache_ == nullptr) {
+    SSL_CTX* ssl_ctx =
+        OpenSSLAdapter::CreateContext(ssl_mode_, /* enable_cache = */ true);
+    if (ssl_ctx == nullptr) {
       return nullptr;
     }
+    // The OpenSSLSessionCache will upref the ssl_ctx.
+    ssl_session_cache_ = MakeUnique<OpenSSLSessionCache>(ssl_mode_, ssl_ctx);
+    SSL_CTX_free(ssl_ctx);
   }
-
-  return new OpenSSLAdapter(socket, this);
+  return new OpenSSLAdapter(socket, ssl_session_cache_.get());
 }
 
-SSL_SESSION* OpenSSLAdapterFactory::LookupSession(const std::string& hostname) {
-  auto it = sessions_.find(hostname);
-  return (it != sessions_.end()) ? it->second : nullptr;
-}
-
-void OpenSSLAdapterFactory::AddSession(const std::string& hostname,
-                                       SSL_SESSION* new_session) {
-  SSL_SESSION* old_session = LookupSession(hostname);
-  SSL_SESSION_free(old_session);
-  sessions_[hostname] = new_session;
-}
-
-} // namespace rtc
+}  // namespace rtc
diff --git a/rtc_base/openssladapter.h b/rtc_base/openssladapter.h
index 2d0474e..5f5eb80 100644
--- a/rtc_base/openssladapter.h
+++ b/rtc_base/openssladapter.h
@@ -11,30 +11,29 @@
 #ifndef RTC_BASE_OPENSSLADAPTER_H_
 #define RTC_BASE_OPENSSLADAPTER_H_
 
+#include <openssl/ossl_typ.h>
+
 #include <map>
+#include <memory>
 #include <string>
+#include <vector>
+
 #include "rtc_base/buffer.h"
 #include "rtc_base/messagehandler.h"
 #include "rtc_base/messagequeue.h"
 #include "rtc_base/opensslidentity.h"
+#include "rtc_base/opensslsessioncache.h"
 #include "rtc_base/ssladapter.h"
 
-typedef struct ssl_st SSL;
-typedef struct ssl_ctx_st SSL_CTX;
-typedef struct x509_store_ctx_st X509_STORE_CTX;
-typedef struct ssl_session_st SSL_SESSION;
-
 namespace rtc {
 
-class OpenSSLAdapterFactory;
-
 class OpenSSLAdapter : public SSLAdapter, public MessageHandler {
  public:
   static bool InitializeSSL(VerificationCallback callback);
   static bool CleanupSSL();
 
   explicit OpenSSLAdapter(AsyncSocket* socket,
-                          OpenSSLAdapterFactory* factory = nullptr);
+                          OpenSSLSessionCache* ssl_session_cache = nullptr);
   ~OpenSSLAdapter() override;
 
   void SetIgnoreBadCert(bool ignore) override;
@@ -91,9 +90,8 @@
 
   void OnMessage(Message* msg) override;
 
-  static bool VerifyServerName(SSL* ssl, const char* host,
-                               bool ignore_bad_cert);
-  bool SSLPostConnectionCheck(SSL* ssl, const char* host);
+  bool SSLPostConnectionCheck(SSL* ssl, const std::string& host);
+
 #if !defined(NDEBUG)
   // In debug builds, logs info about the state of the SSL connection.
   static void SSLInfoCallback(const SSL* ssl, int where, int ret);
@@ -111,7 +109,7 @@
 
   // Parent object that maintains shared state.
   // Can be null if state sharing is not needed.
-  OpenSSLAdapterFactory* factory_;
+  OpenSSLSessionCache* ssl_session_cache_ = nullptr;
 
   SSLState state_;
   std::unique_ptr<OpenSSLIdentity> identity_;
@@ -145,32 +143,32 @@
 std::string TransformAlpnProtocols(const std::vector<std::string>& protos);
 
 /////////////////////////////////////////////////////////////////////////////
+
+// The OpenSSLAdapterFactory is responsbile for creating multiple new
+// OpenSSLAdapters with a shared SSL_CTX and a shared SSL_SESSION cache. The
+// SSL_SESSION cache allows existing SSL_SESSIONS to be reused instead of
+// recreating them leading to a significant performance improvement.
 class OpenSSLAdapterFactory : public SSLAdapterFactory {
  public:
   OpenSSLAdapterFactory();
   ~OpenSSLAdapterFactory() override;
-
+  // Set the SSL Mode to use with this factory. This should only be set before
+  // the first adapter is created with the factory. If it is called after it
+  // will DCHECK.
   void SetMode(SSLMode mode) override;
+  // Constructs a new socket using the shared OpenSSLSessionCache. This means
+  // existing SSLSessions already in the cache will be reused instead of
+  // re-created for improved performance.
   OpenSSLAdapter* CreateAdapter(AsyncSocket* socket) override;
 
-  static OpenSSLAdapterFactory* Create();
-
  private:
-  SSL_CTX* ssl_ctx() { return ssl_ctx_; }
-  // Looks up a session by hostname. The returned SSL_SESSION is not up_refed.
-  SSL_SESSION* LookupSession(const std::string& hostname);
-  // Adds a session to the cache, and up_refs it. Any existing session with the
-  // same hostname is replaced.
-  void AddSession(const std::string& hostname, SSL_SESSION* session);
+  // Holds the SSLMode (DTLS,TLS) that will be used to set the session cache.
+  SSLMode ssl_mode_ = SSL_MODE_TLS;
+  // Holds a cache of existing SSL Sessions.
+  std::unique_ptr<OpenSSLSessionCache> ssl_session_cache_;
+  // TODO(benwright): Remove this when context is moved to OpenSSLCommon.
+  // Hold a friend class to the OpenSSLAdapter to retrieve the context.
   friend class OpenSSLAdapter;
-
-  SSLMode ssl_mode_;
-  // Holds the shared SSL_CTX for all created adapters.
-  SSL_CTX* ssl_ctx_;
-  // Map of hostnames to SSL_SESSIONs; holds references to the SSL_SESSIONs,
-  // which are cleaned up when the factory is destroyed.
-  // TODO(juberti): Add LRU eviction to keep the cache from growing forever.
-  std::map<std::string, SSL_SESSION*> sessions_;
 };
 
 }  // namespace rtc
diff --git a/rtc_base/opensslcommon.cc b/rtc_base/opensslcommon.cc
new file mode 100644
index 0000000..5213392
--- /dev/null
+++ b/rtc_base/opensslcommon.cc
@@ -0,0 +1,95 @@
+/*
+ *  Copyright 2018 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 "rtc_base/opensslcommon.h"
+
+#if defined(WEBRTC_POSIX)
+#include <unistd.h>
+#endif
+
+#if defined(WEBRTC_WIN)
+// Must be included first before openssl headers.
+#include "rtc_base/win32.h"  // NOLINT
+#endif                       // WEBRTC_WIN
+
+#include <openssl/bio.h>
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/openssl.h"
+
+namespace rtc {
+namespace openssl {
+
+// Holds various helper methods.
+namespace {
+void LogCertificates(SSL* ssl, X509* certificate) {
+// Logging certificates is extremely verbose. So it is disabled by default.
+#ifdef LOG_CERTIFICATES
+  BIO* mem = BIO_new(BIO_s_mem());
+  if (mem == nullptr) {
+    RTC_DLOG(LS_ERROR) << "BIO_new() failed to allocate memory.";
+    return;
+  }
+
+  RTC_DLOG(LS_INFO) << "Certificate from server:";
+  X509_print_ex(mem, certificate, XN_FLAG_SEP_CPLUS_SPC, X509_FLAG_NO_HEADER);
+  BIO_write(mem, "\0", 1);
+
+  char* buffer = nullptr;
+  BIO_get_mem_data(mem, &buffer);
+  if (buffer != nullptr) {
+    RTC_DLOG(LS_INFO) << buffer;
+  } else {
+    RTC_DLOG(LS_ERROR) << "BIO_get_mem_data() failed to get buffer.";
+  }
+  BIO_free(mem);
+
+  const char* cipher_name = SSL_CIPHER_get_name(SSL_get_current_cipher(ssl));
+  if (cipher_name != nullptr) {
+    RTC_DLOG(LS_INFO) << "Cipher: " << cipher_name;
+  } else {
+    RTC_DLOG(LS_ERROR) << "SSL_CIPHER_DESCRIPTION() failed to get cipher_name.";
+  }
+#endif
+}
+}  // namespace
+
+bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host) {
+  if (host.empty()) {
+    RTC_DLOG(LS_ERROR) << "Hostname is empty. Cannot verify peer certificate.";
+    return false;
+  }
+
+  if (ssl == nullptr) {
+    RTC_DLOG(LS_ERROR) << "SSL is nullptr. Cannot verify peer certificate.";
+    return false;
+  }
+
+  X509* certificate = SSL_get_peer_certificate(ssl);
+  if (certificate == nullptr) {
+    RTC_DLOG(LS_ERROR)
+        << "SSL_get_peer_certificate failed. This should never happen.";
+    return false;
+  }
+
+  LogCertificates(ssl, certificate);
+
+  bool is_valid_cert_name =
+      X509_check_host(certificate, host.c_str(), host.size(), 0, nullptr) == 1;
+  X509_free(certificate);
+  return is_valid_cert_name;
+}
+
+}  // namespace openssl
+}  // namespace rtc
diff --git a/rtc_base/opensslcommon.h b/rtc_base/opensslcommon.h
new file mode 100644
index 0000000..c05165b
--- /dev/null
+++ b/rtc_base/opensslcommon.h
@@ -0,0 +1,29 @@
+/*
+ *  Copyright 2018 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 RTC_BASE_OPENSSLCOMMON_H_
+#define RTC_BASE_OPENSSLCOMMON_H_
+
+#include <string>
+
+typedef struct ssl_st SSL;
+
+namespace rtc {
+// The openssl namespace holds static helper methods. All methods related
+// to OpenSSL that are commonly used and don't require global state should be
+// placed here.
+namespace openssl {
+// Verifies that the hostname provided matches that in the peer certificate
+// attached to this SSL state.
+bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host);
+}  // namespace openssl
+}  // namespace rtc
+
+#endif  // RTC_BASE_OPENSSLCOMMON_H_
diff --git a/rtc_base/opensslcommon_unittest.cc b/rtc_base/opensslcommon_unittest.cc
new file mode 100644
index 0000000..322a7b9
--- /dev/null
+++ b/rtc_base/opensslcommon_unittest.cc
@@ -0,0 +1,152 @@
+/*
+ *  Copyright 2018 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 <string>
+#include <vector>
+
+#if defined(WEBRTC_POSIX)
+#include <unistd.h>
+#endif
+
+#if defined(WEBRTC_WIN)
+// Must be included first before openssl headers.
+#include "rtc_base/win32.h"  // NOLINT
+#endif                       // WEBRTC_WIN
+
+#include <openssl/bio.h>
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "rtc_base/arraysize.h"
+#include "rtc_base/gunit.h"
+#include "rtc_base/numerics/safe_conversions.h"
+#include "rtc_base/openssl.h"
+#include "rtc_base/opensslcommon.h"
+#include "rtc_base/sslroots.h"
+#include "test/gmock.h"
+
+namespace rtc {
+namespace {
+// Fake Self-Signed SSL Certifiacte with CN: *.webrtc.org.
+// This is only to be used for testing (it isn't signed by a CA anyway).
+const unsigned char kFakeSSLCertificate[] = {
+    0x30, 0x82, 0x02, 0x68, 0x30, 0x82, 0x02, 0x12, 0xA0, 0x03, 0x02, 0x01,
+    0x02, 0x02, 0x09, 0x00, 0xC8, 0x83, 0x59, 0x4D, 0x90, 0xC3, 0x5F, 0xC8,
+    0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01,
+    0x0B, 0x05, 0x00, 0x30, 0x81, 0x8D, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03,
+    0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0B, 0x30, 0x09, 0x06,
+    0x03, 0x55, 0x04, 0x08, 0x0C, 0x02, 0x57, 0x41, 0x31, 0x2C, 0x30, 0x2A,
+    0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x23, 0x46, 0x61, 0x6B, 0x65, 0x20,
+    0x57, 0x65, 0x62, 0x52, 0x54, 0x43, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+    0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x46, 0x6F, 0x72, 0x20, 0x54,
+    0x65, 0x73, 0x74, 0x69, 0x6E, 0x67, 0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03,
+    0x55, 0x04, 0x0B, 0x0C, 0x23, 0x46, 0x61, 0x6B, 0x65, 0x20, 0x57, 0x65,
+    0x62, 0x52, 0x54, 0x43, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+    0x63, 0x61, 0x74, 0x65, 0x20, 0x46, 0x6F, 0x72, 0x20, 0x54, 0x65, 0x73,
+    0x74, 0x69, 0x6E, 0x67, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04,
+    0x03, 0x0C, 0x0C, 0x2A, 0x2E, 0x77, 0x65, 0x62, 0x72, 0x74, 0x63, 0x2E,
+    0x6F, 0x72, 0x67, 0x30, 0x1E, 0x17, 0x0D, 0x31, 0x38, 0x30, 0x34, 0x30,
+    0x33, 0x32, 0x31, 0x35, 0x34, 0x30, 0x38, 0x5A, 0x17, 0x0D, 0x31, 0x39,
+    0x30, 0x34, 0x30, 0x33, 0x32, 0x31, 0x35, 0x34, 0x30, 0x38, 0x5A, 0x30,
+    0x81, 0x8D, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+    0x02, 0x55, 0x53, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08,
+    0x0C, 0x02, 0x57, 0x41, 0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04,
+    0x0A, 0x0C, 0x23, 0x46, 0x61, 0x6B, 0x65, 0x20, 0x57, 0x65, 0x62, 0x52,
+    0x54, 0x43, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+    0x74, 0x65, 0x20, 0x46, 0x6F, 0x72, 0x20, 0x54, 0x65, 0x73, 0x74, 0x69,
+    0x6E, 0x67, 0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C,
+    0x23, 0x46, 0x61, 0x6B, 0x65, 0x20, 0x57, 0x65, 0x62, 0x52, 0x54, 0x43,
+    0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
+    0x20, 0x46, 0x6F, 0x72, 0x20, 0x54, 0x65, 0x73, 0x74, 0x69, 0x6E, 0x67,
+    0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x0C, 0x2A,
+    0x2E, 0x77, 0x65, 0x62, 0x72, 0x74, 0x63, 0x2E, 0x6F, 0x72, 0x67, 0x30,
+    0x5C, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01,
+    0x01, 0x01, 0x05, 0x00, 0x03, 0x4B, 0x00, 0x30, 0x48, 0x02, 0x41, 0x00,
+    0xAE, 0xAE, 0x85, 0x2A, 0x40, 0xD6, 0x99, 0x35, 0x09, 0x34, 0x1B, 0xC5,
+    0xAC, 0x6C, 0x79, 0xC7, 0xC3, 0xDE, 0x1B, 0xCF, 0x17, 0x8D, 0x6B, 0x84,
+    0xEC, 0x8B, 0x4E, 0x2B, 0xC1, 0x83, 0x43, 0xDF, 0x76, 0x0F, 0x5F, 0x5A,
+    0xA9, 0x7D, 0x94, 0xC0, 0x54, 0x5C, 0xFF, 0xBC, 0x7C, 0x86, 0xDC, 0x9A,
+    0xCE, 0xB9, 0xDF, 0xE6, 0x0B, 0xC4, 0x5B, 0x6E, 0x56, 0x9F, 0xBC, 0x40,
+    0xF5, 0xA0, 0x52, 0xA7, 0x02, 0x03, 0x01, 0x00, 0x01, 0xA3, 0x53, 0x30,
+    0x51, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14,
+    0xB7, 0xC0, 0x9A, 0xA7, 0x22, 0xAF, 0xF8, 0x7D, 0xFF, 0x68, 0xDB, 0x80,
+    0xAC, 0x0A, 0xB6, 0xDC, 0x64, 0x89, 0xDB, 0xD4, 0x30, 0x1F, 0x06, 0x03,
+    0x55, 0x1D, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xB7, 0xC0, 0x9A,
+    0xA7, 0x22, 0xAF, 0xF8, 0x7D, 0xFF, 0x68, 0xDB, 0x80, 0xAC, 0x0A, 0xB6,
+    0xDC, 0x64, 0x89, 0xDB, 0xD4, 0x30, 0x0F, 0x06, 0x03, 0x55, 0x1D, 0x13,
+    0x01, 0x01, 0xFF, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xFF, 0x30, 0x0D,
+    0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05,
+    0x00, 0x03, 0x41, 0x00, 0x50, 0x6D, 0xCC, 0x62, 0xAE, 0xD1, 0x7C, 0x4D,
+    0xEF, 0x90, 0x1E, 0x9B, 0x72, 0x73, 0xE0, 0x56, 0x66, 0x32, 0x6A, 0x78,
+    0xE8, 0x0F, 0xAD, 0x21, 0x32, 0x54, 0xA5, 0xB3, 0xB8, 0x14, 0x54, 0xBC,
+    0x50, 0xF7, 0x7F, 0x73, 0xD6, 0x44, 0x1E, 0x82, 0xD9, 0x4B, 0x49, 0x48,
+    0x9E, 0x02, 0x8B, 0xFE, 0xC3, 0xFD, 0x5D, 0x15, 0x02, 0xE1, 0x78, 0xAC,
+    0x9A, 0xAE, 0xFC, 0xC7, 0x48, 0xC6, 0x48, 0x6B};
+
+// Simple testing helper method to create a fake SSL_SESSION with a testing
+// peer connection set.
+SSL_SESSION* CreateSSLSessionWithFakePeerCertificate(SSL_CTX* ssl_ctx) {
+  SSL_SESSION* ssl_session = SSL_SESSION_new(ssl_ctx);
+  const unsigned char* cert_buffer = kFakeSSLCertificate;
+  size_t cert_buffer_len = arraysize(kFakeSSLCertificate);
+  X509* ssl_peer_certificate = d2i_X509(
+      nullptr, &cert_buffer, checked_cast<long>(cert_buffer_len));  // NOLINT
+  EXPECT_NE(ssl_peer_certificate, nullptr);
+#ifdef OPENSSL_IS_BORINGSSL
+  ssl_session->x509_peer = ssl_peer_certificate;
+#else
+  ssl_session->peer = ssl_peer_certificate;
+#endif
+  return ssl_session;
+}
+}  // namespace
+
+TEST(OpenSSLCommonTest, VerifyPeerCertMatchesHostFailsOnNoPeerCertificate) {
+  SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+  SSL* ssl = SSL_new(ssl_ctx);
+
+  EXPECT_FALSE(openssl::VerifyPeerCertMatchesHost(ssl, "webrtc.org"));
+
+  SSL_free(ssl);
+  SSL_CTX_free(ssl_ctx);
+}
+
+TEST(OpenSSLCommonTest, VerifyPeerCertMatchesHostSucceedsOnCorrectHostname) {
+  SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+  SSL* ssl = SSL_new(ssl_ctx);
+  SSL_SESSION* ssl_session = CreateSSLSessionWithFakePeerCertificate(ssl_ctx);
+  SSL_set_session(ssl, ssl_session);
+
+  EXPECT_TRUE(openssl::VerifyPeerCertMatchesHost(ssl, "www.webrtc.org"));
+  EXPECT_TRUE(openssl::VerifyPeerCertMatchesHost(ssl, "alice.webrtc.org"));
+  EXPECT_TRUE(openssl::VerifyPeerCertMatchesHost(ssl, "bob.webrtc.org"));
+
+  SSL_SESSION_free(ssl_session);
+  SSL_free(ssl);
+  SSL_CTX_free(ssl_ctx);
+}
+
+TEST(OpenSSLCommonTest, VerifyPeerCertMatchesHostFailsOnInvalidHostname) {
+  SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+  SSL* ssl = SSL_new(ssl_ctx);
+  SSL_SESSION* ssl_session = CreateSSLSessionWithFakePeerCertificate(ssl_ctx);
+  SSL_set_session(ssl, ssl_session);
+
+  EXPECT_FALSE(openssl::VerifyPeerCertMatchesHost(ssl, "a.b.webrtc.org"));
+  EXPECT_FALSE(openssl::VerifyPeerCertMatchesHost(ssl, "notwebrtc.org"));
+  EXPECT_FALSE(openssl::VerifyPeerCertMatchesHost(ssl, "webrtc.org"));
+
+  SSL_SESSION_free(ssl_session);
+  SSL_free(ssl);
+  SSL_CTX_free(ssl_ctx);
+}
+
+}  // namespace rtc
diff --git a/rtc_base/opensslsessioncache.cc b/rtc_base/opensslsessioncache.cc
new file mode 100644
index 0000000..2e37d55
--- /dev/null
+++ b/rtc_base/opensslsessioncache.cc
@@ -0,0 +1,52 @@
+/*
+ *  Copyright 2018 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 "rtc_base/opensslsessioncache.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/openssl.h"
+
+namespace rtc {
+
+OpenSSLSessionCache::OpenSSLSessionCache(SSLMode ssl_mode, SSL_CTX* ssl_ctx)
+    : ssl_mode_(ssl_mode), ssl_ctx_(ssl_ctx) {
+  // It is invalid to pass in a null context.
+  RTC_DCHECK(ssl_ctx != nullptr);
+  SSL_CTX_up_ref(ssl_ctx);
+}
+
+OpenSSLSessionCache::~OpenSSLSessionCache() {
+  for (auto it : sessions_) {
+    SSL_SESSION_free(it.second);
+  }
+  SSL_CTX_free(ssl_ctx_);
+}
+
+SSL_SESSION* OpenSSLSessionCache::LookupSession(
+    const std::string& hostname) const {
+  auto it = sessions_.find(hostname);
+  return (it != sessions_.end()) ? it->second : nullptr;
+}
+
+void OpenSSLSessionCache::AddSession(const std::string& hostname,
+                                     SSL_SESSION* new_session) {
+  SSL_SESSION* old_session = LookupSession(hostname);
+  SSL_SESSION_free(old_session);
+  sessions_[hostname] = new_session;
+}
+
+SSL_CTX* OpenSSLSessionCache::GetSSLContext() const {
+  return ssl_ctx_;
+}
+
+SSLMode OpenSSLSessionCache::GetSSLMode() const {
+  return ssl_mode_;
+}
+
+}  // namespace rtc
diff --git a/rtc_base/opensslsessioncache.h b/rtc_base/opensslsessioncache.h
new file mode 100644
index 0000000..ee5b525
--- /dev/null
+++ b/rtc_base/opensslsessioncache.h
@@ -0,0 +1,63 @@
+/*
+ *  Copyright 2018 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 RTC_BASE_OPENSSLSESSIONCACHE_H_
+#define RTC_BASE_OPENSSLSESSIONCACHE_H_
+
+#include <openssl/ossl_typ.h>
+#include <map>
+#include <string>
+
+#include "rtc_base/constructormagic.h"
+#include "rtc_base/sslstreamadapter.h"
+
+namespace rtc {
+
+// The OpenSSLSessionCache maps hostnames to SSL_SESSIONS. This cache is
+// owned by the OpenSSLAdapterFactory and is passed down to each OpenSSLAdapter
+// created with the factory.
+class OpenSSLSessionCache final {
+ public:
+  // Creates a new OpenSSLSessionCache using the provided the SSL_CTX and
+  // the ssl_mode. The SSL_CTX will be up_refed. ssl_ctx cannot be nullptr,
+  // the constructor immediately dchecks this.
+  OpenSSLSessionCache(SSLMode ssl_mode, SSL_CTX* ssl_ctx);
+  // Frees the cached SSL_SESSIONS and then frees the SSL_CTX.
+  ~OpenSSLSessionCache();
+  // Looks up a session by hostname. The returned SSL_SESSION is not up_refed.
+  SSL_SESSION* LookupSession(const std::string& hostname) const;
+  // Adds a session to the cache, and up_refs it. Any existing session with the
+  // same hostname is replaced.
+  void AddSession(const std::string& hostname, SSL_SESSION* session);
+  // Returns the true underlying SSL Context that holds these cached sessions.
+  SSL_CTX* GetSSLContext() const;
+  // The SSL Mode tht the OpenSSLSessionCache was constructed with. This cannot
+  // be changed after launch.
+  SSLMode GetSSLMode() const;
+
+ private:
+  // Holds the SSL Mode that the OpenSSLCache was initialized with. This is
+  // immutable after creation and cannot change.
+  const SSLMode ssl_mode_;
+  /// SSL Context for all shared cached sessions. This SSL_CTX is initialized
+  //  with SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT); Meaning
+  //  all client sessions will be added to the cache internal to the context.
+  SSL_CTX* ssl_ctx_ = nullptr;
+  // Map of hostnames to SSL_SESSIONs; holds references to the SSL_SESSIONs,
+  // which are cleaned up when the factory is destroyed.
+  // TODO(juberti): Add LRU eviction to keep the cache from growing forever.
+  std::map<std::string, SSL_SESSION*> sessions_;
+  // The cache should never be copied or assigned directly.
+  RTC_DISALLOW_COPY_AND_ASSIGN(OpenSSLSessionCache);
+};
+
+}  // namespace rtc
+
+#endif  // RTC_BASE_OPENSSLSESSIONCACHE_H_
diff --git a/rtc_base/opensslsessioncache_unittest.cc b/rtc_base/opensslsessioncache_unittest.cc
new file mode 100644
index 0000000..6489b2b
--- /dev/null
+++ b/rtc_base/opensslsessioncache_unittest.cc
@@ -0,0 +1,85 @@
+/*
+ *  Copyright 2018 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 <openssl/ssl.h>
+#include <stdlib.h>
+
+#include <map>
+#include <memory>
+
+#include "rtc_base/gunit.h"
+#include "rtc_base/openssl.h"
+#include "rtc_base/opensslsessioncache.h"
+
+namespace rtc {
+
+TEST(OpenSSLSessionCache, DTLSModeSetCorrectly) {
+  SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+
+  OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
+  EXPECT_EQ(session_cache.GetSSLMode(), SSL_MODE_DTLS);
+
+  SSL_CTX_free(ssl_ctx);
+}
+
+TEST(OpenSSLSessionCache, TLSModeSetCorrectly) {
+  SSL_CTX* ssl_ctx = SSL_CTX_new(TLSv1_2_client_method());
+
+  OpenSSLSessionCache session_cache(SSL_MODE_TLS, ssl_ctx);
+  EXPECT_EQ(session_cache.GetSSLMode(), SSL_MODE_TLS);
+
+  SSL_CTX_free(ssl_ctx);
+}
+
+TEST(OpenSSLSessionCache, SSLContextSetCorrectly) {
+  SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+
+  OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
+  EXPECT_EQ(session_cache.GetSSLContext(), ssl_ctx);
+
+  SSL_CTX_free(ssl_ctx);
+}
+
+TEST(OpenSSLSessionCache, InvalidLookupReturnsNullptr) {
+  SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+
+  OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
+  EXPECT_EQ(session_cache.LookupSession("Invalid"), nullptr);
+  EXPECT_EQ(session_cache.LookupSession(""), nullptr);
+  EXPECT_EQ(session_cache.LookupSession("."), nullptr);
+
+  SSL_CTX_free(ssl_ctx);
+}
+
+TEST(OpenSSLSessionCache, SimpleValidSessionLookup) {
+  SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+  SSL_SESSION* ssl_session = SSL_SESSION_new(ssl_ctx);
+
+  OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
+  session_cache.AddSession("webrtc.org", ssl_session);
+  EXPECT_EQ(session_cache.LookupSession("webrtc.org"), ssl_session);
+
+  SSL_CTX_free(ssl_ctx);
+}
+
+TEST(OpenSSLSessionCache, AddToExistingReplacesPrevious) {
+  SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+  SSL_SESSION* ssl_session_1 = SSL_SESSION_new(ssl_ctx);
+  SSL_SESSION* ssl_session_2 = SSL_SESSION_new(ssl_ctx);
+
+  OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
+  session_cache.AddSession("webrtc.org", ssl_session_1);
+  session_cache.AddSession("webrtc.org", ssl_session_2);
+  EXPECT_EQ(session_cache.LookupSession("webrtc.org"), ssl_session_2);
+
+  SSL_CTX_free(ssl_ctx);
+}
+
+}  // namespace rtc
diff --git a/rtc_base/opensslstreamadapter.h b/rtc_base/opensslstreamadapter.h
index 97ab557..7a6e099 100644
--- a/rtc_base/opensslstreamadapter.h
+++ b/rtc_base/opensslstreamadapter.h
@@ -11,6 +11,8 @@
 #ifndef RTC_BASE_OPENSSLSTREAMADAPTER_H_
 #define RTC_BASE_OPENSSLSTREAMADAPTER_H_
 
+#include <openssl/ossl_typ.h>
+
 #include <string>
 #include <memory>
 #include <vector>
@@ -19,11 +21,6 @@
 #include "rtc_base/opensslidentity.h"
 #include "rtc_base/sslstreamadapter.h"
 
-typedef struct ssl_st SSL;
-typedef struct ssl_ctx_st SSL_CTX;
-typedef struct ssl_cipher_st SSL_CIPHER;
-typedef struct x509_store_ctx_st X509_STORE_CTX;
-
 namespace rtc {
 
 // This class was written with OpenSSLAdapter (a socket adapter) as a
diff --git a/rtc_base/physicalsocketserver_unittest.cc b/rtc_base/physicalsocketserver_unittest.cc
index d09385b..81f1c9d 100644
--- a/rtc_base/physicalsocketserver_unittest.cc
+++ b/rtc_base/physicalsocketserver_unittest.cc
@@ -201,13 +201,13 @@
       server_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
   sink.Monitor(client1.get());
   EXPECT_EQ(AsyncSocket::CS_CLOSED, client1->GetState());
-  EXPECT_PRED1(IsUnspecOrEmptyIP, client1->GetLocalAddress().ipaddr());
+  EXPECT_TRUE(IsUnspecOrEmptyIP(client1->GetLocalAddress().ipaddr()));
 
   std::unique_ptr<AsyncSocket> client2(
       server_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
   sink.Monitor(client2.get());
   EXPECT_EQ(AsyncSocket::CS_CLOSED, client2->GetState());
-  EXPECT_PRED1(IsUnspecOrEmptyIP, client2->GetLocalAddress().ipaddr());
+  EXPECT_TRUE(IsUnspecOrEmptyIP(client2->GetLocalAddress().ipaddr()));
 
   // Create server and listen.
   std::unique_ptr<AsyncSocket> server(
diff --git a/rtc_base/platform_file.cc b/rtc_base/platform_file.cc
index 35a2622..a4c906a 100644
--- a/rtc_base/platform_file.cc
+++ b/rtc_base/platform_file.cc
@@ -23,17 +23,21 @@
 
 namespace rtc {
 
+FILE* FdopenPlatformFileForWriting(PlatformFile file) {
+  return FdopenPlatformFile(file, "w");
+}
+
 #if defined(WEBRTC_WIN)
 const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE;
 
-FILE* FdopenPlatformFileForWriting(PlatformFile file) {
+FILE* FdopenPlatformFile(PlatformFile file, const char* modes) {
   if (file == kInvalidPlatformFileValue)
     return nullptr;
   int fd = _open_osfhandle(reinterpret_cast<intptr_t>(file), 0);
   if (fd < 0)
     return nullptr;
 
-  return _fdopen(fd, "w");
+  return _fdopen(fd, modes);
 }
 
 bool ClosePlatformFile(PlatformFile file) {
@@ -49,6 +53,11 @@
                       nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
 }
 
+PlatformFile OpenPlatformFileReadOnly(const std::string& path) {
+  return ::CreateFile(ToUtf16(path).c_str(), GENERIC_READ, FILE_SHARE_READ,
+                      nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+}
+
 PlatformFile CreatePlatformFile(const std::string& path) {
   return ::CreateFile(ToUtf16(path).c_str(), GENERIC_READ | GENERIC_WRITE, 0,
                       nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
@@ -58,8 +67,8 @@
 
 const PlatformFile kInvalidPlatformFileValue = -1;
 
-FILE* FdopenPlatformFileForWriting(PlatformFile file) {
-  return fdopen(file, "w");
+FILE* FdopenPlatformFile(PlatformFile file, const char* modes) {
+  return fdopen(file, modes);
 }
 
 bool ClosePlatformFile(PlatformFile file) {
@@ -74,6 +83,10 @@
   return ::open(path.c_str(), O_RDWR);
 }
 
+PlatformFile OpenPlatformFileReadOnly(const std::string& path) {
+  return ::open(path.c_str(), O_RDONLY);
+}
+
 PlatformFile CreatePlatformFile(const std::string& path) {
   return ::open(path.c_str(), O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
 }
diff --git a/rtc_base/platform_file.h b/rtc_base/platform_file.h
index 8e911be..52fbaff 100644
--- a/rtc_base/platform_file.h
+++ b/rtc_base/platform_file.h
@@ -35,6 +35,11 @@
 // the PlatformFile should no longer be used.
 FILE* FdopenPlatformFileForWriting(PlatformFile file);
 
+// Associates a standard FILE stream with an existing PlatformFile.
+// Note that after this function has returned a valid FILE stream,
+// the PlatformFile should no longer be used.
+FILE* FdopenPlatformFile(PlatformFile file, const char* modes);
+
 // Closes a PlatformFile. Returns true on success, false on failure.
 // Don't use ClosePlatformFile to close a file opened with FdopenPlatformFile.
 // Use fclose instead.
@@ -47,6 +52,10 @@
 // instead.
 PlatformFile OpenPlatformFile(const std::string& path);
 
+// Opens a file for reading only. You might want to use base/file.h
+// instead.
+PlatformFile OpenPlatformFileReadOnly(const std::string& path);
+
 // Creates a new file for reading and writing. If the file already exists it
 // will be overwritten. You might want to use base/file.h instead.
 PlatformFile CreatePlatformFile(const std::string& path);
diff --git a/rtc_base/platform_file_unittest.cc b/rtc_base/platform_file_unittest.cc
new file mode 100644
index 0000000..4e72400
--- /dev/null
+++ b/rtc_base/platform_file_unittest.cc
@@ -0,0 +1,77 @@
+/*
+ *  Copyright (c) 2018 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 "rtc_base/platform_file.h"
+#include "test/gtest.h"
+#include "test/testsupport/fileutils.h"
+
+namespace rtc {
+
+void FillWithDummyDataAndClose(FILE* const file, const std::string& filename) {
+  EXPECT_GT(fprintf(file, "%s", "Dummy data"), 0)
+      << "Failed to write to file: " << filename;
+  fclose(file);
+}
+
+TEST(PlatformFileTest, CreateWriteAndDelete) {
+  const std::string filename = webrtc::test::GenerateTempFilename(
+      webrtc::test::OutputPath(), ".testfile");
+  const PlatformFile fd = rtc::CreatePlatformFile(filename);
+  ASSERT_NE(fd, rtc::kInvalidPlatformFileValue)
+      << "Failed to create file descriptor for file: " << filename;
+  FILE* const file = rtc::FdopenPlatformFile(fd, "w");
+  ASSERT_TRUE(file != nullptr) << "Failed to open file: " << filename;
+  FillWithDummyDataAndClose(file, filename);
+  webrtc::test::RemoveFile(filename);
+}
+
+TEST(PlatformFileTest, OpenExistingWriteAndDelete) {
+  const std::string filename = webrtc::test::GenerateTempFilename(
+      webrtc::test::OutputPath(), ".testfile");
+
+  // Create file with dummy data.
+  FILE* file = fopen(filename.c_str(), "wb");
+  ASSERT_TRUE(file != nullptr) << "Failed to open file: " << filename;
+  FillWithDummyDataAndClose(file, filename);
+
+  // Open it for write, write and delete.
+  const PlatformFile fd = rtc::OpenPlatformFile(filename);
+  ASSERT_NE(fd, rtc::kInvalidPlatformFileValue)
+      << "Failed to open file descriptor for file: " << filename;
+  file = rtc::FdopenPlatformFile(fd, "w");
+  ASSERT_TRUE(file != nullptr) << "Failed to open file: " << filename;
+  FillWithDummyDataAndClose(file, filename);
+  webrtc::test::RemoveFile(filename);
+}
+
+TEST(PlatformFileTest, OpenExistingReadOnlyAndDelete) {
+  const std::string filename = webrtc::test::GenerateTempFilename(
+      webrtc::test::OutputPath(), ".testfile");
+
+  // Create file with dummy data.
+  FILE* file = fopen(filename.c_str(), "wb");
+  ASSERT_TRUE(file != nullptr) << "Failed to open file: " << filename;
+  FillWithDummyDataAndClose(file, filename);
+
+  // Open it for read, read and delete.
+  const PlatformFile fd = rtc::OpenPlatformFileReadOnly(filename);
+  ASSERT_NE(fd, rtc::kInvalidPlatformFileValue)
+      << "Failed to open file descriptor for file: " << filename;
+  file = rtc::FdopenPlatformFile(fd, "r");
+  ASSERT_TRUE(file != nullptr) << "Failed to open file: " << filename;
+
+  int buf[]{0};
+  ASSERT_GT(fread(&buf, 1, 1, file), 0u)
+      << "Failed to read from file: " << filename;
+  fclose(file);
+  webrtc::test::RemoveFile(filename);
+}
+
+}  // namespace rtc
diff --git a/rtc_base/platform_thread.cc b/rtc_base/platform_thread.cc
index ca2ce13..79d9d53 100644
--- a/rtc_base/platform_thread.cc
+++ b/rtc_base/platform_thread.cc
@@ -13,7 +13,6 @@
 #include "rtc_base/atomicops.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/timeutils.h"
-#include "rtc_base/trace_event.h"
 
 #if defined(WEBRTC_LINUX)
 #include <sys/prctl.h>
@@ -178,8 +177,6 @@
 #endif
 
   do {
-    TRACE_EVENT1("webrtc", "PlatformThread::Run", "name", name_.c_str());
-
     // The interface contract of Start/Stop is that for a successful call to
     // Start, there should be at least one call to the run function.  So we
     // call the function before checking |stop_|.
diff --git a/rtc_base/ratelimiter.h b/rtc_base/ratelimiter.h
deleted file mode 100644
index 8aa84aa..0000000
--- a/rtc_base/ratelimiter.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- *  Copyright 2018 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 RTC_BASE_RATELIMITER_H_
-#define RTC_BASE_RATELIMITER_H_
-
-#include "rtc_base/data_rate_limiter.h"
-
-namespace rtc {
-// Deprecated, use DataRateLimiter instead
-class RateLimiter : public DataRateLimiter {
- public:
-  using DataRateLimiter::DataRateLimiter;
-};
-}  // namespace rtc
-
-#endif  // RTC_BASE_RATELIMITER_H_
diff --git a/rtc_base/sanitizer.h b/rtc_base/sanitizer.h
index 1b94e1e..23a748f 100644
--- a/rtc_base/sanitizer.h
+++ b/rtc_base/sanitizer.h
@@ -11,7 +11,11 @@
 #ifndef RTC_BASE_SANITIZER_H_
 #define RTC_BASE_SANITIZER_H_
 
-#include <stddef.h>  // for size_t
+#include <stddef.h>  // For size_t.
+
+#ifdef __cplusplus
+#include <type_traits>
+#endif
 
 #if defined(__has_feature)
 #if __has_feature(address_sanitizer)
@@ -90,6 +94,17 @@
 #ifdef __cplusplus
 
 namespace rtc {
+namespace sanitizer_impl {
+
+template <typename T>
+constexpr bool IsTriviallyCopyable() {
+  return static_cast<bool>(std::is_trivially_copy_constructible<T>::value &&
+                           (std::is_trivially_copy_assignable<T>::value ||
+                            !std::is_copy_assignable<T>::value) &&
+                           std::is_trivially_destructible<T>::value);
+}
+
+}  // namespace sanitizer_impl
 
 template <typename T>
 inline void AsanPoison(const T& mem) {
@@ -107,6 +122,15 @@
 }
 
 template <typename T>
+inline T MsanUninitialized(T t) {
+  // TODO(bugs.webrtc.org/8762): Switch to std::is_trivially_copyable when it
+  // becomes available in downstream projects.
+  static_assert(sanitizer_impl::IsTriviallyCopyable<T>(), "");
+  rtc_MsanMarkUninitialized(&t, sizeof(T), 1);
+  return t;
+}
+
+template <typename T>
 inline void MsanCheckInitialized(const T& mem) {
   rtc_MsanCheckInitialized(mem.data(), sizeof(mem.data()[0]), mem.size());
 }
diff --git a/rtc_base/sanitizer_unittest.cc b/rtc_base/sanitizer_unittest.cc
new file mode 100644
index 0000000..21ef432
--- /dev/null
+++ b/rtc_base/sanitizer_unittest.cc
@@ -0,0 +1,158 @@
+/*
+ *  Copyright 2018 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 "rtc_base/sanitizer.h"
+
+#include "rtc_base/gunit.h"
+#include "rtc_base/logging.h"
+
+#if RTC_HAS_MSAN
+#include <sanitizer/msan_interface.h>
+#endif
+
+namespace rtc {
+namespace {
+
+// Test sanitizer_impl::IsTriviallyCopyable (at compile time).
+
+// Trivially copyable.
+
+struct TrTrTr {
+  TrTrTr(const TrTrTr&) = default;
+  TrTrTr& operator=(const TrTrTr&) = default;
+  ~TrTrTr() = default;
+};
+static_assert(sanitizer_impl::IsTriviallyCopyable<TrTrTr>(), "");
+
+struct TrDeTr {
+  TrDeTr(const TrDeTr&) = default;
+  TrDeTr& operator=(const TrDeTr&) = delete;
+  ~TrDeTr() = default;
+};
+static_assert(sanitizer_impl::IsTriviallyCopyable<TrDeTr>(), "");
+
+// Non trivially copyable.
+
+struct TrTrNt {
+  TrTrNt(const TrTrNt&) = default;
+  TrTrNt& operator=(const TrTrNt&) = default;
+  ~TrTrNt();
+};
+static_assert(!sanitizer_impl::IsTriviallyCopyable<TrTrNt>(), "");
+
+struct TrNtTr {
+  TrNtTr(const TrNtTr&) = default;
+  TrNtTr& operator=(const TrNtTr&);
+  ~TrNtTr() = default;
+};
+static_assert(!sanitizer_impl::IsTriviallyCopyable<TrNtTr>(), "");
+
+struct TrNtNt {
+  TrNtNt(const TrNtNt&) = default;
+  TrNtNt& operator=(const TrNtNt&);
+  ~TrNtNt();
+};
+static_assert(!sanitizer_impl::IsTriviallyCopyable<TrNtNt>(), "");
+
+struct TrDeNt {
+  TrDeNt(const TrDeNt&) = default;
+  TrDeNt& operator=(const TrDeNt&) = delete;
+  ~TrDeNt();
+};
+static_assert(!sanitizer_impl::IsTriviallyCopyable<TrDeNt>(), "");
+
+struct NtTrTr {
+  NtTrTr(const NtTrTr&);
+  NtTrTr& operator=(const NtTrTr&) = default;
+  ~NtTrTr() = default;
+};
+static_assert(!sanitizer_impl::IsTriviallyCopyable<NtTrTr>(), "");
+
+struct NtTrNt {
+  NtTrNt(const NtTrNt&);
+  NtTrNt& operator=(const NtTrNt&) = default;
+  ~NtTrNt();
+};
+static_assert(!sanitizer_impl::IsTriviallyCopyable<NtTrNt>(), "");
+
+struct NtNtTr {
+  NtNtTr(const NtNtTr&);
+  NtNtTr& operator=(const NtNtTr&);
+  ~NtNtTr() = default;
+};
+static_assert(!sanitizer_impl::IsTriviallyCopyable<NtNtTr>(), "");
+
+struct NtNtNt {
+  NtNtNt(const NtNtNt&);
+  NtNtNt& operator=(const NtNtNt&);
+  ~NtNtNt();
+};
+static_assert(!sanitizer_impl::IsTriviallyCopyable<NtNtNt>(), "");
+
+struct NtDeTr {
+  NtDeTr(const NtDeTr&);
+  NtDeTr& operator=(const NtDeTr&) = delete;
+  ~NtDeTr() = default;
+};
+static_assert(!sanitizer_impl::IsTriviallyCopyable<NtDeTr>(), "");
+
+struct NtDeNt {
+  NtDeNt(const NtDeNt&);
+  NtDeNt& operator=(const NtDeNt&) = delete;
+  ~NtDeNt();
+};
+static_assert(!sanitizer_impl::IsTriviallyCopyable<NtDeNt>(), "");
+
+// Trivially copyable types.
+
+struct Foo {
+  uint32_t field1;
+  uint16_t field2;
+};
+
+struct Bar {
+  uint32_t ID;
+  Foo foo;
+};
+
+// Run the callback, and crash if it *doesn't* make an uninitialized memory
+// read. If MSan isn't on, just run the callback.
+template <typename F>
+void MsanExpectUninitializedRead(F&& f) {
+#if RTC_HAS_MSAN
+  // Allow uninitialized memory reads.
+  RTC_LOG(LS_INFO) << "__msan_set_expect_umr(1)";
+  __msan_set_expect_umr(1);
+#endif
+  f();
+#if RTC_HAS_MSAN
+  // Disallow uninitialized memory reads again, and verify that at least
+  // one uninitialized memory read happened while we weren't looking.
+  RTC_LOG(LS_INFO) << "__msan_set_expect_umr(0)";
+  __msan_set_expect_umr(0);
+#endif
+}
+
+}  // namespace
+
+// TODO(b/9116): Enable the test when the bug is fixed.
+TEST(SanitizerTest, DISABLED_MsanUninitialized) {
+  Bar bar = MsanUninitialized<Bar>({});
+  // Check that a read after initialization is OK.
+  bar.ID = 1;
+  EXPECT_EQ(1u, bar.ID);
+  RTC_LOG(LS_INFO) << "read after init passed";
+  // Check that other fields are uninitialized and equal to zero.
+  MsanExpectUninitializedRead([&] { EXPECT_EQ(0u, bar.foo.field1); });
+  MsanExpectUninitializedRead([&] { EXPECT_EQ(0u, bar.foo.field2); });
+  RTC_LOG(LS_INFO) << "read with no init passed";
+}
+
+}  // namespace rtc
diff --git a/rtc_base/scoped_ref_ptr.h b/rtc_base/scoped_ref_ptr.h
index 0f4698a..8fefc73 100644
--- a/rtc_base/scoped_ref_ptr.h
+++ b/rtc_base/scoped_ref_ptr.h
@@ -103,11 +103,11 @@
   operator T*() const { return ptr_; }
   T* operator->() const { return ptr_; }
 
-  // Release a pointer.
-  // The return value is the current pointer held by this object.
-  // If this object holds a null pointer, the return value is null.
-  // After this operation, this object will hold a null pointer,
-  // and will not own the object any more.
+  // Returns the (possibly null) raw pointer, and makes the scoped_refptr hold a
+  // null pointer, all without touching the reference count of the underlying
+  // pointed-to object. The object is still reference counted, and the caller of
+  // release() is now the proud owner of one reference, so it is responsible for
+  // calling Release() once on the object when no longer using it.
   T* release() {
     T* retVal = ptr_;
     ptr_ = nullptr;
diff --git a/rtc_base/sequenced_task_checker_impl.cc b/rtc_base/sequenced_task_checker_impl.cc
index d7f46ea..16069c2 100644
--- a/rtc_base/sequenced_task_checker_impl.cc
+++ b/rtc_base/sequenced_task_checker_impl.cc
@@ -14,7 +14,6 @@
 #include <dispatch/dispatch.h>
 #endif
 
-#include "rtc_base/platform_thread.h"
 #include "rtc_base/sequenced_task_checker.h"
 #include "rtc_base/task_queue.h"
 
diff --git a/rtc_base/sequenced_task_checker_impl.h b/rtc_base/sequenced_task_checker_impl.h
index 86d5ef0..293b1ac 100644
--- a/rtc_base/sequenced_task_checker_impl.h
+++ b/rtc_base/sequenced_task_checker_impl.h
@@ -11,6 +11,7 @@
 #ifndef RTC_BASE_SEQUENCED_TASK_CHECKER_IMPL_H_
 #define RTC_BASE_SEQUENCED_TASK_CHECKER_IMPL_H_
 
+#include "rtc_base/criticalsection.h"
 #include "rtc_base/thread_checker.h"
 
 namespace rtc {
diff --git a/rtc_base/socket.cc b/rtc_base/socket.cc
new file mode 100644
index 0000000..a9749a4
--- /dev/null
+++ b/rtc_base/socket.cc
@@ -0,0 +1,27 @@
+/*
+ *  Copyright 2018 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 "rtc_base/socket.h"
+
+namespace rtc {
+
+PacketInfo::PacketInfo() = default;
+PacketInfo::PacketInfo(const PacketInfo& info) = default;
+PacketInfo::~PacketInfo() = default;
+
+SentPacket::SentPacket() = default;
+SentPacket::SentPacket(int64_t packet_id, int64_t send_time_ms)
+    : packet_id(packet_id), send_time_ms(send_time_ms) {}
+SentPacket::SentPacket(int64_t packet_id,
+                       int64_t send_time_ms,
+                       const rtc::PacketInfo& info)
+    : packet_id(packet_id), send_time_ms(send_time_ms), info(info) {}
+
+}  // namespace rtc
diff --git a/rtc_base/socket.h b/rtc_base/socket.h
index ca1a302..ee2c73f 100644
--- a/rtc_base/socket.h
+++ b/rtc_base/socket.h
@@ -25,6 +25,7 @@
 #include "rtc_base/win32.h"
 #endif
 
+#include "api/optional.h"
 #include "rtc_base/basictypes.h"
 #include "rtc_base/constructormagic.h"
 #include "rtc_base/socketaddress.h"
@@ -123,13 +124,48 @@
   return (e == EWOULDBLOCK) || (e == EAGAIN) || (e == EINPROGRESS);
 }
 
-struct SentPacket {
-  SentPacket() : packet_id(-1), send_time_ms(-1) {}
-  SentPacket(int packet_id, int64_t send_time_ms)
-      : packet_id(packet_id), send_time_ms(send_time_ms) {}
+enum class PacketType {
+  kUnknown,
+  kData,
+  kIceConnectivityCheck,
+  kIceConnectivityCheckResponse,
+  kStunMessage,
+  kTurnMessage,
+};
 
-  int packet_id;
-  int64_t send_time_ms;
+enum class PacketInfoProtocolType {
+  kUnknown,
+  kUdp,
+  kTcp,
+  kSsltcp,
+  kTls,
+};
+
+struct PacketInfo {
+  PacketInfo();
+  PacketInfo(const PacketInfo& info);
+  ~PacketInfo();
+
+  PacketType packet_type = PacketType::kUnknown;
+  PacketInfoProtocolType protocol = PacketInfoProtocolType::kUnknown;
+  // A unique id assigned by the network manager, and rtc::nullopt if not set.
+  rtc::Optional<uint16_t> network_id;
+  size_t packet_size_bytes = 0;
+  size_t turn_overhead_bytes = 0;
+  SocketAddress local_socket_address;
+  SocketAddress remote_socket_address;
+};
+
+struct SentPacket {
+  SentPacket();
+  SentPacket(int64_t packet_id, int64_t send_time_ms);
+  SentPacket(int64_t packet_id,
+             int64_t send_time_ms,
+             const rtc::PacketInfo& info);
+
+  int64_t packet_id = -1;
+  int64_t send_time_ms = -1;
+  rtc::PacketInfo info;
 };
 
 // General interface for the socket implementations of various networks.  The
diff --git a/rtc_base/socket_unittest.cc b/rtc_base/socket_unittest.cc
index a31cc02..be958d3 100644
--- a/rtc_base/socket_unittest.cc
+++ b/rtc_base/socket_unittest.cc
@@ -224,7 +224,7 @@
       ss_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
   sink.Monitor(client.get());
   EXPECT_EQ(AsyncSocket::CS_CLOSED, client->GetState());
-  EXPECT_PRED1(IsUnspecOrEmptyIP, client->GetLocalAddress().ipaddr());
+  EXPECT_TRUE(IsUnspecOrEmptyIP(client->GetLocalAddress().ipaddr()));
 
   // Create server and listen.
   std::unique_ptr<AsyncSocket> server(
diff --git a/rtc_base/socketaddress.cc b/rtc_base/socketaddress.cc
index 54a41d4..a58c001 100644
--- a/rtc_base/socketaddress.cc
+++ b/rtc_base/socketaddress.cc
@@ -26,12 +26,11 @@
 #include <unistd.h>
 #endif
 
-#include <sstream>
-
 #include "rtc_base/byteorder.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/nethelpers.h"
+#include "rtc_base/strings/string_builder.h"
 
 #if defined(WEBRTC_WIN)
 #include "rtc_base/win32.h"
@@ -161,21 +160,21 @@
 }
 
 std::string SocketAddress::PortAsString() const {
-  std::ostringstream ost;
-  ost << port_;
-  return ost.str();
+  return std::to_string(port_);
 }
 
 std::string SocketAddress::ToString() const {
-  std::ostringstream ost;
-  ost << *this;
-  return ost.str();
+  char buf[1024];
+  rtc::SimpleStringBuilder sb(buf);
+  sb << HostAsURIString() << ":" << port();
+  return sb.str();
 }
 
 std::string SocketAddress::ToSensitiveString() const {
-  std::ostringstream ost;
-  ost << HostAsSensitiveURIString() << ":" << port();
-  return ost.str();
+  char buf[1024];
+  rtc::SimpleStringBuilder sb(buf);
+  sb << HostAsSensitiveURIString() << ":" << port();
+  return sb.str();
 }
 
 bool SocketAddress::FromString(const std::string& str) {
@@ -200,11 +199,6 @@
   return true;
 }
 
-std::ostream& operator<<(std::ostream& os, const SocketAddress& addr) {
-  os << addr.HostAsURIString() << ":" << addr.port();
-  return os;
-}
-
 bool SocketAddress::IsAnyIP() const {
   return IPIsAny(ip_);
 }
diff --git a/rtc_base/socketaddress.h b/rtc_base/socketaddress.h
index d58eed8..90919c2 100644
--- a/rtc_base/socketaddress.h
+++ b/rtc_base/socketaddress.h
@@ -13,6 +13,9 @@
 
 #include <iosfwd>
 #include <string>
+#ifdef UNIT_TEST
+#include <ostream>  // no-presubmit-check TODO(webrtc:8982)
+#endif              // UNIT_TEST
 #include <vector>
 #include "rtc_base/basictypes.h"
 #include "rtc_base/ipaddress.h"
@@ -126,7 +129,12 @@
   // Parses hostname:port and [hostname]:port.
   bool FromString(const std::string& str);
 
-  friend std::ostream& operator<<(std::ostream& os, const SocketAddress& addr);
+#ifdef UNIT_TEST
+  inline std::ostream& operator<<(  // no-presubmit-check TODO(webrtc:8982)
+      std::ostream& os) {           // no-presubmit-check TODO(webrtc:8982)
+    return os << HostAsURIString() << ":" << port();
+  }
+#endif  // UNIT_TEST
 
   // Determines whether this represents a missing / any IP address.
   // That is, 0.0.0.0 or ::.
diff --git a/rtc_base/socketaddress_unittest.cc b/rtc_base/socketaddress_unittest.cc
index 0d168df..fb195b6 100644
--- a/rtc_base/socketaddress_unittest.cc
+++ b/rtc_base/socketaddress_unittest.cc
@@ -255,34 +255,34 @@
 TEST(SocketAddressTest, TestEqualityOperators) {
   SocketAddress addr1("1.2.3.4", 5678);
   SocketAddress addr2("1.2.3.4", 5678);
-  EXPECT_PRED2(AreEqual, addr1, addr2);
+  EXPECT_TRUE(AreEqual(addr1, addr2));
 
   addr2 = SocketAddress("0.0.0.1", 5678);
-  EXPECT_PRED2(AreUnequal, addr1, addr2);
+  EXPECT_TRUE(AreUnequal(addr1, addr2));
 
   addr2 = SocketAddress("1.2.3.4", 1234);
-  EXPECT_PRED2(AreUnequal, addr1, addr2);
+  EXPECT_TRUE(AreUnequal(addr1, addr2));
 
   addr2 = SocketAddress(kTestV6AddrString, 5678);
-  EXPECT_PRED2(AreUnequal, addr1, addr2);
+  EXPECT_TRUE(AreUnequal(addr1, addr2));
 
   addr1 = SocketAddress(kTestV6AddrString, 5678);
-  EXPECT_PRED2(AreEqual, addr1, addr2);
+  EXPECT_TRUE(AreEqual(addr1, addr2));
 
   addr2 = SocketAddress(kTestV6AddrString, 1234);
-  EXPECT_PRED2(AreUnequal, addr1, addr2);
+  EXPECT_TRUE(AreUnequal(addr1, addr2));
 
   addr2 = SocketAddress("fe80::1", 5678);
-  EXPECT_PRED2(AreUnequal, addr1, addr2);
+  EXPECT_TRUE(AreUnequal(addr1, addr2));
 
   SocketAddress addr3("a.b.c.d", 1);
   SocketAddress addr4("b.b.c.d", 1);
-  EXPECT_PRED2(AreUnequal, addr3, addr4);
-  EXPECT_PRED2(AreEqual, addr3, addr3);
+  EXPECT_TRUE(AreUnequal(addr3, addr4));
+  EXPECT_TRUE(AreEqual(addr3, addr3));
 
   addr3.SetIP(addr1.ip());
   addr4.SetIP(addr1.ip());
-  EXPECT_PRED2(AreEqual,addr3, addr4);
+  EXPECT_TRUE(AreEqual(addr3, addr4));
 }
 
 bool IsLessThan(const SocketAddress& addr1, const SocketAddress& addr2) {
@@ -299,19 +299,19 @@
   EXPECT_FALSE(addr2 < addr1);
 
   addr2 = SocketAddress("1.2.3.4", 5679);
-  EXPECT_PRED2(IsLessThan, addr1, addr2);
+  EXPECT_TRUE(IsLessThan(addr1, addr2));
 
   addr2 = SocketAddress("2.2.3.4", 49152);
-  EXPECT_PRED2(IsLessThan, addr1, addr2);
+  EXPECT_TRUE(IsLessThan(addr1, addr2));
 
   addr2 = SocketAddress(kTestV6AddrString, 5678);
-  EXPECT_PRED2(IsLessThan, addr1, addr2);
+  EXPECT_TRUE(IsLessThan(addr1, addr2));
 
   addr1 = SocketAddress("fe80::1", 5678);
-  EXPECT_PRED2(IsLessThan, addr2, addr1);
+  EXPECT_TRUE(IsLessThan(addr2, addr1));
 
   addr2 = SocketAddress("fe80::1", 5679);
-  EXPECT_PRED2(IsLessThan, addr1, addr2);
+  EXPECT_TRUE(IsLessThan(addr1, addr2));
 
   addr2 = SocketAddress("fe80::1", 5678);
   EXPECT_FALSE(addr1 < addr2);
@@ -319,7 +319,7 @@
 
   SocketAddress addr3("a.b.c.d", 1);
   SocketAddress addr4("b.b.c.d", 1);
-  EXPECT_PRED2(IsLessThan, addr3, addr4);
+  EXPECT_TRUE(IsLessThan(addr3, addr4));
 }
 
 TEST(SocketAddressTest, TestToSensitiveString) {
diff --git a/rtc_base/ssladapter_unittest.cc b/rtc_base/ssladapter_unittest.cc
index 0996b01..c15ecfe 100644
--- a/rtc_base/ssladapter_unittest.cc
+++ b/rtc_base/ssladapter_unittest.cc
@@ -81,7 +81,7 @@
   }
 
   int Connect(const std::string& hostname, const rtc::SocketAddress& address) {
-    RTC_LOG(LS_INFO) << "Initiating connection with " << address;
+    RTC_LOG(LS_INFO) << "Initiating connection with " << address.ToString();
 
     int rv = ssl_adapter_->Connect(address);
 
@@ -157,7 +157,7 @@
 
     RTC_LOG(LS_INFO) << ((ssl_mode_ == rtc::SSL_MODE_DTLS) ? "UDP" : "TCP")
                      << " server listening on "
-                     << server_socket_->GetLocalAddress();
+                     << server_socket_->GetLocalAddress().ToString();
   }
 
   rtc::SocketAddress GetAddress() const {
diff --git a/rtc_base/sslstreamadapter.cc b/rtc_base/sslstreamadapter.cc
index b09c144..d52dc45 100644
--- a/rtc_base/sslstreamadapter.cc
+++ b/rtc_base/sslstreamadapter.cc
@@ -105,7 +105,11 @@
   // Note: SRTP_AES128_CM_SHA1_80 is what is required to be supported (by
   // draft-ietf-rtcweb-security-arch), but SRTP_AES128_CM_SHA1_32 is allowed as
   // well, and saves a few bytes per packet if it ends up selected.
-  crypto_suites.push_back(rtc::SRTP_AES128_CM_SHA1_32);
+  // As the cipher suite is potentially insecure, it will only be used if
+  // enabled by both peers.
+  if (crypto_options.enable_aes128_sha1_32_crypto_cipher) {
+    crypto_suites.push_back(rtc::SRTP_AES128_CM_SHA1_32);
+  }
   crypto_suites.push_back(rtc::SRTP_AES128_CM_SHA1_80);
   return crypto_suites;
 }
diff --git a/rtc_base/sslstreamadapter.h b/rtc_base/sslstreamadapter.h
index c04fb34..827dc45 100644
--- a/rtc_base/sslstreamadapter.h
+++ b/rtc_base/sslstreamadapter.h
@@ -80,6 +80,12 @@
   // if both sides enable it.
   bool enable_gcm_crypto_suites = false;
 
+  // If set to true, the (potentially insecure) crypto cipher
+  // SRTP_AES128_CM_SHA1_32 will be included in the list of supported ciphers
+  // during negotiation. It will only be used if both peers support it and no
+  // other ciphers get preferred.
+  bool enable_aes128_sha1_32_crypto_cipher = false;
+
   // If set to true, encrypted RTP header extensions as defined in RFC 6904
   // will be negotiated. They will only be used if both peers support them.
   bool enable_encrypted_rtp_header_extensions = false;
diff --git a/rtc_base/string_to_number.cc b/rtc_base/string_to_number.cc
index ad49d64..fe17f34 100644
--- a/rtc_base/string_to_number.cc
+++ b/rtc_base/string_to_number.cc
@@ -8,10 +8,12 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
+#include "rtc_base/string_to_number.h"
+
 #include <cerrno>
 #include <cstdlib>
 
-#include "rtc_base/string_to_number.h"
+#include "rtc_base/checks.h"
 
 namespace rtc {
 namespace string_to_number_internal {
diff --git a/rtc_base/string_to_number_unittest.cc b/rtc_base/string_to_number_unittest.cc
index f5e5b57..538ee10 100644
--- a/rtc_base/string_to_number_unittest.cc
+++ b/rtc_base/string_to_number_unittest.cc
@@ -42,15 +42,16 @@
   using T = TypeParam;
   constexpr T min_value = std::numeric_limits<T>::lowest();
   constexpr T max_value = std::numeric_limits<T>::max();
+  constexpr T zero_value = 0;
   const std::string min_string = std::to_string(min_value);
   const std::string max_string = std::to_string(max_value);
   EXPECT_EQ(min_value, StringToNumber<T>(min_string));
   EXPECT_EQ(min_value, StringToNumber<T>(min_string.c_str()));
   EXPECT_EQ(max_value, StringToNumber<T>(max_string));
   EXPECT_EQ(max_value, StringToNumber<T>(max_string.c_str()));
-  EXPECT_EQ(0, StringToNumber<T>("0"));
-  EXPECT_EQ(0, StringToNumber<T>("-0"));
-  EXPECT_EQ(0, StringToNumber<T>(std::string("-0000000000000")));
+  EXPECT_EQ(zero_value, StringToNumber<T>("0"));
+  EXPECT_EQ(zero_value, StringToNumber<T>("-0"));
+  EXPECT_EQ(zero_value, StringToNumber<T>(std::string("-0000000000000")));
 }
 
 TYPED_TEST_P(BasicNumberTest, TestInvalidNumbers) {
diff --git a/rtc_base/stringencode.cc b/rtc_base/stringencode.cc
index 755cb2c..f2a1508 100644
--- a/rtc_base/stringencode.cc
+++ b/rtc_base/stringencode.cc
@@ -139,7 +139,7 @@
 size_t hex_encode_with_delimiter(char* buffer, size_t buflen,
                                  const char* csource, size_t srclen,
                                  char delimiter) {
-  RTC_DCHECK(buffer);  // TODO(grunell): estimate output size
+  RTC_DCHECK(buffer);  // TODO(kwiberg): estimate output size
   if (buflen == 0)
     return 0;
 
@@ -195,7 +195,7 @@
 size_t hex_decode_with_delimiter(char* cbuffer, size_t buflen,
                                  const char* source, size_t srclen,
                                  char delimiter) {
-  RTC_DCHECK(cbuffer);  // TODO(grunell): estimate output size
+  RTC_DCHECK(cbuffer);  // TODO(kwiberg): estimate output size
   if (buflen == 0)
     return 0;
 
diff --git a/rtc_base/strings/audio_format_to_string.cc b/rtc_base/strings/audio_format_to_string.cc
new file mode 100644
index 0000000..2d0b53b
--- /dev/null
+++ b/rtc_base/strings/audio_format_to_string.cc
@@ -0,0 +1,52 @@
+/*
+ *  Copyright 2018 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 "rtc_base/strings/audio_format_to_string.h"
+
+#include "rtc_base/strings/string_builder.h"
+
+namespace rtc {
+  std::string ToString(const webrtc::SdpAudioFormat& saf) {
+    char sb_buf[1024];
+    rtc::SimpleStringBuilder sb(sb_buf);
+    sb << "{name: " << saf.name;
+    sb << ", clockrate_hz: " << saf.clockrate_hz;
+    sb << ", num_channels: " << saf.num_channels;
+    sb << ", parameters: {";
+    const char* sep = "";
+    for (const auto& kv : saf.parameters) {
+      sb << sep << kv.first << ": " << kv.second;
+      sep = ", ";
+    }
+    sb << "}}";
+    return sb.str();
+  }
+  std::string ToString(const webrtc::AudioCodecInfo& aci) {
+    char sb_buf[1024];
+    rtc::SimpleStringBuilder sb(sb_buf);
+    sb << "{sample_rate_hz: " << aci.sample_rate_hz;
+    sb << ", num_channels: " << aci.num_channels;
+    sb << ", default_bitrate_bps: " << aci.default_bitrate_bps;
+    sb << ", min_bitrate_bps: " << aci.min_bitrate_bps;
+    sb << ", max_bitrate_bps: " << aci.max_bitrate_bps;
+    sb << ", allow_comfort_noise: " << aci.allow_comfort_noise;
+    sb << ", supports_network_adaption: " << aci.supports_network_adaption;
+    sb << "}";
+    return sb.str();
+  }
+  std::string ToString(const webrtc::AudioCodecSpec& acs) {
+    char sb_buf[1024];
+    rtc::SimpleStringBuilder sb(sb_buf);
+    sb << "{format: " << ToString(acs.format);
+    sb << ", info: " << ToString(acs.info);
+    sb << "}";
+    return sb.str();
+  }
+}  // namespace rtc
diff --git a/rtc_base/strings/audio_format_to_string.h b/rtc_base/strings/audio_format_to_string.h
new file mode 100644
index 0000000..de0ce16
--- /dev/null
+++ b/rtc_base/strings/audio_format_to_string.h
@@ -0,0 +1,24 @@
+/*
+ *  Copyright 2018 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 RTC_BASE_STRINGS_AUDIO_FORMAT_TO_STRING_H_
+#define RTC_BASE_STRINGS_AUDIO_FORMAT_TO_STRING_H_
+
+#include <string>
+
+#include "api/audio_codecs/audio_format.h"
+
+namespace rtc {
+  std::string ToString(const webrtc::SdpAudioFormat& saf);
+  std::string ToString(const webrtc::AudioCodecInfo& saf);
+  std::string ToString(const webrtc::AudioCodecSpec& acs);
+}  // namespace rtc
+
+#endif  // RTC_BASE_STRINGS_AUDIO_FORMAT_TO_STRING_H_
diff --git a/rtc_base/strings/string_builder.cc b/rtc_base/strings/string_builder.cc
index 528f099..70a14da 100644
--- a/rtc_base/strings/string_builder.cc
+++ b/rtc_base/strings/string_builder.cc
@@ -67,15 +67,15 @@
 }
 
 SimpleStringBuilder& SimpleStringBuilder::operator<<(float f) {
-  return AppendFormat("%f", f);
+  return AppendFormat("%g", f);
 }
 
 SimpleStringBuilder& SimpleStringBuilder::operator<<(double f) {
-  return AppendFormat("%f", f);
+  return AppendFormat("%g", f);
 }
 
 SimpleStringBuilder& SimpleStringBuilder::operator<<(long double f) {
-  return AppendFormat("%Lf", f);
+  return AppendFormat("%Lg", f);
 }
 
 SimpleStringBuilder& SimpleStringBuilder::AppendFormat(const char* fmt, ...) {
diff --git a/rtc_base/strings/string_builder_unittest.cc b/rtc_base/strings/string_builder_unittest.cc
index 8d6312f..aa570eb 100644
--- a/rtc_base/strings/string_builder_unittest.cc
+++ b/rtc_base/strings/string_builder_unittest.cc
@@ -33,7 +33,7 @@
   SimpleStringBuilder sb(sb_buf);
   sb << 1 << ':' << 2.1 << ":" << 2.2f << ':' << 78187493520ll << ':'
      << 78187493520ul;
-  EXPECT_EQ(0, strcmp(sb.str(), "1:2.100000:2.200000:78187493520:78187493520"));
+  EXPECT_EQ(0, strcmp(sb.str(), "1:2.1:2.2:78187493520:78187493520"));
 }
 
 TEST(SimpleStringBuilder, Format) {
diff --git a/rtc_base/stringutils.cc b/rtc_base/stringutils.cc
index 8671b52..7664bb8 100644
--- a/rtc_base/stringutils.cc
+++ b/rtc_base/stringutils.cc
@@ -7,6 +7,8 @@
  *  in the file PATENTS.  All contributing project authors may
  *  be found in the AUTHORS file in the root of the source tree.
  */
+#include <algorithm>
+#include <cstdio>
 
 #include "rtc_base/stringutils.h"
 #include "rtc_base/checks.h"
@@ -130,4 +132,11 @@
   return s.substr(first, last - first + 1);
 }
 
+std::string ToHex(const int i) {
+  char buffer[50];
+  snprintf(buffer, sizeof(buffer), "%x", i);
+
+  return std::string(buffer);
+}
+
 }  // namespace rtc
diff --git a/rtc_base/stringutils.h b/rtc_base/stringutils.h
index 686402c..b42cfa5 100644
--- a/rtc_base/stringutils.h
+++ b/rtc_base/stringutils.h
@@ -78,7 +78,7 @@
   return strncasecmp(s1, s2, n);
 }
 
-#endif // WEBRTC_POSIX
+#endif  // WEBRTC_POSIX
 
 ///////////////////////////////////////////////////////////////////////////////
 // Traits simplifies porting string functions to be CTYPE-agnostic
@@ -91,9 +91,9 @@
 template<class CTYPE>
 struct Traits {
   // STL string type
-  //typedef XXX string;
+  // typedef XXX string;
   // Null-terminated string
-  //inline static const CTYPE* empty_str();
+  // inline static const CTYPE* empty_str();
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -119,7 +119,7 @@
 
 template<class CTYPE>
 const CTYPE* strchrn(const CTYPE* str, size_t slen, CTYPE ch) {
-  for (size_t i=0; i<slen && str[i]; ++i) {
+  for (size_t i=0; i < slen && str[i]; ++i) {
     if (str[i] == ch) {
       return str + i;
     }
@@ -312,6 +312,8 @@
 // Remove leading and trailing whitespaces.
 std::string string_trim(const std::string& s);
 
+// TODO(jonasolsson): replace with absl::Hex when that becomes available.
+std::string ToHex(const int i);
 }  // namespace rtc
 
-#endif // RTC_BASE_STRINGUTILS_H_
+#endif  // RTC_BASE_STRINGUTILS_H_
diff --git a/rtc_base/stringutils_unittest.cc b/rtc_base/stringutils_unittest.cc
index 85d0c3c..bb0e7b5 100644
--- a/rtc_base/stringutils_unittest.cc
+++ b/rtc_base/stringutils_unittest.cc
@@ -16,10 +16,10 @@
 // Tests for string_match().
 
 TEST(string_matchTest, Matches) {
-  EXPECT_TRUE( string_match("A.B.C.D", "a.b.c.d"));
-  EXPECT_TRUE( string_match("www.TEST.GOOGLE.COM", "www.*.com"));
-  EXPECT_TRUE( string_match("127.0.0.1",  "12*.0.*1"));
-  EXPECT_TRUE( string_match("127.1.0.21", "12*.0.*1"));
+  EXPECT_TRUE(string_match("A.B.C.D", "a.b.c.d"));
+  EXPECT_TRUE(string_match("www.TEST.GOOGLE.COM", "www.*.com"));
+  EXPECT_TRUE(string_match("127.0.0.1",  "12*.0.*1"));
+  EXPECT_TRUE(string_match("127.1.0.21", "12*.0.*1"));
   EXPECT_FALSE(string_match("127.0.0.0",  "12*.0.*1"));
   EXPECT_FALSE(string_match("127.0.0.0",  "12*.0.*1"));
   EXPECT_FALSE(string_match("127.1.1.21", "12*.0.*1"));
@@ -75,7 +75,7 @@
   EXPECT_EQ(1, ascii_string_compare(L"xyz", "xy", 5, identity));
   EXPECT_EQ(1, ascii_string_compare(L"abc", "ABB", 5, tolowercase));
 }
-#endif  // WEBRTC_WIN 
+#endif  // WEBRTC_WIN
 
 TEST(string_trim_Test, Trimming) {
   EXPECT_EQ("temp", string_trim("\n\r\t temp \n\r\t"));
@@ -105,4 +105,10 @@
   EXPECT_FALSE(ends_with("", "f"));
 }
 
-} // namespace rtc
+TEST(string_toHexTest, ToHex) {
+  EXPECT_EQ(ToHex(0), "0");
+  EXPECT_EQ(ToHex(0X1243E), "1243e");
+  EXPECT_EQ(ToHex(-20), "ffffffec");
+}
+
+}  // namespace rtc
diff --git a/rtc_base/synchronization/BUILD.gn b/rtc_base/synchronization/BUILD.gn
new file mode 100644
index 0000000..9d9ea6f
--- /dev/null
+++ b/rtc_base/synchronization/BUILD.gn
@@ -0,0 +1,38 @@
+# Copyright (c) 2018 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.
+
+import("../../webrtc.gni")
+if (is_android) {
+  import("//build/config/android/config.gni")
+  import("//build/config/android/rules.gni")
+}
+
+rtc_source_set("rw_lock_wrapper") {
+  public = [
+    "rw_lock_wrapper.h",
+  ]
+  sources = [
+    "rw_lock_wrapper.cc",
+  ]
+  deps = [
+    "..:macromagic",
+    "../..:typedefs",
+  ]
+  if (is_win) {
+    sources += [
+      "rw_lock_win.cc",
+      "rw_lock_win.h",
+    ]
+    deps += [ "..:logging" ]
+  } else {
+    sources += [
+      "rw_lock_posix.cc",
+      "rw_lock_posix.h",
+    ]
+  }
+}
diff --git a/system_wrappers/source/rw_lock_posix.cc b/rtc_base/synchronization/rw_lock_posix.cc
similarity index 95%
rename from system_wrappers/source/rw_lock_posix.cc
rename to rtc_base/synchronization/rw_lock_posix.cc
index 412873c..7e37dc9 100644
--- a/system_wrappers/source/rw_lock_posix.cc
+++ b/rtc_base/synchronization/rw_lock_posix.cc
@@ -8,7 +8,7 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "system_wrappers/source/rw_lock_posix.h"
+#include "rtc_base/synchronization/rw_lock_posix.h"
 
 namespace webrtc {
 
diff --git a/system_wrappers/source/rw_lock_posix.h b/rtc_base/synchronization/rw_lock_posix.h
similarity index 80%
rename from system_wrappers/source/rw_lock_posix.h
rename to rtc_base/synchronization/rw_lock_posix.h
index d15682b..9a92bcd 100644
--- a/system_wrappers/source/rw_lock_posix.h
+++ b/rtc_base/synchronization/rw_lock_posix.h
@@ -8,14 +8,14 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef SYSTEM_WRAPPERS_SOURCE_RW_LOCK_POSIX_H_
-#define SYSTEM_WRAPPERS_SOURCE_RW_LOCK_POSIX_H_
-
-#include "system_wrappers/include/rw_lock_wrapper.h"
-#include "typedefs.h"  // NOLINT(build/include)
+#ifndef RTC_BASE_SYNCHRONIZATION_RW_LOCK_POSIX_H_
+#define RTC_BASE_SYNCHRONIZATION_RW_LOCK_POSIX_H_
 
 #include <pthread.h>
 
+#include "rtc_base/synchronization/rw_lock_wrapper.h"
+#include "typedefs.h"  // NOLINT(build/include)
+
 namespace webrtc {
 
 class RWLockPosix : public RWLockWrapper {
@@ -38,4 +38,4 @@
 
 }  // namespace webrtc
 
-#endif  // SYSTEM_WRAPPERS_SOURCE_RW_LOCK_POSIX_H_
+#endif  // RTC_BASE_SYNCHRONIZATION_RW_LOCK_POSIX_H_
diff --git a/system_wrappers/source/rw_lock_win.cc b/rtc_base/synchronization/rw_lock_win.cc
similarity index 97%
rename from system_wrappers/source/rw_lock_win.cc
rename to rtc_base/synchronization/rw_lock_win.cc
index 23df15a..44cc0a7 100644
--- a/system_wrappers/source/rw_lock_win.cc
+++ b/rtc_base/synchronization/rw_lock_win.cc
@@ -8,7 +8,7 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "system_wrappers/source/rw_lock_win.h"
+#include "rtc_base/synchronization/rw_lock_win.h"
 
 #include "rtc_base/logging.h"
 
diff --git a/system_wrappers/source/rw_lock_win.h b/rtc_base/synchronization/rw_lock_win.h
similarity index 63%
rename from system_wrappers/source/rw_lock_win.h
rename to rtc_base/synchronization/rw_lock_win.h
index 41537ba..52ad9bb 100644
--- a/system_wrappers/source/rw_lock_win.h
+++ b/rtc_base/synchronization/rw_lock_win.h
@@ -8,10 +8,10 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef SYSTEM_WRAPPERS_SOURCE_RW_LOCK_WIN_H_
-#define SYSTEM_WRAPPERS_SOURCE_RW_LOCK_WIN_H_
+#ifndef RTC_BASE_SYNCHRONIZATION_RW_LOCK_WIN_H_
+#define RTC_BASE_SYNCHRONIZATION_RW_LOCK_WIN_H_
 
-#include "system_wrappers/include/rw_lock_wrapper.h"
+#include "rtc_base/synchronization/rw_lock_wrapper.h"
 
 #include <Windows.h>
 
@@ -20,13 +20,12 @@
 class RWLockWin : public RWLockWrapper {
  public:
   static RWLockWin* Create();
-  ~RWLockWin() {}
 
-  virtual void AcquireLockExclusive();
-  virtual void ReleaseLockExclusive();
+  void AcquireLockExclusive() override;
+  void ReleaseLockExclusive() override;
 
-  virtual void AcquireLockShared();
-  virtual void ReleaseLockShared();
+  void AcquireLockShared() override;
+  void ReleaseLockShared() override;
 
  private:
   RWLockWin();
@@ -37,4 +36,4 @@
 
 }  // namespace webrtc
 
-#endif  // SYSTEM_WRAPPERS_SOURCE_RW_LOCK_WIN_H_
+#endif  // RTC_BASE_SYNCHRONIZATION_RW_LOCK_WIN_H_
diff --git a/system_wrappers/source/rw_lock.cc b/rtc_base/synchronization/rw_lock_wrapper.cc
similarity index 79%
rename from system_wrappers/source/rw_lock.cc
rename to rtc_base/synchronization/rw_lock_wrapper.cc
index c38c44a..c8cd17e 100644
--- a/system_wrappers/source/rw_lock.cc
+++ b/rtc_base/synchronization/rw_lock_wrapper.cc
@@ -8,14 +8,14 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "system_wrappers/include/rw_lock_wrapper.h"
+#include "rtc_base/synchronization/rw_lock_wrapper.h"
 
 #include <assert.h>
 
 #if defined(_WIN32)
-#include "system_wrappers/source/rw_lock_win.h"
+#include "rtc_base/synchronization/rw_lock_win.h"
 #else
-#include "system_wrappers/source/rw_lock_posix.h"
+#include "rtc_base/synchronization/rw_lock_posix.h"
 #endif
 
 namespace webrtc {
diff --git a/system_wrappers/include/rw_lock_wrapper.h b/rtc_base/synchronization/rw_lock_wrapper.h
similarity index 82%
rename from system_wrappers/include/rw_lock_wrapper.h
rename to rtc_base/synchronization/rw_lock_wrapper.h
index a22b6ab..39f52fc 100644
--- a/system_wrappers/include/rw_lock_wrapper.h
+++ b/rtc_base/synchronization/rw_lock_wrapper.h
@@ -8,8 +8,8 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef SYSTEM_WRAPPERS_INCLUDE_RW_LOCK_WRAPPER_H_
-#define SYSTEM_WRAPPERS_INCLUDE_RW_LOCK_WRAPPER_H_
+#ifndef RTC_BASE_SYNCHRONIZATION_RW_LOCK_WRAPPER_H_
+#define RTC_BASE_SYNCHRONIZATION_RW_LOCK_WRAPPER_H_
 
 #include "rtc_base/thread_annotations.h"
 
@@ -35,7 +35,8 @@
 // provides more compact locking syntax.
 class RTC_SCOPED_LOCKABLE ReadLockScoped {
  public:
-  ReadLockScoped(RWLockWrapper& rw_lock) RTC_SHARED_LOCK_FUNCTION(rw_lock)
+  explicit ReadLockScoped(RWLockWrapper& rw_lock)
+      RTC_SHARED_LOCK_FUNCTION(rw_lock)
       : rw_lock_(rw_lock) {
     rw_lock_.AcquireLockShared();
   }
@@ -48,7 +49,8 @@
 
 class RTC_SCOPED_LOCKABLE WriteLockScoped {
  public:
-  WriteLockScoped(RWLockWrapper& rw_lock) RTC_EXCLUSIVE_LOCK_FUNCTION(rw_lock)
+  explicit WriteLockScoped(RWLockWrapper& rw_lock)
+      RTC_EXCLUSIVE_LOCK_FUNCTION(rw_lock)
       : rw_lock_(rw_lock) {
     rw_lock_.AcquireLockExclusive();
   }
@@ -61,4 +63,4 @@
 
 }  // namespace webrtc
 
-#endif  // SYSTEM_WRAPPERS_INCLUDE_RW_LOCK_WRAPPER_H_
+#endif  // RTC_BASE_SYNCHRONIZATION_RW_LOCK_WRAPPER_H_
diff --git a/rtc_base/system/BUILD.gn b/rtc_base/system/BUILD.gn
index 23d802a..a3dc4c1 100644
--- a/rtc_base/system/BUILD.gn
+++ b/rtc_base/system/BUILD.gn
@@ -12,8 +12,27 @@
   import("//build/config/android/rules.gni")
 }
 
+rtc_source_set("asm_defines") {
+  sources = [
+    "asm_defines.h",
+  ]
+}
+
 rtc_source_set("fallthrough") {
   sources = [
     "fallthrough.h",
   ]
 }
+
+rtc_source_set("file_wrapper") {
+  sources = [
+    "file_wrapper.cc",
+    "file_wrapper.h",
+  ]
+  deps = [
+    "..:checks",
+    "..:criticalsection",
+    "../..:typedefs",
+    "../..:webrtc_common",
+  ]
+}
diff --git a/system_wrappers/include/asm_defines.h b/rtc_base/system/asm_defines.h
similarity index 90%
rename from system_wrappers/include/asm_defines.h
rename to rtc_base/system/asm_defines.h
index 7f4c80e..e1c7453 100644
--- a/system_wrappers/include/asm_defines.h
+++ b/rtc_base/system/asm_defines.h
@@ -8,8 +8,8 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef SYSTEM_WRAPPERS_INCLUDE_ASM_DEFINES_H_
-#define SYSTEM_WRAPPERS_INCLUDE_ASM_DEFINES_H_
+#ifndef RTC_BASE_SYSTEM_ASM_DEFINES_H_
+#define RTC_BASE_SYSTEM_ASM_DEFINES_H_
 
 #if defined(__linux__) && defined(__ELF__)
 .section .note.GNU-stack,"",%progbits
@@ -63,4 +63,4 @@
 
 .text
 
-#endif  // SYSTEM_WRAPPERS_INCLUDE_ASM_DEFINES_H_
+#endif  // RTC_BASE_SYSTEM_ASM_DEFINES_H_
diff --git a/system_wrappers/source/file_impl.cc b/rtc_base/system/file_wrapper.cc
similarity index 97%
rename from system_wrappers/source/file_impl.cc
rename to rtc_base/system/file_wrapper.cc
index 350aaeb..72f5f25 100644
--- a/system_wrappers/source/file_impl.cc
+++ b/rtc_base/system/file_wrapper.cc
@@ -8,7 +8,7 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "system_wrappers/include/file_wrapper.h"
+#include "rtc_base/system/file_wrapper.h"
 
 #ifdef _WIN32
 #include <Windows.h>
@@ -17,6 +17,8 @@
 #include <string.h>
 #endif
 
+#include <utility>
+
 #include "rtc_base/checks.h"
 
 namespace webrtc {
diff --git a/system_wrappers/include/file_wrapper.h b/rtc_base/system/file_wrapper.h
similarity index 79%
rename from system_wrappers/include/file_wrapper.h
rename to rtc_base/system/file_wrapper.h
index 143da13..4672cc4 100644
--- a/system_wrappers/include/file_wrapper.h
+++ b/rtc_base/system/file_wrapper.h
@@ -8,8 +8,8 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef SYSTEM_WRAPPERS_INCLUDE_FILE_WRAPPER_H_
-#define SYSTEM_WRAPPERS_INCLUDE_FILE_WRAPPER_H_
+#ifndef RTC_BASE_SYSTEM_FILE_WRAPPER_H_
+#define RTC_BASE_SYSTEM_FILE_WRAPPER_H_
 
 #include <stddef.h>
 #include <stdio.h>
@@ -18,13 +18,12 @@
 #include "rtc_base/criticalsection.h"
 #include "typedefs.h"  // NOLINT(build/include)
 
-// Implementation of an InStream and OutStream that can read (exclusive) or
-// write from/to a file.
+// Implementation that can read (exclusive) or write from/to a file.
 
 namespace webrtc {
 
-// TODO(tommi): Remove the base classes, rename to rtc::File and move to base.
-class FileWrapper : public InStream, public OutStream {
+// TODO(tommi): Rename to rtc::File and move to base.
+class FileWrapper final {
  public:
   static const size_t kMaxFileNameSize = 1024;
 
@@ -34,7 +33,7 @@
   static FileWrapper Open(const char* file_name_utf8, bool read_only);
 
   FileWrapper(FILE* file, size_t max_size);
-  ~FileWrapper() override;
+  ~FileWrapper();
 
   // Support for move semantics.
   FileWrapper(FileWrapper&& other);
@@ -61,9 +60,9 @@
   int Flush();
 
   // Rewinds the file to the start.
-  int Rewind() override;
-  int Read(void* buf, size_t length) override;
-  bool Write(const void* buf, size_t length) override;
+  int Rewind();
+  int Read(void* buf, size_t length);
+  bool Write(const void* buf, size_t length);
 
  private:
   FileWrapper();
@@ -85,4 +84,4 @@
 
 }  // namespace webrtc
 
-#endif  // SYSTEM_WRAPPERS_INCLUDE_FILE_WRAPPER_H_
+#endif  // RTC_BASE_SYSTEM_FILE_WRAPPER_H_
diff --git a/rtc_base/system/module.mk b/rtc_base/system/module.mk
new file mode 100644
index 0000000..37c23f4
--- /dev/null
+++ b/rtc_base/system/module.mk
@@ -0,0 +1,5 @@
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+include common.mk
diff --git a/rtc_base/thread_checker_impl.cc b/rtc_base/thread_checker_impl.cc
index 6ec5c91..850f2ac 100644
--- a/rtc_base/thread_checker_impl.cc
+++ b/rtc_base/thread_checker_impl.cc
@@ -12,8 +12,6 @@
 
 #include "rtc_base/thread_checker_impl.h"
 
-#include "rtc_base/platform_thread.h"
-
 namespace rtc {
 
 ThreadCheckerImpl::ThreadCheckerImpl() : valid_thread_(CurrentThreadRef()) {
diff --git a/rtc_base/time/BUILD.gn b/rtc_base/time/BUILD.gn
new file mode 100644
index 0000000..f8f6244
--- /dev/null
+++ b/rtc_base/time/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright (c) 2018 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.
+
+import("../../webrtc.gni")
+if (is_android) {
+  import("//build/config/android/config.gni")
+  import("//build/config/android/rules.gni")
+}
+
+rtc_source_set("timestamp_extrapolator") {
+  sources = [
+    "timestamp_extrapolator.cc",
+    "timestamp_extrapolator.h",
+  ]
+  deps = [
+    "../..:typedefs",
+    "../synchronization:rw_lock_wrapper",
+  ]
+}
diff --git a/system_wrappers/source/timestamp_extrapolator.cc b/rtc_base/time/timestamp_extrapolator.cc
similarity index 92%
rename from system_wrappers/source/timestamp_extrapolator.cc
rename to rtc_base/time/timestamp_extrapolator.cc
index b8c6ba0..bf9f726 100644
--- a/system_wrappers/source/timestamp_extrapolator.cc
+++ b/rtc_base/time/timestamp_extrapolator.cc
@@ -8,7 +8,7 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "system_wrappers/include/timestamp_extrapolator.h"
+#include "rtc_base/time/timestamp_extrapolator.h"
 
 #include <algorithm>
 
@@ -178,12 +178,14 @@
       // Forward wrap around
       _wrapArounds++;
     }
-  }
-  // This difference will probably be less than -2^31 if we have had a backward
-  // wrap around. Since it is casted to a Word32, it should be positive.
-  else if (static_cast<int32_t>(_prevWrapTimestamp - ts90khz) > 0) {
-    // Backward wrap around
-    _wrapArounds--;
+  } else {
+    // This difference will probably be less than -2^31 if we have had a
+    // backward wrap around. Since it is casted to a Word32, it should be
+    // positive.
+    if (static_cast<int32_t>(_prevWrapTimestamp - ts90khz) > 0) {
+      // Backward wrap around
+      _wrapArounds--;
+    }
   }
   _prevWrapTimestamp = ts90khz;
 }
@@ -193,9 +195,9 @@
   error = (error > 0) ? std::min(error, _accMaxError)
                       : std::max(error, -_accMaxError);
   _detectorAccumulatorPos =
-      std::max(_detectorAccumulatorPos + error - _accDrift, (double)0);
+      std::max(_detectorAccumulatorPos + error - _accDrift, double{0});
   _detectorAccumulatorNeg =
-      std::min(_detectorAccumulatorNeg + error + _accDrift, (double)0);
+      std::min(_detectorAccumulatorNeg + error + _accDrift, double{0});
   if (_detectorAccumulatorPos > _alarmThreshold ||
       _detectorAccumulatorNeg < -_alarmThreshold) {
     // Alarm
diff --git a/system_wrappers/include/timestamp_extrapolator.h b/rtc_base/time/timestamp_extrapolator.h
similarity index 85%
rename from system_wrappers/include/timestamp_extrapolator.h
rename to rtc_base/time/timestamp_extrapolator.h
index 9418100..6638184 100644
--- a/system_wrappers/include/timestamp_extrapolator.h
+++ b/rtc_base/time/timestamp_extrapolator.h
@@ -8,10 +8,10 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef SYSTEM_WRAPPERS_INCLUDE_TIMESTAMP_EXTRAPOLATOR_H_
-#define SYSTEM_WRAPPERS_INCLUDE_TIMESTAMP_EXTRAPOLATOR_H_
+#ifndef RTC_BASE_TIME_TIMESTAMP_EXTRAPOLATOR_H_
+#define RTC_BASE_TIME_TIMESTAMP_EXTRAPOLATOR_H_
 
-#include "system_wrappers/include/rw_lock_wrapper.h"
+#include "rtc_base/synchronization/rw_lock_wrapper.h"
 #include "typedefs.h"  // NOLINT(build/include)
 
 namespace webrtc {
@@ -51,4 +51,4 @@
 
 }  // namespace webrtc
 
-#endif  // SYSTEM_WRAPPERS_INCLUDE_TIMESTAMP_EXTRAPOLATOR_H_
+#endif  // RTC_BASE_TIME_TIMESTAMP_EXTRAPOLATOR_H_
diff --git a/rtc_base/timeutils.h b/rtc_base/timeutils.h
index f602d48..5eb73c7 100644
--- a/rtc_base/timeutils.h
+++ b/rtc_base/timeutils.h
@@ -148,6 +148,8 @@
     return min_ == o.min_ && max_ == o.max_;
   }
 
+  bool operator!=(const IntervalRange& o) const { return !operator==(o); }
+
  private:
   int min_;
   int max_;
diff --git a/rtc_base/virtualsocketserver.cc b/rtc_base/virtualsocketserver.cc
index d8771e7..fab5900 100644
--- a/rtc_base/virtualsocketserver.cc
+++ b/rtc_base/virtualsocketserver.cc
@@ -409,7 +409,8 @@
     } else if ((SOCK_STREAM == type_) && (CS_CONNECTING == state_)) {
       CompleteConnect(data->addr, true);
     } else {
-      RTC_LOG(LS_VERBOSE) << "Socket at " << local_addr_ << " is not listening";
+      RTC_LOG(LS_VERBOSE) << "Socket at " << local_addr_.ToString()
+                          << " is not listening";
       server_->Disconnect(server_->LookupBinding(data->addr));
     }
     delete data;
@@ -805,7 +806,8 @@
   VirtualSocket* remote = LookupBinding(remote_addr);
   if (!CanInteractWith(socket, remote)) {
     RTC_LOG(LS_INFO) << "Address family mismatch between "
-                     << socket->GetLocalAddress() << " and " << remote_addr;
+                     << socket->GetLocalAddress().ToString() << " and "
+                     << remote_addr.ToString();
     return -1;
   }
   if (remote != nullptr) {
@@ -813,7 +815,7 @@
     msg_queue_->PostDelayed(RTC_FROM_HERE, delay, remote, MSG_ID_CONNECT,
                             new MessageAddress(addr));
   } else {
-    RTC_LOG(LS_INFO) << "No one listening at " << remote_addr;
+    RTC_LOG(LS_INFO) << "No one listening at " << remote_addr.ToString();
     msg_queue_->PostDelayed(RTC_FROM_HERE, delay, socket, MSG_ID_DISCONNECT);
   }
   return 0;
@@ -856,17 +858,18 @@
     dummy_socket->SetLocalAddress(remote_addr);
     if (!CanInteractWith(socket, dummy_socket.get())) {
       RTC_LOG(LS_VERBOSE) << "Incompatible address families: "
-                          << socket->GetLocalAddress() << " and "
-                          << remote_addr;
+                          << socket->GetLocalAddress().ToString() << " and "
+                          << remote_addr.ToString();
       return -1;
     }
-    RTC_LOG(LS_VERBOSE) << "No one listening at " << remote_addr;
+    RTC_LOG(LS_VERBOSE) << "No one listening at " << remote_addr.ToString();
     return static_cast<int>(data_size);
   }
 
   if (!CanInteractWith(socket, recipient)) {
     RTC_LOG(LS_VERBOSE) << "Incompatible address families: "
-                        << socket->GetLocalAddress() << " and " << remote_addr;
+                        << socket->GetLocalAddress().ToString() << " and "
+                        << remote_addr.ToString();
     return -1;
   }
 
diff --git a/rtc_base/win32.h b/rtc_base/win32.h
index 78f66a7..4e91687 100644
--- a/rtc_base/win32.h
+++ b/rtc_base/win32.h
@@ -54,6 +54,7 @@
 enum WindowsMajorVersions {
   kWindows2000 = 5,
   kWindowsVista = 6,
+  kWindows10 = 10,
 };
 bool GetOsVersion(int* major, int* minor, int* build);
 
@@ -74,6 +75,11 @@
           (major > kWindowsVista || (major == kWindowsVista && minor >= 2)));
 }
 
+inline bool IsWindows10OrLater() {
+  int major;
+  return (GetOsVersion(&major, nullptr, nullptr) && (major >= kWindows10));
+}
+
 // Determine the current integrity level of the process.
 bool GetCurrentProcessIntegrityLevel(int* level);
 
diff --git a/system_wrappers/BUILD.gn b/system_wrappers/BUILD.gn
index 93549fd..af778fd 100644
--- a/system_wrappers/BUILD.gn
+++ b/system_wrappers/BUILD.gn
@@ -15,35 +15,20 @@
 rtc_static_library("system_wrappers") {
   visibility = [ "*" ]
   sources = [
-    "include/aligned_array.h",
-    "include/aligned_malloc.h",
     "include/clock.h",
     "include/cpu_info.h",
     "include/event_wrapper.h",
-    "include/file_wrapper.h",
     "include/ntp_time.h",
     "include/rtp_to_ntp_estimator.h",
-    "include/rw_lock_wrapper.h",
     "include/sleep.h",
-    "include/timestamp_extrapolator.h",
-    "source/aligned_malloc.cc",
     "source/clock.cc",
     "source/cpu_features.cc",
     "source/cpu_info.cc",
     "source/event.cc",
-    "source/event_timer_posix.cc",
-    "source/event_timer_posix.h",
     "source/event_timer_win.cc",
     "source/event_timer_win.h",
-    "source/file_impl.cc",
     "source/rtp_to_ntp_estimator.cc",
-    "source/rw_lock.cc",
-    "source/rw_lock_posix.cc",
-    "source/rw_lock_posix.h",
-    "source/rw_lock_win.cc",
-    "source/rw_lock_win.h",
     "source/sleep.cc",
-    "source/timestamp_extrapolator.cc",
   ]
 
   defines = []
@@ -58,8 +43,16 @@
     "../api:optional",
     "../modules:module_api_public",
     "../rtc_base:checks",
+    "../rtc_base/synchronization:rw_lock_wrapper",
   ]
 
+  if (is_posix || is_fuchsia) {
+    sources += [
+      "source/event_timer_posix.cc",
+      "source/event_timer_posix.h",
+    ]
+  }
+
   if (is_android) {
     defines += [ "WEBRTC_THREAD_RR" ]
 
@@ -124,12 +117,6 @@
   ]
 }
 
-rtc_source_set("asm_defines") {
-  sources = [
-    "include/asm_defines.h",
-  ]
-}
-
 rtc_source_set("field_trial_api") {
   sources = [
     "include/field_trial.h",
@@ -225,10 +212,7 @@
   rtc_test("system_wrappers_unittests") {
     testonly = true
     sources = [
-      "source/aligned_array_unittest.cc",
-      "source/aligned_malloc_unittest.cc",
       "source/clock_unittest.cc",
-      "source/event_timer_posix_unittest.cc",
       "source/metrics_default_unittest.cc",
       "source/metrics_unittest.cc",
       "source/ntp_time_unittest.cc",
@@ -236,6 +220,12 @@
     ]
     configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 
+    if (is_posix || is_fuchsia) {
+      sources += [
+        "source/event_timer_posix_unittest.cc",
+      ]
+    }
+
     if (!build_with_chromium && is_clang) {
       # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
       suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
diff --git a/system_wrappers/OWNERS b/system_wrappers/OWNERS
index 65b8dee..13b08a3 100644
--- a/system_wrappers/OWNERS
+++ b/system_wrappers/OWNERS
@@ -1,9 +1,8 @@
-perkj@webrtc.org
 henrika@webrtc.org
-henrikg@webrtc.org
 mflodman@webrtc.org
 niklas.enbom@webrtc.org
 nisse@webrtc.org
+perkj@webrtc.org
 
 # These are for the common case of adding or renaming files. If you're doing
 # structural changes, please get a review from a reviewer in this file.
diff --git a/system_wrappers/include/clock.h b/system_wrappers/include/clock.h
index aec5ca5..0164288 100644
--- a/system_wrappers/include/clock.h
+++ b/system_wrappers/include/clock.h
@@ -13,8 +13,8 @@
 
 #include <memory>
 
+#include "rtc_base/synchronization/rw_lock_wrapper.h"
 #include "system_wrappers/include/ntp_time.h"
-#include "system_wrappers/include/rw_lock_wrapper.h"
 #include "typedefs.h"  // NOLINT(build/include)
 
 namespace webrtc {
diff --git a/system_wrappers/source/clock.cc b/system_wrappers/source/clock.cc
index 631974d..00cdff0 100644
--- a/system_wrappers/source/clock.cc
+++ b/system_wrappers/source/clock.cc
@@ -15,7 +15,7 @@
 // Windows needs to be included before mmsystem.h
 #include "rtc_base/win32.h"
 
-#include <MMSystem.h>
+#include <mmsystem.h>
 
 #elif defined(WEBRTC_POSIX)
 
@@ -25,8 +25,8 @@
 #endif  // defined(WEBRTC_POSIX)
 
 #include "rtc_base/criticalsection.h"
+#include "rtc_base/synchronization/rw_lock_wrapper.h"
 #include "rtc_base/timeutils.h"
-#include "system_wrappers/include/rw_lock_wrapper.h"
 
 namespace webrtc {
 
diff --git a/system_wrappers/source/cpu_info.cc b/system_wrappers/source/cpu_info.cc
index 0bfe015..c17d59d 100644
--- a/system_wrappers/source/cpu_info.cc
+++ b/system_wrappers/source/cpu_info.cc
@@ -12,10 +12,6 @@
 
 #if defined(WEBRTC_WIN)
 #include <windows.h>
-#include <winsock2.h>
-#ifndef EXCLUDE_D3D9
-#include <d3d9.h>
-#endif
 #elif defined(WEBRTC_LINUX)
 #include <unistd.h>
 #endif
diff --git a/system_wrappers/source/event_timer_win.cc b/system_wrappers/source/event_timer_win.cc
index b6a93fe..c2ad9f3 100644
--- a/system_wrappers/source/event_timer_win.cc
+++ b/system_wrappers/source/event_timer_win.cc
@@ -10,7 +10,7 @@
 
 #include "system_wrappers/source/event_timer_win.h"
 
-#include "Mmsystem.h"
+#include "mmsystem.h"
 
 namespace webrtc {
 
diff --git a/system_wrappers/source/module.mk b/system_wrappers/source/module.mk
index 92c7dda..3b84a7d 100644
--- a/system_wrappers/source/module.mk
+++ b/system_wrappers/source/module.mk
@@ -5,18 +5,13 @@
 include common.mk
 
 system_wrappers_source_CXX_OBJECTS = \
-	system_wrappers/source/aligned_malloc.o \
 	system_wrappers/source/clock.o \
 	system_wrappers/source/cpu_features.o \
 	system_wrappers/source/cpu_info.o \
 	system_wrappers/source/event.o \
 	system_wrappers/source/event_timer_posix.o \
-	system_wrappers/source/file_impl.o \
 	system_wrappers/source/rtp_to_ntp_estimator.o \
-	system_wrappers/source/rw_lock.o \
-	system_wrappers/source/rw_lock_posix.o \
-	system_wrappers/source/sleep.o \
-	system_wrappers/source/timestamp_extrapolator.o
+	system_wrappers/source/sleep.o
 
 CXX_STATIC_LIBRARY(system_wrappers/source/libsystem_wrappers.pic.a): \
 	$(system_wrappers_source_CXX_OBJECTS) \