Home | History | Annotate | Download | only in red
      1 /*
      2  *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include <vector>
     12 
     13 #include "testing/gtest/include/gtest/gtest.h"
     14 #include "webrtc/base/checks.h"
     15 #include "webrtc/base/scoped_ptr.h"
     16 #include "webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h"
     17 #include "webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h"
     18 
     19 using ::testing::Return;
     20 using ::testing::_;
     21 using ::testing::SetArgPointee;
     22 using ::testing::InSequence;
     23 using ::testing::Invoke;
     24 using ::testing::MockFunction;
     25 
     26 namespace webrtc {
     27 
     28 namespace {
     29 static const size_t kMockMaxEncodedBytes = 1000;
     30 static const size_t kMaxNumSamples = 48 * 10 * 2;  // 10 ms @ 48 kHz stereo.
     31 }
     32 
     33 class AudioEncoderCopyRedTest : public ::testing::Test {
     34  protected:
     35   AudioEncoderCopyRedTest()
     36       : timestamp_(4711),
     37         sample_rate_hz_(16000),
     38         num_audio_samples_10ms(sample_rate_hz_ / 100),
     39         red_payload_type_(200) {
     40     AudioEncoderCopyRed::Config config;
     41     config.payload_type = red_payload_type_;
     42     config.speech_encoder = &mock_encoder_;
     43     red_.reset(new AudioEncoderCopyRed(config));
     44     memset(audio_, 0, sizeof(audio_));
     45     EXPECT_CALL(mock_encoder_, NumChannels()).WillRepeatedly(Return(1U));
     46     EXPECT_CALL(mock_encoder_, SampleRateHz())
     47         .WillRepeatedly(Return(sample_rate_hz_));
     48     EXPECT_CALL(mock_encoder_, MaxEncodedBytes())
     49         .WillRepeatedly(Return(kMockMaxEncodedBytes));
     50     encoded_.resize(red_->MaxEncodedBytes(), 0);
     51   }
     52 
     53   void TearDown() override {
     54     red_.reset();
     55     // Don't expect the red_ object to delete the AudioEncoder object. But it
     56     // will be deleted with the test fixture. This is why we explicitly delete
     57     // the red_ object above, and set expectations on mock_encoder_ afterwards.
     58     EXPECT_CALL(mock_encoder_, Die()).Times(1);
     59   }
     60 
     61   void Encode() {
     62     ASSERT_TRUE(red_.get() != NULL);
     63     encoded_info_ = red_->Encode(
     64         timestamp_,
     65         rtc::ArrayView<const int16_t>(audio_, num_audio_samples_10ms),
     66         encoded_.size(), &encoded_[0]);
     67     timestamp_ += num_audio_samples_10ms;
     68   }
     69 
     70   MockAudioEncoder mock_encoder_;
     71   rtc::scoped_ptr<AudioEncoderCopyRed> red_;
     72   uint32_t timestamp_;
     73   int16_t audio_[kMaxNumSamples];
     74   const int sample_rate_hz_;
     75   size_t num_audio_samples_10ms;
     76   std::vector<uint8_t> encoded_;
     77   AudioEncoder::EncodedInfo encoded_info_;
     78   const int red_payload_type_;
     79 };
     80 
     81 class MockEncodeHelper {
     82  public:
     83   MockEncodeHelper() : write_payload_(false), payload_(NULL) {
     84     memset(&info_, 0, sizeof(info_));
     85   }
     86 
     87   AudioEncoder::EncodedInfo Encode(uint32_t timestamp,
     88                                    rtc::ArrayView<const int16_t> audio,
     89                                    size_t max_encoded_bytes,
     90                                    uint8_t* encoded) {
     91     if (write_payload_) {
     92       RTC_CHECK(encoded);
     93       RTC_CHECK_LE(info_.encoded_bytes, max_encoded_bytes);
     94       memcpy(encoded, payload_, info_.encoded_bytes);
     95     }
     96     return info_;
     97   }
     98 
     99   AudioEncoder::EncodedInfo info_;
    100   bool write_payload_;
    101   uint8_t* payload_;
    102 };
    103 
    104 TEST_F(AudioEncoderCopyRedTest, CreateAndDestroy) {
    105 }
    106 
    107 TEST_F(AudioEncoderCopyRedTest, CheckSampleRatePropagation) {
    108   EXPECT_CALL(mock_encoder_, SampleRateHz()).WillOnce(Return(17));
    109   EXPECT_EQ(17, red_->SampleRateHz());
    110 }
    111 
    112 TEST_F(AudioEncoderCopyRedTest, CheckNumChannelsPropagation) {
    113   EXPECT_CALL(mock_encoder_, NumChannels()).WillOnce(Return(17U));
    114   EXPECT_EQ(17U, red_->NumChannels());
    115 }
    116 
    117 TEST_F(AudioEncoderCopyRedTest, CheckFrameSizePropagation) {
    118   EXPECT_CALL(mock_encoder_, Num10MsFramesInNextPacket()).WillOnce(Return(17U));
    119   EXPECT_EQ(17U, red_->Num10MsFramesInNextPacket());
    120 }
    121 
    122 TEST_F(AudioEncoderCopyRedTest, CheckMaxFrameSizePropagation) {
    123   EXPECT_CALL(mock_encoder_, Max10MsFramesInAPacket()).WillOnce(Return(17U));
    124   EXPECT_EQ(17U, red_->Max10MsFramesInAPacket());
    125 }
    126 
    127 TEST_F(AudioEncoderCopyRedTest, CheckSetBitratePropagation) {
    128   EXPECT_CALL(mock_encoder_, SetTargetBitrate(4711));
    129   red_->SetTargetBitrate(4711);
    130 }
    131 
    132 TEST_F(AudioEncoderCopyRedTest, CheckProjectedPacketLossRatePropagation) {
    133   EXPECT_CALL(mock_encoder_, SetProjectedPacketLossRate(0.5));
    134   red_->SetProjectedPacketLossRate(0.5);
    135 }
    136 
    137 // Checks that the an Encode() call is immediately propagated to the speech
    138 // encoder.
    139 TEST_F(AudioEncoderCopyRedTest, CheckImmediateEncode) {
    140   // Interleaving the EXPECT_CALL sequence with expectations on the MockFunction
    141   // check ensures that exactly one call to EncodeInternal happens in each
    142   // Encode call.
    143   InSequence s;
    144   MockFunction<void(int check_point_id)> check;
    145   for (int i = 1; i <= 6; ++i) {
    146     EXPECT_CALL(mock_encoder_, EncodeInternal(_, _, _, _))
    147         .WillRepeatedly(Return(AudioEncoder::EncodedInfo()));
    148     EXPECT_CALL(check, Call(i));
    149     Encode();
    150     check.Call(i);
    151   }
    152 }
    153 
    154 // Checks that no output is produced if the underlying codec doesn't emit any
    155 // new data, even if the RED codec is loaded with a secondary encoding.
    156 TEST_F(AudioEncoderCopyRedTest, CheckNoOutput) {
    157   // Start with one Encode() call that will produce output.
    158   static const size_t kEncodedSize = 17;
    159   AudioEncoder::EncodedInfo info;
    160   info.encoded_bytes = kEncodedSize;
    161   EXPECT_CALL(mock_encoder_, EncodeInternal(_, _, _, _))
    162       .WillOnce(Return(info));
    163   Encode();
    164   // First call is a special case, since it does not include a secondary
    165   // payload.
    166   EXPECT_EQ(1u, encoded_info_.redundant.size());
    167   EXPECT_EQ(kEncodedSize, encoded_info_.encoded_bytes);
    168 
    169   // Next call to the speech encoder will not produce any output.
    170   info.encoded_bytes = 0;
    171   EXPECT_CALL(mock_encoder_, EncodeInternal(_, _, _, _))
    172       .WillOnce(Return(info));
    173   Encode();
    174   EXPECT_EQ(0u, encoded_info_.encoded_bytes);
    175 
    176   // Final call to the speech encoder will produce output.
    177   info.encoded_bytes = kEncodedSize;
    178   EXPECT_CALL(mock_encoder_, EncodeInternal(_, _, _, _))
    179       .WillOnce(Return(info));
    180   Encode();
    181   EXPECT_EQ(2 * kEncodedSize, encoded_info_.encoded_bytes);
    182   ASSERT_EQ(2u, encoded_info_.redundant.size());
    183 }
    184 
    185 // Checks that the correct payload sizes are populated into the redundancy
    186 // information.
    187 TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes) {
    188   // Let the mock encoder return payload sizes 1, 2, 3, ..., 10 for the sequence
    189   // of calls.
    190   static const int kNumPackets = 10;
    191   InSequence s;
    192   for (int encode_size = 1; encode_size <= kNumPackets; ++encode_size) {
    193     AudioEncoder::EncodedInfo info;
    194     info.encoded_bytes = encode_size;
    195     EXPECT_CALL(mock_encoder_, EncodeInternal(_, _, _, _))
    196         .WillOnce(Return(info));
    197   }
    198 
    199   // First call is a special case, since it does not include a secondary
    200   // payload.
    201   Encode();
    202   EXPECT_EQ(1u, encoded_info_.redundant.size());
    203   EXPECT_EQ(1u, encoded_info_.encoded_bytes);
    204 
    205   for (size_t i = 2; i <= kNumPackets; ++i) {
    206     Encode();
    207     ASSERT_EQ(2u, encoded_info_.redundant.size());
    208     EXPECT_EQ(i, encoded_info_.redundant[0].encoded_bytes);
    209     EXPECT_EQ(i - 1, encoded_info_.redundant[1].encoded_bytes);
    210     EXPECT_EQ(i + i - 1, encoded_info_.encoded_bytes);
    211   }
    212 }
    213 
    214 // Checks that the correct timestamps are returned.
    215 TEST_F(AudioEncoderCopyRedTest, CheckTimestamps) {
    216   MockEncodeHelper helper;
    217 
    218   helper.info_.encoded_bytes = 17;
    219   helper.info_.encoded_timestamp = timestamp_;
    220   uint32_t primary_timestamp = timestamp_;
    221   EXPECT_CALL(mock_encoder_, EncodeInternal(_, _, _, _))
    222       .WillRepeatedly(Invoke(&helper, &MockEncodeHelper::Encode));
    223 
    224   // First call is a special case, since it does not include a secondary
    225   // payload.
    226   Encode();
    227   EXPECT_EQ(primary_timestamp, encoded_info_.encoded_timestamp);
    228 
    229   uint32_t secondary_timestamp = primary_timestamp;
    230   primary_timestamp = timestamp_;
    231   helper.info_.encoded_timestamp = timestamp_;
    232   Encode();
    233   ASSERT_EQ(2u, encoded_info_.redundant.size());
    234   EXPECT_EQ(primary_timestamp, encoded_info_.redundant[0].encoded_timestamp);
    235   EXPECT_EQ(secondary_timestamp, encoded_info_.redundant[1].encoded_timestamp);
    236   EXPECT_EQ(primary_timestamp, encoded_info_.encoded_timestamp);
    237 }
    238 
    239 // Checks that the primary and secondary payloads are written correctly.
    240 TEST_F(AudioEncoderCopyRedTest, CheckPayloads) {
    241   // Let the mock encoder write payloads with increasing values. The first
    242   // payload will have values 0, 1, 2, ..., kPayloadLenBytes - 1.
    243   MockEncodeHelper helper;
    244   static const size_t kPayloadLenBytes = 5;
    245   helper.info_.encoded_bytes = kPayloadLenBytes;
    246   helper.write_payload_ = true;
    247   uint8_t payload[kPayloadLenBytes];
    248   for (uint8_t i = 0; i < kPayloadLenBytes; ++i) {
    249     payload[i] = i;
    250   }
    251   helper.payload_ = payload;
    252   EXPECT_CALL(mock_encoder_, EncodeInternal(_, _, _, _))
    253       .WillRepeatedly(Invoke(&helper, &MockEncodeHelper::Encode));
    254 
    255   // First call is a special case, since it does not include a secondary
    256   // payload.
    257   Encode();
    258   EXPECT_EQ(kPayloadLenBytes, encoded_info_.encoded_bytes);
    259   for (size_t i = 0; i < kPayloadLenBytes; ++i) {
    260     EXPECT_EQ(i, encoded_[i]);
    261   }
    262 
    263   for (int j = 0; j < 5; ++j) {
    264     // Increment all values of the payload by 10.
    265     for (size_t i = 0; i < kPayloadLenBytes; ++i)
    266       helper.payload_[i] += 10;
    267 
    268     Encode();
    269     ASSERT_EQ(2u, encoded_info_.redundant.size());
    270     EXPECT_EQ(kPayloadLenBytes, encoded_info_.redundant[0].encoded_bytes);
    271     EXPECT_EQ(kPayloadLenBytes, encoded_info_.redundant[1].encoded_bytes);
    272     for (size_t i = 0; i < kPayloadLenBytes; ++i) {
    273       // Check primary payload.
    274       EXPECT_EQ((j + 1) * 10 + i, encoded_[i]);
    275       // Check secondary payload.
    276       EXPECT_EQ(j * 10 + i, encoded_[i + kPayloadLenBytes]);
    277     }
    278   }
    279 }
    280 
    281 // Checks correct propagation of payload type.
    282 // Checks that the correct timestamps are returned.
    283 TEST_F(AudioEncoderCopyRedTest, CheckPayloadType) {
    284   MockEncodeHelper helper;
    285 
    286   helper.info_.encoded_bytes = 17;
    287   const int primary_payload_type = red_payload_type_ + 1;
    288   helper.info_.payload_type = primary_payload_type;
    289   EXPECT_CALL(mock_encoder_, EncodeInternal(_, _, _, _))
    290       .WillRepeatedly(Invoke(&helper, &MockEncodeHelper::Encode));
    291 
    292   // First call is a special case, since it does not include a secondary
    293   // payload.
    294   Encode();
    295   ASSERT_EQ(1u, encoded_info_.redundant.size());
    296   EXPECT_EQ(primary_payload_type, encoded_info_.redundant[0].payload_type);
    297   EXPECT_EQ(red_payload_type_, encoded_info_.payload_type);
    298 
    299   const int secondary_payload_type = red_payload_type_ + 2;
    300   helper.info_.payload_type = secondary_payload_type;
    301   Encode();
    302   ASSERT_EQ(2u, encoded_info_.redundant.size());
    303   EXPECT_EQ(secondary_payload_type, encoded_info_.redundant[0].payload_type);
    304   EXPECT_EQ(primary_payload_type, encoded_info_.redundant[1].payload_type);
    305   EXPECT_EQ(red_payload_type_, encoded_info_.payload_type);
    306 }
    307 
    308 #if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
    309 
    310 // This test fixture tests various error conditions that makes the
    311 // AudioEncoderCng die via CHECKs.
    312 class AudioEncoderCopyRedDeathTest : public AudioEncoderCopyRedTest {
    313  protected:
    314   AudioEncoderCopyRedDeathTest() : AudioEncoderCopyRedTest() {}
    315 };
    316 
    317 TEST_F(AudioEncoderCopyRedDeathTest, WrongFrameSize) {
    318   num_audio_samples_10ms *= 2;  // 20 ms frame.
    319   EXPECT_DEATH(Encode(), "");
    320   num_audio_samples_10ms = 0;  // Zero samples.
    321   EXPECT_DEATH(Encode(), "");
    322 }
    323 
    324 TEST_F(AudioEncoderCopyRedDeathTest, NullSpeechEncoder) {
    325   AudioEncoderCopyRed* red = NULL;
    326   AudioEncoderCopyRed::Config config;
    327   config.speech_encoder = NULL;
    328   EXPECT_DEATH(red = new AudioEncoderCopyRed(config),
    329                "Speech encoder not provided.");
    330   // The delete operation is needed to avoid leak reports from memcheck.
    331   delete red;
    332 }
    333 
    334 #endif  // GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
    335 
    336 }  // namespace webrtc
    337