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