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