| Index: webrtc/modules/audio_coding/main/acm2/rent_a_codec_unittest.cc
 | 
| 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 01ba02457a500e29a71a7e067b7ba1c7505a20e4..6938051644e788233e1124062e8cd19321b75ef0 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};
 | 
| 
 |