OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2013 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 "webrtc/modules/audio_coding/main/acm2/acm_receiver.h" | |
12 | |
13 #include <algorithm> // std::min | |
14 | |
15 #include "testing/gtest/include/gtest/gtest.h" | |
16 #include "webrtc/base/scoped_ptr.h" | |
17 #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" | |
18 #include "webrtc/modules/audio_coding/main/acm2/audio_coding_module_impl.h" | |
19 #include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h" | |
20 #include "webrtc/modules/audio_coding/neteq/tools/rtp_generator.h" | |
21 #include "webrtc/system_wrappers/interface/clock.h" | |
22 #include "webrtc/test/test_suite.h" | |
23 #include "webrtc/test/testsupport/fileutils.h" | |
24 #include "webrtc/test/testsupport/gtest_disable.h" | |
25 | |
26 namespace webrtc { | |
27 | |
28 namespace acm2 { | |
29 namespace { | |
30 | |
31 bool CodecsEqual(const CodecInst& codec_a, const CodecInst& codec_b) { | |
32 if (strcmp(codec_a.plname, codec_b.plname) != 0 || | |
33 codec_a.plfreq != codec_b.plfreq || | |
34 codec_a.pltype != codec_b.pltype || | |
35 codec_b.channels != codec_a.channels) | |
36 return false; | |
37 return true; | |
38 } | |
39 | |
40 } // namespace | |
41 | |
42 class AcmReceiverTest : public AudioPacketizationCallback, | |
43 public ::testing::Test { | |
44 protected: | |
45 AcmReceiverTest() | |
46 : timestamp_(0), | |
47 packet_sent_(false), | |
48 last_packet_send_timestamp_(timestamp_), | |
49 last_frame_type_(kEmptyFrame) { | |
50 AudioCoding::Config config; | |
51 config.transport = this; | |
52 acm_.reset(new AudioCodingImpl(config)); | |
53 receiver_.reset(new AcmReceiver(config.ToOldConfig())); | |
54 } | |
55 | |
56 ~AcmReceiverTest() {} | |
57 | |
58 void SetUp() override { | |
59 ASSERT_TRUE(receiver_.get() != NULL); | |
60 ASSERT_TRUE(acm_.get() != NULL); | |
61 for (int n = 0; n < ACMCodecDB::kNumCodecs; n++) { | |
62 ASSERT_EQ(0, ACMCodecDB::Codec(n, &codecs_[n])); | |
63 } | |
64 | |
65 rtp_header_.header.sequenceNumber = 0; | |
66 rtp_header_.header.timestamp = 0; | |
67 rtp_header_.header.markerBit = false; | |
68 rtp_header_.header.ssrc = 0x12345678; // Arbitrary. | |
69 rtp_header_.header.numCSRCs = 0; | |
70 rtp_header_.header.payloadType = 0; | |
71 rtp_header_.frameType = kAudioFrameSpeech; | |
72 rtp_header_.type.Audio.isCNG = false; | |
73 } | |
74 | |
75 void TearDown() override {} | |
76 | |
77 void InsertOnePacketOfSilence(int codec_id) { | |
78 CodecInst codec; | |
79 ACMCodecDB::Codec(codec_id, &codec); | |
80 if (timestamp_ == 0) { // This is the first time inserting audio. | |
81 ASSERT_TRUE(acm_->RegisterSendCodec(codec_id, codec.pltype)); | |
82 } else { | |
83 const CodecInst* current_codec = acm_->GetSenderCodecInst(); | |
84 ASSERT_TRUE(current_codec); | |
85 if (!CodecsEqual(codec, *current_codec)) | |
86 ASSERT_TRUE(acm_->RegisterSendCodec(codec_id, codec.pltype)); | |
87 } | |
88 AudioFrame frame; | |
89 // Frame setup according to the codec. | |
90 frame.sample_rate_hz_ = codec.plfreq; | |
91 frame.samples_per_channel_ = codec.plfreq / 100; // 10 ms. | |
92 frame.num_channels_ = codec.channels; | |
93 memset(frame.data_, 0, frame.samples_per_channel_ * frame.num_channels_ * | |
94 sizeof(int16_t)); | |
95 int num_bytes = 0; | |
96 packet_sent_ = false; | |
97 last_packet_send_timestamp_ = timestamp_; | |
98 while (num_bytes == 0) { | |
99 frame.timestamp_ = timestamp_; | |
100 timestamp_ += frame.samples_per_channel_; | |
101 num_bytes = acm_->Add10MsAudio(frame); | |
102 ASSERT_GE(num_bytes, 0); | |
103 } | |
104 ASSERT_TRUE(packet_sent_); // Sanity check. | |
105 } | |
106 | |
107 // Last element of id should be negative. | |
108 void AddSetOfCodecs(const int* id) { | |
109 int n = 0; | |
110 while (id[n] >= 0) { | |
111 ASSERT_EQ(0, receiver_->AddCodec(id[n], codecs_[id[n]].pltype, | |
112 codecs_[id[n]].channels, | |
113 codecs_[id[n]].plfreq, NULL)); | |
114 ++n; | |
115 } | |
116 } | |
117 | |
118 int32_t SendData(FrameType frame_type, | |
119 uint8_t payload_type, | |
120 uint32_t timestamp, | |
121 const uint8_t* payload_data, | |
122 size_t payload_len_bytes, | |
123 const RTPFragmentationHeader* fragmentation) override { | |
124 if (frame_type == kEmptyFrame) | |
125 return 0; | |
126 | |
127 rtp_header_.header.payloadType = payload_type; | |
128 rtp_header_.frameType = frame_type; | |
129 if (frame_type == kAudioFrameSpeech) | |
130 rtp_header_.type.Audio.isCNG = false; | |
131 else | |
132 rtp_header_.type.Audio.isCNG = true; | |
133 rtp_header_.header.timestamp = timestamp; | |
134 | |
135 int ret_val = receiver_->InsertPacket(rtp_header_, payload_data, | |
136 payload_len_bytes); | |
137 if (ret_val < 0) { | |
138 assert(false); | |
139 return -1; | |
140 } | |
141 rtp_header_.header.sequenceNumber++; | |
142 packet_sent_ = true; | |
143 last_frame_type_ = frame_type; | |
144 return 0; | |
145 } | |
146 | |
147 rtc::scoped_ptr<AcmReceiver> receiver_; | |
148 CodecInst codecs_[ACMCodecDB::kMaxNumCodecs]; | |
149 rtc::scoped_ptr<AudioCoding> acm_; | |
150 WebRtcRTPHeader rtp_header_; | |
151 uint32_t timestamp_; | |
152 bool packet_sent_; // Set when SendData is called reset when inserting audio. | |
153 uint32_t last_packet_send_timestamp_; | |
154 FrameType last_frame_type_; | |
155 }; | |
156 | |
157 TEST_F(AcmReceiverTest, DISABLED_ON_ANDROID(AddCodecGetCodec)) { | |
158 // Add codec. | |
159 for (int n = 0; n < ACMCodecDB::kNumCodecs; ++n) { | |
160 if (n & 0x1) // Just add codecs with odd index. | |
161 EXPECT_EQ(0, | |
162 receiver_->AddCodec(n, codecs_[n].pltype, codecs_[n].channels, | |
163 codecs_[n].plfreq, NULL)); | |
164 } | |
165 // Get codec and compare. | |
166 for (int n = 0; n < ACMCodecDB::kNumCodecs; ++n) { | |
167 CodecInst my_codec; | |
168 if (n & 0x1) { | |
169 // Codecs with odd index should match the reference. | |
170 EXPECT_EQ(0, receiver_->DecoderByPayloadType(codecs_[n].pltype, | |
171 &my_codec)); | |
172 EXPECT_TRUE(CodecsEqual(codecs_[n], my_codec)); | |
173 } else { | |
174 // Codecs with even index are not registered. | |
175 EXPECT_EQ(-1, receiver_->DecoderByPayloadType(codecs_[n].pltype, | |
176 &my_codec)); | |
177 } | |
178 } | |
179 } | |
180 | |
181 TEST_F(AcmReceiverTest, DISABLED_ON_ANDROID(AddCodecChangePayloadType)) { | |
182 const int codec_id = ACMCodecDB::kPCMA; | |
183 CodecInst ref_codec1; | |
184 EXPECT_EQ(0, ACMCodecDB::Codec(codec_id, &ref_codec1)); | |
185 CodecInst ref_codec2 = ref_codec1; | |
186 ++ref_codec2.pltype; | |
187 CodecInst test_codec; | |
188 | |
189 // Register the same codec with different payload types. | |
190 EXPECT_EQ( | |
191 0, receiver_->AddCodec(codec_id, ref_codec1.pltype, ref_codec1.channels, | |
192 ref_codec1.plfreq, NULL)); | |
193 EXPECT_EQ( | |
194 0, receiver_->AddCodec(codec_id, ref_codec2.pltype, ref_codec2.channels, | |
195 ref_codec2.plfreq, NULL)); | |
196 | |
197 // Both payload types should exist. | |
198 EXPECT_EQ(0, receiver_->DecoderByPayloadType(ref_codec1.pltype, &test_codec)); | |
199 EXPECT_EQ(true, CodecsEqual(ref_codec1, test_codec)); | |
200 EXPECT_EQ(0, receiver_->DecoderByPayloadType(ref_codec2.pltype, &test_codec)); | |
201 EXPECT_EQ(true, CodecsEqual(ref_codec2, test_codec)); | |
202 } | |
203 | |
204 TEST_F(AcmReceiverTest, DISABLED_ON_ANDROID(AddCodecChangeCodecId)) { | |
205 const int codec_id1 = ACMCodecDB::kPCMU; | |
206 CodecInst ref_codec1; | |
207 EXPECT_EQ(0, ACMCodecDB::Codec(codec_id1, &ref_codec1)); | |
208 const int codec_id2 = ACMCodecDB::kPCMA; | |
209 CodecInst ref_codec2; | |
210 EXPECT_EQ(0, ACMCodecDB::Codec(codec_id2, &ref_codec2)); | |
211 ref_codec2.pltype = ref_codec1.pltype; | |
212 CodecInst test_codec; | |
213 | |
214 // Register the same payload type with different codec ID. | |
215 EXPECT_EQ( | |
216 0, receiver_->AddCodec(codec_id1, ref_codec1.pltype, ref_codec1.channels, | |
217 ref_codec1.plfreq, NULL)); | |
218 EXPECT_EQ( | |
219 0, receiver_->AddCodec(codec_id2, ref_codec2.pltype, ref_codec2.channels, | |
220 ref_codec2.plfreq, NULL)); | |
221 | |
222 // Make sure that the last codec is used. | |
223 EXPECT_EQ(0, receiver_->DecoderByPayloadType(ref_codec2.pltype, &test_codec)); | |
224 EXPECT_EQ(true, CodecsEqual(ref_codec2, test_codec)); | |
225 } | |
226 | |
227 TEST_F(AcmReceiverTest, DISABLED_ON_ANDROID(AddCodecRemoveCodec)) { | |
228 CodecInst codec; | |
229 const int codec_id = ACMCodecDB::kPCMA; | |
230 EXPECT_EQ(0, ACMCodecDB::Codec(codec_id, &codec)); | |
231 const int payload_type = codec.pltype; | |
232 EXPECT_EQ(0, receiver_->AddCodec(codec_id, codec.pltype, codec.channels, | |
233 codec.plfreq, NULL)); | |
234 | |
235 // Remove non-existing codec should not fail. ACM1 legacy. | |
236 EXPECT_EQ(0, receiver_->RemoveCodec(payload_type + 1)); | |
237 | |
238 // Remove an existing codec. | |
239 EXPECT_EQ(0, receiver_->RemoveCodec(payload_type)); | |
240 | |
241 // Ask for the removed codec, must fail. | |
242 EXPECT_EQ(-1, receiver_->DecoderByPayloadType(payload_type, &codec)); | |
243 } | |
244 | |
245 TEST_F(AcmReceiverTest, DISABLED_ON_ANDROID(SampleRate)) { | |
246 const int kCodecId[] = { | |
247 ACMCodecDB::kISAC, ACMCodecDB::kISACSWB, | |
248 -1 // Terminator. | |
249 }; | |
250 AddSetOfCodecs(kCodecId); | |
251 | |
252 AudioFrame frame; | |
253 const int kOutSampleRateHz = 8000; // Different than codec sample rate. | |
254 int n = 0; | |
255 while (kCodecId[n] >= 0) { | |
256 const int num_10ms_frames = codecs_[kCodecId[n]].pacsize / | |
257 (codecs_[kCodecId[n]].plfreq / 100); | |
258 InsertOnePacketOfSilence(kCodecId[n]); | |
259 for (int k = 0; k < num_10ms_frames; ++k) { | |
260 EXPECT_EQ(0, receiver_->GetAudio(kOutSampleRateHz, &frame)); | |
261 } | |
262 EXPECT_EQ(std::min(32000, codecs_[kCodecId[n]].plfreq), | |
263 receiver_->current_sample_rate_hz()); | |
264 ++n; | |
265 } | |
266 } | |
267 | |
268 TEST_F(AcmReceiverTest, DISABLED_ON_ANDROID(PostdecodingVad)) { | |
269 receiver_->EnableVad(); | |
270 EXPECT_TRUE(receiver_->vad_enabled()); | |
271 | |
272 const int id = ACMCodecDB::kPCM16Bwb; | |
273 ASSERT_EQ(0, receiver_->AddCodec(id, codecs_[id].pltype, codecs_[id].channels, | |
274 codecs_[id].plfreq, NULL)); | |
275 const int kNumPackets = 5; | |
276 const int num_10ms_frames = codecs_[id].pacsize / (codecs_[id].plfreq / 100); | |
277 AudioFrame frame; | |
278 for (int n = 0; n < kNumPackets; ++n) { | |
279 InsertOnePacketOfSilence(id); | |
280 for (int k = 0; k < num_10ms_frames; ++k) | |
281 ASSERT_EQ(0, receiver_->GetAudio(codecs_[id].plfreq, &frame)); | |
282 } | |
283 EXPECT_EQ(AudioFrame::kVadPassive, frame.vad_activity_); | |
284 | |
285 receiver_->DisableVad(); | |
286 EXPECT_FALSE(receiver_->vad_enabled()); | |
287 | |
288 for (int n = 0; n < kNumPackets; ++n) { | |
289 InsertOnePacketOfSilence(id); | |
290 for (int k = 0; k < num_10ms_frames; ++k) | |
291 ASSERT_EQ(0, receiver_->GetAudio(codecs_[id].plfreq, &frame)); | |
292 } | |
293 EXPECT_EQ(AudioFrame::kVadUnknown, frame.vad_activity_); | |
294 } | |
295 | |
296 #ifdef WEBRTC_CODEC_ISAC | |
297 #define IF_ISAC_FLOAT(x) x | |
298 #else | |
299 #define IF_ISAC_FLOAT(x) DISABLED_##x | |
300 #endif | |
301 | |
302 TEST_F(AcmReceiverTest, DISABLED_ON_ANDROID(IF_ISAC_FLOAT(LastAudioCodec))) { | |
303 const int kCodecId[] = { | |
304 ACMCodecDB::kISAC, ACMCodecDB::kPCMA, ACMCodecDB::kISACSWB, | |
305 ACMCodecDB::kPCM16Bswb32kHz, | |
306 -1 // Terminator. | |
307 }; | |
308 AddSetOfCodecs(kCodecId); | |
309 | |
310 const int kCngId[] = { // Not including full-band. | |
311 ACMCodecDB::kCNNB, ACMCodecDB::kCNWB, ACMCodecDB::kCNSWB, | |
312 -1 // Terminator. | |
313 }; | |
314 AddSetOfCodecs(kCngId); | |
315 | |
316 // Register CNG at sender side. | |
317 int n = 0; | |
318 while (kCngId[n] > 0) { | |
319 ASSERT_TRUE(acm_->RegisterSendCodec(kCngId[n], codecs_[kCngId[n]].pltype)); | |
320 ++n; | |
321 } | |
322 | |
323 CodecInst codec; | |
324 // No audio payload is received. | |
325 EXPECT_EQ(-1, receiver_->LastAudioCodec(&codec)); | |
326 | |
327 // Start with sending DTX. | |
328 ASSERT_TRUE(acm_->SetVad(true, true, VADVeryAggr)); | |
329 packet_sent_ = false; | |
330 InsertOnePacketOfSilence(kCodecId[0]); // Enough to test with one codec. | |
331 ASSERT_TRUE(packet_sent_); | |
332 EXPECT_EQ(kAudioFrameCN, last_frame_type_); | |
333 | |
334 // Has received, only, DTX. Last Audio codec is undefined. | |
335 EXPECT_EQ(-1, receiver_->LastAudioCodec(&codec)); | |
336 EXPECT_EQ(-1, receiver_->last_audio_codec_id()); | |
337 | |
338 n = 0; | |
339 while (kCodecId[n] >= 0) { // Loop over codecs. | |
340 // Set DTX off to send audio payload. | |
341 acm_->SetVad(false, false, VADAggr); | |
342 packet_sent_ = false; | |
343 InsertOnePacketOfSilence(kCodecId[n]); | |
344 | |
345 // Sanity check if Actually an audio payload received, and it should be | |
346 // of type "speech." | |
347 ASSERT_TRUE(packet_sent_); | |
348 ASSERT_EQ(kAudioFrameSpeech, last_frame_type_); | |
349 EXPECT_EQ(kCodecId[n], receiver_->last_audio_codec_id()); | |
350 | |
351 // Set VAD on to send DTX. Then check if the "Last Audio codec" returns | |
352 // the expected codec. | |
353 acm_->SetVad(true, true, VADAggr); | |
354 | |
355 // Do as many encoding until a DTX is sent. | |
356 while (last_frame_type_ != kAudioFrameCN) { | |
357 packet_sent_ = false; | |
358 InsertOnePacketOfSilence(kCodecId[n]); | |
359 ASSERT_TRUE(packet_sent_); | |
360 } | |
361 EXPECT_EQ(kCodecId[n], receiver_->last_audio_codec_id()); | |
362 EXPECT_EQ(0, receiver_->LastAudioCodec(&codec)); | |
363 EXPECT_TRUE(CodecsEqual(codecs_[kCodecId[n]], codec)); | |
364 ++n; | |
365 } | |
366 } | |
367 | |
368 } // namespace acm2 | |
369 | |
370 } // namespace webrtc | |
OLD | NEW |