Move CNG and RED management into the Rent-A-Codec

This leaves CodecOwner without a job, so we eliminate it.

BUG=webrtc:5028

Review URL: https://codereview.webrtc.org/1443653004

Cr-Commit-Position: refs/heads/master@{#10650}
diff --git a/webrtc/modules/audio_coding/BUILD.gn b/webrtc/modules/audio_coding/BUILD.gn
index 13f9a5d..94717f1 100644
--- a/webrtc/modules/audio_coding/BUILD.gn
+++ b/webrtc/modules/audio_coding/BUILD.gn
@@ -63,8 +63,6 @@
     "main/acm2/call_statistics.h",
     "main/acm2/codec_manager.cc",
     "main/acm2/codec_manager.h",
-    "main/acm2/codec_owner.cc",
-    "main/acm2/codec_owner.h",
     "main/acm2/initial_delay_manager.cc",
     "main/acm2/initial_delay_manager.h",
     "main/include/audio_coding_module.h",
diff --git a/webrtc/modules/audio_coding/main/acm2/codec_manager.cc b/webrtc/modules/audio_coding/main/acm2/codec_manager.cc
index ac21020..47bbbde 100644
--- a/webrtc/modules/audio_coding/main/acm2/codec_manager.cc
+++ b/webrtc/modules/audio_coding/main/acm2/codec_manager.cc
@@ -240,7 +240,7 @@
 
   // Check if the codec is already registered as send codec.
   bool new_codec = true;
-  if (codec_owner_.Encoder()) {
+  if (CurrentEncoder()) {
     auto new_codec_id = RentACodec::CodecIdByInst(send_codec_inst_);
     RTC_DCHECK(new_codec_id);
     auto old_codec_id = RentACodec::CodecIdFromIndex(codec_id);
@@ -263,10 +263,8 @@
     AudioEncoder* enc = rent_a_codec_.RentEncoder(send_codec);
     if (!enc)
       return -1;
-    codec_owner_.SetEncoders(
-        enc, dtx_enabled_ ? CngPayloadType(send_codec.plfreq) : -1,
-        vad_mode_, red_enabled_ ? RedPayloadType(send_codec.plfreq) : -1);
-    RTC_DCHECK(codec_owner_.Encoder());
+    RentEncoderStack(enc, send_codec.plfreq);
+    RTC_DCHECK(CurrentEncoder());
 
     codec_fec_enabled_ = codec_fec_enabled_ &&
                          enc->SetFec(codec_fec_enabled_);
@@ -282,10 +280,8 @@
     AudioEncoder* enc = rent_a_codec_.RentEncoder(send_codec);
     if (!enc)
       return -1;
-    codec_owner_.SetEncoders(
-        enc, dtx_enabled_ ? CngPayloadType(send_codec.plfreq) : -1,
-        vad_mode_, red_enabled_ ? RedPayloadType(send_codec.plfreq) : -1);
-    RTC_DCHECK(codec_owner_.Encoder());
+    RentEncoderStack(enc, send_codec.plfreq);
+    RTC_DCHECK(CurrentEncoder());
   }
   send_codec_inst_.plfreq = send_codec.plfreq;
   send_codec_inst_.pacsize = send_codec.pacsize;
@@ -294,12 +290,12 @@
 
   // Check if a change in Rate is required.
   if (send_codec.rate != send_codec_inst_.rate) {
-    codec_owner_.Encoder()->SetTargetBitrate(send_codec.rate);
+    CurrentEncoder()->SetTargetBitrate(send_codec.rate);
     send_codec_inst_.rate = send_codec.rate;
   }
 
   codec_fec_enabled_ =
-      codec_fec_enabled_ && codec_owner_.Encoder()->SetFec(codec_fec_enabled_);
+      codec_fec_enabled_ && CurrentEncoder()->SetFec(codec_fec_enabled_);
 
   return 0;
 }
@@ -328,11 +324,9 @@
     const bool success = external_speech_encoder->SetFec(false);
     RTC_DCHECK(success);
   }
-  int cng_pt = dtx_enabled_
-                   ? CngPayloadType(external_speech_encoder->SampleRateHz())
-                   : -1;
-  int red_pt = red_enabled_ ? RedPayloadType(send_codec_inst_.plfreq) : -1;
-  codec_owner_.SetEncoders(external_speech_encoder, cng_pt, vad_mode_, red_pt);
+
+  RentEncoderStack(external_speech_encoder,
+                   external_speech_encoder->SampleRateHz());
 }
 
 rtc::Optional<CodecInst> CodecManager::GetCodecInst() const {
@@ -340,7 +334,7 @@
   WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, dummy_id,
                "SendCodec()");
 
-  if (!codec_owner_.Encoder()) {
+  if (!CurrentEncoder()) {
     WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, dummy_id,
                  "SendCodec Failed, no codec is registered");
     return rtc::Optional<CodecInst>();
@@ -361,11 +355,8 @@
   }
   if (red_enabled_ != enable) {
     red_enabled_ = enable;
-    if (codec_owner_.Encoder()) {
-      int cng_pt = dtx_enabled_ ? CngPayloadType(send_codec_inst_.plfreq) : -1;
-      int red_pt = red_enabled_ ? RedPayloadType(send_codec_inst_.plfreq) : -1;
-      codec_owner_.ChangeCngAndRed(cng_pt, vad_mode_, red_pt);
-    }
+    if (CurrentEncoder())
+      RentEncoderStack(rent_a_codec_.GetEncoder(), send_codec_inst_.plfreq);
   }
   return true;
 }
@@ -377,7 +368,7 @@
 
   // Check that the send codec is mono. We don't support VAD/DTX for stereo
   // sending.
-  const auto* enc = codec_owner_.Encoder();
+  auto* enc = rent_a_codec_.GetEncoder();
   const bool stereo_send = enc ? (enc->NumChannels() != 1) : false;
   if (enable && stereo_send) {
     WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, 0,
@@ -396,11 +387,8 @@
   if (dtx_enabled_ != enable || vad_mode_ != mode) {
     dtx_enabled_ = enable;
     vad_mode_ = mode;
-    if (enc) {
-      int cng_pt = dtx_enabled_ ? CngPayloadType(send_codec_inst_.plfreq) : -1;
-      int red_pt = red_enabled_ ? RedPayloadType(send_codec_inst_.plfreq) : -1;
-      codec_owner_.ChangeCngAndRed(cng_pt, vad_mode_, red_pt);
-    }
+    if (enc)
+      RentEncoderStack(enc, send_codec_inst_.plfreq);
   }
   return 0;
 }
@@ -420,9 +408,9 @@
     return -1;
   }
 
-  RTC_CHECK(codec_owner_.Encoder());
+  RTC_CHECK(CurrentEncoder());
   codec_fec_enabled_ =
-      codec_owner_.Encoder()->SetFec(enable_codec_fec) && enable_codec_fec;
+      CurrentEncoder()->SetFec(enable_codec_fec) && enable_codec_fec;
   return codec_fec_enabled_ == enable_codec_fec ? 0 : -1;
 }
 
@@ -460,5 +448,17 @@
   }
 }
 
+void CodecManager::RentEncoderStack(AudioEncoder* speech_encoder,
+                                    int sample_rate_hz) {
+  auto cng_config =
+      dtx_enabled_ ? rtc::Optional<RentACodec::CngConfig>(RentACodec::CngConfig{
+                         CngPayloadType(sample_rate_hz), vad_mode_})
+                   : rtc::Optional<RentACodec::CngConfig>();
+  auto red_pt = red_enabled_
+                    ? rtc::Optional<int>(RedPayloadType(sample_rate_hz))
+                    : rtc::Optional<int>();
+  rent_a_codec_.RentEncoderStack(speech_encoder, cng_config, red_pt);
+}
+
 }  // namespace acm2
 }  // namespace webrtc
diff --git a/webrtc/modules/audio_coding/main/acm2/codec_manager.h b/webrtc/modules/audio_coding/main/acm2/codec_manager.h
index 37018d3..584b7c4 100644
--- a/webrtc/modules/audio_coding/main/acm2/codec_manager.h
+++ b/webrtc/modules/audio_coding/main/acm2/codec_manager.h
@@ -15,7 +15,6 @@
 #include "webrtc/base/optional.h"
 #include "webrtc/base/scoped_ptr.h"
 #include "webrtc/base/thread_checker.h"
-#include "webrtc/modules/audio_coding/main/acm2/codec_owner.h"
 #include "webrtc/modules/audio_coding/main/acm2/rent_a_codec.h"
 #include "webrtc/modules/audio_coding/main/include/audio_coding_module_typedefs.h"
 #include "webrtc/common_types.h"
@@ -57,15 +56,17 @@
 
   bool codec_fec_enabled() const { return codec_fec_enabled_; }
 
-  AudioEncoder* CurrentEncoder() { return codec_owner_.Encoder(); }
-  const AudioEncoder* CurrentEncoder() const { return codec_owner_.Encoder(); }
+  AudioEncoder* CurrentEncoder() { return rent_a_codec_.GetEncoderStack(); }
+  const AudioEncoder* CurrentEncoder() const {
+    return rent_a_codec_.GetEncoderStack();
+  }
 
   bool CurrentEncoderIsOpus() const { return encoder_is_opus_; }
 
  private:
   int CngPayloadType(int sample_rate_hz) const;
-
   int RedPayloadType(int sample_rate_hz) const;
+  void RentEncoderStack(AudioEncoder* speech_encoder, int sample_rate_hz);
 
   rtc::ThreadChecker thread_checker_;
   uint8_t cng_nb_pltype_;
@@ -78,7 +79,6 @@
   CodecInst send_codec_inst_;
   bool red_enabled_;
   bool codec_fec_enabled_;
-  CodecOwner codec_owner_;
   RentACodec rent_a_codec_;
   bool encoder_is_opus_;
 
diff --git a/webrtc/modules/audio_coding/main/acm2/codec_owner.cc b/webrtc/modules/audio_coding/main/acm2/codec_owner.cc
deleted file mode 100644
index 6b9809f..0000000
--- a/webrtc/modules/audio_coding/main/acm2/codec_owner.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- *  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.
- */
-
-#include "webrtc/modules/audio_coding/main/acm2/codec_owner.h"
-
-#include "webrtc/base/checks.h"
-#include "webrtc/base/logging.h"
-#include "webrtc/engine_configurations.h"
-#include "webrtc/modules/audio_coding/codecs/cng/include/audio_encoder_cng.h"
-#ifdef WEBRTC_CODEC_RED
-#include "webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h"
-#endif
-
-namespace webrtc {
-namespace acm2 {
-
-CodecOwner::CodecOwner() : speech_encoder_(nullptr) {
-}
-
-CodecOwner::~CodecOwner() = default;
-
-namespace {
-
-AudioEncoder* CreateRedEncoder(int red_payload_type,
-                               AudioEncoder* encoder,
-                               rtc::scoped_ptr<AudioEncoder>* red_encoder) {
-#ifdef WEBRTC_CODEC_RED
-  if (red_payload_type != -1) {
-    AudioEncoderCopyRed::Config config;
-    config.payload_type = red_payload_type;
-    config.speech_encoder = encoder;
-    red_encoder->reset(new AudioEncoderCopyRed(config));
-    return red_encoder->get();
-  }
-#endif
-
-  red_encoder->reset();
-  return encoder;
-}
-
-void CreateCngEncoder(int cng_payload_type,
-                      ACMVADMode vad_mode,
-                      AudioEncoder* encoder,
-                      rtc::scoped_ptr<AudioEncoder>* cng_encoder) {
-  if (cng_payload_type == -1) {
-    cng_encoder->reset();
-    return;
-  }
-  AudioEncoderCng::Config config;
-  config.num_channels = encoder->NumChannels();
-  config.payload_type = cng_payload_type;
-  config.speech_encoder = encoder;
-  switch (vad_mode) {
-    case VADNormal:
-      config.vad_mode = Vad::kVadNormal;
-      break;
-    case VADLowBitrate:
-      config.vad_mode = Vad::kVadLowBitrate;
-      break;
-    case VADAggr:
-      config.vad_mode = Vad::kVadAggressive;
-      break;
-    case VADVeryAggr:
-      config.vad_mode = Vad::kVadVeryAggressive;
-      break;
-    default:
-      FATAL();
-  }
-  cng_encoder->reset(new AudioEncoderCng(config));
-}
-}  // namespace
-
-void CodecOwner::SetEncoders(AudioEncoder* external_speech_encoder,
-                             int cng_payload_type,
-                             ACMVADMode vad_mode,
-                             int red_payload_type) {
-  speech_encoder_ = external_speech_encoder;
-  ChangeCngAndRed(cng_payload_type, vad_mode, red_payload_type);
-}
-
-void CodecOwner::ChangeCngAndRed(int cng_payload_type,
-                                 ACMVADMode vad_mode,
-                                 int red_payload_type) {
-  RTC_DCHECK(speech_encoder_);
-  if (cng_payload_type != -1 || red_payload_type != -1) {
-    // The RED and CNG encoders need to be in sync with the speech encoder, so
-    // reset the latter to ensure its buffer is empty.
-    speech_encoder_->Reset();
-  }
-  AudioEncoder* encoder = CreateRedEncoder(
-      red_payload_type, speech_encoder_, &red_encoder_);
-  CreateCngEncoder(cng_payload_type, vad_mode, encoder, &cng_encoder_);
-}
-
-AudioEncoder* CodecOwner::Encoder() {
-  const auto& const_this = *this;
-  return const_cast<AudioEncoder*>(const_this.Encoder());
-}
-
-const AudioEncoder* CodecOwner::Encoder() const {
-  if (cng_encoder_)
-    return cng_encoder_.get();
-  if (red_encoder_)
-    return red_encoder_.get();
-  return speech_encoder_;
-}
-
-}  // namespace acm2
-}  // namespace webrtc
diff --git a/webrtc/modules/audio_coding/main/acm2/codec_owner.h b/webrtc/modules/audio_coding/main/acm2/codec_owner.h
deleted file mode 100644
index bfeaee6..0000000
--- a/webrtc/modules/audio_coding/main/acm2/codec_owner.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- *  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 WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_CODEC_OWNER_H_
-#define WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_CODEC_OWNER_H_
-
-#include "webrtc/base/constructormagic.h"
-#include "webrtc/base/scoped_ptr.h"
-#include "webrtc/common_types.h"
-#include "webrtc/modules/audio_coding/codecs/audio_encoder.h"
-#include "webrtc/modules/audio_coding/codecs/audio_decoder.h"
-#include "webrtc/modules/audio_coding/main/include/audio_coding_module_typedefs.h"
-
-namespace webrtc {
-namespace acm2 {
-
-class CodecOwner {
- public:
-  CodecOwner();
-  ~CodecOwner();
-
-  void SetEncoders(AudioEncoder* external_speech_encoder,
-                   int cng_payload_type,
-                   ACMVADMode vad_mode,
-                   int red_payload_type);
-
-  void ChangeCngAndRed(int cng_payload_type,
-                       ACMVADMode vad_mode,
-                       int red_payload_type);
-
-  AudioEncoder* Encoder();
-  const AudioEncoder* Encoder() const;
-
- private:
-  AudioEncoder* speech_encoder_;
-
-  // |cng_encoder_| and |red_encoder_| are valid iff CNG or RED, respectively,
-  // are active.
-  rtc::scoped_ptr<AudioEncoder> cng_encoder_;
-  rtc::scoped_ptr<AudioEncoder> red_encoder_;
-
-  RTC_DISALLOW_COPY_AND_ASSIGN(CodecOwner);
-};
-
-}  // namespace acm2
-}  // namespace webrtc
-#endif  // WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_CODEC_OWNER_H_
diff --git a/webrtc/modules/audio_coding/main/acm2/codec_owner_unittest.cc b/webrtc/modules/audio_coding/main/acm2/codec_owner_unittest.cc
deleted file mode 100644
index 317c6bd..0000000
--- a/webrtc/modules/audio_coding/main/acm2/codec_owner_unittest.cc
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- *  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.
- */
-
-#include <cstring>
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "webrtc/base/arraysize.h"
-#include "webrtc/base/safe_conversions.h"
-#include "webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h"
-#include "webrtc/modules/audio_coding/main/acm2/codec_owner.h"
-#include "webrtc/modules/audio_coding/main/acm2/rent_a_codec.h"
-
-namespace webrtc {
-namespace acm2 {
-
-using ::testing::Return;
-using ::testing::InSequence;
-
-namespace {
-const int kDataLengthSamples = 80;
-const int kPacketSizeSamples = 2 * kDataLengthSamples;
-const int16_t kZeroData[kDataLengthSamples] = {0};
-const CodecInst kDefaultCodecInst =
-    {0, "pcmu", 8000, kPacketSizeSamples, 1, 64000};
-const int kCngPt = 13;
-}  // namespace
-
-class CodecOwnerTest : public ::testing::Test {
- protected:
-  CodecOwnerTest() : timestamp_(0) {}
-
-  void CreateCodec() {
-    AudioEncoder *enc = rent_a_codec_.RentEncoder(kDefaultCodecInst);
-    ASSERT_TRUE(enc);
-    codec_owner_.SetEncoders(enc, kCngPt, VADNormal, -1);
-  }
-
-  void EncodeAndVerify(size_t expected_out_length,
-                       uint32_t expected_timestamp,
-                       int expected_payload_type,
-                       int expected_send_even_if_empty) {
-    uint8_t out[kPacketSizeSamples];
-    AudioEncoder::EncodedInfo encoded_info;
-    encoded_info = codec_owner_.Encoder()->Encode(timestamp_, kZeroData,
-                                                  kPacketSizeSamples, out);
-    timestamp_ += kDataLengthSamples;
-    EXPECT_TRUE(encoded_info.redundant.empty());
-    EXPECT_EQ(expected_out_length, encoded_info.encoded_bytes);
-    EXPECT_EQ(expected_timestamp, encoded_info.encoded_timestamp);
-    if (expected_payload_type >= 0)
-      EXPECT_EQ(expected_payload_type, encoded_info.payload_type);
-    if (expected_send_even_if_empty >= 0)
-      EXPECT_EQ(static_cast<bool>(expected_send_even_if_empty),
-                encoded_info.send_even_if_empty);
-  }
-
-  // Verify that the speech encoder's Reset method is called when CNG or RED
-  // (or both) are switched on, but not when they're switched off.
-  void TestCngAndRedResetSpeechEncoder(bool use_cng, bool use_red) {
-    MockAudioEncoder speech_encoder;
-    EXPECT_CALL(speech_encoder, NumChannels())
-        .WillRepeatedly(Return(1));
-    EXPECT_CALL(speech_encoder, Max10MsFramesInAPacket())
-        .WillRepeatedly(Return(2));
-    EXPECT_CALL(speech_encoder, SampleRateHz())
-        .WillRepeatedly(Return(8000));
-    {
-      InSequence s;
-      EXPECT_CALL(speech_encoder, Mark("start off"));
-      EXPECT_CALL(speech_encoder, Mark("switch on"));
-      if (use_cng || use_red)
-        EXPECT_CALL(speech_encoder, Reset());
-      EXPECT_CALL(speech_encoder, Mark("start on"));
-      if (use_cng || use_red)
-        EXPECT_CALL(speech_encoder, Reset());
-      EXPECT_CALL(speech_encoder, Mark("switch off"));
-      EXPECT_CALL(speech_encoder, Die());
-    }
-
-    int cng_pt = use_cng ? 17 : -1;
-    int red_pt = use_red ? 19 : -1;
-    speech_encoder.Mark("start off");
-    codec_owner_.SetEncoders(&speech_encoder, -1, VADNormal, -1);
-    speech_encoder.Mark("switch on");
-    codec_owner_.ChangeCngAndRed(cng_pt, VADNormal, red_pt);
-    speech_encoder.Mark("start on");
-    codec_owner_.SetEncoders(&speech_encoder, cng_pt, VADNormal, red_pt);
-    speech_encoder.Mark("switch off");
-    codec_owner_.ChangeCngAndRed(-1, VADNormal, -1);
-  }
-
-  CodecOwner codec_owner_;
-  RentACodec rent_a_codec_;
-  uint32_t timestamp_;
-};
-
-// This test verifies that CNG frames are delivered as expected. Since the frame
-// size is set to 20 ms, we expect the first encode call to produce no output
-// (which is signaled as 0 bytes output of type kNoEncoding). The next encode
-// call should produce one SID frame of 9 bytes. The third call should not
-// result in any output (just like the first one). The fourth and final encode
-// call should produce an "empty frame", which is like no output, but with
-// AudioEncoder::EncodedInfo::send_even_if_empty set to true. (The reason to
-// produce an empty frame is to drive sending of DTMF packets in the RTP/RTCP
-// module.)
-TEST_F(CodecOwnerTest, VerifyCngFrames) {
-  CreateCodec();
-  uint32_t expected_timestamp = timestamp_;
-  // Verify no frame.
-  {
-    SCOPED_TRACE("First encoding");
-    EncodeAndVerify(0, expected_timestamp, -1, -1);
-  }
-
-  // Verify SID frame delivered.
-  {
-    SCOPED_TRACE("Second encoding");
-    EncodeAndVerify(9, expected_timestamp, kCngPt, 1);
-  }
-
-  // Verify no frame.
-  {
-    SCOPED_TRACE("Third encoding");
-    EncodeAndVerify(0, expected_timestamp, -1, -1);
-  }
-
-  // Verify NoEncoding.
-  expected_timestamp += 2 * kDataLengthSamples;
-  {
-    SCOPED_TRACE("Fourth encoding");
-    EncodeAndVerify(0, expected_timestamp, kCngPt, 1);
-  }
-}
-
-TEST_F(CodecOwnerTest, ExternalEncoder) {
-  MockAudioEncoder external_encoder;
-  codec_owner_.SetEncoders(&external_encoder, -1, VADNormal, -1);
-  const int kSampleRateHz = 8000;
-  const int kPacketSizeSamples = kSampleRateHz / 100;
-  int16_t audio[kPacketSizeSamples] = {0};
-  uint8_t encoded[kPacketSizeSamples];
-  AudioEncoder::EncodedInfo info;
-  EXPECT_CALL(external_encoder, SampleRateHz())
-      .WillRepeatedly(Return(kSampleRateHz));
-  EXPECT_CALL(external_encoder, NumChannels()).WillRepeatedly(Return(1));
-
-  {
-    InSequence s;
-    info.encoded_timestamp = 0;
-    EXPECT_CALL(external_encoder,
-                EncodeInternal(0, rtc::ArrayView<const int16_t>(audio),
-                               arraysize(encoded), encoded))
-        .WillOnce(Return(info));
-    EXPECT_CALL(external_encoder, Mark("A"));
-    EXPECT_CALL(external_encoder, Mark("B"));
-    info.encoded_timestamp = 2;
-    EXPECT_CALL(external_encoder,
-                EncodeInternal(2, rtc::ArrayView<const int16_t>(audio),
-                               arraysize(encoded), encoded))
-        .WillOnce(Return(info));
-    EXPECT_CALL(external_encoder, Die());
-  }
-
-  info = codec_owner_.Encoder()->Encode(0, audio, arraysize(encoded), encoded);
-  EXPECT_EQ(0u, info.encoded_timestamp);
-  external_encoder.Mark("A");
-
-  // Change to internal encoder.
-  CodecInst codec_inst = kDefaultCodecInst;
-  codec_inst.pacsize = kPacketSizeSamples;
-  AudioEncoder* enc = rent_a_codec_.RentEncoder(codec_inst);
-  ASSERT_TRUE(enc);
-  codec_owner_.SetEncoders(enc, -1, VADNormal, -1);
-  // Don't expect any more calls to the external encoder.
-  info = codec_owner_.Encoder()->Encode(1, audio, arraysize(encoded), encoded);
-  external_encoder.Mark("B");
-
-  // Change back to external encoder again.
-  codec_owner_.SetEncoders(&external_encoder, -1, VADNormal, -1);
-  info = codec_owner_.Encoder()->Encode(2, audio, arraysize(encoded), encoded);
-  EXPECT_EQ(2u, info.encoded_timestamp);
-}
-
-TEST_F(CodecOwnerTest, CngResetsSpeechEncoder) {
-  TestCngAndRedResetSpeechEncoder(true, false);
-}
-
-TEST_F(CodecOwnerTest, RedResetsSpeechEncoder) {
-  TestCngAndRedResetSpeechEncoder(false, true);
-}
-
-TEST_F(CodecOwnerTest, CngAndRedResetsSpeechEncoder) {
-  TestCngAndRedResetSpeechEncoder(true, true);
-}
-
-TEST_F(CodecOwnerTest, NoCngAndRedNoSpeechEncoderReset) {
-  TestCngAndRedResetSpeechEncoder(false, false);
-}
-
-}  // namespace acm2
-}  // namespace webrtc
diff --git a/webrtc/modules/audio_coding/main/acm2/rent_a_codec.cc b/webrtc/modules/audio_coding/main/acm2/rent_a_codec.cc
index 2ee953c..e3310ad 100644
--- a/webrtc/modules/audio_coding/main/acm2/rent_a_codec.cc
+++ b/webrtc/modules/audio_coding/main/acm2/rent_a_codec.cc
@@ -11,6 +11,7 @@
 #include "webrtc/modules/audio_coding/main/acm2/rent_a_codec.h"
 
 #include "webrtc/base/logging.h"
+#include "webrtc/modules/audio_coding/codecs/cng/include/audio_encoder_cng.h"
 #include "webrtc/modules/audio_coding/codecs/g711/include/audio_encoder_pcm.h"
 #ifdef WEBRTC_CODEC_G722
 #include "webrtc/modules/audio_coding/codecs/g722/include/audio_encoder_g722.h"
@@ -30,6 +31,9 @@
 #include "webrtc/modules/audio_coding/codecs/opus/include/audio_encoder_opus.h"
 #endif
 #include "webrtc/modules/audio_coding/codecs/pcm16b/include/audio_encoder_pcm16b.h"
+#ifdef WEBRTC_CODEC_RED
+#include "webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h"
+#endif
 #include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h"
 #include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h"
 
@@ -140,6 +144,44 @@
   return rtc::scoped_ptr<AudioEncoder>();
 }
 
+rtc::scoped_ptr<AudioEncoder> CreateRedEncoder(AudioEncoder* encoder,
+                                               int red_payload_type) {
+#ifdef WEBRTC_CODEC_RED
+  AudioEncoderCopyRed::Config config;
+  config.payload_type = red_payload_type;
+  config.speech_encoder = encoder;
+  return rtc::scoped_ptr<AudioEncoder>(new AudioEncoderCopyRed(config));
+#else
+  return rtc::scoped_ptr<AudioEncoder>();
+#endif
+}
+
+rtc::scoped_ptr<AudioEncoder> CreateCngEncoder(
+    AudioEncoder* encoder,
+    RentACodec::CngConfig cng_config) {
+  AudioEncoderCng::Config config;
+  config.num_channels = encoder->NumChannels();
+  config.payload_type = cng_config.cng_payload_type;
+  config.speech_encoder = encoder;
+  switch (cng_config.vad_mode) {
+    case VADNormal:
+      config.vad_mode = Vad::kVadNormal;
+      break;
+    case VADLowBitrate:
+      config.vad_mode = Vad::kVadLowBitrate;
+      break;
+    case VADAggr:
+      config.vad_mode = Vad::kVadAggressive;
+      break;
+    case VADVeryAggr:
+      config.vad_mode = Vad::kVadVeryAggressive;
+      break;
+    default:
+      FATAL();
+  }
+  return rtc::scoped_ptr<AudioEncoder>(new AudioEncoderCng(config));
+}
+
 rtc::scoped_ptr<AudioDecoder> CreateIsacDecoder(
     LockedIsacBandwidthInfo* bwinfo) {
 #if defined(WEBRTC_CODEC_ISACFX)
@@ -162,8 +204,35 @@
       CreateEncoder(codec_inst, &isac_bandwidth_info_);
   if (!enc)
     return nullptr;
-  encoder_ = enc.Pass();
-  return encoder_.get();
+  speech_encoder_ = enc.Pass();
+  return speech_encoder_.get();
+}
+
+AudioEncoder* RentACodec::RentEncoderStack(
+    AudioEncoder* speech_encoder,
+    rtc::Optional<CngConfig> cng_config,
+    rtc::Optional<int> red_payload_type) {
+  RTC_DCHECK(speech_encoder);
+  if (cng_config || red_payload_type) {
+    // The RED and CNG encoders need to be in sync with the speech encoder, so
+    // reset the latter to ensure its buffer is empty.
+    speech_encoder->Reset();
+  }
+  encoder_stack_ = speech_encoder;
+  if (red_payload_type) {
+    red_encoder_ = CreateRedEncoder(encoder_stack_, *red_payload_type);
+    if (red_encoder_)
+      encoder_stack_ = red_encoder_.get();
+  } else {
+    red_encoder_.reset();
+  }
+  if (cng_config) {
+    cng_encoder_ = CreateCngEncoder(encoder_stack_, *cng_config);
+    encoder_stack_ = cng_encoder_.get();
+  } else {
+    cng_encoder_.reset();
+  }
+  return encoder_stack_;
 }
 
 AudioDecoder* RentACodec::RentIsacDecoder() {
diff --git a/webrtc/modules/audio_coding/main/acm2/rent_a_codec.h b/webrtc/modules/audio_coding/main/acm2/rent_a_codec.h
index 4daac6a..4571560 100644
--- a/webrtc/modules/audio_coding/main/acm2/rent_a_codec.h
+++ b/webrtc/modules/audio_coding/main/acm2/rent_a_codec.h
@@ -17,9 +17,10 @@
 #include "webrtc/base/constructormagic.h"
 #include "webrtc/base/optional.h"
 #include "webrtc/base/scoped_ptr.h"
-#include "webrtc/typedefs.h"
-#include "webrtc/modules/audio_coding/codecs/audio_encoder.h"
 #include "webrtc/modules/audio_coding/codecs/audio_decoder.h"
+#include "webrtc/modules/audio_coding/codecs/audio_encoder.h"
+#include "webrtc/modules/audio_coding/main/include/audio_coding_module_typedefs.h"
+#include "webrtc/typedefs.h"
 
 #if defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)
 #include "webrtc/modules/audio_coding/codecs/isac/locked_bandwidth_info.h"
@@ -188,14 +189,35 @@
   // successful call to this function, or until the Rent-A-Codec is destroyed.
   AudioEncoder* RentEncoder(const CodecInst& codec_inst);
 
+  // Creates and returns an audio encoder stack where the given speech encoder
+  // is augmented with the specified CNG/VAD and RED encoders. Leave either
+  // optional field blank if you don't want the corresponding gizmo in the
+  // stack. The returned encoder is live until the next successful call to this
+  // function, or until the Rent-A-Codec is destroyed.
+  struct CngConfig {
+    int cng_payload_type;
+    ACMVADMode vad_mode;
+  };
+  AudioEncoder* RentEncoderStack(AudioEncoder* speech_encoder,
+                                 rtc::Optional<CngConfig> cng_config,
+                                 rtc::Optional<int> red_payload_type);
+
+  // Get the last return values of RentEncoder and RentEncoderStack, or null if
+  // they haven't been called.
+  AudioEncoder* GetEncoder() const { return speech_encoder_.get(); }
+  AudioEncoder* GetEncoderStack() const { return encoder_stack_; }
+
   // Creates and returns an iSAC decoder, which will remain live until the
   // Rent-A-Codec is destroyed. Subsequent calls will simply return the same
   // object.
   AudioDecoder* RentIsacDecoder();
 
  private:
-  rtc::scoped_ptr<AudioEncoder> encoder_;
+  rtc::scoped_ptr<AudioEncoder> speech_encoder_;
+  rtc::scoped_ptr<AudioEncoder> cng_encoder_;
+  rtc::scoped_ptr<AudioEncoder> red_encoder_;
   rtc::scoped_ptr<AudioDecoder> isac_decoder_;
+  AudioEncoder* encoder_stack_ = nullptr;
   LockedIsacBandwidthInfo isac_bandwidth_info_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(RentACodec);
diff --git a/webrtc/modules/audio_coding/main/acm2/rent_a_codec_unittest.cc b/webrtc/modules/audio_coding/main/acm2/rent_a_codec_unittest.cc
index 01ba024..6938051 100644
--- a/webrtc/modules/audio_coding/main/acm2/rent_a_codec_unittest.cc
+++ b/webrtc/modules/audio_coding/main/acm2/rent_a_codec_unittest.cc
@@ -9,11 +9,202 @@
  */
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/base/arraysize.h"
+#include "webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h"
 #include "webrtc/modules/audio_coding/main/acm2/rent_a_codec.h"
 
 namespace webrtc {
 namespace acm2 {
 
+using ::testing::Return;
+
+namespace {
+const int kDataLengthSamples = 80;
+const int kPacketSizeSamples = 2 * kDataLengthSamples;
+const int16_t kZeroData[kDataLengthSamples] = {0};
+const CodecInst kDefaultCodecInst = {0, "pcmu", 8000, kPacketSizeSamples,
+                                     1, 64000};
+const int kCngPt = 13;
+}  // namespace
+
+class RentACodecTestF : public ::testing::Test {
+ protected:
+  void CreateCodec() {
+    speech_encoder_ = rent_a_codec_.RentEncoder(kDefaultCodecInst);
+    ASSERT_TRUE(speech_encoder_);
+    encoder_ = rent_a_codec_.RentEncoderStack(
+        speech_encoder_, rtc::Optional<RentACodec::CngConfig>(
+                             RentACodec::CngConfig{kCngPt, VADNormal}),
+        rtc::Optional<int>());
+  }
+
+  void EncodeAndVerify(size_t expected_out_length,
+                       uint32_t expected_timestamp,
+                       int expected_payload_type,
+                       int expected_send_even_if_empty) {
+    uint8_t out[kPacketSizeSamples];
+    AudioEncoder::EncodedInfo encoded_info;
+    encoded_info =
+        encoder_->Encode(timestamp_, kZeroData, kPacketSizeSamples, out);
+    timestamp_ += kDataLengthSamples;
+    EXPECT_TRUE(encoded_info.redundant.empty());
+    EXPECT_EQ(expected_out_length, encoded_info.encoded_bytes);
+    EXPECT_EQ(expected_timestamp, encoded_info.encoded_timestamp);
+    if (expected_payload_type >= 0)
+      EXPECT_EQ(expected_payload_type, encoded_info.payload_type);
+    if (expected_send_even_if_empty >= 0)
+      EXPECT_EQ(static_cast<bool>(expected_send_even_if_empty),
+                encoded_info.send_even_if_empty);
+  }
+
+  RentACodec rent_a_codec_;
+  AudioEncoder* speech_encoder_ = nullptr;
+  AudioEncoder* encoder_ = nullptr;
+  uint32_t timestamp_ = 0;
+};
+
+// This test verifies that CNG frames are delivered as expected. Since the frame
+// size is set to 20 ms, we expect the first encode call to produce no output
+// (which is signaled as 0 bytes output of type kNoEncoding). The next encode
+// call should produce one SID frame of 9 bytes. The third call should not
+// result in any output (just like the first one). The fourth and final encode
+// call should produce an "empty frame", which is like no output, but with
+// AudioEncoder::EncodedInfo::send_even_if_empty set to true. (The reason to
+// produce an empty frame is to drive sending of DTMF packets in the RTP/RTCP
+// module.)
+TEST_F(RentACodecTestF, VerifyCngFrames) {
+  CreateCodec();
+  uint32_t expected_timestamp = timestamp_;
+  // Verify no frame.
+  {
+    SCOPED_TRACE("First encoding");
+    EncodeAndVerify(0, expected_timestamp, -1, -1);
+  }
+
+  // Verify SID frame delivered.
+  {
+    SCOPED_TRACE("Second encoding");
+    EncodeAndVerify(9, expected_timestamp, kCngPt, 1);
+  }
+
+  // Verify no frame.
+  {
+    SCOPED_TRACE("Third encoding");
+    EncodeAndVerify(0, expected_timestamp, -1, -1);
+  }
+
+  // Verify NoEncoding.
+  expected_timestamp += 2 * kDataLengthSamples;
+  {
+    SCOPED_TRACE("Fourth encoding");
+    EncodeAndVerify(0, expected_timestamp, kCngPt, 1);
+  }
+}
+
+TEST(RentACodecTest, ExternalEncoder) {
+  MockAudioEncoder external_encoder;
+  RentACodec rac;
+  EXPECT_EQ(&external_encoder,
+            rac.RentEncoderStack(&external_encoder,
+                                 rtc::Optional<RentACodec::CngConfig>(),
+                                 rtc::Optional<int>()));
+  const int kSampleRateHz = 8000;
+  const int kPacketSizeSamples = kSampleRateHz / 100;
+  int16_t audio[kPacketSizeSamples] = {0};
+  uint8_t encoded[kPacketSizeSamples];
+  AudioEncoder::EncodedInfo info;
+  EXPECT_CALL(external_encoder, SampleRateHz())
+      .WillRepeatedly(Return(kSampleRateHz));
+  EXPECT_CALL(external_encoder, NumChannels()).WillRepeatedly(Return(1));
+
+  {
+    ::testing::InSequence s;
+    info.encoded_timestamp = 0;
+    EXPECT_CALL(external_encoder,
+                EncodeInternal(0, rtc::ArrayView<const int16_t>(audio),
+                               arraysize(encoded), encoded))
+        .WillOnce(Return(info));
+    EXPECT_CALL(external_encoder, Mark("A"));
+    EXPECT_CALL(external_encoder, Mark("B"));
+    info.encoded_timestamp = 2;
+    EXPECT_CALL(external_encoder,
+                EncodeInternal(2, rtc::ArrayView<const int16_t>(audio),
+                               arraysize(encoded), encoded))
+        .WillOnce(Return(info));
+    EXPECT_CALL(external_encoder, Die());
+  }
+
+  info = rac.GetEncoderStack()->Encode(0, audio, arraysize(encoded), encoded);
+  EXPECT_EQ(0u, info.encoded_timestamp);
+  external_encoder.Mark("A");
+
+  // Change to internal encoder.
+  CodecInst codec_inst = kDefaultCodecInst;
+  codec_inst.pacsize = kPacketSizeSamples;
+  AudioEncoder* enc = rac.RentEncoder(codec_inst);
+  ASSERT_TRUE(enc);
+  EXPECT_EQ(enc,
+            rac.RentEncoderStack(enc, rtc::Optional<RentACodec::CngConfig>(),
+                                 rtc::Optional<int>()));
+
+  // Don't expect any more calls to the external encoder.
+  info = rac.GetEncoderStack()->Encode(1, audio, arraysize(encoded), encoded);
+  external_encoder.Mark("B");
+
+  // Change back to external encoder again.
+  EXPECT_EQ(&external_encoder,
+            rac.RentEncoderStack(&external_encoder,
+                                 rtc::Optional<RentACodec::CngConfig>(),
+                                 rtc::Optional<int>()));
+  info = rac.GetEncoderStack()->Encode(2, audio, arraysize(encoded), encoded);
+  EXPECT_EQ(2u, info.encoded_timestamp);
+}
+
+// Verify that the speech encoder's Reset method is called when CNG or RED
+// (or both) are switched on, but not when they're switched off.
+void TestCngAndRedResetSpeechEncoder(bool use_cng, bool use_red) {
+  MockAudioEncoder speech_encoder;
+  EXPECT_CALL(speech_encoder, NumChannels()).WillRepeatedly(Return(1));
+  EXPECT_CALL(speech_encoder, Max10MsFramesInAPacket())
+      .WillRepeatedly(Return(2));
+  EXPECT_CALL(speech_encoder, SampleRateHz()).WillRepeatedly(Return(8000));
+  {
+    ::testing::InSequence s;
+    EXPECT_CALL(speech_encoder, Mark("disabled"));
+    EXPECT_CALL(speech_encoder, Mark("enabled"));
+    if (use_cng || use_red)
+      EXPECT_CALL(speech_encoder, Reset());
+    EXPECT_CALL(speech_encoder, Die());
+  }
+
+  auto cng_cfg = use_cng ? rtc::Optional<RentACodec::CngConfig>(
+                               RentACodec::CngConfig{17, VADNormal})
+                         : rtc::Optional<RentACodec::CngConfig>();
+  auto red_pt = use_red ? rtc::Optional<int>(19) : rtc::Optional<int>();
+  speech_encoder.Mark("disabled");
+  RentACodec rac;
+  rac.RentEncoderStack(&speech_encoder, rtc::Optional<RentACodec::CngConfig>(),
+                       rtc::Optional<int>());
+  speech_encoder.Mark("enabled");
+  rac.RentEncoderStack(&speech_encoder, cng_cfg, red_pt);
+}
+
+TEST(RentACodecTest, CngResetsSpeechEncoder) {
+  TestCngAndRedResetSpeechEncoder(true, false);
+}
+
+TEST(RentACodecTest, RedResetsSpeechEncoder) {
+  TestCngAndRedResetSpeechEncoder(false, true);
+}
+
+TEST(RentACodecTest, CngAndRedResetsSpeechEncoder) {
+  TestCngAndRedResetSpeechEncoder(true, true);
+}
+
+TEST(RentACodecTest, NoCngAndRedNoSpeechEncoderReset) {
+  TestCngAndRedResetSpeechEncoder(false, false);
+}
+
 TEST(RentACodecTest, RentEncoderError) {
   const CodecInst codec_inst = {
       0, "Robert'); DROP TABLE Students;", 8000, 160, 1, 64000};
diff --git a/webrtc/modules/audio_coding/main/audio_coding_module.gypi b/webrtc/modules/audio_coding/main/audio_coding_module.gypi
index 09ac0a8..061ffaa 100644
--- a/webrtc/modules/audio_coding/main/audio_coding_module.gypi
+++ b/webrtc/modules/audio_coding/main/audio_coding_module.gypi
@@ -109,8 +109,6 @@
         'acm2/call_statistics.h',
         'acm2/codec_manager.cc',
         'acm2/codec_manager.h',
-        'acm2/codec_owner.cc',
-        'acm2/codec_owner.h',
         'acm2/initial_delay_manager.cc',
         'acm2/initial_delay_manager.h',
         'include/audio_coding_module.h',
diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp
index 22dbff2..7cac619 100644
--- a/webrtc/modules/modules.gyp
+++ b/webrtc/modules/modules.gyp
@@ -102,7 +102,6 @@
             'audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc',
             'audio_coding/main/acm2/call_statistics_unittest.cc',
             'audio_coding/main/acm2/codec_manager_unittest.cc',
-            'audio_coding/main/acm2/codec_owner_unittest.cc',
             'audio_coding/main/acm2/initial_delay_manager_unittest.cc',
             'audio_coding/main/acm2/rent_a_codec_unittest.cc',
             'audio_coding/codecs/cng/cng_unittest.cc',