Remove voe::TransmitMixer

TransmitMixer's functionality is moved into the AudioTransportProxy
owned by AudioState. This removes the need for an AudioTransport
implementation in VoEBaseImpl, which means that the proxy is no longer
a proxy, hence AudioTransportProxy is renamed to AudioTransportImpl.

In the short term, AudioState needs to know which AudioDeviceModule is
used, so it is added in AudioState::Config. AudioTransportImpl needs
to know which AudioSendStream:s are currently enabled to send, so
AudioState maintains a map of them, which is reduced into a simple
vector for AudioTransportImpl.

To encode and transmit audio,
AudioSendStream::OnAudioData(std::unique_ptr<AudioFrame> audio_frame)
is introduced, which is used in both the Chromium and standalone use
cases. This removes the need for two different instances of
voe::Channel::ProcessAndEncodeAudio(), so there is now only one,
taking an AudioFrame as argument. Callers need to allocate their own
AudioFrame:s, which is wasteful but not a regression since this was
already happening in the voe::Channel functions.

Most of the logic changed resides in
AudioTransportImpl::RecordedDataIsAvailable(), where two strange
things were found:

  1. The clock drift parameter was ineffective since
     apm->echo_cancellation()->enable_drift_compensation(false) is
     called during initialization.

  2. The output parameter 'new_mic_volume' was never set - instead it
     was returned as a result, causing the ADM to never update the
     analog mic gain
     (https://cs.chromium.org/chromium/src/third_party/webrtc/voice_engine/voe_base_impl.cc?q=voe_base_impl.cc&dr&l=100).

Besides this, tests are updated, and some dead code is removed which
was found in the process.

Bug: webrtc:4690, webrtc:8591
Change-Id: I789d5296bf5efb7299a5ee05a4f3ce6abf9124b2
Reviewed-on: https://webrtc-review.googlesource.com/26681
Commit-Queue: Fredrik Solenberg <solenberg@webrtc.org>
Reviewed-by: Oskar Sundbom <ossu@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21301}
diff --git a/audio/BUILD.gn b/audio/BUILD.gn
index 65e3050..8459385 100644
--- a/audio/BUILD.gn
+++ b/audio/BUILD.gn
@@ -20,8 +20,8 @@
     "audio_send_stream.h",
     "audio_state.cc",
     "audio_state.h",
-    "audio_transport_proxy.cc",
-    "audio_transport_proxy.h",
+    "audio_transport_impl.cc",
+    "audio_transport_impl.h",
     "conversion.h",
     "null_audio_poller.cc",
     "null_audio_poller.h",
@@ -61,6 +61,8 @@
     "../system_wrappers",
     "../system_wrappers:field_trial_api",
     "../voice_engine",
+    "../voice_engine:audio_level",
+    "utility:audio_frame_operations",
   ]
 }
 if (rtc_include_tests) {
@@ -99,6 +101,7 @@
       ":audio",
       ":audio_end_to_end_test",
       "../api:mock_audio_mixer",
+      "../call:mock_call_interfaces",
       "../call:mock_rtp_interfaces",
       "../call:rtp_interfaces",
       "../call:rtp_receiver",
diff --git a/audio/DEPS b/audio/DEPS
index 0f952a3..70e3346 100644
--- a/audio/DEPS
+++ b/audio/DEPS
@@ -19,10 +19,7 @@
   "audio_send_stream.cc": [
     "+modules/audio_coding/codecs/cng/audio_encoder_cng.h",
   ],
-  # TODO(ossu): Remove this exception when builtin_audio_encoder_factory.h
-  # has moved to api/, or when the proper mocks have been made.
-  "audio_send_stream_unittest.cc": [
-    "+modules/audio_coding/codecs/builtin_audio_encoder_factory.h",
-  ],
+  "audio_transport_impl.h": [
+    "+modules/audio_processing/typing_detection.h",
+  ]
 }
-
diff --git a/audio/audio_receive_stream_unittest.cc b/audio/audio_receive_stream_unittest.cc
index d6c2dbe..d24bed5 100644
--- a/audio/audio_receive_stream_unittest.cc
+++ b/audio/audio_receive_stream_unittest.cc
@@ -17,6 +17,7 @@
 #include "audio/conversion.h"
 #include "call/rtp_stream_receiver_controller.h"
 #include "logging/rtc_event_log/mock/mock_rtc_event_log.h"
+#include "modules/audio_device/include/mock_audio_device.h"
 #include "modules/audio_processing/include/mock_audio_processing.h"
 #include "modules/bitrate_controller/include/mock/mock_bitrate_controller.h"
 #include "modules/pacing/packet_router.h"
@@ -75,12 +76,12 @@
         audio_mixer_(new rtc::RefCountedObject<MockAudioMixer>()) {
     using testing::Invoke;
 
-    EXPECT_CALL(voice_engine_, audio_transport());
-
     AudioState::Config config;
     config.voice_engine = &voice_engine_;
     config.audio_mixer = audio_mixer_;
     config.audio_processing = new rtc::RefCountedObject<MockAudioProcessing>();
+    config.audio_device_module =
+        new rtc::RefCountedObject<MockAudioDeviceModule>();
     audio_state_ = AudioState::Create(config);
 
     EXPECT_CALL(voice_engine_, ChannelProxyFactory(kChannelId))
diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc
index 1596c96..6aa469d 100644
--- a/audio/audio_send_stream.cc
+++ b/audio/audio_send_stream.cc
@@ -31,7 +31,6 @@
 #include "system_wrappers/include/field_trial.h"
 #include "voice_engine/channel_proxy.h"
 #include "voice_engine/include/voe_base.h"
-#include "voice_engine/transmit_mixer.h"
 #include "voice_engine/voice_engine_impl.h"
 
 namespace webrtc {
@@ -121,6 +120,7 @@
 AudioSendStream::~AudioSendStream() {
   RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
   RTC_LOG(LS_INFO) << "~AudioSendStream: " << config_.ToString();
+  RTC_DCHECK(!sending_);
   transport_->send_side_cc()->DeRegisterPacketFeedbackObserver(this);
   channel_proxy_->RegisterTransport(nullptr);
   channel_proxy_->ResetSenderCongestionControlObjects();
@@ -135,6 +135,7 @@
 
 void AudioSendStream::Reconfigure(
     const webrtc::AudioSendStream::Config& new_config) {
+  RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
   ConfigureStream(this, new_config, false);
 }
 
@@ -232,6 +233,10 @@
 
 void AudioSendStream::Start() {
   RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
+  if (sending_) {
+    return;
+  }
+
   if (config_.min_bitrate_bps != -1 && config_.max_bitrate_bps != -1 &&
       (FindExtensionIds(config_.rtp.extensions).transport_sequence_number !=
            0 ||
@@ -246,10 +251,17 @@
   if (error != 0) {
     RTC_LOG(LS_ERROR) << "AudioSendStream::Start failed with error: " << error;
   }
+  sending_ = true;
+  audio_state()->AddSendingStream(this, encoder_sample_rate_hz_,
+                                  encoder_num_channels_);
 }
 
 void AudioSendStream::Stop() {
   RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
+  if (!sending_) {
+    return;
+  }
+
   RemoveBitrateObserver();
 
   ScopedVoEInterface<VoEBase> base(voice_engine());
@@ -257,6 +269,13 @@
   if (error != 0) {
     RTC_LOG(LS_ERROR) << "AudioSendStream::Stop failed with error: " << error;
   }
+  sending_ = false;
+  audio_state()->RemoveSendingStream(this);
+}
+
+void AudioSendStream::SendAudioData(std::unique_ptr<AudioFrame> audio_frame) {
+  RTC_CHECK_RUNS_SERIALIZED(&audio_capture_race_checker_);
+  channel_proxy_->ProcessAndEncodeAudio(std::move(audio_frame));
 }
 
 bool AudioSendStream::SendTelephoneEvent(int payload_type,
@@ -313,17 +332,12 @@
     }
   }
 
-  ScopedVoEInterface<VoEBase> base(voice_engine());
-  RTC_DCHECK(base->transmit_mixer());
-  stats.audio_level = base->transmit_mixer()->AudioLevelFullRange();
-  RTC_DCHECK_LE(0, stats.audio_level);
+  AudioState::Stats input_stats = audio_state()->GetAudioInputStats();
+  stats.audio_level = input_stats.audio_level;
+  stats.total_input_energy = input_stats.total_energy;
+  stats.total_input_duration = input_stats.total_duration;
 
-  stats.total_input_energy = base->transmit_mixer()->GetTotalInputEnergy();
-  stats.total_input_duration = base->transmit_mixer()->GetTotalInputDuration();
-
-  internal::AudioState* audio_state =
-      static_cast<internal::AudioState*>(audio_state_.get());
-  stats.typing_noise_detected = audio_state->typing_noise_detected();
+  stats.typing_noise_detected = audio_state()->typing_noise_detected();
   stats.ana_statistics = channel_proxy_->GetANAStatistics();
   RTC_DCHECK(audio_state_->audio_processing());
   stats.apm_statistics =
@@ -418,6 +432,20 @@
   return active_lifetime_;
 }
 
+internal::AudioState* AudioSendStream::audio_state() {
+  internal::AudioState* audio_state =
+      static_cast<internal::AudioState*>(audio_state_.get());
+  RTC_DCHECK(audio_state);
+  return audio_state;
+}
+
+const internal::AudioState* AudioSendStream::audio_state() const {
+  internal::AudioState* audio_state =
+      static_cast<internal::AudioState*>(audio_state_.get());
+  RTC_DCHECK(audio_state);
+  return audio_state;
+}
+
 VoiceEngine* AudioSendStream::voice_engine() const {
   internal::AudioState* audio_state =
       static_cast<internal::AudioState*>(audio_state_.get());
@@ -426,6 +454,17 @@
   return voice_engine;
 }
 
+void AudioSendStream::StoreEncoderProperties(int sample_rate_hz,
+                                             size_t num_channels) {
+  RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
+  encoder_sample_rate_hz_ = sample_rate_hz;
+  encoder_num_channels_ = num_channels;
+  if (sending_) {
+    // Update AudioState's information about the stream.
+    audio_state()->AddSendingStream(this, sample_rate_hz, num_channels);
+  }
+}
+
 // Apply current codec settings to a single voe::Channel used for sending.
 bool AudioSendStream::SetupSendCodec(AudioSendStream* stream,
                                      const Config& new_config) {
@@ -472,6 +511,8 @@
         new_config.send_codec_spec->format.clockrate_hz);
   }
 
+  stream->StoreEncoderProperties(encoder->SampleRateHz(),
+                                 encoder->NumChannels());
   stream->channel_proxy_->SetEncoder(new_config.send_codec_spec->payload_type,
                                      std::move(encoder));
   return true;
diff --git a/audio/audio_send_stream.h b/audio/audio_send_stream.h
index 1414e39..3771767 100644
--- a/audio/audio_send_stream.h
+++ b/audio/audio_send_stream.h
@@ -20,6 +20,7 @@
 #include "call/bitrate_allocator.h"
 #include "modules/rtp_rtcp/include/rtp_rtcp.h"
 #include "rtc_base/constructormagic.h"
+#include "rtc_base/race_checker.h"
 #include "rtc_base/thread_checker.h"
 #include "voice_engine/transport_feedback_packet_loss_tracker.h"
 
@@ -35,6 +36,8 @@
 }  // namespace voe
 
 namespace internal {
+class AudioState;
+
 class AudioSendStream final : public webrtc::AudioSendStream,
                               public webrtc::BitrateAllocatorObserver,
                               public webrtc::PacketFeedbackObserver {
@@ -54,6 +57,7 @@
   void Reconfigure(const webrtc::AudioSendStream::Config& config) override;
   void Start() override;
   void Stop() override;
+  void SendAudioData(std::unique_ptr<AudioFrame> audio_frame) override;
   bool SendTelephoneEvent(int payload_type, int payload_frequency, int event,
                           int duration_ms) override;
   void SetMuted(bool muted) override;
@@ -83,8 +87,12 @@
  private:
   class TimedTransport;
 
+  internal::AudioState* audio_state();
+  const internal::AudioState* audio_state() const;
   VoiceEngine* voice_engine() const;
 
+  void StoreEncoderProperties(int sample_rate_hz, size_t num_channels);
+
   // These are all static to make it less likely that (the old) config_ is
   // accessed unintentionally.
   static void ConfigureStream(AudioSendStream* stream,
@@ -105,12 +113,17 @@
 
   rtc::ThreadChecker worker_thread_checker_;
   rtc::ThreadChecker pacer_thread_checker_;
+  rtc::RaceChecker audio_capture_race_checker_;
   rtc::TaskQueue* worker_queue_;
   webrtc::AudioSendStream::Config config_;
   rtc::scoped_refptr<webrtc::AudioState> audio_state_;
   std::unique_ptr<voe::ChannelProxy> channel_proxy_;
   RtcEventLog* const event_log_;
 
+  int encoder_sample_rate_hz_ = 0;
+  size_t encoder_num_channels_ = 0;
+  bool sending_ = false;
+
   BitrateAllocator* const bitrate_allocator_;
   RtpTransportControllerSendInterface* const transport_;
 
diff --git a/audio/audio_send_stream_unittest.cc b/audio/audio_send_stream_unittest.cc
index 145a8e2..f605637 100644
--- a/audio/audio_send_stream_unittest.cc
+++ b/audio/audio_send_stream_unittest.cc
@@ -18,6 +18,7 @@
 #include "call/fake_rtp_transport_controller_send.h"
 #include "call/rtp_transport_controller_send_interface.h"
 #include "logging/rtc_event_log/mock/mock_rtc_event_log.h"
+#include "modules/audio_device/include/mock_audio_device.h"
 #include "modules/audio_mixer/audio_mixer_impl.h"
 #include "modules/audio_processing/include/audio_processing_statistics.h"
 #include "modules/audio_processing/include/mock_audio_processing.h"
@@ -33,7 +34,6 @@
 #include "test/mock_audio_encoder_factory.h"
 #include "test/mock_voe_channel_proxy.h"
 #include "test/mock_voice_engine.h"
-#include "voice_engine/transmit_mixer.h"
 
 namespace webrtc {
 namespace test {
@@ -58,9 +58,6 @@
 const double kEchoReturnLossEnhancement = 101;
 const double kResidualEchoLikelihood = -1.0f;
 const double kResidualEchoLikelihoodMax = 23.0f;
-const int32_t kSpeechInputLevel = 96;
-const double kTotalInputEnergy = 0.25;
-const double kTotalInputDuration = 0.5;
 const CallStatistics kCallStats = {
     1345,  1678,  1901, 1234,  112, 13456, 17890, 1567, -1890, -1123};
 const ReportBlock kReportBlock = {456, 780, 123, 567, 890, 132, 143, 13354};
@@ -85,14 +82,6 @@
                     uint32_t max_padding_bitrate_bps));
 };
 
-class MockTransmitMixer : public voe::TransmitMixer {
- public:
-  MOCK_CONST_METHOD0(AudioLevelFullRange, int16_t());
-  MOCK_CONST_METHOD0(GetTotalInputEnergy, double());
-  MOCK_CONST_METHOD0(GetTotalInputDuration, double());
-  MOCK_CONST_METHOD0(typing_noise_detected, bool());
-};
-
 std::unique_ptr<MockAudioEncoder> SetupAudioEncoderMock(
     int payload_type,
     const SdpAudioFormat& format) {
@@ -151,12 +140,12 @@
         audio_encoder_(nullptr) {
     using testing::Invoke;
 
-    EXPECT_CALL(voice_engine_, audio_transport());
-
     AudioState::Config config;
     config.voice_engine = &voice_engine_;
     config.audio_mixer = AudioMixerImpl::Create();
     config.audio_processing = audio_processing_;
+    config.audio_device_module =
+        new rtc::RefCountedObject<MockAudioDeviceModule>();
     audio_state_ = AudioState::Create(config);
 
     SetupDefaultChannelProxy(audio_bwe_enabled);
@@ -301,17 +290,6 @@
         .WillRepeatedly(Return(report_blocks));
     EXPECT_CALL(*channel_proxy_, GetANAStatistics())
         .WillRepeatedly(Return(ANAStats()));
-    EXPECT_CALL(voice_engine_, transmit_mixer())
-        .WillRepeatedly(Return(&transmit_mixer_));
-
-    EXPECT_CALL(transmit_mixer_, AudioLevelFullRange())
-        .WillRepeatedly(Return(kSpeechInputLevel));
-    EXPECT_CALL(transmit_mixer_, GetTotalInputEnergy())
-        .WillRepeatedly(Return(kTotalInputEnergy));
-    EXPECT_CALL(transmit_mixer_, GetTotalInputDuration())
-        .WillRepeatedly(Return(kTotalInputDuration));
-    EXPECT_CALL(transmit_mixer_, typing_noise_detected())
-        .WillRepeatedly(Return(true));
 
     audio_processing_stats_.echo_return_loss = kEchoReturnLoss;
     audio_processing_stats_.echo_return_loss_enhancement =
@@ -334,7 +312,6 @@
   AudioSendStream::Config stream_config_;
   testing::StrictMock<MockVoEChannelProxy>* channel_proxy_ = nullptr;
   rtc::scoped_refptr<MockAudioProcessing> audio_processing_;
-  MockTransmitMixer transmit_mixer_;
   AudioProcessingStats audio_processing_stats_;
   SimulatedClock simulated_clock_;
   PacketRouter packet_router_;
@@ -447,9 +424,9 @@
                                  (kIsacCodec.plfreq / 1000)),
             stats.jitter_ms);
   EXPECT_EQ(kCallStats.rttMs, stats.rtt_ms);
-  EXPECT_EQ(static_cast<int32_t>(kSpeechInputLevel), stats.audio_level);
-  EXPECT_EQ(kTotalInputEnergy, stats.total_input_energy);
-  EXPECT_EQ(kTotalInputDuration, stats.total_input_duration);
+  EXPECT_EQ(0, stats.audio_level);
+  EXPECT_EQ(0, stats.total_input_energy);
+  EXPECT_EQ(0, stats.total_input_duration);
   EXPECT_EQ(kEchoDelayMedian, stats.apm_statistics.delay_median_ms);
   EXPECT_EQ(kEchoDelayStdDev, stats.apm_statistics.delay_standard_deviation_ms);
   EXPECT_EQ(kEchoReturnLoss, stats.apm_statistics.echo_return_loss);
@@ -461,7 +438,7 @@
             stats.apm_statistics.residual_echo_likelihood);
   EXPECT_EQ(kResidualEchoLikelihoodMax,
             stats.apm_statistics.residual_echo_likelihood_recent_max);
-  EXPECT_TRUE(stats.typing_noise_detected);
+  EXPECT_FALSE(stats.typing_noise_detected);
 }
 
 TEST(AudioSendStreamTest, SendCodecAppliesAudioNetworkAdaptor) {
@@ -594,7 +571,5 @@
   }
   send_stream.Reconfigure(new_config);
 }
-
-
 }  // namespace test
 }  // namespace webrtc
diff --git a/audio/audio_state.cc b/audio/audio_state.cc
index 5a30c53..a83b681 100644
--- a/audio/audio_state.cc
+++ b/audio/audio_state.cc
@@ -10,13 +10,16 @@
 
 #include "audio/audio_state.h"
 
+#include <algorithm>
+#include <utility>
+#include <vector>
+
 #include "modules/audio_device/include/audio_device.h"
 #include "rtc_base/atomicops.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/ptr_util.h"
 #include "rtc_base/thread.h"
-#include "voice_engine/transmit_mixer.h"
 
 namespace webrtc {
 namespace internal {
@@ -24,15 +27,16 @@
 AudioState::AudioState(const AudioState::Config& config)
     : config_(config),
       voe_base_(config.voice_engine),
-      audio_transport_proxy_(voe_base_->audio_transport(),
-                             config_.audio_processing.get(),
-                             config_.audio_mixer) {
+      audio_transport_(config_.audio_mixer,
+                       config_.audio_processing.get(),
+                       config_.audio_device_module.get()) {
   process_thread_checker_.DetachFromThread();
   RTC_DCHECK(config_.audio_mixer);
 }
 
 AudioState::~AudioState() {
   RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  RTC_DCHECK(sending_streams_.empty());
 }
 
 VoiceEngine* AudioState::voice_engine() {
@@ -47,11 +51,23 @@
 
 bool AudioState::typing_noise_detected() const {
   RTC_DCHECK(thread_checker_.CalledOnValidThread());
-  // TODO(solenberg): Remove const_cast once AudioState owns transmit mixer
-  //                  functionality.
-  voe::TransmitMixer* transmit_mixer =
-      const_cast<AudioState*>(this)->voe_base_->transmit_mixer();
-  return transmit_mixer->typing_noise_detected();
+  return audio_transport_.typing_noise_detected();
+}
+
+void AudioState::AddSendingStream(webrtc::AudioSendStream* stream,
+                                  int sample_rate_hz, size_t num_channels) {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  auto& properties = sending_streams_[stream];
+  properties.sample_rate_hz = sample_rate_hz;
+  properties.num_channels = num_channels;
+  UpdateAudioTransportWithSendingStreams();
+}
+
+void AudioState::RemoveSendingStream(webrtc::AudioSendStream* stream) {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  auto count = sending_streams_.erase(stream);
+  RTC_DCHECK_EQ(1, count);
+  UpdateAudioTransportWithSendingStreams();
 }
 
 void AudioState::SetPlayout(bool enabled) {
@@ -61,33 +77,47 @@
   if (enabled == currently_enabled) {
     return;
   }
-  VoEBase* const voe = VoEBase::GetInterface(voice_engine());
-  RTC_DCHECK(voe);
   if (enabled) {
     null_audio_poller_.reset();
   }
   // Will stop/start playout of the underlying device, if necessary, and
   // remember the setting for when it receives subsequent calls of
   // StartPlayout.
-  voe->SetPlayout(enabled);
+  voe_base_->SetPlayout(enabled);
   if (!enabled) {
     null_audio_poller_ =
-        rtc::MakeUnique<NullAudioPoller>(&audio_transport_proxy_);
+        rtc::MakeUnique<NullAudioPoller>(&audio_transport_);
   }
-  voe->Release();
 }
 
 void AudioState::SetRecording(bool enabled) {
   RTC_LOG(INFO) << "SetRecording(" << enabled << ")";
   RTC_DCHECK(thread_checker_.CalledOnValidThread());
   // TODO(henrika): keep track of state as in SetPlayout().
-  VoEBase* const voe = VoEBase::GetInterface(voice_engine());
-  RTC_DCHECK(voe);
   // Will stop/start recording of the underlying device, if necessary, and
   // remember the setting for when it receives subsequent calls of
   // StartPlayout.
-  voe->SetRecording(enabled);
-  voe->Release();
+  voe_base_->SetRecording(enabled);
+}
+
+AudioState::Stats AudioState::GetAudioInputStats() const {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  const voe::AudioLevel& audio_level = audio_transport_.audio_level();
+  Stats result;
+  result.audio_level = audio_level.LevelFullRange();
+  RTC_DCHECK_LE(0, result.audio_level);
+  RTC_DCHECK_GE(32767, result.audio_level);
+  result.quantized_audio_level = audio_level.Level();
+  RTC_DCHECK_LE(0, result.quantized_audio_level);
+  RTC_DCHECK_GE(9, result.quantized_audio_level);
+  result.total_energy = audio_level.TotalEnergy();
+  result.total_duration = audio_level.TotalDuration();
+  return result;
+}
+
+void AudioState::SetStereoChannelSwapping(bool enable) {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  audio_transport_.SetStereoChannelSwapping(enable);
 }
 
 // Reference count; implementation copied from rtc::RefCountedObject.
@@ -103,6 +133,20 @@
   }
   return rtc::RefCountReleaseStatus::kOtherRefsRemained;
 }
+
+void AudioState::UpdateAudioTransportWithSendingStreams() {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  std::vector<AudioSendStream*> sending_streams;
+  int max_sample_rate_hz = 8000;
+  size_t max_num_channels = 1;
+  for (const auto& kv : sending_streams_) {
+    sending_streams.push_back(kv.first);
+    max_sample_rate_hz = std::max(max_sample_rate_hz, kv.second.sample_rate_hz);
+    max_num_channels = std::max(max_num_channels, kv.second.num_channels);
+  }
+  audio_transport_.UpdateSendingStreams(std::move(sending_streams),
+                                        max_sample_rate_hz, max_num_channels);
+}
 }  // namespace internal
 
 rtc::scoped_refptr<AudioState> AudioState::Create(
diff --git a/audio/audio_state.h b/audio/audio_state.h
index f4bddbf..14dc788 100644
--- a/audio/audio_state.h
+++ b/audio/audio_state.h
@@ -11,9 +11,10 @@
 #ifndef AUDIO_AUDIO_STATE_H_
 #define AUDIO_AUDIO_STATE_H_
 
+#include <map>
 #include <memory>
 
-#include "audio/audio_transport_proxy.h"
+#include "audio/audio_transport_impl.h"
 #include "audio/null_audio_poller.h"
 #include "audio/scoped_voe_interface.h"
 #include "call/audio_state.h"
@@ -24,6 +25,9 @@
 #include "voice_engine/include/voe_base.h"
 
 namespace webrtc {
+
+class AudioSendStream;
+
 namespace internal {
 
 class AudioState final : public webrtc::AudioState {
@@ -36,21 +40,30 @@
     return config_.audio_processing.get();
   }
   AudioTransport* audio_transport() override {
-    return &audio_transport_proxy_;
+    return &audio_transport_;
   }
 
   void SetPlayout(bool enabled) override;
   void SetRecording(bool enabled) override;
 
+  Stats GetAudioInputStats() const override;
+  void SetStereoChannelSwapping(bool enable) override;
+
   VoiceEngine* voice_engine();
   rtc::scoped_refptr<AudioMixer> mixer();
   bool typing_noise_detected() const;
 
+  void AddSendingStream(webrtc::AudioSendStream* stream,
+                        int sample_rate_hz, size_t num_channels);
+  void RemoveSendingStream(webrtc::AudioSendStream* stream);
+
  private:
   // rtc::RefCountInterface implementation.
   void AddRef() const override;
   rtc::RefCountReleaseStatus Release() const override;
 
+  void UpdateAudioTransportWithSendingStreams();
+
   rtc::ThreadChecker thread_checker_;
   rtc::ThreadChecker process_thread_checker_;
   const webrtc::AudioState::Config config_;
@@ -63,14 +76,20 @@
   mutable volatile int ref_count_ = 0;
 
   // Transports mixed audio from the mixer to the audio device and
-  // recorded audio to the VoE AudioTransport.
-  AudioTransportProxy audio_transport_proxy_;
+  // recorded audio to the sending streams.
+  AudioTransportImpl audio_transport_;
 
   // Null audio poller is used to continue polling the audio streams if audio
   // playout is disabled so that audio processing still happens and the audio
   // stats are still updated.
   std::unique_ptr<NullAudioPoller> null_audio_poller_;
 
+  struct StreamProperties {
+    int sample_rate_hz = 0;
+    size_t num_channels = 0;
+  };
+  std::map<webrtc::AudioSendStream*, StreamProperties> sending_streams_;
+
   RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(AudioState);
 };
 }  // namespace internal
diff --git a/audio/audio_state_unittest.cc b/audio/audio_state_unittest.cc
index 28b0a71..abae985 100644
--- a/audio/audio_state_unittest.cc
+++ b/audio/audio_state_unittest.cc
@@ -9,8 +9,11 @@
  */
 
 #include <memory>
+#include <vector>
 
 #include "audio/audio_state.h"
+#include "call/test/mock_audio_send_stream.h"
+#include "modules/audio_device/include/mock_audio_device.h"
 #include "modules/audio_mixer/audio_mixer_impl.h"
 #include "modules/audio_processing/include/mock_audio_processing.h"
 #include "test/gtest.h"
@@ -20,30 +23,26 @@
 namespace test {
 namespace {
 
-const int kSampleRate = 8000;
-const int kNumberOfChannels = 1;
-const int kBytesPerSample = 2;
+constexpr int kSampleRate = 16000;
+constexpr int kNumberOfChannels = 1;
 
 struct ConfigHelper {
   ConfigHelper() : audio_mixer(AudioMixerImpl::Create()) {
-    EXPECT_CALL(mock_voice_engine, audio_transport())
-        .WillRepeatedly(testing::Return(&audio_transport));
-
     audio_state_config.voice_engine = &mock_voice_engine;
     audio_state_config.audio_mixer = audio_mixer;
     audio_state_config.audio_processing =
-        new rtc::RefCountedObject<MockAudioProcessing>();
+        new rtc::RefCountedObject<testing::NiceMock<MockAudioProcessing>>();
+    audio_state_config.audio_device_module =
+        new rtc::RefCountedObject<MockAudioDeviceModule>();
   }
   AudioState::Config& config() { return audio_state_config; }
   MockVoiceEngine& voice_engine() { return mock_voice_engine; }
   rtc::scoped_refptr<AudioMixer> mixer() { return audio_mixer; }
-  MockAudioTransport& original_audio_transport() { return audio_transport; }
 
  private:
   testing::StrictMock<MockVoiceEngine> mock_voice_engine;
   AudioState::Config audio_state_config;
   rtc::scoped_refptr<AudioMixer> audio_mixer;
-  MockAudioTransport audio_transport;
 };
 
 class FakeAudioSource : public AudioMixer::Source {
@@ -60,12 +59,44 @@
                AudioFrameInfo(int sample_rate_hz, AudioFrame* audio_frame));
 };
 
+std::vector<int16_t> Create10msSilentTestData(int sample_rate_hz,
+                                              size_t num_channels) {
+  const int samples_per_channel = sample_rate_hz / 100;
+  std::vector<int16_t> audio_data(samples_per_channel * num_channels, 0);
+  return audio_data;
+}
+
+std::vector<int16_t> Create10msTestData(int sample_rate_hz,
+                                        size_t num_channels) {
+  const int samples_per_channel = sample_rate_hz / 100;
+  std::vector<int16_t> audio_data(samples_per_channel * num_channels, 0);
+  // Fill the first channel with a 1kHz sine wave.
+  const float inc = (2 * 3.14159265f * 1000) / sample_rate_hz;
+  float w = 0.f;
+  for (int i = 0; i < samples_per_channel; ++i) {
+    audio_data[i * num_channels] =
+        static_cast<int16_t>(32767.f * std::sin(w));
+    w += inc;
+  }
+  return audio_data;
+}
+
+std::vector<uint32_t> ComputeChannelLevels(AudioFrame* audio_frame) {
+  const size_t num_channels = audio_frame->num_channels_;
+  const size_t samples_per_channel = audio_frame->samples_per_channel_;
+  std::vector<uint32_t> levels(num_channels, 0);
+  for (size_t i = 0; i < samples_per_channel; ++i) {
+    for (size_t j = 0; j < num_channels; ++j) {
+      levels[j] += std::abs(audio_frame->data()[i * num_channels + j]);
+    }
+  }
+  return levels;
+}
 }  // namespace
 
 TEST(AudioStateTest, Create) {
   ConfigHelper helper;
-  rtc::scoped_refptr<AudioState> audio_state =
-      AudioState::Create(helper.config());
+  auto audio_state = AudioState::Create(helper.config());
   EXPECT_TRUE(audio_state.get());
 }
 
@@ -82,35 +113,165 @@
   EXPECT_EQ(audio_state->voice_engine(), &helper.voice_engine());
 }
 
-// Test that RecordedDataIsAvailable calls get to the original transport.
-TEST(AudioStateAudioPathTest, RecordedAudioArrivesAtOriginalTransport) {
+TEST(AudioStateTest, RecordedAudioArrivesAtSingleStream) {
   ConfigHelper helper;
+  std::unique_ptr<internal::AudioState> audio_state(
+      new internal::AudioState(helper.config()));
 
-  rtc::scoped_refptr<AudioState> audio_state =
-      AudioState::Create(helper.config());
+  MockAudioSendStream stream;
+  audio_state->AddSendingStream(&stream, 8000, 2);
 
-  // Setup completed. Ensure call of original transport is forwarded to new.
-  uint32_t new_mic_level;
-  EXPECT_CALL(
-      helper.original_audio_transport(),
-      RecordedDataIsAvailable(nullptr, kSampleRate / 100, kBytesPerSample,
-                              kNumberOfChannels, kSampleRate, 0, 0, 0, false,
-                              testing::Ref(new_mic_level)));
+  EXPECT_CALL(stream, SendAudioDataForMock(testing::AllOf(
+      testing::Field(&AudioFrame::sample_rate_hz_, testing::Eq(8000)),
+      testing::Field(&AudioFrame::num_channels_, testing::Eq(2u)))))
+          .WillOnce(
+              // Verify that channels are not swapped by default.
+              testing::Invoke([](AudioFrame* audio_frame) {
+                auto levels = ComputeChannelLevels(audio_frame);
+                EXPECT_LT(0u, levels[0]);
+                EXPECT_EQ(0u, levels[1]);
+              }));
+  MockAudioProcessing* ap =
+      static_cast<MockAudioProcessing*>(audio_state->audio_processing());
+  EXPECT_CALL(*ap, set_stream_delay_ms(0));
+  EXPECT_CALL(*ap, set_stream_key_pressed(false));
+  EXPECT_CALL(*ap, ProcessStream(testing::_));
 
+  constexpr int kSampleRate = 16000;
+  constexpr size_t kNumChannels = 2;
+  auto audio_data = Create10msTestData(kSampleRate, kNumChannels);
+  uint32_t new_mic_level = 667;
   audio_state->audio_transport()->RecordedDataIsAvailable(
-      nullptr, kSampleRate / 100, kBytesPerSample, kNumberOfChannels,
-      kSampleRate, 0, 0, 0, false, new_mic_level);
+      &audio_data[0], kSampleRate / 100, kNumChannels * 2,
+      kNumChannels, kSampleRate, 0, 0, 0, false, new_mic_level);
+  EXPECT_EQ(667u, new_mic_level);
+
+  audio_state->RemoveSendingStream(&stream);
 }
 
-TEST(AudioStateAudioPathTest,
-     QueryingProxyForAudioShouldResultInGetAudioCallOnMixerSource) {
+TEST(AudioStateTest, RecordedAudioArrivesAtMultipleStreams) {
   ConfigHelper helper;
+  std::unique_ptr<internal::AudioState> audio_state(
+      new internal::AudioState(helper.config()));
 
-  rtc::scoped_refptr<AudioState> audio_state =
-      AudioState::Create(helper.config());
+  MockAudioSendStream stream_1;
+  MockAudioSendStream stream_2;
+  audio_state->AddSendingStream(&stream_1, 8001, 2);
+  audio_state->AddSendingStream(&stream_2, 32000, 1);
+
+  EXPECT_CALL(stream_1, SendAudioDataForMock(testing::AllOf(
+      testing::Field(&AudioFrame::sample_rate_hz_, testing::Eq(16000)),
+      testing::Field(&AudioFrame::num_channels_, testing::Eq(1u)))))
+          .WillOnce(
+              // Verify that there is output signal.
+              testing::Invoke([](AudioFrame* audio_frame) {
+                auto levels = ComputeChannelLevels(audio_frame);
+                EXPECT_LT(0u, levels[0]);
+              }));
+  EXPECT_CALL(stream_2, SendAudioDataForMock(testing::AllOf(
+      testing::Field(&AudioFrame::sample_rate_hz_, testing::Eq(16000)),
+      testing::Field(&AudioFrame::num_channels_, testing::Eq(1u)))))
+          .WillOnce(
+              // Verify that there is output signal.
+              testing::Invoke([](AudioFrame* audio_frame) {
+                auto levels = ComputeChannelLevels(audio_frame);
+                EXPECT_LT(0u, levels[0]);
+              }));
+  MockAudioProcessing* ap =
+      static_cast<MockAudioProcessing*>(audio_state->audio_processing());
+  EXPECT_CALL(*ap, set_stream_delay_ms(5));
+  EXPECT_CALL(*ap, set_stream_key_pressed(true));
+  EXPECT_CALL(*ap, ProcessStream(testing::_));
+
+  constexpr int kSampleRate = 16000;
+  constexpr size_t kNumChannels = 1;
+  auto audio_data = Create10msTestData(kSampleRate, kNumChannels);
+  uint32_t new_mic_level = 667;
+  audio_state->audio_transport()->RecordedDataIsAvailable(
+      &audio_data[0], kSampleRate / 100, kNumChannels * 2,
+      kNumChannels, kSampleRate, 5, 0, 0, true, new_mic_level);
+  EXPECT_EQ(667u, new_mic_level);
+
+  audio_state->RemoveSendingStream(&stream_1);
+  audio_state->RemoveSendingStream(&stream_2);
+}
+
+TEST(AudioStateTest, EnableChannelSwap) {
+  constexpr int kSampleRate = 16000;
+  constexpr size_t kNumChannels = 2;
+
+  ConfigHelper helper;
+  std::unique_ptr<internal::AudioState> audio_state(
+      new internal::AudioState(helper.config()));
+  audio_state->SetStereoChannelSwapping(true);
+
+  MockAudioSendStream stream;
+  audio_state->AddSendingStream(&stream, kSampleRate, kNumChannels);
+
+  EXPECT_CALL(stream, SendAudioDataForMock(testing::_))
+      .WillOnce(
+          // Verify that channels are swapped.
+          testing::Invoke([](AudioFrame* audio_frame) {
+            auto levels = ComputeChannelLevels(audio_frame);
+            EXPECT_EQ(0u, levels[0]);
+            EXPECT_LT(0u, levels[1]);
+          }));
+
+  auto audio_data = Create10msTestData(kSampleRate, kNumChannels);
+  uint32_t new_mic_level = 667;
+  audio_state->audio_transport()->RecordedDataIsAvailable(
+      &audio_data[0], kSampleRate / 100, kNumChannels * 2,
+      kNumChannels, kSampleRate, 0, 0, 0, false, new_mic_level);
+  EXPECT_EQ(667u, new_mic_level);
+
+  audio_state->RemoveSendingStream(&stream);
+}
+
+TEST(AudioStateTest, InputLevelStats) {
+  constexpr int kSampleRate = 16000;
+  constexpr size_t kNumChannels = 1;
+
+  ConfigHelper helper;
+  std::unique_ptr<internal::AudioState> audio_state(
+      new internal::AudioState(helper.config()));
+
+  // Push a silent buffer -> Level stats should be zeros except for duration.
+  {
+    auto audio_data = Create10msSilentTestData(kSampleRate, kNumChannels);
+    uint32_t new_mic_level = 667;
+    audio_state->audio_transport()->RecordedDataIsAvailable(
+        &audio_data[0], kSampleRate / 100, kNumChannels * 2,
+        kNumChannels, kSampleRate, 0, 0, 0, false, new_mic_level);
+    auto stats = audio_state->GetAudioInputStats();
+    EXPECT_EQ(0, stats.audio_level);
+    EXPECT_EQ(0, stats.quantized_audio_level);
+    EXPECT_THAT(stats.total_energy, testing::DoubleEq(0.0));
+    EXPECT_THAT(stats.total_duration, testing::DoubleEq(0.01));
+  }
+
+  // Push 10 non-silent buffers -> Level stats should be non-zero.
+  {
+    auto audio_data = Create10msTestData(kSampleRate, kNumChannels);
+    uint32_t new_mic_level = 667;
+    for (int i = 0; i < 10; ++i) {
+      audio_state->audio_transport()->RecordedDataIsAvailable(
+          &audio_data[0], kSampleRate / 100, kNumChannels * 2,
+          kNumChannels, kSampleRate, 0, 0, 0, false, new_mic_level);
+    }
+    auto stats = audio_state->GetAudioInputStats();
+    EXPECT_EQ(32767, stats.audio_level);
+    EXPECT_EQ(9, stats.quantized_audio_level);
+    EXPECT_THAT(stats.total_energy, testing::DoubleEq(0.01));
+    EXPECT_THAT(stats.total_duration, testing::DoubleEq(0.11));
+  }
+}
+
+TEST(AudioStateTest,
+     QueryingTransportForAudioShouldResultInGetAudioCallOnMixerSource) {
+  ConfigHelper helper;
+  auto audio_state = AudioState::Create(helper.config());
 
   FakeAudioSource fake_source;
-
   helper.mixer()->AddSource(&fake_source);
 
   EXPECT_CALL(fake_source, GetAudioFrameWithInfo(testing::_, testing::_))
@@ -127,7 +288,7 @@
   int64_t elapsed_time_ms;
   int64_t ntp_time_ms;
   audio_state->audio_transport()->NeedMorePlayData(
-      kSampleRate / 100, kBytesPerSample, kNumberOfChannels, kSampleRate,
+      kSampleRate / 100, kNumberOfChannels * 2, kNumberOfChannels, kSampleRate,
       audio_buffer, n_samples_out, &elapsed_time_ms, &ntp_time_ms);
 }
 }  // namespace test
diff --git a/audio/audio_transport_impl.cc b/audio/audio_transport_impl.cc
new file mode 100644
index 0000000..586fc46
--- /dev/null
+++ b/audio/audio_transport_impl.cc
@@ -0,0 +1,294 @@
+/*
+ *  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 "audio/audio_transport_impl.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "audio/utility/audio_frame_operations.h"
+#include "call/audio_send_stream.h"
+#include "rtc_base/logging.h"
+#include "voice_engine/utility.h"
+
+namespace webrtc {
+
+namespace {
+
+// We want to process at the lowest sample rate and channel count possible
+// without losing information. Choose the lowest native rate at least equal to
+// the minimum of input and codec rates, choose lowest channel count, and
+// configure the audio frame.
+void InitializeCaptureFrame(int input_sample_rate,
+                            int send_sample_rate_hz,
+                            size_t input_num_channels,
+                            size_t send_num_channels,
+                            AudioFrame* audio_frame) {
+  RTC_DCHECK(audio_frame);
+  int min_processing_rate_hz = std::min(input_sample_rate, send_sample_rate_hz);
+  for (int native_rate_hz : AudioProcessing::kNativeSampleRatesHz) {
+    audio_frame->sample_rate_hz_ = native_rate_hz;
+    if (audio_frame->sample_rate_hz_ >= min_processing_rate_hz) {
+      break;
+    }
+  }
+  audio_frame->num_channels_ = std::min(input_num_channels, send_num_channels);
+}
+
+void ProcessCaptureFrame(int analog_level,
+                         uint32_t delay_ms,
+                         bool key_pressed,
+                         bool swap_stereo_channels,
+                         AudioProcessing* audio_processing,
+                         AudioFrame* audio_frame) {
+  RTC_DCHECK(audio_processing);
+  RTC_DCHECK(audio_frame);
+  RTC_DCHECK(
+      !audio_processing->echo_cancellation()->is_drift_compensation_enabled());
+  GainControl* agc = audio_processing->gain_control();
+  int error = agc->set_stream_analog_level(analog_level);
+  RTC_DCHECK_EQ(0, error) <<
+      "set_stream_analog_level failed: analog_level = " << analog_level;
+  audio_processing->set_stream_delay_ms(delay_ms);
+  audio_processing->set_stream_key_pressed(key_pressed);
+  error = audio_processing->ProcessStream(audio_frame);
+  RTC_DCHECK_EQ(0, error) << "ProcessStream() error: " << error;
+  if (swap_stereo_channels) {
+    AudioFrameOperations::SwapStereoChannels(audio_frame);
+  }
+}
+
+// Resample audio in |frame| to given sample rate preserving the
+// channel count and place the result in |destination|.
+int Resample(const AudioFrame& frame,
+             const int destination_sample_rate,
+             PushResampler<int16_t>* resampler,
+             int16_t* destination) {
+  const int number_of_channels = static_cast<int>(frame.num_channels_);
+  const int target_number_of_samples_per_channel =
+      destination_sample_rate / 100;
+  resampler->InitializeIfNeeded(frame.sample_rate_hz_, destination_sample_rate,
+                                number_of_channels);
+
+  // TODO(yujo): make resampler take an AudioFrame, and add special case
+  // handling of muted frames.
+  return resampler->Resample(
+      frame.data(), frame.samples_per_channel_ * number_of_channels,
+      destination, number_of_channels * target_number_of_samples_per_channel);
+}
+}  // namespace
+
+AudioTransportImpl::AudioTransportImpl(AudioMixer* mixer,
+                                       AudioProcessing* audio_processing,
+                                       AudioDeviceModule* audio_device_module)
+    : audio_processing_(audio_processing),
+      audio_device_module_(audio_device_module),
+      mixer_(mixer) {
+  RTC_DCHECK(mixer);
+  RTC_DCHECK(audio_processing);
+  RTC_DCHECK(audio_device_module);
+}
+
+AudioTransportImpl::~AudioTransportImpl() {}
+
+// Not used in Chromium. Process captured audio and distribute to all sending
+// streams, and try to do this at the lowest possible sample rate.
+int32_t AudioTransportImpl::RecordedDataIsAvailable(
+    const void* audio_data,
+    const size_t number_of_frames,
+    const size_t bytes_per_sample,
+    const size_t number_of_channels,
+    const uint32_t sample_rate,
+    const uint32_t audio_delay_milliseconds,
+    const int32_t /*clock_drift*/,
+    const uint32_t volume,
+    const bool key_pressed,
+    uint32_t& /*new_mic_volume*/) {  // NOLINT: to avoid changing APIs
+  RTC_DCHECK(audio_data);
+  RTC_DCHECK_GE(number_of_channels, 1);
+  RTC_DCHECK_LE(number_of_channels, 2);
+  RTC_DCHECK_EQ(2 * number_of_channels, bytes_per_sample);
+  RTC_DCHECK_GE(sample_rate, AudioProcessing::NativeRate::kSampleRate8kHz);
+  // 100 = 1 second / data duration (10 ms).
+  RTC_DCHECK_EQ(number_of_frames * 100, sample_rate);
+  RTC_DCHECK_LE(bytes_per_sample * number_of_frames * number_of_channels,
+                AudioFrame::kMaxDataSizeBytes);
+
+  // TODO(solenberg): Remove volume handling since it is now always 0.
+  uint16_t voe_mic_level = 0;
+  {
+    constexpr uint32_t kMaxVolumeLevel = 255;
+    uint32_t max_volume = 0;
+
+    // Check for zero to skip this calculation; the consumer may use this to
+    // indicate no volume is available.
+    if (volume != 0) {
+      // Scale from ADM to VoE level range
+      if (audio_device_module_->MaxMicrophoneVolume(&max_volume) == 0) {
+        if (max_volume != 0) {
+          voe_mic_level = static_cast<uint16_t>(
+              (volume * kMaxVolumeLevel + static_cast<int>(max_volume / 2)) /
+              max_volume);
+        }
+      }
+      // We learned that on certain systems (e.g Linux) the voe_mic_level
+      // can be greater than the maxVolumeLevel therefore
+      // we are going to cap the voe_mic_level to the maxVolumeLevel
+      // and change the maxVolume to volume if it turns out that
+      // the voe_mic_level is indeed greater than the maxVolumeLevel.
+      if (voe_mic_level > kMaxVolumeLevel) {
+        voe_mic_level = kMaxVolumeLevel;
+        max_volume = volume;
+      }
+    }
+  }
+
+  int send_sample_rate_hz = 0;
+  size_t send_num_channels = 0;
+  bool swap_stereo_channels = false;
+  {
+    rtc::CritScope lock(&capture_lock_);
+    send_sample_rate_hz = send_sample_rate_hz_;
+    send_num_channels = send_num_channels_;
+    swap_stereo_channels = swap_stereo_channels_;
+  }
+
+  std::unique_ptr<AudioFrame> audio_frame(new AudioFrame());
+  InitializeCaptureFrame(sample_rate, send_sample_rate_hz,
+                         number_of_channels, send_num_channels,
+                         audio_frame.get());
+  voe::RemixAndResample(static_cast<const int16_t*>(audio_data),
+                        number_of_frames, number_of_channels, sample_rate,
+                        &capture_resampler_, audio_frame.get());
+  ProcessCaptureFrame(voe_mic_level, audio_delay_milliseconds, key_pressed,
+                      swap_stereo_channels, audio_processing_,
+                      audio_frame.get());
+
+  // Typing detection (utilizes the APM/VAD decision). We let the VAD determine
+  // if we're using this feature or not.
+  // TODO(solenberg): is_enabled() takes a lock. Work around that.
+  bool typing_detected = false;
+  if (audio_processing_->voice_detection()->is_enabled()) {
+    if (audio_frame->vad_activity_ != AudioFrame::kVadUnknown) {
+      bool vad_active = audio_frame->vad_activity_ == AudioFrame::kVadActive;
+      typing_detected = typing_detection_.Process(key_pressed, vad_active);
+    }
+  }
+
+  // Measure audio level of speech after all processing.
+  double sample_duration = static_cast<double>(number_of_frames) / sample_rate;
+  audio_level_.ComputeLevel(*audio_frame.get(), sample_duration);
+
+  // Copy frame and push to each sending stream. The copy is required since an
+  // encoding task will be posted internally to each stream.
+  {
+    rtc::CritScope lock(&capture_lock_);
+    typing_noise_detected_ = typing_detected;
+
+    RTC_DCHECK_GT(audio_frame->samples_per_channel_, 0);
+    if (!sending_streams_.empty()) {
+      auto it = sending_streams_.begin();
+      while (++it != sending_streams_.end()) {
+        std::unique_ptr<AudioFrame> audio_frame_copy(new AudioFrame());
+        audio_frame_copy->CopyFrom(*audio_frame.get());
+        (*it)->SendAudioData(std::move(audio_frame_copy));
+      }
+      // Send the original frame to the first stream w/o copying.
+      (*sending_streams_.begin())->SendAudioData(std::move(audio_frame));
+    }
+  }
+
+  return 0;
+}
+
+// Mix all received streams, feed the result to the AudioProcessing module, then
+// resample the result to the requested output rate.
+int32_t AudioTransportImpl::NeedMorePlayData(const size_t nSamples,
+                                              const size_t nBytesPerSample,
+                                              const size_t nChannels,
+                                              const uint32_t samplesPerSec,
+                                              void* audioSamples,
+                                              size_t& nSamplesOut,
+                                              int64_t* elapsed_time_ms,
+                                              int64_t* ntp_time_ms) {
+  RTC_DCHECK_EQ(sizeof(int16_t) * nChannels, nBytesPerSample);
+  RTC_DCHECK_GE(nChannels, 1);
+  RTC_DCHECK_LE(nChannels, 2);
+  RTC_DCHECK_GE(
+      samplesPerSec,
+      static_cast<uint32_t>(AudioProcessing::NativeRate::kSampleRate8kHz));
+
+  // 100 = 1 second / data duration (10 ms).
+  RTC_DCHECK_EQ(nSamples * 100, samplesPerSec);
+  RTC_DCHECK_LE(nBytesPerSample * nSamples * nChannels,
+                AudioFrame::kMaxDataSizeBytes);
+
+  mixer_->Mix(nChannels, &mixed_frame_);
+  *elapsed_time_ms = mixed_frame_.elapsed_time_ms_;
+  *ntp_time_ms = mixed_frame_.ntp_time_ms_;
+
+  const auto error = audio_processing_->ProcessReverseStream(&mixed_frame_);
+  RTC_DCHECK_EQ(error, AudioProcessing::kNoError);
+
+  nSamplesOut = Resample(mixed_frame_, samplesPerSec, &render_resampler_,
+                         static_cast<int16_t*>(audioSamples));
+  RTC_DCHECK_EQ(nSamplesOut, nChannels * nSamples);
+  return 0;
+}
+
+// Used by Chromium - same as NeedMorePlayData() but because Chrome has its
+// own APM instance, does not call audio_processing_->ProcessReverseStream().
+void AudioTransportImpl::PullRenderData(int bits_per_sample,
+                                         int sample_rate,
+                                         size_t number_of_channels,
+                                         size_t number_of_frames,
+                                         void* audio_data,
+                                         int64_t* elapsed_time_ms,
+                                         int64_t* ntp_time_ms) {
+  RTC_DCHECK_EQ(bits_per_sample, 16);
+  RTC_DCHECK_GE(number_of_channels, 1);
+  RTC_DCHECK_LE(number_of_channels, 2);
+  RTC_DCHECK_GE(sample_rate, AudioProcessing::NativeRate::kSampleRate8kHz);
+
+  // 100 = 1 second / data duration (10 ms).
+  RTC_DCHECK_EQ(number_of_frames * 100, sample_rate);
+
+  // 8 = bits per byte.
+  RTC_DCHECK_LE(bits_per_sample / 8 * number_of_frames * number_of_channels,
+                AudioFrame::kMaxDataSizeBytes);
+  mixer_->Mix(number_of_channels, &mixed_frame_);
+  *elapsed_time_ms = mixed_frame_.elapsed_time_ms_;
+  *ntp_time_ms = mixed_frame_.ntp_time_ms_;
+
+  auto output_samples = Resample(mixed_frame_, sample_rate, &render_resampler_,
+                                 static_cast<int16_t*>(audio_data));
+  RTC_DCHECK_EQ(output_samples, number_of_channels * number_of_frames);
+}
+
+void AudioTransportImpl::UpdateSendingStreams(
+    std::vector<AudioSendStream*> streams, int send_sample_rate_hz,
+    size_t send_num_channels) {
+  rtc::CritScope lock(&capture_lock_);
+  sending_streams_ = std::move(streams);
+  send_sample_rate_hz_ = send_sample_rate_hz;
+  send_num_channels_ = send_num_channels;
+}
+
+void AudioTransportImpl::SetStereoChannelSwapping(bool enable) {
+  rtc::CritScope lock(&capture_lock_);
+  swap_stereo_channels_ = enable;
+}
+
+bool AudioTransportImpl::typing_noise_detected() const {
+  rtc::CritScope lock(&capture_lock_);
+  return typing_noise_detected_;
+}
+}  // namespace webrtc
diff --git a/audio/audio_transport_impl.h b/audio/audio_transport_impl.h
new file mode 100644
index 0000000..e7de7e9
--- /dev/null
+++ b/audio/audio_transport_impl.h
@@ -0,0 +1,100 @@
+/*
+ *  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 AUDIO_AUDIO_TRANSPORT_IMPL_H_
+#define AUDIO_AUDIO_TRANSPORT_IMPL_H_
+
+#include <vector>
+
+#include "api/audio/audio_mixer.h"
+#include "common_audio/resampler/include/push_resampler.h"
+#include "modules/audio_device/include/audio_device.h"
+#include "modules/audio_processing/include/audio_processing.h"
+#include "modules/audio_processing/typing_detection.h"
+#include "rtc_base/constructormagic.h"
+#include "rtc_base/criticalsection.h"
+#include "rtc_base/scoped_ref_ptr.h"
+#include "rtc_base/thread_annotations.h"
+#include "voice_engine/audio_level.h"
+
+namespace webrtc {
+
+class AudioSendStream;
+
+class AudioTransportImpl : public AudioTransport {
+ public:
+  AudioTransportImpl(AudioMixer* mixer,
+                     AudioProcessing* audio_processing,
+                     AudioDeviceModule* audio_device_module);
+  ~AudioTransportImpl() override;
+
+  int32_t RecordedDataIsAvailable(const void* audioSamples,
+                                  const size_t nSamples,
+                                  const size_t nBytesPerSample,
+                                  const size_t nChannels,
+                                  const uint32_t samplesPerSec,
+                                  const uint32_t totalDelayMS,
+                                  const int32_t clockDrift,
+                                  const uint32_t currentMicLevel,
+                                  const bool keyPressed,
+                                  uint32_t& newMicLevel) override;
+
+  int32_t NeedMorePlayData(const size_t nSamples,
+                           const size_t nBytesPerSample,
+                           const size_t nChannels,
+                           const uint32_t samplesPerSec,
+                           void* audioSamples,
+                           size_t& nSamplesOut,
+                           int64_t* elapsed_time_ms,
+                           int64_t* ntp_time_ms) override;
+
+  void PullRenderData(int bits_per_sample,
+                      int sample_rate,
+                      size_t number_of_channels,
+                      size_t number_of_frames,
+                      void* audio_data,
+                      int64_t* elapsed_time_ms,
+                      int64_t* ntp_time_ms) override;
+
+  void UpdateSendingStreams(std::vector<AudioSendStream*> streams,
+                            int send_sample_rate_hz, size_t send_num_channels);
+  void SetStereoChannelSwapping(bool enable);
+  bool typing_noise_detected() const;
+  const voe::AudioLevel& audio_level() const {
+    return audio_level_;
+  }
+
+ private:
+  // Shared.
+  AudioProcessing* audio_processing_ = nullptr;
+
+  // Capture side.
+  rtc::CriticalSection capture_lock_;
+  std::vector<AudioSendStream*> sending_streams_ RTC_GUARDED_BY(capture_lock_);
+  int send_sample_rate_hz_ RTC_GUARDED_BY(capture_lock_) = 8000;
+  size_t send_num_channels_ RTC_GUARDED_BY(capture_lock_) = 1;
+  bool typing_noise_detected_ RTC_GUARDED_BY(capture_lock_) = false;
+  bool swap_stereo_channels_ RTC_GUARDED_BY(capture_lock_) = false;
+  AudioDeviceModule* audio_device_module_ = nullptr;
+  PushResampler<int16_t> capture_resampler_;
+  voe::AudioLevel audio_level_;
+  TypingDetection typing_detection_;
+
+  // Render side.
+  rtc::scoped_refptr<AudioMixer> mixer_;
+  AudioFrame mixed_frame_;
+  // Converts mixed audio to the audio device output rate.
+  PushResampler<int16_t> render_resampler_;
+
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(AudioTransportImpl);
+};
+}  // namespace webrtc
+
+#endif  // AUDIO_AUDIO_TRANSPORT_IMPL_H_
diff --git a/audio/audio_transport_proxy.cc b/audio/audio_transport_proxy.cc
deleted file mode 100644
index e3201ea..0000000
--- a/audio/audio_transport_proxy.cc
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- *  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 "audio/audio_transport_proxy.h"
-
-namespace webrtc {
-
-namespace {
-// Resample audio in |frame| to given sample rate preserving the
-// channel count and place the result in |destination|.
-int Resample(const AudioFrame& frame,
-             const int destination_sample_rate,
-             PushResampler<int16_t>* resampler,
-             int16_t* destination) {
-  const int number_of_channels = static_cast<int>(frame.num_channels_);
-  const int target_number_of_samples_per_channel =
-      destination_sample_rate / 100;
-  resampler->InitializeIfNeeded(frame.sample_rate_hz_, destination_sample_rate,
-                                number_of_channels);
-
-  // TODO(yujo): make resampler take an AudioFrame, and add special case
-  // handling of muted frames.
-  return resampler->Resample(
-      frame.data(), frame.samples_per_channel_ * number_of_channels,
-      destination, number_of_channels * target_number_of_samples_per_channel);
-}
-}  // namespace
-
-AudioTransportProxy::AudioTransportProxy(AudioTransport* voe_audio_transport,
-                                         AudioProcessing* audio_processing,
-                                         AudioMixer* mixer)
-    : voe_audio_transport_(voe_audio_transport),
-      audio_processing_(audio_processing),
-      mixer_(mixer) {
-  RTC_DCHECK(voe_audio_transport);
-  RTC_DCHECK(audio_processing);
-  RTC_DCHECK(mixer);
-}
-
-AudioTransportProxy::~AudioTransportProxy() {}
-
-int32_t AudioTransportProxy::RecordedDataIsAvailable(
-    const void* audioSamples,
-    const size_t nSamples,
-    const size_t nBytesPerSample,
-    const size_t nChannels,
-    const uint32_t samplesPerSec,
-    const uint32_t totalDelayMS,
-    const int32_t clockDrift,
-    const uint32_t currentMicLevel,
-    const bool keyPressed,
-    uint32_t& newMicLevel) {  // NOLINT: to avoid changing APIs
-  // Pass call through to original audio transport instance.
-  return voe_audio_transport_->RecordedDataIsAvailable(
-      audioSamples, nSamples, nBytesPerSample, nChannels, samplesPerSec,
-      totalDelayMS, clockDrift, currentMicLevel, keyPressed, newMicLevel);
-}
-
-int32_t AudioTransportProxy::NeedMorePlayData(const size_t nSamples,
-                                              const size_t nBytesPerSample,
-                                              const size_t nChannels,
-                                              const uint32_t samplesPerSec,
-                                              void* audioSamples,
-                                              size_t& nSamplesOut,
-                                              int64_t* elapsed_time_ms,
-                                              int64_t* ntp_time_ms) {
-  RTC_DCHECK_EQ(sizeof(int16_t) * nChannels, nBytesPerSample);
-  RTC_DCHECK_GE(nChannels, 1);
-  RTC_DCHECK_LE(nChannels, 2);
-  RTC_DCHECK_GE(
-      samplesPerSec,
-      static_cast<uint32_t>(AudioProcessing::NativeRate::kSampleRate8kHz));
-
-  // 100 = 1 second / data duration (10 ms).
-  RTC_DCHECK_EQ(nSamples * 100, samplesPerSec);
-  RTC_DCHECK_LE(nBytesPerSample * nSamples * nChannels,
-                AudioFrame::kMaxDataSizeBytes);
-
-  mixer_->Mix(nChannels, &mixed_frame_);
-  *elapsed_time_ms = mixed_frame_.elapsed_time_ms_;
-  *ntp_time_ms = mixed_frame_.ntp_time_ms_;
-
-  const auto error = audio_processing_->ProcessReverseStream(&mixed_frame_);
-  RTC_DCHECK_EQ(error, AudioProcessing::kNoError);
-
-  nSamplesOut = Resample(mixed_frame_, samplesPerSec, &resampler_,
-                         static_cast<int16_t*>(audioSamples));
-  RTC_DCHECK_EQ(nSamplesOut, nChannels * nSamples);
-  return 0;
-}
-
-void AudioTransportProxy::PushCaptureData(int voe_channel,
-                                          const void* audio_data,
-                                          int bits_per_sample,
-                                          int sample_rate,
-                                          size_t number_of_channels,
-                                          size_t number_of_frames) {
-  // This is part of deprecated VoE interface operating on specific
-  // VoE channels. It should not be used.
-  RTC_NOTREACHED();
-}
-
-void AudioTransportProxy::PullRenderData(int bits_per_sample,
-                                         int sample_rate,
-                                         size_t number_of_channels,
-                                         size_t number_of_frames,
-                                         void* audio_data,
-                                         int64_t* elapsed_time_ms,
-                                         int64_t* ntp_time_ms) {
-  RTC_DCHECK_EQ(bits_per_sample, 16);
-  RTC_DCHECK_GE(number_of_channels, 1);
-  RTC_DCHECK_LE(number_of_channels, 2);
-  RTC_DCHECK_GE(sample_rate, AudioProcessing::NativeRate::kSampleRate8kHz);
-
-  // 100 = 1 second / data duration (10 ms).
-  RTC_DCHECK_EQ(number_of_frames * 100, sample_rate);
-
-  // 8 = bits per byte.
-  RTC_DCHECK_LE(bits_per_sample / 8 * number_of_frames * number_of_channels,
-                AudioFrame::kMaxDataSizeBytes);
-  mixer_->Mix(number_of_channels, &mixed_frame_);
-  *elapsed_time_ms = mixed_frame_.elapsed_time_ms_;
-  *ntp_time_ms = mixed_frame_.ntp_time_ms_;
-
-  const auto output_samples = Resample(mixed_frame_, sample_rate, &resampler_,
-                                       static_cast<int16_t*>(audio_data));
-  RTC_DCHECK_EQ(output_samples, number_of_channels * number_of_frames);
-}
-
-}  // namespace webrtc
diff --git a/audio/audio_transport_proxy.h b/audio/audio_transport_proxy.h
deleted file mode 100644
index a51a7db..0000000
--- a/audio/audio_transport_proxy.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- *  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 AUDIO_AUDIO_TRANSPORT_PROXY_H_
-#define AUDIO_AUDIO_TRANSPORT_PROXY_H_
-
-#include "api/audio/audio_mixer.h"
-#include "common_audio/resampler/include/push_resampler.h"
-#include "modules/audio_device/include/audio_device_defines.h"
-#include "modules/audio_processing/include/audio_processing.h"
-#include "rtc_base/constructormagic.h"
-#include "rtc_base/scoped_ref_ptr.h"
-
-namespace webrtc {
-
-class AudioTransportProxy : public AudioTransport {
- public:
-  AudioTransportProxy(AudioTransport* voe_audio_transport,
-                      AudioProcessing* audio_processing,
-                      AudioMixer* mixer);
-
-  ~AudioTransportProxy() override;
-
-  int32_t RecordedDataIsAvailable(const void* audioSamples,
-                                  const size_t nSamples,
-                                  const size_t nBytesPerSample,
-                                  const size_t nChannels,
-                                  const uint32_t samplesPerSec,
-                                  const uint32_t totalDelayMS,
-                                  const int32_t clockDrift,
-                                  const uint32_t currentMicLevel,
-                                  const bool keyPressed,
-                                  uint32_t& newMicLevel) override;
-
-  int32_t NeedMorePlayData(const size_t nSamples,
-                           const size_t nBytesPerSample,
-                           const size_t nChannels,
-                           const uint32_t samplesPerSec,
-                           void* audioSamples,
-                           size_t& nSamplesOut,
-                           int64_t* elapsed_time_ms,
-                           int64_t* ntp_time_ms) override;
-
-  void PushCaptureData(int voe_channel,
-                       const void* audio_data,
-                       int bits_per_sample,
-                       int sample_rate,
-                       size_t number_of_channels,
-                       size_t number_of_frames) override;
-
-  void PullRenderData(int bits_per_sample,
-                      int sample_rate,
-                      size_t number_of_channels,
-                      size_t number_of_frames,
-                      void* audio_data,
-                      int64_t* elapsed_time_ms,
-                      int64_t* ntp_time_ms) override;
-
- private:
-  AudioTransport* voe_audio_transport_;
-  AudioProcessing* audio_processing_;
-  rtc::scoped_refptr<AudioMixer> mixer_;
-  AudioFrame mixed_frame_;
-  // Converts mixed audio to the audio device output rate.
-  PushResampler<int16_t> resampler_;
-
-  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(AudioTransportProxy);
-};
-}  // namespace webrtc
-
-#endif  // AUDIO_AUDIO_TRANSPORT_PROXY_H_
diff --git a/call/BUILD.gn b/call/BUILD.gn
index 6f936b8..903df3b 100644
--- a/call/BUILD.gn
+++ b/call/BUILD.gn
@@ -294,4 +294,16 @@
       "//testing/gmock",
     ]
   }
+
+  rtc_source_set("mock_call_interfaces") {
+    testonly = true
+
+    sources = [
+      "test/mock_audio_send_stream.h",
+    ]
+    deps = [
+      ":call_interfaces",
+      "//test:test_support",
+    ]
+  }
 }
diff --git a/call/audio_send_stream.h b/call/audio_send_stream.h
index 4912182..55f4e2f 100644
--- a/call/audio_send_stream.h
+++ b/call/audio_send_stream.h
@@ -28,6 +28,8 @@
 
 namespace webrtc {
 
+class AudioFrame;
+
 // WORK IN PROGRESS
 // This class is under development and is not yet intended for for use outside
 // of WebRtc/Libjingle. Please use the VoiceEngine API instead.
@@ -146,6 +148,10 @@
   // When a stream is stopped, it can't receive, process or deliver packets.
   virtual void Stop() = 0;
 
+  // Encode and send audio.
+  virtual void SendAudioData(
+      std::unique_ptr<webrtc::AudioFrame> audio_frame) = 0;
+
   // TODO(solenberg): Make payload_type a config property instead.
   virtual bool SendTelephoneEvent(int payload_type, int payload_frequency,
                                   int event, int duration_ms) = 0;
diff --git a/call/audio_state.h b/call/audio_state.h
index 3f96da1..56d5d44 100644
--- a/call/audio_state.h
+++ b/call/audio_state.h
@@ -47,6 +47,16 @@
     rtc::scoped_refptr<webrtc::AudioDeviceModule> audio_device_module;
   };
 
+  struct Stats {
+    // Audio peak level (max(abs())), linearly on the interval [0,32767].
+    int32_t audio_level = -1;
+    // Audio peak level (max(abs())), logarithmically on the interval [0,9].
+    int8_t quantized_audio_level = -1;
+    // See: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-totalaudioenergy
+    double total_energy = 0.0f;
+    double total_duration = 0.0f;
+  };
+
   virtual AudioProcessing* audio_processing() = 0;
   virtual AudioTransport* audio_transport() = 0;
 
@@ -61,6 +71,9 @@
   // packets will be encoded or transmitted.
   virtual void SetRecording(bool enabled) = 0;
 
+  virtual Stats GetAudioInputStats() const = 0;
+  virtual void SetStereoChannelSwapping(bool enable) = 0;
+
   // TODO(solenberg): Replace scoped_refptr with shared_ptr once we can use it.
   static rtc::scoped_refptr<AudioState> Create(
       const AudioState::Config& config);
diff --git a/call/call_perf_tests.cc b/call/call_perf_tests.cc
index 90eaa5a..db3b5e4 100644
--- a/call/call_perf_tests.cc
+++ b/call/call_perf_tests.cc
@@ -158,10 +158,8 @@
   audio_net_config.queue_delay_ms = 500;
   audio_net_config.loss_percent = 5;
 
-  rtc::scoped_refptr<AudioProcessing> audio_processing;
   VoiceEngine* voice_engine;
   VoEBase* voe_base;
-  std::unique_ptr<FakeAudioDevice> fake_audio_device;
   VideoRtcpAndSyncObserver observer(Clock::GetRealTimeClock());
 
   std::map<uint8_t, MediaType> audio_pt_map;
@@ -177,14 +175,14 @@
 
   task_queue_.SendTask([&]() {
     metrics::Reset();
-    audio_processing = AudioProcessing::Create();
     voice_engine = VoiceEngine::Create();
     voe_base = VoEBase::GetInterface(voice_engine);
-    fake_audio_device = rtc::MakeUnique<FakeAudioDevice>(
-        FakeAudioDevice::CreatePulsedNoiseCapturer(256, 48000),
-        FakeAudioDevice::CreateDiscardRenderer(48000), audio_rtp_speed);
+    rtc::scoped_refptr<FakeAudioDevice> fake_audio_device =
+        new rtc::RefCountedObject<FakeAudioDevice>(
+            FakeAudioDevice::CreatePulsedNoiseCapturer(256, 48000),
+            FakeAudioDevice::CreateDiscardRenderer(48000), audio_rtp_speed);
     EXPECT_EQ(0, fake_audio_device->Init());
-    EXPECT_EQ(0, voe_base->Init(fake_audio_device.get(), audio_processing.get(),
+    EXPECT_EQ(0, voe_base->Init(fake_audio_device.get(), nullptr,
                                 decoder_factory_));
     VoEBase::ChannelConfig config;
     config.enable_voice_pacing = true;
@@ -194,7 +192,8 @@
     AudioState::Config send_audio_state_config;
     send_audio_state_config.voice_engine = voice_engine;
     send_audio_state_config.audio_mixer = AudioMixerImpl::Create();
-    send_audio_state_config.audio_processing = audio_processing;
+    send_audio_state_config.audio_processing = AudioProcessing::Create();
+    send_audio_state_config.audio_device_module = fake_audio_device;
     Call::Config sender_config(event_log_.get());
 
     auto audio_state = AudioState::Create(send_audio_state_config);
@@ -311,8 +310,6 @@
     DestroyCalls();
 
     VoiceEngine::Delete(voice_engine);
-
-    fake_audio_device.reset();
   });
 
   observer.PrintResults();
diff --git a/call/call_unittest.cc b/call/call_unittest.cc
index 7b16271..bf1fe53 100644
--- a/call/call_unittest.cc
+++ b/call/call_unittest.cc
@@ -20,7 +20,7 @@
 #include "call/fake_rtp_transport_controller_send.h"
 #include "logging/rtc_event_log/rtc_event_log.h"
 #include "modules/audio_device/include/mock_audio_device.h"
-#include "modules/audio_mixer/audio_mixer_impl.h"
+#include "modules/audio_processing/include/mock_audio_processing.h"
 #include "modules/congestion_controller/include/mock/mock_send_side_congestion_controller.h"
 #include "modules/pacing/mock/mock_paced_sender.h"
 #include "modules/rtp_rtcp/include/rtp_rtcp.h"
@@ -39,9 +39,12 @@
       : voice_engine_(decoder_factory) {
     webrtc::AudioState::Config audio_state_config;
     audio_state_config.voice_engine = &voice_engine_;
-    audio_state_config.audio_mixer = webrtc::AudioMixerImpl::Create();
-    audio_state_config.audio_processing = webrtc::AudioProcessing::Create();
-    EXPECT_CALL(voice_engine_, audio_transport());
+    audio_state_config.audio_mixer =
+        new rtc::RefCountedObject<webrtc::test::MockAudioMixer>();
+    audio_state_config.audio_processing =
+        new rtc::RefCountedObject<webrtc::test::MockAudioProcessing>();
+    audio_state_config.audio_device_module =
+        new rtc::RefCountedObject<webrtc::test::MockAudioDeviceModule>();
     webrtc::Call::Config config(&event_log_);
     config.audio_state = webrtc::AudioState::Create(audio_state_config);
     call_.reset(webrtc::Call::Create(config));
@@ -431,9 +434,6 @@
 
 TEST(CallTest, RecreatingAudioStreamWithSameSsrcReusesRtpState) {
   constexpr uint32_t kSSRC = 12345;
-  testing::NiceMock<test::MockAudioDeviceModule> mock_adm;
-  rtc::scoped_refptr<test::MockAudioMixer> mock_mixer(
-      new rtc::RefCountedObject<test::MockAudioMixer>);
 
   // There's similar functionality in cricket::VoEWrapper but it's not reachable
   // from here. Since we're working on removing VoE interfaces, I doubt it's
@@ -454,9 +454,13 @@
 
   AudioState::Config audio_state_config;
   audio_state_config.voice_engine = voice_engine.voe;
-  audio_state_config.audio_mixer = mock_mixer;
-  audio_state_config.audio_processing = AudioProcessing::Create();
-  voice_engine.base->Init(&mock_adm, audio_state_config.audio_processing.get(),
+  audio_state_config.audio_mixer =
+      new rtc::RefCountedObject<test::MockAudioMixer>();
+  audio_state_config.audio_processing =
+      new rtc::RefCountedObject<test::MockAudioProcessing>();
+  audio_state_config.audio_device_module =
+      new rtc::RefCountedObject<test::MockAudioDeviceModule>();
+  voice_engine.base->Init(audio_state_config.audio_device_module, nullptr,
                           CreateBuiltinAudioDecoderFactory());
   auto audio_state = AudioState::Create(audio_state_config);
 
diff --git a/call/test/mock_audio_send_stream.h b/call/test/mock_audio_send_stream.h
new file mode 100644
index 0000000..4eb1166
--- /dev/null
+++ b/call/test/mock_audio_send_stream.h
@@ -0,0 +1,45 @@
+/*
+ *  Copyright (c) 2015 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 CALL_TEST_MOCK_AUDIO_SEND_STREAM_H_
+#define CALL_TEST_MOCK_AUDIO_SEND_STREAM_H_
+
+#include <memory>
+
+#include "call/audio_send_stream.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+namespace test {
+
+class MockAudioSendStream : public AudioSendStream {
+ public:
+  MOCK_CONST_METHOD0(GetConfig, const webrtc::AudioSendStream::Config&());
+  MOCK_METHOD1(Reconfigure, void(const Config& config));
+  MOCK_METHOD0(Start, void());
+  MOCK_METHOD0(Stop, void());
+  // GMock doesn't like move-only types, such as std::unique_ptr.
+  virtual void SendAudioData(
+      std::unique_ptr<webrtc::AudioFrame> audio_frame) {
+    SendAudioDataForMock(audio_frame.get());
+  }
+  MOCK_METHOD1(SendAudioDataForMock,
+               void(webrtc::AudioFrame* audio_frame));
+  MOCK_METHOD4(SendTelephoneEvent,
+               bool(int payload_type, int payload_frequency, int event,
+                    int duration_ms));
+  MOCK_METHOD1(SetMuted, void(bool muted));
+  MOCK_CONST_METHOD0(GetStats, Stats());
+  MOCK_CONST_METHOD1(GetStats, Stats(bool has_remote_tracks));
+};
+}  // namespace test
+}  // namespace webrtc
+
+#endif  // CALL_TEST_MOCK_AUDIO_SEND_STREAM_H_
diff --git a/media/engine/apm_helpers.cc b/media/engine/apm_helpers.cc
index eca3e20..cf5dde2 100644
--- a/media/engine/apm_helpers.cc
+++ b/media/engine/apm_helpers.cc
@@ -13,7 +13,6 @@
 #include "modules/audio_device/include/audio_device.h"
 #include "modules/audio_processing/include/audio_processing.h"
 #include "rtc_base/logging.h"
-#include "voice_engine/transmit_mixer.h"
 
 namespace webrtc {
 namespace apm_helpers {
@@ -156,17 +155,6 @@
 
 void SetTypingDetectionStatus(AudioProcessing* apm, bool enable) {
   RTC_DCHECK(apm);
-#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION
-  // Typing detection takes place in TransmitMixer::PrepareDemux() and
-  // TransmitMixer::TypingDetection(). The typing detection algorithm takes as
-  // input two booleans:
-  //   1. A signal whether a key was pressed during the audio frame.
-  //   2. Whether VAD is active or not.
-  // TransmitMixer will not even call the detector if APM has set kVadUnknown in
-  // the audio frame after near end processing, so enabling/disabling VAD is
-  // sufficient for turning typing detection on/off.
-  // TODO(solenberg): Rather than relying on a side effect, consider forcing the
-  //                  feature on/off in TransmitMixer.
   VoiceDetection* vd = apm->voice_detection();
   if (vd->Enable(enable)) {
     RTC_LOG(LS_ERROR) << "Failed to enable/disable VAD: " << enable;
@@ -177,7 +165,6 @@
     return;
   }
   RTC_LOG(LS_INFO) << "VAD set to " << enable << " for typing detection.";
-#endif
 }
 }  // namespace apm_helpers
 }  // namespace webrtc
diff --git a/media/engine/apm_helpers_unittest.cc b/media/engine/apm_helpers_unittest.cc
index 70edce2..e1139b2 100644
--- a/media/engine/apm_helpers_unittest.cc
+++ b/media/engine/apm_helpers_unittest.cc
@@ -10,13 +10,10 @@
 
 #include "media/engine/apm_helpers.h"
 
-#include "media/engine/webrtcvoe.h"
-#include "modules/audio_device/include/mock_audio_device.h"
 #include "modules/audio_processing/include/audio_processing.h"
+#include "rtc_base/scoped_ref_ptr.h"
 #include "test/gmock.h"
 #include "test/gtest.h"
-#include "test/mock_audio_decoder_factory.h"
-#include "voice_engine/transmit_mixer.h"
 
 namespace webrtc {
 namespace {
@@ -30,19 +27,12 @@
     config.Set<ExperimentalAgc>(new ExperimentalAgc(false));
     apm_ = rtc::scoped_refptr<AudioProcessing>(AudioProcessing::Create(config));
     apm_helpers::Init(apm());
-    EXPECT_EQ(0, voe_wrapper_.base()->Init(
-                     &mock_audio_device_, apm_,
-                     MockAudioDecoderFactory::CreateEmptyFactory()));
   }
 
   AudioProcessing* apm() { return apm_.get(); }
 
   const AudioProcessing* apm() const { return apm_.get(); }
 
-  voe::TransmitMixer* transmit_mixer() {
-    return voe_wrapper_.base()->transmit_mixer();
-  }
-
   bool GetEcMetricsStatus() const {
     EchoCancellation* ec = apm()->echo_cancellation();
     bool metrics_enabled = ec->are_metrics_enabled();
@@ -63,8 +53,6 @@
   }
 
  private:
-  testing::NiceMock<test::MockAudioDeviceModule> mock_audio_device_;
-  cricket::VoEWrapper voe_wrapper_;
   rtc::scoped_refptr<AudioProcessing> apm_;
 };
 }  // namespace
@@ -236,21 +224,14 @@
   EXPECT_FALSE(vd->is_enabled());
 }
 
-// TODO(kthelgason): Reenable this test on simulator.
-// See bugs.webrtc.org/5569
-#if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR
-#define MAYBE_TypingDetectionStatus_EnableDisable \
-  DISABLED_TypingDetectionStatus_EnableDisable
-#else
-#define MAYBE_TypingDetectionStatus_EnableDisable \
-  TypingDetectionStatus_EnableDisable
-#endif
-TEST(ApmHelpersTest, MAYBE_TypingDetectionStatus_EnableDisable) {
+TEST(ApmHelpersTest, TypingDetectionStatus_EnableDisable) {
   TestHelper helper;
   VoiceDetection* vd = helper.apm()->voice_detection();
   apm_helpers::SetTypingDetectionStatus(helper.apm(), true);
+  EXPECT_EQ(VoiceDetection::kVeryLowLikelihood, vd->likelihood());
   EXPECT_TRUE(vd->is_enabled());
   apm_helpers::SetTypingDetectionStatus(helper.apm(), false);
+  EXPECT_EQ(VoiceDetection::kVeryLowLikelihood, vd->likelihood());
   EXPECT_FALSE(vd->is_enabled());
 }
 
@@ -260,21 +241,4 @@
   TestHelper helper;
   EXPECT_FALSE(helper.apm()->high_pass_filter()->is_enabled());
 }
-
-// TODO(solenberg): Move this test to a better place - added here for the sake
-// of duplicating all relevant tests from audio_processing_test.cc.
-TEST(ApmHelpersTest, StereoSwapping_DefaultMode) {
-  TestHelper helper;
-  EXPECT_FALSE(helper.transmit_mixer()->IsStereoChannelSwappingEnabled());
-}
-
-// TODO(solenberg): Move this test to a better place - added here for the sake
-// of duplicating all relevant tests from audio_processing_test.cc.
-TEST(ApmHelpersTest, StereoSwapping_EnableDisable) {
-  TestHelper helper;
-  helper.transmit_mixer()->EnableStereoChannelSwapping(true);
-  EXPECT_TRUE(helper.transmit_mixer()->IsStereoChannelSwappingEnabled());
-  helper.transmit_mixer()->EnableStereoChannelSwapping(false);
-  EXPECT_FALSE(helper.transmit_mixer()->IsStereoChannelSwappingEnabled());
-}
 }  // namespace webrtc
diff --git a/media/engine/fakewebrtccall.h b/media/engine/fakewebrtccall.h
index 4544ff1..6fa71db 100644
--- a/media/engine/fakewebrtccall.h
+++ b/media/engine/fakewebrtccall.h
@@ -60,7 +60,8 @@
 
   void Start() override { sending_ = true; }
   void Stop() override { sending_ = false; }
-
+  void SendAudioData(std::unique_ptr<webrtc::AudioFrame> audio_frame) override {
+  }
   bool SendTelephoneEvent(int payload_type, int payload_frequency, int event,
                           int duration_ms) override;
   void SetMuted(bool muted) override;
diff --git a/media/engine/fakewebrtcvoiceengine.h b/media/engine/fakewebrtcvoiceengine.h
index 444afdd..163edd8 100644
--- a/media/engine/fakewebrtcvoiceengine.h
+++ b/media/engine/fakewebrtcvoiceengine.h
@@ -17,12 +17,6 @@
 #include "media/engine/webrtcvoe.h"
 #include "rtc_base/checks.h"
 
-namespace webrtc {
-namespace voe {
-class TransmitMixer;
-}  // namespace voe
-}  // namespace webrtc
-
 namespace cricket {
 
 #define WEBRTC_CHECK_CHANNEL(channel) \
@@ -41,8 +35,7 @@
     bool neteq_fast_accelerate = false;
   };
 
-  explicit FakeWebRtcVoiceEngine(webrtc::voe::TransmitMixer* transmit_mixer)
-      : transmit_mixer_(transmit_mixer) {}
+  FakeWebRtcVoiceEngine() {}
   ~FakeWebRtcVoiceEngine() override {
     RTC_CHECK(channels_.empty());
   }
@@ -68,9 +61,6 @@
   void Terminate() override {
     inited_ = false;
   }
-  webrtc::voe::TransmitMixer* transmit_mixer() override {
-    return transmit_mixer_;
-  }
   WEBRTC_FUNC(CreateChannel, ()) {
     return CreateChannel(webrtc::VoEBase::ChannelConfig());
   }
@@ -114,9 +104,8 @@
   int last_channel_ = -1;
   std::map<int, Channel*> channels_;
   bool fail_create_channel_ = false;
-  webrtc::voe::TransmitMixer* transmit_mixer_ = nullptr;
 
-  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(FakeWebRtcVoiceEngine);
+  RTC_DISALLOW_COPY_AND_ASSIGN(FakeWebRtcVoiceEngine);
 };
 
 }  // namespace cricket
diff --git a/media/engine/webrtcvoe.h b/media/engine/webrtcvoe.h
index d303d30..5d53429 100644
--- a/media/engine/webrtcvoe.h
+++ b/media/engine/webrtcvoe.h
@@ -74,7 +74,8 @@
   VoEWrapper()
       : engine_(webrtc::VoiceEngine::Create()), base_(engine_) {
   }
-  explicit VoEWrapper(webrtc::VoEBase* base) : engine_(NULL), base_(base) {}
+  explicit VoEWrapper(webrtc::VoEBase* base)
+      : engine_(webrtc::VoiceEngine::Create()), base_(base) {}
   ~VoEWrapper() {}
   webrtc::VoiceEngine* engine() const { return engine_.get(); }
   webrtc::VoEBase* base() const { return base_.get(); }
diff --git a/media/engine/webrtcvoiceengine.cc b/media/engine/webrtcvoiceengine.cc
index 061a16b..98f0aa4 100644
--- a/media/engine/webrtcvoiceengine.cc
+++ b/media/engine/webrtcvoiceengine.cc
@@ -44,7 +44,6 @@
 #include "rtc_base/trace_event.h"
 #include "system_wrappers/include/field_trial.h"
 #include "system_wrappers/include/metrics.h"
-#include "voice_engine/transmit_mixer.h"
 
 namespace cricket {
 namespace {
@@ -158,21 +157,6 @@
   return rtc::nullopt;
 }
 
-webrtc::AudioState::Config MakeAudioStateConfig(
-    VoEWrapper* voe_wrapper,
-    rtc::scoped_refptr<webrtc::AudioMixer> audio_mixer,
-    rtc::scoped_refptr<webrtc::AudioProcessing> audio_processing) {
-  webrtc::AudioState::Config config;
-  config.voice_engine = voe_wrapper->engine();
-  if (audio_mixer) {
-    config.audio_mixer = audio_mixer;
-  } else {
-    config.audio_mixer = webrtc::AudioMixerImpl::Create();
-  }
-  config.audio_processing = audio_processing;
-  return config;
-}
-
 // |max_send_bitrate_bps| is the bitrate from "b=" in SDP.
 // |rtp_max_bitrate_bps| is the bitrate from RtpSender::SetParameters.
 rtc::Optional<int> ComputeSendBitrate(int max_send_bitrate_bps,
@@ -299,9 +283,24 @@
   RTC_CHECK(adm());
   webrtc::adm_helpers::Init(adm());
   webrtc::apm_helpers::Init(apm());
-  RTC_CHECK_EQ(0, voe_wrapper_->base()->Init(adm(), apm(), decoder_factory_));
-  transmit_mixer_ = voe_wrapper_->base()->transmit_mixer();
-  RTC_DCHECK(transmit_mixer_);
+  RTC_CHECK_EQ(0, voe_wrapper_->base()->Init(adm(), nullptr, decoder_factory_));
+
+  // Set up AudioState.
+  {
+    webrtc::AudioState::Config config;
+    config.voice_engine = voe()->engine();
+    if (audio_mixer_) {
+      config.audio_mixer = audio_mixer_;
+    } else {
+      config.audio_mixer = webrtc::AudioMixerImpl::Create();
+    }
+    config.audio_processing = apm_;
+    config.audio_device_module = adm_;
+    audio_state_ = webrtc::AudioState::Create(config);
+  }
+
+  // Connect the ADM to our audio path.
+  adm()->RegisterAudioCallback(audio_state()->audio_transport());
 
   // Save the default AGC configuration settings. This must happen before
   // calling ApplyOptions or the default will be overwritten.
@@ -329,15 +328,6 @@
     RTC_DCHECK(error);
   }
 
-  // May be null for VoE injected for testing.
-  if (voe()->engine()) {
-    audio_state_ = webrtc::AudioState::Create(
-        MakeAudioStateConfig(voe(), audio_mixer_, apm_));
-
-    // Connect the ADM to our audio path.
-    adm()->RegisterAudioCallback(audio_state_->audio_transport());
-  }
-
   initialized_ = true;
 }
 
@@ -531,7 +521,7 @@
 
   if (options.stereo_swapping) {
     RTC_LOG(LS_INFO) << "Stereo swapping enabled? " << *options.stereo_swapping;
-    transmit_mixer()->EnableStereoChannelSwapping(*options.stereo_swapping);
+    audio_state()->SetStereoChannelSwapping(*options.stereo_swapping);
   }
 
   if (options.audio_jitter_buffer_max_packets) {
@@ -623,9 +613,7 @@
 // TODO(solenberg): Remove, once AudioMonitor is gone.
 int WebRtcVoiceEngine::GetInputLevel() {
   RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
-  int8_t level = transmit_mixer()->AudioLevel();
-  RTC_DCHECK_LE(0, level);
-  return level;
+  return audio_state()->GetAudioInputStats().quantized_audio_level;
 }
 
 const std::vector<AudioCodec>& WebRtcVoiceEngine::send_codecs() const {
@@ -709,10 +697,10 @@
   return apm_.get();
 }
 
-webrtc::voe::TransmitMixer* WebRtcVoiceEngine::transmit_mixer() {
+webrtc::AudioState* WebRtcVoiceEngine::audio_state() {
   RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
-  RTC_DCHECK(transmit_mixer_);
-  return transmit_mixer_;
+  RTC_DCHECK(audio_state_);
+  return audio_state_.get();
 }
 
 AudioCodecs WebRtcVoiceEngine::CollectCodecs(
@@ -796,7 +784,6 @@
  public:
   WebRtcAudioSendStream(
       int ch,
-      webrtc::AudioTransport* voe_audio_transport,
       uint32_t ssrc,
       const std::string& c_name,
       const std::string track_id,
@@ -808,16 +795,13 @@
       webrtc::Call* call,
       webrtc::Transport* send_transport,
       const rtc::scoped_refptr<webrtc::AudioEncoderFactory>& encoder_factory)
-      : voe_audio_transport_(voe_audio_transport),
-        call_(call),
+      : call_(call),
         config_(send_transport),
         send_side_bwe_with_overhead_(
             webrtc::field_trial::IsEnabled("WebRTC-SendSideBwe-WithOverhead")),
         max_send_bitrate_bps_(max_send_bitrate_bps),
         rtp_parameters_(CreateRtpParametersWithOneEncoding()) {
     RTC_DCHECK_GE(ch, 0);
-    // TODO(solenberg): Once we're not using FakeWebRtcVoiceEngine anymore:
-    // RTC_DCHECK(voe_audio_transport);
     RTC_DCHECK(call);
     RTC_DCHECK(encoder_factory);
     config_.rtp.ssrc = ssrc;
@@ -952,11 +936,18 @@
               int sample_rate,
               size_t number_of_channels,
               size_t number_of_frames) override {
+    RTC_DCHECK_EQ(16, bits_per_sample);
     RTC_CHECK_RUNS_SERIALIZED(&audio_capture_race_checker_);
-    RTC_DCHECK(voe_audio_transport_);
-    voe_audio_transport_->PushCaptureData(config_.voe_channel_id, audio_data,
-                                          bits_per_sample, sample_rate,
-                                          number_of_channels, number_of_frames);
+    RTC_DCHECK(stream_);
+    std::unique_ptr<webrtc::AudioFrame> audio_frame(new webrtc::AudioFrame());
+    audio_frame->UpdateFrame(audio_frame->timestamp_,
+                             static_cast<const int16_t*>(audio_data),
+                             number_of_frames,
+                             sample_rate,
+                             audio_frame->speech_type_,
+                             audio_frame->vad_activity_,
+                             number_of_channels);
+    stream_->SendAudioData(std::move(audio_frame));
   }
 
   // Callback from the |source_| when it is going away. In case Start() has
@@ -1119,7 +1110,6 @@
 
   rtc::ThreadChecker worker_thread_checker_;
   rtc::RaceChecker audio_capture_race_checker_;
-  webrtc::AudioTransport* const voe_audio_transport_ = nullptr;
   webrtc::Call* call_ = nullptr;
   webrtc::AudioSendStream::Config config_;
   const bool send_side_bwe_with_overhead_;
@@ -1821,17 +1811,12 @@
     return false;
   }
 
-  // Save the channel to send_streams_, so that RemoveSendStream() can still
-  // delete the channel in case failure happens below.
-  webrtc::AudioTransport* audio_transport =
-      engine()->voe()->base()->audio_transport();
-
   rtc::Optional<std::string> audio_network_adaptor_config =
       GetAudioNetworkAdaptorConfig(options_);
   WebRtcAudioSendStream* stream = new WebRtcAudioSendStream(
-      channel, audio_transport, ssrc, sp.cname, sp.id, send_codec_spec_,
-      send_rtp_extensions_, max_send_bitrate_bps_, audio_network_adaptor_config,
-      call_, this, engine()->encoder_factory_);
+      channel, ssrc, sp.cname, sp.id, send_codec_spec_, send_rtp_extensions_,
+      max_send_bitrate_bps_, audio_network_adaptor_config, call_, this,
+      engine()->encoder_factory_);
   send_streams_.insert(std::make_pair(ssrc, stream));
 
   // At this point the stream's local SSRC has been updated. If it is the first
diff --git a/media/engine/webrtcvoiceengine.h b/media/engine/webrtcvoiceengine.h
index 8984299..e5880e2 100644
--- a/media/engine/webrtcvoiceengine.h
+++ b/media/engine/webrtcvoiceengine.h
@@ -32,12 +32,6 @@
 #include "rtc_base/task_queue.h"
 #include "rtc_base/thread_checker.h"
 
-namespace webrtc {
-namespace voe {
-class TransmitMixer;
-}  // namespace voe
-}  // namespace webrtc
-
 namespace cricket {
 
 class AudioDeviceModule;
@@ -115,7 +109,7 @@
 
   webrtc::AudioDeviceModule* adm();
   webrtc::AudioProcessing* apm() const;
-  webrtc::voe::TransmitMixer* transmit_mixer();
+  webrtc::AudioState* audio_state();
 
   AudioCodecs CollectCodecs(
       const std::vector<webrtc::AudioCodecSpec>& specs) const;
@@ -123,15 +117,13 @@
   rtc::ThreadChecker signal_thread_checker_;
   rtc::ThreadChecker worker_thread_checker_;
 
-  // The audio device manager.
+  // The audio device module.
   rtc::scoped_refptr<webrtc::AudioDeviceModule> adm_;
   rtc::scoped_refptr<webrtc::AudioEncoderFactory> encoder_factory_;
   rtc::scoped_refptr<webrtc::AudioDecoderFactory> decoder_factory_;
   rtc::scoped_refptr<webrtc::AudioMixer> audio_mixer_;
-  // Reference to the APM, owned by VoE.
+  // The audio processing module.
   rtc::scoped_refptr<webrtc::AudioProcessing> apm_;
-  // Reference to the TransmitMixer, owned by VoE.
-  webrtc::voe::TransmitMixer* transmit_mixer_ = nullptr;
   // The primary instance of WebRtc VoiceEngine.
   std::unique_ptr<VoEWrapper> voe_wrapper_;
   rtc::scoped_refptr<webrtc::AudioState> audio_state_;
diff --git a/media/engine/webrtcvoiceengine_unittest.cc b/media/engine/webrtcvoiceengine_unittest.cc
index 4fd087a..4df63b3 100644
--- a/media/engine/webrtcvoiceengine_unittest.cc
+++ b/media/engine/webrtcvoiceengine_unittest.cc
@@ -33,7 +33,6 @@
 #include "test/gtest.h"
 #include "test/mock_audio_decoder_factory.h"
 #include "test/mock_audio_encoder_factory.h"
-#include "voice_engine/transmit_mixer.h"
 
 using testing::_;
 using testing::ContainerEq;
@@ -78,27 +77,13 @@
 constexpr webrtc::NoiseSuppression::Level kDefaultNsLevel =
     webrtc::NoiseSuppression::kHigh;
 
-class FakeVoEWrapper : public cricket::VoEWrapper {
- public:
-  explicit FakeVoEWrapper(cricket::FakeWebRtcVoiceEngine* engine)
-      : cricket::VoEWrapper(engine) {
-  }
-};
-
-class MockTransmitMixer : public webrtc::voe::TransmitMixer {
- public:
-  MockTransmitMixer() = default;
-  virtual ~MockTransmitMixer() = default;
-
-  MOCK_METHOD1(EnableStereoChannelSwapping, void(bool enable));
-};
-
 void AdmSetupExpectations(webrtc::test::MockAudioDeviceModule* adm) {
   RTC_DCHECK(adm);
 
   // Setup.
-  EXPECT_CALL(*adm, AddRef()).Times(1);
+  EXPECT_CALL(*adm, AddRef()).Times(3);
   EXPECT_CALL(*adm, Init()).WillOnce(Return(0));
+  EXPECT_CALL(*adm, RegisterAudioCallback(_)).WillOnce(Return(0));
 #if defined(WEBRTC_WIN)
   EXPECT_CALL(*adm, SetPlayoutDevice(
       testing::Matcher<webrtc::AudioDeviceModule::WindowsDeviceType>(
@@ -130,8 +115,8 @@
   EXPECT_CALL(*adm, StopRecording()).WillOnce(Return(0));
   EXPECT_CALL(*adm, RegisterAudioCallback(nullptr)).WillOnce(Return(0));
   EXPECT_CALL(*adm, Terminate()).WillOnce(Return(0));
-  EXPECT_CALL(*adm, Release())
-      .WillOnce(Return(rtc::RefCountReleaseStatus::kDroppedLastRef));
+  EXPECT_CALL(*adm, Release()).Times(3)
+      .WillRepeatedly(Return(rtc::RefCountReleaseStatus::kDroppedLastRef));
 }
 }  // namespace
 
@@ -147,15 +132,13 @@
   EXPECT_CALL(*apm, ApplyConfig(_)).WillRepeatedly(SaveArg<0>(&apm_config));
   EXPECT_CALL(*apm, SetExtraOptions(testing::_));
   EXPECT_CALL(*apm, DetachAecDump());
-  StrictMock<MockTransmitMixer> transmit_mixer;
-  EXPECT_CALL(transmit_mixer, EnableStereoChannelSwapping(false));
-  cricket::FakeWebRtcVoiceEngine voe(&transmit_mixer);
+  cricket::FakeWebRtcVoiceEngine voe;
   EXPECT_FALSE(voe.IsInited());
   {
     cricket::WebRtcVoiceEngine engine(
         &adm, webrtc::MockAudioEncoderFactory::CreateUnusedFactory(),
         webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, apm,
-        new FakeVoEWrapper(&voe));
+        new cricket::VoEWrapper(&voe));
     engine.Init();
     EXPECT_TRUE(voe.IsInited());
   }
@@ -183,7 +166,7 @@
         apm_ns_(*apm_->noise_suppression()),
         apm_vd_(*apm_->voice_detection()),
         call_(webrtc::Call::Config(&event_log_)),
-        voe_(&transmit_mixer_),
+        voe_(),
         override_field_trials_(field_trials) {
     // AudioDeviceModule.
     AdmSetupExpectations(&adm_);
@@ -202,7 +185,6 @@
     EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0));
     EXPECT_CALL(apm_ns_, Enable(true)).WillOnce(Return(0));
     EXPECT_CALL(apm_vd_, Enable(true)).WillOnce(Return(0));
-    EXPECT_CALL(transmit_mixer_, EnableStereoChannelSwapping(false));
     // Init does not overwrite default AGC config.
     EXPECT_CALL(apm_gc_, target_level_dbfs()).WillOnce(Return(1));
     EXPECT_CALL(apm_gc_, compression_gain_db()).WillRepeatedly(Return(5));
@@ -214,9 +196,9 @@
     // factories. Those tests should probably be moved elsewhere.
     auto encoder_factory = webrtc::CreateBuiltinAudioEncoderFactory();
     auto decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory();
-    engine_.reset(new cricket::WebRtcVoiceEngine(&adm_, encoder_factory,
-                                                 decoder_factory, nullptr, apm_,
-                                                 new FakeVoEWrapper(&voe_)));
+    engine_.reset(new cricket::WebRtcVoiceEngine(
+        &adm_, encoder_factory, decoder_factory, nullptr, apm_,
+        new cricket::VoEWrapper(&voe_)));
     engine_->Init();
     send_parameters_.codecs.push_back(kPcmuCodec);
     recv_parameters_.codecs.push_back(kPcmuCodec);
@@ -723,7 +705,6 @@
   webrtc::test::MockEchoCancellation& apm_ec_;
   webrtc::test::MockNoiseSuppression& apm_ns_;
   webrtc::test::MockVoiceDetection& apm_vd_;
-  StrictMock<MockTransmitMixer> transmit_mixer_;
   webrtc::RtcEventLogNullImpl event_log_;
   cricket::FakeCall call_;
   cricket::FakeWebRtcVoiceEngine voe_;
@@ -2781,7 +2762,7 @@
   send_parameters_.options.auto_gain_control = true;
   SetSendParameters(send_parameters_);
 
-  // Turn off other options (and stereo swapping on).
+  // Turn off other options.
   EXPECT_CALL(apm_ec_, Enable(true)).WillOnce(Return(0));
   EXPECT_CALL(apm_ec_, enable_metrics(true)).WillOnce(Return(0));
   EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0));
@@ -2789,7 +2770,6 @@
   EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0));
   EXPECT_CALL(apm_vd_, Enable(false)).WillOnce(Return(0));
-  EXPECT_CALL(transmit_mixer_, EnableStereoChannelSwapping(true));
   send_parameters_.options.noise_suppression = false;
   send_parameters_.options.highpass_filter = false;
   send_parameters_.options.typing_detection = false;
@@ -2805,7 +2785,6 @@
   EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0));
   EXPECT_CALL(apm_vd_, Enable(false)).WillOnce(Return(0));
-  EXPECT_CALL(transmit_mixer_, EnableStereoChannelSwapping(true));
   SetSendParameters(send_parameters_);
 }
 
@@ -3335,9 +3314,9 @@
 // Tests that reference counting on the external ADM is correct.
 TEST(WebRtcVoiceEngineTest, StartupShutdownWithExternalADM) {
   testing::NiceMock<webrtc::test::MockAudioDeviceModule> adm;
-  EXPECT_CALL(adm, AddRef()).Times(3);
+  EXPECT_CALL(adm, AddRef()).Times(5);
   EXPECT_CALL(adm, Release())
-      .Times(3)
+      .Times(5)
       .WillRepeatedly(Return(rtc::RefCountReleaseStatus::kDroppedLastRef));
   {
     rtc::scoped_refptr<webrtc::AudioProcessing> apm =
diff --git a/modules/audio_device/BUILD.gn b/modules/audio_device/BUILD.gn
index 6c19146..38ce742 100644
--- a/modules/audio_device/BUILD.gn
+++ b/modules/audio_device/BUILD.gn
@@ -104,6 +104,7 @@
     "../../api:array_view",
     "../../common_audio",
     "../../rtc_base:checks",
+    "../../rtc_base:deprecation",
     "../../rtc_base:rtc_base_approved",
     "../../rtc_base:rtc_task_queue",
     "../../system_wrappers",
diff --git a/modules/audio_device/include/audio_device_defines.h b/modules/audio_device/include/audio_device_defines.h
index 510b07c..04119f6 100644
--- a/modules/audio_device/include/audio_device_defines.h
+++ b/modules/audio_device/include/audio_device_defines.h
@@ -13,6 +13,8 @@
 
 #include <stddef.h>
 
+#include "rtc_base/checks.h"
+#include "rtc_base/deprecation.h"
 #include "typedefs.h"  // NOLINT(build/include)
 
 namespace webrtc {
@@ -54,17 +56,19 @@
   // The data will not undergo audio processing.
   // |voe_channel| is the id of the VoE channel which is the sink to the
   // capture data.
-  virtual void PushCaptureData(int voe_channel,
-                               const void* audio_data,
-                               int bits_per_sample,
-                               int sample_rate,
-                               size_t number_of_channels,
-                               size_t number_of_frames) = 0;
+  // TODO(bugs.webrtc.org/8659): Remove this method once clients updated.
+  RTC_DEPRECATED virtual void PushCaptureData(
+      int voe_channel,
+      const void* audio_data,
+      int bits_per_sample,
+      int sample_rate,
+      size_t number_of_channels,
+      size_t number_of_frames) {
+    RTC_NOTREACHED();
+  }
 
   // Method to pull mixed render audio data from all active VoE channels.
   // The data will not be passed as reference for audio processing internally.
-  // TODO(xians): Support getting the unmixed render data from specific VoE
-  // channel.
   virtual void PullRenderData(int bits_per_sample,
                               int sample_rate,
                               size_t number_of_channels,
diff --git a/test/call_test.cc b/test/call_test.cc
index 3210e64..ceee841 100644
--- a/test/call_test.cc
+++ b/test/call_test.cc
@@ -44,14 +44,12 @@
       num_flexfec_streams_(0),
       decoder_factory_(CreateBuiltinAudioDecoderFactory()),
       encoder_factory_(CreateBuiltinAudioEncoderFactory()),
-      task_queue_("CallTestTaskQueue"),
-      fake_send_audio_device_(nullptr),
-      fake_recv_audio_device_(nullptr) {}
+      task_queue_("CallTestTaskQueue") {}
 
 CallTest::~CallTest() {
   task_queue_.SendTask([this]() {
-    fake_send_audio_device_.reset();
-    fake_recv_audio_device_.reset();
+    fake_send_audio_device_ = nullptr;
+    fake_recv_audio_device_ = nullptr;
     frame_generator_capturer_.reset();
   });
 }
@@ -74,6 +72,7 @@
       audio_state_config.voice_engine = voe_send_.voice_engine;
       audio_state_config.audio_mixer = AudioMixerImpl::Create();
       audio_state_config.audio_processing = apm_send_;
+      audio_state_config.audio_device_module = fake_send_audio_device_;
       send_config.audio_state = AudioState::Create(audio_state_config);
       fake_send_audio_device_->RegisterAudioCallback(
           send_config.audio_state->audio_transport());
@@ -90,6 +89,7 @@
         audio_state_config.voice_engine = voe_recv_.voice_engine;
         audio_state_config.audio_mixer = AudioMixerImpl::Create();
         audio_state_config.audio_processing = apm_recv_;
+        audio_state_config.audio_device_module = fake_recv_audio_device_;
         recv_config.audio_state = AudioState::Create(audio_state_config);
         fake_recv_audio_device_->RegisterAudioCallback(
             recv_config.audio_state->audio_transport());      }
@@ -315,10 +315,10 @@
 void CallTest::CreateFakeAudioDevices(
     std::unique_ptr<FakeAudioDevice::Capturer> capturer,
     std::unique_ptr<FakeAudioDevice::Renderer> renderer) {
-  fake_send_audio_device_.reset(new FakeAudioDevice(
-      std::move(capturer), nullptr, 1.f));
-  fake_recv_audio_device_.reset(new FakeAudioDevice(
-      nullptr, std::move(renderer), 1.f));
+  fake_send_audio_device_ = new rtc::RefCountedObject<FakeAudioDevice>(
+      std::move(capturer), nullptr, 1.f);
+  fake_recv_audio_device_ = new rtc::RefCountedObject<FakeAudioDevice>(
+      nullptr, std::move(renderer), 1.f);
 }
 
 void CallTest::CreateVideoStreams() {
@@ -432,7 +432,7 @@
   voe_send_.base = VoEBase::GetInterface(voe_send_.voice_engine);
   EXPECT_EQ(0, fake_send_audio_device_->Init());
   EXPECT_EQ(0, voe_send_.base->Init(fake_send_audio_device_.get(),
-                                    apm_send_.get(), decoder_factory_));
+                                    nullptr, decoder_factory_));
   VoEBase::ChannelConfig config;
   config.enable_voice_pacing = true;
   voe_send_.channel_id = voe_send_.base->CreateChannel(config);
@@ -442,7 +442,7 @@
   voe_recv_.base = VoEBase::GetInterface(voe_recv_.voice_engine);
   EXPECT_EQ(0, fake_recv_audio_device_->Init());
   EXPECT_EQ(0, voe_recv_.base->Init(fake_recv_audio_device_.get(),
-                                    apm_recv_.get(), decoder_factory_));
+                                    nullptr, decoder_factory_));
   voe_recv_.channel_id = voe_recv_.base->CreateChannel();
   EXPECT_GE(voe_recv_.channel_id, 0);
 }
diff --git a/test/call_test.h b/test/call_test.h
index cf15aac..11ded0b 100644
--- a/test/call_test.h
+++ b/test/call_test.h
@@ -161,10 +161,8 @@
   VoiceEngineState voe_recv_;
   rtc::scoped_refptr<AudioProcessing> apm_send_;
   rtc::scoped_refptr<AudioProcessing> apm_recv_;
-
-  // The audio devices must outlive the voice engines.
-  std::unique_ptr<test::FakeAudioDevice> fake_send_audio_device_;
-  std::unique_ptr<test::FakeAudioDevice> fake_recv_audio_device_;
+  rtc::scoped_refptr<test::FakeAudioDevice> fake_send_audio_device_;
+  rtc::scoped_refptr<test::FakeAudioDevice> fake_recv_audio_device_;
 };
 
 class BaseTest : public RtpRtcpObserver {
diff --git a/test/mock_voe_channel_proxy.h b/test/mock_voe_channel_proxy.h
index 9bc48b1..e075124 100644
--- a/test/mock_voe_channel_proxy.h
+++ b/test/mock_voe_channel_proxy.h
@@ -22,9 +22,9 @@
 
 class MockVoEChannelProxy : public voe::ChannelProxy {
  public:
-  // GTest doesn't like move-only types, like std::unique_ptr
-  bool SetEncoder(int payload_type,
-                  std::unique_ptr<AudioEncoder> encoder) {
+  // GMock doesn't like move-only types, like std::unique_ptr.
+  virtual bool SetEncoder(int payload_type,
+                          std::unique_ptr<AudioEncoder> encoder) {
     return SetEncoderForMock(payload_type, &encoder);
   }
   MOCK_METHOD2(SetEncoderForMock,
@@ -77,6 +77,12 @@
       AudioMixer::Source::AudioFrameInfo(int sample_rate_hz,
                                          AudioFrame* audio_frame));
   MOCK_CONST_METHOD0(PreferredSampleRate, int());
+  // GMock doesn't like move-only types, like std::unique_ptr.
+  virtual void ProcessAndEncodeAudio(std::unique_ptr<AudioFrame> audio_frame) {
+    ProcessAndEncodeAudioForMock(&audio_frame);
+  }
+  MOCK_METHOD1(ProcessAndEncodeAudioForMock,
+               void(std::unique_ptr<AudioFrame>* audio_frame));
   MOCK_METHOD1(SetTransportOverhead, void(int transport_overhead_per_packet));
   MOCK_METHOD1(AssociateSendChannel,
                void(const ChannelProxy& send_channel_proxy));
diff --git a/test/mock_voice_engine.h b/test/mock_voice_engine.h
index 57c6b58..5c966bd 100644
--- a/test/mock_voice_engine.h
+++ b/test/mock_voice_engine.h
@@ -13,17 +13,12 @@
 
 #include <memory>
 
-#include "modules/audio_device/include/mock_audio_transport.h"
 #include "modules/rtp_rtcp/mocks/mock_rtp_rtcp.h"
 #include "test/gmock.h"
 #include "test/mock_voe_channel_proxy.h"
 #include "voice_engine/voice_engine_impl.h"
 
 namespace webrtc {
-namespace voe {
-class TransmitMixer;
-}  // namespace voe
-
 namespace test {
 
 // NOTE: This class inherits from VoiceEngineImpl so that its clients will be
@@ -61,9 +56,6 @@
                   testing::SetArgPointee<0>(GetMockRtpRtcp(channel_id)));
           return proxy;
         }));
-
-    ON_CALL(*this, audio_transport())
-        .WillByDefault(testing::Return(&mock_audio_transport_));
   }
   virtual ~MockVoiceEngine() /* override */ {
     // Decrease ref count before base class d-tor is called; otherwise it will
@@ -94,7 +86,6 @@
       int(AudioDeviceModule* external_adm,
           AudioProcessing* external_apm,
           const rtc::scoped_refptr<AudioDecoderFactory>& decoder_factory));
-  MOCK_METHOD0(transmit_mixer, voe::TransmitMixer*());
   MOCK_METHOD0(Terminate, void());
   MOCK_METHOD0(CreateChannel, int());
   MOCK_METHOD1(CreateChannel, int(const ChannelConfig& config));
@@ -103,7 +94,6 @@
   MOCK_METHOD1(StopPlayout, int(int channel));
   MOCK_METHOD1(StartSend, int(int channel));
   MOCK_METHOD1(StopSend, int(int channel));
-  MOCK_METHOD0(audio_transport, AudioTransport*());
 
  private:
   // TODO(ossu): I'm not particularly happy about keeping the decoder factory
@@ -115,8 +105,6 @@
   rtc::scoped_refptr<AudioDecoderFactory> decoder_factory_;
 
   std::map<int, std::unique_ptr<MockRtpRtcp>> mock_rtp_rtcps_;
-
-  MockAudioTransport mock_audio_transport_;
 };
 }  // namespace test
 }  // namespace webrtc
diff --git a/video/video_quality_test.cc b/video/video_quality_test.cc
index 30a49d4..0aa12bb 100644
--- a/video/video_quality_test.cc
+++ b/video/video_quality_test.cc
@@ -93,12 +93,11 @@
 void CreateVoiceEngine(
     VoiceEngineState* voe,
     webrtc::AudioDeviceModule* adm,
-    webrtc::AudioProcessing* apm,
     rtc::scoped_refptr<webrtc::AudioDecoderFactory> decoder_factory) {
   voe->voice_engine = webrtc::VoiceEngine::Create();
   voe->base = webrtc::VoEBase::GetInterface(voe->voice_engine);
   EXPECT_EQ(0, adm->Init());
-  EXPECT_EQ(0, voe->base->Init(adm, apm, decoder_factory));
+  EXPECT_EQ(0, voe->base->Init(adm, nullptr, decoder_factory));
   webrtc::VoEBase::ChannelConfig config;
   config.enable_voice_pacing = true;
   voe->send_channel_id = voe->base->CreateChannel(config);
@@ -1961,7 +1960,6 @@
 void VideoQualityTest::RunWithRenderers(const Params& params) {
   std::unique_ptr<test::LayerFilteringTransport> send_transport;
   std::unique_ptr<test::DirectTransport> recv_transport;
-  std::unique_ptr<test::FakeAudioDevice> fake_audio_device;
   ::VoiceEngineState voe;
   std::unique_ptr<test::VideoRenderer> local_preview;
   std::vector<std::unique_ptr<test::VideoRenderer>> loopback_renderers;
@@ -1976,21 +1974,19 @@
     Call::Config call_config(event_log_.get());
     call_config.bitrate_config = params_.call.call_bitrate_config;
 
-    fake_audio_device.reset(new test::FakeAudioDevice(
-        test::FakeAudioDevice::CreatePulsedNoiseCapturer(32000, 48000),
-        test::FakeAudioDevice::CreateDiscardRenderer(48000),
-        1.f));
-
-    rtc::scoped_refptr<webrtc::AudioProcessing> audio_processing(
-        webrtc::AudioProcessing::Create());
+    rtc::scoped_refptr<test::FakeAudioDevice> fake_audio_device =
+        new rtc::RefCountedObject<test::FakeAudioDevice>(
+            test::FakeAudioDevice::CreatePulsedNoiseCapturer(32000, 48000),
+            test::FakeAudioDevice::CreateDiscardRenderer(48000),
+            1.f);
 
     if (params_.audio.enabled) {
-      CreateVoiceEngine(&voe, fake_audio_device.get(), audio_processing.get(),
-                        decoder_factory_);
+      CreateVoiceEngine(&voe, fake_audio_device.get(), decoder_factory_);
       AudioState::Config audio_state_config;
       audio_state_config.voice_engine = voe.voice_engine;
       audio_state_config.audio_mixer = AudioMixerImpl::Create();
-      audio_state_config.audio_processing = audio_processing;
+      audio_state_config.audio_processing = AudioProcessing::Create();
+      audio_state_config.audio_device_module = fake_audio_device;
       call_config.audio_state = AudioState::Create(audio_state_config);
       fake_audio_device->RegisterAudioCallback(
           call_config.audio_state->audio_transport());
diff --git a/voice_engine/BUILD.gn b/voice_engine/BUILD.gn
index e993931..a10b792 100644
--- a/voice_engine/BUILD.gn
+++ b/voice_engine/BUILD.gn
@@ -23,8 +23,6 @@
     "include/voe_errors.h",
     "shared_data.cc",
     "shared_data.h",
-    "transmit_mixer.cc",
-    "transmit_mixer.h",
     "transport_feedback_packet_loss_tracker.cc",
     "transport_feedback_packet_loss_tracker.h",
     "utility.cc",
@@ -36,8 +34,6 @@
   ]
 
   if (is_win) {
-    defines = [ "WEBRTC_DRIFT_COMPENSATION_SUPPORTED" ]
-
     cflags = [
       # TODO(kjellander): Bug 261: fix this warning.
       "/wd4373",  # Virtual function override.
@@ -88,6 +84,10 @@
 }
 
 rtc_static_library("audio_level") {
+  visibility += [
+    ":voice_engine",
+    "../audio:audio",
+  ]
   sources = [
     "audio_level.cc",
     "audio_level.h",
@@ -146,8 +146,6 @@
     ]
 
     if (is_win) {
-      defines = [ "WEBRTC_DRIFT_COMPENSATION_SUPPORTED" ]
-
       cflags = [
         # TODO(kjellander): Bug 261: fix this warning.
         "/wd4373",  # Virtual function override.
diff --git a/voice_engine/channel.cc b/voice_engine/channel.cc
index 5bc66ff..de2d4a7 100644
--- a/voice_engine/channel.cc
+++ b/voice_engine/channel.cc
@@ -46,7 +46,6 @@
 #include "rtc_base/timeutils.h"
 #include "system_wrappers/include/field_trial.h"
 #include "system_wrappers/include/metrics.h"
-#include "voice_engine/utility.h"
 
 namespace webrtc {
 namespace voe {
@@ -489,9 +488,6 @@
     AudioFrameOperations::Mute(audio_frame);
   }
 
-  // Store speech type for dead-or-alive detection
-  _outputSpeechType = audio_frame->speech_type_;
-
   {
     // Pass the audio buffers to an optional sink callback, before applying
     // scaling/panning, as that applies to the mix operation.
@@ -626,7 +622,6 @@
       _includeAudioLevelIndication(false),
       transport_overhead_per_packet_(0),
       rtp_overhead_per_packet_(0),
-      _outputSpeechType(AudioFrame::kNormalSpeech),
       rtcp_observer_(new VoERtcpObserver(this)),
       associate_send_channel_(ChannelOwner(nullptr)),
       pacing_enabled_(config.enable_voice_pacing),
@@ -785,11 +780,7 @@
     return 0;
   }
   channel_state_.SetSending(true);
-  {
-    // It is now OK to start posting tasks to the encoder task queue.
-    rtc::CritScope cs(&encoder_queue_lock_);
-    encoder_queue_is_active_ = true;
-  }
+
   // Resume the previous sequence number which was reset by StopSend(). This
   // needs to be done before |sending| is set to true on the RTP/RTCP module.
   if (send_sequence_number_) {
@@ -803,7 +794,11 @@
     channel_state_.SetSending(false);
     return -1;
   }
-
+  {
+    // It is now OK to start posting tasks to the encoder task queue.
+    rtc::CritScope cs(&encoder_queue_lock_);
+    encoder_queue_is_active_ = true;
+  }
   return 0;
 }
 
@@ -870,9 +865,6 @@
   rtp_codec.channels = encoder->NumChannels();
   rtp_codec.rate = 0;
 
-  cached_encoder_props_.emplace(
-      EncoderProps{encoder->SampleRateHz(), encoder->NumChannels()});
-
   if (_rtpRtcpModule->RegisterSendPayload(rtp_codec) != 0) {
     _rtpRtcpModule->DeRegisterSendPayload(payload_type);
     if (_rtpRtcpModule->RegisterSendPayload(rtp_codec) != 0) {
@@ -891,10 +883,6 @@
   audio_coding_->ModifyEncoder(modifier);
 }
 
-rtc::Optional<Channel::EncoderProps> Channel::GetEncoderProps() const {
-  return cached_encoder_props_;
-}
-
 int32_t Channel::GetRecCodec(CodecInst& codec) {
   return (audio_coding_->ReceiveCodec(&codec));
 }
@@ -1349,16 +1337,12 @@
   return _rtpRtcpModule->SendNACK(sequence_numbers, length);
 }
 
-void Channel::ProcessAndEncodeAudio(const AudioFrame& audio_input) {
+void Channel::ProcessAndEncodeAudio(std::unique_ptr<AudioFrame> audio_frame) {
   // Avoid posting any new tasks if sending was already stopped in StopSend().
   rtc::CritScope cs(&encoder_queue_lock_);
   if (!encoder_queue_is_active_) {
     return;
   }
-  std::unique_ptr<AudioFrame> audio_frame(new AudioFrame());
-  // TODO(henrika): try to avoid copying by moving ownership of audio frame
-  // either into pool of frames or into the task itself.
-  audio_frame->CopyFrom(audio_input);
   // Profile time between when the audio frame is added to the task queue and
   // when the task is actually executed.
   audio_frame->UpdateProfileTimeStamp();
@@ -1366,27 +1350,6 @@
       new ProcessAndEncodeAudioTask(std::move(audio_frame), this)));
 }
 
-void Channel::ProcessAndEncodeAudio(const int16_t* audio_data,
-                                    int sample_rate,
-                                    size_t number_of_frames,
-                                    size_t number_of_channels) {
-  // Avoid posting as new task if sending was already stopped in StopSend().
-  rtc::CritScope cs(&encoder_queue_lock_);
-  if (!encoder_queue_is_active_) {
-    return;
-  }
-  std::unique_ptr<AudioFrame> audio_frame(new AudioFrame());
-  const auto props = GetEncoderProps();
-  RTC_CHECK(props);
-  audio_frame->sample_rate_hz_ = std::min(props->sample_rate_hz, sample_rate);
-  audio_frame->num_channels_ =
-      std::min(props->num_channels, number_of_channels);
-  RemixAndResample(audio_data, number_of_frames, number_of_channels,
-                   sample_rate, &input_resampler_, audio_frame.get());
-  encoder_queue_->PostTask(std::unique_ptr<rtc::QueuedTask>(
-      new ProcessAndEncodeAudioTask(std::move(audio_frame), this)));
-}
-
 void Channel::ProcessAndEncodeAudioOnTaskQueue(AudioFrame* audio_input) {
   RTC_DCHECK_RUN_ON(encoder_queue_);
   RTC_DCHECK_GT(audio_input->samples_per_channel_, 0);
diff --git a/voice_engine/channel.h b/voice_engine/channel.h
index 8431b01..9018f9f 100644
--- a/voice_engine/channel.h
+++ b/voice_engine/channel.h
@@ -18,7 +18,6 @@
 #include "api/call/audio_sink.h"
 #include "api/call/transport.h"
 #include "api/optional.h"
-#include "common_audio/resampler/include/push_resampler.h"
 #include "common_types.h"  // NOLINT(build/include)
 #include "modules/audio_coding/include/audio_coding_module.h"
 #include "modules/audio_processing/rms_level.h"
@@ -181,11 +180,6 @@
   void StopSend();
 
   // Codecs
-  struct EncoderProps {
-    int sample_rate_hz;
-    size_t num_channels;
-  };
-  rtc::Optional<EncoderProps> GetEncoderProps() const;
   int32_t GetRecCodec(CodecInst& codec);
   void SetBitRate(int bitrate_bps, int64_t probing_interval_ms);
   bool EnableAudioNetworkAdaptor(const std::string& config_string);
@@ -283,26 +277,16 @@
   RtpRtcp* RtpRtcpModulePtr() const { return _rtpRtcpModule.get(); }
   int8_t OutputEnergyLevel() const { return _outputAudioLevel.Level(); }
 
-  // ProcessAndEncodeAudio() creates an audio frame copy and posts a task
-  // on the shared encoder task queue, wich in turn calls (on the queue)
-  // ProcessAndEncodeAudioOnTaskQueue() where the actual processing of the
-  // audio takes place. The processing mainly consists of encoding and preparing
-  // the result for sending by adding it to a send queue.
+  // ProcessAndEncodeAudio() posts a task on the shared encoder task queue,
+  // which in turn calls (on the queue) ProcessAndEncodeAudioOnTaskQueue() where
+  // the actual processing of the audio takes place. The processing mainly
+  // consists of encoding and preparing the result for sending by adding it to a
+  // send queue.
   // The main reason for using a task queue here is to release the native,
   // OS-specific, audio capture thread as soon as possible to ensure that it
   // can go back to sleep and be prepared to deliver an new captured audio
   // packet.
-  void ProcessAndEncodeAudio(const AudioFrame& audio_input);
-
-  // This version of ProcessAndEncodeAudio() is used by PushCaptureData() in
-  // VoEBase and the audio in |audio_data| has not been subject to any APM
-  // processing. Some extra steps are therfore needed when building up the
-  // audio frame copy before using the same task as in the default call to
-  // ProcessAndEncodeAudio(const AudioFrame& audio_input).
-  void ProcessAndEncodeAudio(const int16_t* audio_data,
-                             int sample_rate,
-                             size_t number_of_frames,
-                             size_t number_of_channels);
+  void ProcessAndEncodeAudio(std::unique_ptr<AudioFrame> audio_frame);
 
   // Associate to a send channel.
   // Used for obtaining RTT for a receive-only channel.
@@ -382,8 +366,6 @@
   std::unique_ptr<AudioCodingModule> audio_coding_;
   std::unique_ptr<AudioSinkInterface> audio_sink_;
   AudioLevel _outputAudioLevel;
-  // Downsamples to the codec rate if necessary.
-  PushResampler<int16_t> input_resampler_;
   uint32_t _timeStamp RTC_ACCESS_ON(encoder_queue_);
 
   RemoteNtpTimeEstimator ntp_estimator_ RTC_GUARDED_BY(ts_stats_lock_);
@@ -421,8 +403,6 @@
       RTC_GUARDED_BY(overhead_per_packet_lock_);
   size_t rtp_overhead_per_packet_ RTC_GUARDED_BY(overhead_per_packet_lock_);
   rtc::CriticalSection overhead_per_packet_lock_;
-  // VoENetwork
-  AudioFrame::SpeechType _outputSpeechType;
   // RtcpBandwidthObserver
   std::unique_ptr<VoERtcpObserver> rtcp_observer_;
   // An associated send channel.
@@ -439,8 +419,6 @@
   // TODO(ossu): Remove once GetAudioDecoderFactory() is no longer needed.
   rtc::scoped_refptr<AudioDecoderFactory> decoder_factory_;
 
-  rtc::Optional<EncoderProps> cached_encoder_props_;
-
   rtc::ThreadChecker construction_thread_;
 
   const bool use_twcc_plr_for_ana_;
diff --git a/voice_engine/channel_proxy.cc b/voice_engine/channel_proxy.cc
index 8d56db0..505113d 100644
--- a/voice_engine/channel_proxy.cc
+++ b/voice_engine/channel_proxy.cc
@@ -256,6 +256,12 @@
   return channel()->PreferredSampleRate();
 }
 
+void ChannelProxy::ProcessAndEncodeAudio(
+    std::unique_ptr<AudioFrame> audio_frame) {
+  RTC_DCHECK_RUNS_SERIALIZED(&audio_thread_race_checker_);
+  return channel()->ProcessAndEncodeAudio(std::move(audio_frame));
+}
+
 void ChannelProxy::SetTransportOverhead(int transport_overhead_per_packet) {
   RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
   channel()->SetTransportOverhead(transport_overhead_per_packet);
diff --git a/voice_engine/channel_proxy.h b/voice_engine/channel_proxy.h
index 8c913fa..1dec23a 100644
--- a/voice_engine/channel_proxy.h
+++ b/voice_engine/channel_proxy.h
@@ -107,6 +107,7 @@
       int sample_rate_hz,
       AudioFrame* audio_frame);
   virtual int PreferredSampleRate() const;
+  virtual void ProcessAndEncodeAudio(std::unique_ptr<AudioFrame> audio_frame);
   virtual void SetTransportOverhead(int transport_overhead_per_packet);
   virtual void AssociateSendChannel(const ChannelProxy& send_channel_proxy);
   virtual void DisassociateSendChannel();
diff --git a/voice_engine/include/voe_base.h b/voice_engine/include/voe_base.h
index a995325..238de1d 100644
--- a/voice_engine/include/voe_base.h
+++ b/voice_engine/include/voe_base.h
@@ -43,10 +43,6 @@
 
 class AudioDeviceModule;
 class AudioProcessing;
-class AudioTransport;
-namespace voe {
-class TransmitMixer;
-}  // namespace voe
 
 // VoiceEngine
 class WEBRTC_DLLEXPORT VoiceEngine {
@@ -90,17 +86,13 @@
   // modules:
   // - The Audio Device Module (ADM) which implements all the audio layer
   // functionality in a separate (reference counted) module.
-  // - The AudioProcessing module handles capture-side processing.
+  // - The AudioProcessing module is unused - only kept for API compatibility.
   // - An AudioDecoderFactory - used to create audio decoders.
   virtual int Init(
       AudioDeviceModule* audio_device,
       AudioProcessing* audio_processing,
       const rtc::scoped_refptr<AudioDecoderFactory>& decoder_factory) = 0;
 
-  // This method is WIP - DO NOT USE!
-  // Returns NULL before Init() is called.
-  virtual voe::TransmitMixer* transmit_mixer() = 0;
-
   // Terminates all VoiceEngine functions and releases allocated resources.
   virtual void Terminate() = 0;
 
@@ -147,10 +139,6 @@
   // By default, recording is enabled.
   virtual int SetRecording(bool enabled) = 0;
 
-  // TODO(xians): Make the interface pure virtual after libjingle
-  // implements the interface in its FakeWebRtcVoiceEngine.
-  virtual AudioTransport* audio_transport() { return NULL; }
-
  protected:
   VoEBase() {}
   virtual ~VoEBase() {}
diff --git a/voice_engine/shared_data.cc b/voice_engine/shared_data.cc
index 01e163b..b60633d 100644
--- a/voice_engine/shared_data.cc
+++ b/voice_engine/shared_data.cc
@@ -10,9 +10,7 @@
 
 #include "voice_engine/shared_data.h"
 
-#include "modules/audio_processing/include/audio_processing.h"
 #include "voice_engine/channel.h"
-#include "voice_engine/transmit_mixer.h"
 
 namespace webrtc {
 
@@ -26,14 +24,10 @@
       _audioDevicePtr(NULL),
       _moduleProcessThreadPtr(ProcessThread::Create("VoiceProcessThread")),
       encoder_queue_("AudioEncoderQueue") {
-  if (TransmitMixer::Create(_transmitMixerPtr) == 0) {
-    _transmitMixerPtr->SetEngineInformation(&_channelManager);
-  }
 }
 
 SharedData::~SharedData()
 {
-    TransmitMixer::Destroy(_transmitMixerPtr);
     if (_audioDevicePtr) {
         _audioDevicePtr->Release();
     }
@@ -50,10 +44,6 @@
   _audioDevicePtr = audio_device;
 }
 
-void SharedData::set_audio_processing(AudioProcessing* audioproc) {
-  _transmitMixerPtr->SetAudioProcessingModule(audioproc);
-}
-
 int SharedData::NumOfSendingChannels() {
   ChannelManager::Iterator it(&_channelManager);
   int sending_channels = 0;
diff --git a/voice_engine/shared_data.h b/voice_engine/shared_data.h
index 7978ff7..1a7985a 100644
--- a/voice_engine/shared_data.h
+++ b/voice_engine/shared_data.h
@@ -14,7 +14,6 @@
 #include <memory>
 
 #include "modules/audio_device/include/audio_device.h"
-#include "modules/audio_processing/include/audio_processing.h"
 #include "modules/utility/include/process_thread.h"
 #include "rtc_base/criticalsection.h"
 #include "rtc_base/scoped_ref_ptr.h"
@@ -28,8 +27,6 @@
 namespace webrtc {
 namespace voe {
 
-class TransmitMixer;
-
 class SharedData
 {
 public:
@@ -39,8 +36,6 @@
     AudioDeviceModule* audio_device() { return _audioDevicePtr.get(); }
     void set_audio_device(
         const rtc::scoped_refptr<AudioDeviceModule>& audio_device);
-    void set_audio_processing(AudioProcessing* audio_processing);
-    TransmitMixer* transmit_mixer() { return _transmitMixerPtr; }
     rtc::CriticalSection* crit_sec() { return &_apiCritPtr; }
     ProcessThread* process_thread() { return _moduleProcessThreadPtr.get(); }
     rtc::TaskQueue* encoder_queue();
@@ -54,7 +49,6 @@
  rtc::CriticalSection _apiCritPtr;
  ChannelManager _channelManager;
  rtc::scoped_refptr<AudioDeviceModule> _audioDevicePtr;
- TransmitMixer* _transmitMixerPtr;
  std::unique_ptr<ProcessThread> _moduleProcessThreadPtr;
  // |encoder_queue| is defined last to ensure all pending tasks are cancelled
  // and deleted before any other members.
diff --git a/voice_engine/transmit_mixer.cc b/voice_engine/transmit_mixer.cc
deleted file mode 100644
index 049a64c..0000000
--- a/voice_engine/transmit_mixer.cc
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
- *
- *  Use of this source code is governed by a BSD-style license
- *  that can be found in the LICENSE file in the root of the source
- *  tree. An additional intellectual property rights grant can be found
- *  in the file PATENTS.  All contributing project authors may
- *  be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "voice_engine/transmit_mixer.h"
-
-#include <memory>
-
-#include "audio/utility/audio_frame_operations.h"
-#include "rtc_base/format_macros.h"
-#include "rtc_base/location.h"
-#include "rtc_base/logging.h"
-#include "system_wrappers/include/event_wrapper.h"
-#include "voice_engine/channel.h"
-#include "voice_engine/channel_manager.h"
-#include "voice_engine/utility.h"
-
-namespace webrtc {
-namespace voe {
-
-// TODO(solenberg): The thread safety in this class is dubious.
-
-int32_t
-TransmitMixer::Create(TransmitMixer*& mixer)
-{
-    mixer = new TransmitMixer();
-    if (mixer == NULL)
-    {
-      RTC_DLOG(LS_ERROR) <<
-          "TransmitMixer::Create() unable to allocate memory for mixer";
-      return -1;
-    }
-    return 0;
-}
-
-void
-TransmitMixer::Destroy(TransmitMixer*& mixer)
-{
-    if (mixer)
-    {
-        delete mixer;
-        mixer = NULL;
-    }
-}
-
-TransmitMixer::~TransmitMixer() = default;
-
-void TransmitMixer::SetEngineInformation(ChannelManager* channelManager) {
-  _channelManagerPtr = channelManager;
-}
-
-int32_t
-TransmitMixer::SetAudioProcessingModule(AudioProcessing* audioProcessingModule)
-{
-    audioproc_ = audioProcessingModule;
-    return 0;
-}
-
-void TransmitMixer::GetSendCodecInfo(int* max_sample_rate,
-                                     size_t* max_channels) {
-  *max_sample_rate = 8000;
-  *max_channels = 1;
-  for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
-       it.Increment()) {
-    Channel* channel = it.GetChannel();
-    if (channel->Sending()) {
-      const auto props = channel->GetEncoderProps();
-      RTC_CHECK(props);
-      *max_sample_rate = std::max(*max_sample_rate, props->sample_rate_hz);
-      *max_channels = std::max(*max_channels, props->num_channels);
-    }
-  }
-}
-
-int32_t
-TransmitMixer::PrepareDemux(const void* audioSamples,
-                            size_t nSamples,
-                            size_t nChannels,
-                            uint32_t samplesPerSec,
-                            uint16_t totalDelayMS,
-                            int32_t clockDrift,
-                            uint16_t currentMicLevel,
-                            bool keyPressed)
-{
-    // --- Resample input audio and create/store the initial audio frame
-    GenerateAudioFrame(static_cast<const int16_t*>(audioSamples),
-                       nSamples,
-                       nChannels,
-                       samplesPerSec);
-
-    // --- Near-end audio processing.
-    ProcessAudio(totalDelayMS, clockDrift, currentMicLevel, keyPressed);
-
-    if (swap_stereo_channels_ && stereo_codec_)
-      // Only bother swapping if we're using a stereo codec.
-      AudioFrameOperations::SwapStereoChannels(&_audioFrame);
-
-    // --- Annoying typing detection (utilizes the APM/VAD decision)
-#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION
-    TypingDetection(keyPressed);
-#endif
-
-    // --- Measure audio level of speech after all processing.
-    double sample_duration = static_cast<double>(nSamples) / samplesPerSec;
-    _audioLevel.ComputeLevel(_audioFrame, sample_duration);
-
-    return 0;
-}
-
-void TransmitMixer::ProcessAndEncodeAudio() {
-  RTC_DCHECK_GT(_audioFrame.samples_per_channel_, 0);
-  for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
-       it.Increment()) {
-    Channel* const channel = it.GetChannel();
-    if (channel->Sending()) {
-      channel->ProcessAndEncodeAudio(_audioFrame);
-    }
-  }
-}
-
-uint32_t TransmitMixer::CaptureLevel() const
-{
-    return _captureLevel;
-}
-
-int32_t
-TransmitMixer::StopSend()
-{
-    _audioLevel.Clear();
-    return 0;
-}
-
-int8_t TransmitMixer::AudioLevel() const
-{
-    // Speech + file level [0,9]
-    return _audioLevel.Level();
-}
-
-int16_t TransmitMixer::AudioLevelFullRange() const
-{
-    // Speech + file level [0,32767]
-    return _audioLevel.LevelFullRange();
-}
-
-double TransmitMixer::GetTotalInputEnergy() const {
-  return _audioLevel.TotalEnergy();
-}
-
-double TransmitMixer::GetTotalInputDuration() const {
-  return _audioLevel.TotalDuration();
-}
-
-void TransmitMixer::GenerateAudioFrame(const int16_t* audio,
-                                       size_t samples_per_channel,
-                                       size_t num_channels,
-                                       int sample_rate_hz) {
-  int codec_rate;
-  size_t num_codec_channels;
-  GetSendCodecInfo(&codec_rate, &num_codec_channels);
-  stereo_codec_ = num_codec_channels == 2;
-
-  // We want to process at the lowest rate possible without losing information.
-  // Choose the lowest native rate at least equal to the input and codec rates.
-  const int min_processing_rate = std::min(sample_rate_hz, codec_rate);
-  for (size_t i = 0; i < AudioProcessing::kNumNativeSampleRates; ++i) {
-    _audioFrame.sample_rate_hz_ = AudioProcessing::kNativeSampleRatesHz[i];
-    if (_audioFrame.sample_rate_hz_ >= min_processing_rate) {
-      break;
-    }
-  }
-  _audioFrame.num_channels_ = std::min(num_channels, num_codec_channels);
-  RemixAndResample(audio, samples_per_channel, num_channels, sample_rate_hz,
-                   &resampler_, &_audioFrame);
-}
-
-void TransmitMixer::ProcessAudio(int delay_ms, int clock_drift,
-                                 int current_mic_level, bool key_pressed) {
-  if (audioproc_->set_stream_delay_ms(delay_ms) != 0) {
-    // Silently ignore this failure to avoid flooding the logs.
-  }
-
-  GainControl* agc = audioproc_->gain_control();
-  if (agc->set_stream_analog_level(current_mic_level) != 0) {
-    RTC_DLOG(LS_ERROR) << "set_stream_analog_level failed: current_mic_level = "
-                       << current_mic_level;
-    assert(false);
-  }
-
-  EchoCancellation* aec = audioproc_->echo_cancellation();
-  if (aec->is_drift_compensation_enabled()) {
-    aec->set_stream_drift_samples(clock_drift);
-  }
-
-  audioproc_->set_stream_key_pressed(key_pressed);
-
-  int err = audioproc_->ProcessStream(&_audioFrame);
-  if (err != 0) {
-    RTC_DLOG(LS_ERROR) << "ProcessStream() error: " << err;
-    assert(false);
-  }
-
-  // Store new capture level. Only updated when analog AGC is enabled.
-  _captureLevel = agc->stream_analog_level();
-}
-
-#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION
-void TransmitMixer::TypingDetection(bool key_pressed)
-{
-  // We let the VAD determine if we're using this feature or not.
-  if (_audioFrame.vad_activity_ == AudioFrame::kVadUnknown) {
-    return;
-  }
-
-  bool vad_active = _audioFrame.vad_activity_ == AudioFrame::kVadActive;
-  bool typing_detected = typing_detection_.Process(key_pressed, vad_active);
-
-  rtc::CritScope cs(&lock_);
-  typing_noise_detected_ = typing_detected;
-}
-#endif
-
-void TransmitMixer::EnableStereoChannelSwapping(bool enable) {
-  swap_stereo_channels_ = enable;
-}
-
-bool TransmitMixer::IsStereoChannelSwappingEnabled() {
-  return swap_stereo_channels_;
-}
-
-bool TransmitMixer::typing_noise_detected() const {
-  rtc::CritScope cs(&lock_);
-  return typing_noise_detected_;
-}
-
-}  // namespace voe
-}  // namespace webrtc
diff --git a/voice_engine/transmit_mixer.h b/voice_engine/transmit_mixer.h
deleted file mode 100644
index 42b6212..0000000
--- a/voice_engine/transmit_mixer.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
- *
- *  Use of this source code is governed by a BSD-style license
- *  that can be found in the LICENSE file in the root of the source
- *  tree. An additional intellectual property rights grant can be found
- *  in the file PATENTS.  All contributing project authors may
- *  be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef VOICE_ENGINE_TRANSMIT_MIXER_H_
-#define VOICE_ENGINE_TRANSMIT_MIXER_H_
-
-#include <memory>
-
-#include "common_audio/resampler/include/push_resampler.h"
-#include "common_types.h"  // NOLINT(build/include)
-#include "modules/audio_processing/typing_detection.h"
-#include "modules/include/module_common_types.h"
-#include "rtc_base/criticalsection.h"
-#include "voice_engine/audio_level.h"
-#include "voice_engine/include/voe_base.h"
-
-#if !defined(WEBRTC_ANDROID) && !defined(WEBRTC_IOS)
-#define WEBRTC_VOICE_ENGINE_TYPING_DETECTION 1
-#else
-#define WEBRTC_VOICE_ENGINE_TYPING_DETECTION 0
-#endif
-
-namespace webrtc {
-class AudioProcessing;
-class ProcessThread;
-
-namespace voe {
-
-class ChannelManager;
-class MixedAudio;
-
-class TransmitMixer {
-public:
-    static int32_t Create(TransmitMixer*& mixer);
-
-    static void Destroy(TransmitMixer*& mixer);
-
-    void SetEngineInformation(ChannelManager* channelManager);
-
-    int32_t SetAudioProcessingModule(AudioProcessing* audioProcessingModule);
-
-    int32_t PrepareDemux(const void* audioSamples,
-                         size_t nSamples,
-                         size_t nChannels,
-                         uint32_t samplesPerSec,
-                         uint16_t totalDelayMS,
-                         int32_t  clockDrift,
-                         uint16_t currentMicLevel,
-                         bool keyPressed);
-
-    void ProcessAndEncodeAudio();
-
-    // Must be called on the same thread as PrepareDemux().
-    uint32_t CaptureLevel() const;
-
-    int32_t StopSend();
-
-    // TODO(solenberg): Remove, once AudioMonitor is gone.
-    int8_t AudioLevel() const;
-
-    // 'virtual' to allow mocking.
-    virtual int16_t AudioLevelFullRange() const;
-
-    // See description of "totalAudioEnergy" in the WebRTC stats spec:
-    // https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-totalaudioenergy
-    // 'virtual' to allow mocking.
-    virtual double GetTotalInputEnergy() const;
-
-    // 'virtual' to allow mocking.
-    virtual double GetTotalInputDuration() const;
-
-    virtual ~TransmitMixer();
-
-  // Virtual to allow mocking.
-  virtual void EnableStereoChannelSwapping(bool enable);
-  bool IsStereoChannelSwappingEnabled();
-
-  // Virtual to allow mocking.
-  virtual bool typing_noise_detected() const;
-
-protected:
-    TransmitMixer() = default;
-
-private:
-    // Gets the maximum sample rate and number of channels over all currently
-    // sending codecs.
-    void GetSendCodecInfo(int* max_sample_rate, size_t* max_channels);
-
-    void GenerateAudioFrame(const int16_t audioSamples[],
-                            size_t nSamples,
-                            size_t nChannels,
-                            int samplesPerSec);
-
-    void ProcessAudio(int delay_ms, int clock_drift, int current_mic_level,
-                      bool key_pressed);
-
-#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION
-    void TypingDetection(bool key_pressed);
-#endif
-
-    // uses
-    ChannelManager* _channelManagerPtr = nullptr;
-    AudioProcessing* audioproc_ = nullptr;
-
-    // owns
-    AudioFrame _audioFrame;
-    PushResampler<int16_t> resampler_;  // ADM sample rate -> mixing rate
-    voe::AudioLevel _audioLevel;
-
-#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION
-    webrtc::TypingDetection typing_detection_;
-#endif
-
-    rtc::CriticalSection lock_;
-    bool typing_noise_detected_ RTC_GUARDED_BY(lock_) = false;
-
-    uint32_t _captureLevel = 0;
-    bool stereo_codec_ = false;
-    bool swap_stereo_channels_ = false;
-};
-}  // namespace voe
-}  // namespace webrtc
-
-#endif  // VOICE_ENGINE_TRANSMIT_MIXER_H_
diff --git a/voice_engine/voe_base_impl.cc b/voice_engine/voe_base_impl.cc
index 5d49872..094288d 100644
--- a/voice_engine/voe_base_impl.cc
+++ b/voice_engine/voe_base_impl.cc
@@ -13,13 +13,11 @@
 #include "common_audio/signal_processing/include/signal_processing_library.h"
 #include "modules/audio_coding/include/audio_coding_module.h"
 #include "modules/audio_device/audio_device_impl.h"
-#include "modules/audio_processing/include/audio_processing.h"
 #include "rtc_base/format_macros.h"
 #include "rtc_base/location.h"
 #include "rtc_base/logging.h"
 #include "voice_engine/channel.h"
 #include "voice_engine/include/voe_errors.h"
-#include "voice_engine/transmit_mixer.h"
 #include "voice_engine/voice_engine_impl.h"
 
 namespace webrtc {
@@ -40,122 +38,17 @@
   TerminateInternal();
 }
 
-int32_t VoEBaseImpl::RecordedDataIsAvailable(
-    const void* audio_data,
-    const size_t number_of_frames,
-    const size_t bytes_per_sample,
-    const size_t number_of_channels,
-    const uint32_t sample_rate,
-    const uint32_t audio_delay_milliseconds,
-    const int32_t clock_drift,
-    const uint32_t volume,
-    const bool key_pressed,
-    uint32_t& new_mic_volume) {
-  RTC_DCHECK_EQ(2 * number_of_channels, bytes_per_sample);
-  RTC_DCHECK(shared_->transmit_mixer() != nullptr);
-  RTC_DCHECK(shared_->audio_device() != nullptr);
-
-  constexpr uint32_t kMaxVolumeLevel = 255;
-
-  uint32_t max_volume = 0;
-  uint16_t voe_mic_level = 0;
-  // Check for zero to skip this calculation; the consumer may use this to
-  // indicate no volume is available.
-  if (volume != 0) {
-    // Scale from ADM to VoE level range
-    if (shared_->audio_device()->MaxMicrophoneVolume(&max_volume) == 0) {
-      if (max_volume) {
-        voe_mic_level = static_cast<uint16_t>(
-            (volume * kMaxVolumeLevel + static_cast<int>(max_volume / 2)) /
-            max_volume);
-      }
-    }
-    // We learned that on certain systems (e.g Linux) the voe_mic_level
-    // can be greater than the maxVolumeLevel therefore
-    // we are going to cap the voe_mic_level to the maxVolumeLevel
-    // and change the maxVolume to volume if it turns out that
-    // the voe_mic_level is indeed greater than the maxVolumeLevel.
-    if (voe_mic_level > kMaxVolumeLevel) {
-      voe_mic_level = kMaxVolumeLevel;
-      max_volume = volume;
-    }
-  }
-
-  // Perform channel-independent operations
-  // (APM, mix with file, record to file, mute, etc.)
-  shared_->transmit_mixer()->PrepareDemux(
-      audio_data, number_of_frames, number_of_channels, sample_rate,
-      static_cast<uint16_t>(audio_delay_milliseconds), clock_drift,
-      voe_mic_level, key_pressed);
-
-  // Copy the audio frame to each sending channel and perform
-  // channel-dependent operations (file mixing, mute, etc.), encode and
-  // packetize+transmit the RTP packet.
-  shared_->transmit_mixer()->ProcessAndEncodeAudio();
-
-  // Scale from VoE to ADM level range.
-  uint32_t new_voe_mic_level = shared_->transmit_mixer()->CaptureLevel();
-  if (new_voe_mic_level != voe_mic_level) {
-    // Return the new volume if AGC has changed the volume.
-    return static_cast<int>((new_voe_mic_level * max_volume +
-                             static_cast<int>(kMaxVolumeLevel / 2)) /
-                            kMaxVolumeLevel);
-  }
-
-  return 0;
-}
-
-int32_t VoEBaseImpl::NeedMorePlayData(const size_t nSamples,
-                                      const size_t nBytesPerSample,
-                                      const size_t nChannels,
-                                      const uint32_t samplesPerSec,
-                                      void* audioSamples,
-                                      size_t& nSamplesOut,
-                                      int64_t* elapsed_time_ms,
-                                      int64_t* ntp_time_ms) {
-  RTC_NOTREACHED();
-  return 0;
-}
-
-void VoEBaseImpl::PushCaptureData(int voe_channel, const void* audio_data,
-                                  int bits_per_sample, int sample_rate,
-                                  size_t number_of_channels,
-                                  size_t number_of_frames) {
-  voe::ChannelOwner ch = shared_->channel_manager().GetChannel(voe_channel);
-  voe::Channel* channel = ch.channel();
-  if (!channel)
-    return;
-  if (channel->Sending()) {
-    // Send the audio to each channel directly without using the APM in the
-    // transmit mixer.
-    channel->ProcessAndEncodeAudio(static_cast<const int16_t*>(audio_data),
-                                   sample_rate, number_of_frames,
-                                   number_of_channels);
-  }
-}
-
-void VoEBaseImpl::PullRenderData(int bits_per_sample,
-                                 int sample_rate,
-                                 size_t number_of_channels,
-                                 size_t number_of_frames,
-                                 void* audio_data, int64_t* elapsed_time_ms,
-                                 int64_t* ntp_time_ms) {
-  RTC_NOTREACHED();
-}
-
 int VoEBaseImpl::Init(
     AudioDeviceModule* audio_device,
     AudioProcessing* audio_processing,
     const rtc::scoped_refptr<AudioDecoderFactory>& decoder_factory) {
   RTC_DCHECK(audio_device);
-  RTC_DCHECK(audio_processing);
   rtc::CritScope cs(shared_->crit_sec());
   if (shared_->process_thread()) {
     shared_->process_thread()->Start();
   }
 
   shared_->set_audio_device(audio_device);
-  shared_->set_audio_processing(audio_processing);
 
   RTC_DCHECK(decoder_factory);
   decoder_factory_ = decoder_factory;
@@ -338,7 +231,6 @@
       RTC_LOG(LS_ERROR) << "StopSend() failed to stop recording";
       return -1;
     }
-    shared_->transmit_mixer()->StopSend();
   }
 
   return 0;
@@ -405,6 +297,5 @@
   }
 
   shared_->set_audio_device(nullptr);
-  shared_->set_audio_processing(nullptr);
 }
 }  // namespace webrtc
diff --git a/voice_engine/voe_base_impl.h b/voice_engine/voe_base_impl.h
index b96be9e..2649b8c 100644
--- a/voice_engine/voe_base_impl.h
+++ b/voice_engine/voe_base_impl.h
@@ -21,16 +21,12 @@
 
 class ProcessThread;
 
-class VoEBaseImpl : public VoEBase,
-                    public AudioTransport {
+class VoEBaseImpl : public VoEBase {
  public:
   int Init(
       AudioDeviceModule* audio_device,
       AudioProcessing* audio_processing,
       const rtc::scoped_refptr<AudioDecoderFactory>& decoder_factory) override;
-  voe::TransmitMixer* transmit_mixer() override {
-    return shared_->transmit_mixer();
-  }
   void Terminate() override;
 
   int CreateChannel() override;
@@ -45,41 +41,6 @@
   int SetPlayout(bool enabled) override;
   int SetRecording(bool enabled) override;
 
-  AudioTransport* audio_transport() override { return this; }
-
-  // AudioTransport
-  int32_t RecordedDataIsAvailable(const void* audio_data,
-                                  const size_t number_of_frames,
-                                  const size_t bytes_per_sample,
-                                  const size_t number_of_channels,
-                                  const uint32_t sample_rate,
-                                  const uint32_t audio_delay_milliseconds,
-                                  const int32_t clock_drift,
-                                  const uint32_t volume,
-                                  const bool key_pressed,
-                                  uint32_t& new_mic_volume) override;
-  RTC_DEPRECATED int32_t NeedMorePlayData(const size_t nSamples,
-                                          const size_t nBytesPerSample,
-                                          const size_t nChannels,
-                                          const uint32_t samplesPerSec,
-                                          void* audioSamples,
-                                          size_t& nSamplesOut,
-                                          int64_t* elapsed_time_ms,
-                                          int64_t* ntp_time_ms) override;
-  void PushCaptureData(int voe_channel,
-                       const void* audio_data,
-                       int bits_per_sample,
-                       int sample_rate,
-                       size_t number_of_channels,
-                       size_t number_of_frames) override;
-  RTC_DEPRECATED void PullRenderData(int bits_per_sample,
-                                     int sample_rate,
-                                     size_t number_of_channels,
-                                     size_t number_of_frames,
-                                     void* audio_data,
-                                     int64_t* elapsed_time_ms,
-                                     int64_t* ntp_time_ms) override;
-
  protected:
   VoEBaseImpl(voe::SharedData* shared);
   ~VoEBaseImpl() override;
diff --git a/voice_engine/voe_base_unittest.cc b/voice_engine/voe_base_unittest.cc
index 56c3d13..2642a10 100644
--- a/voice_engine/voe_base_unittest.cc
+++ b/voice_engine/voe_base_unittest.cc
@@ -12,8 +12,6 @@
 
 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
 #include "modules/audio_device/include/fake_audio_device.h"
-#include "modules/audio_processing/include/mock_audio_processing.h"
-#include "rtc_base/refcountedobject.h"
 #include "test/gtest.h"
 
 namespace webrtc {
@@ -24,7 +22,6 @@
       : voe_(VoiceEngine::Create()),
         base_(VoEBase::GetInterface(voe_)) {
     EXPECT_NE(nullptr, base_);
-    apm_ = new rtc::RefCountedObject<test::MockAudioProcessing>();
   }
 
   ~VoEBaseTest() {
@@ -36,17 +33,16 @@
   VoiceEngine* voe_;
   VoEBase* base_;
   FakeAudioDeviceModule adm_;
-  rtc::scoped_refptr<AudioProcessing> apm_;
 };
 
 TEST_F(VoEBaseTest, InitWithExternalAudioDevice) {
   EXPECT_EQ(0,
-            base_->Init(&adm_, apm_.get(), CreateBuiltinAudioDecoderFactory()));
+            base_->Init(&adm_, nullptr, CreateBuiltinAudioDecoderFactory()));
 }
 
 TEST_F(VoEBaseTest, CreateChannelAfterInit) {
   EXPECT_EQ(0,
-            base_->Init(&adm_, apm_.get(), CreateBuiltinAudioDecoderFactory()));
+            base_->Init(&adm_, nullptr, CreateBuiltinAudioDecoderFactory()));
   int channelID = base_->CreateChannel();
   EXPECT_NE(channelID, -1);
   EXPECT_EQ(0, base_->DeleteChannel(channelID));