2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
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.
14 #include "webrtc/system_wrappers/interface/sleep.h"
15 #include "webrtc/test/testsupport/fileutils.h"
16 #include "webrtc/voice_engine/test/auto_test/fixtures/after_initialization_fixture.h"
21 const int16_t kLimiterHeadroom = 29204; // == -1 dbFS
22 const int16_t kInt16Max = 0x7fff;
23 const int kSampleRateHz = 16000;
24 const int kTestDurationMs = 3000;
28 class MixingTest : public AfterInitializationFixture {
31 : output_filename_(test::OutputPath() + "mixing_test_output.pcm") {
34 transport_ = new LoopBackTransport(voe_network_);
40 // Creates and mixes |num_remote_streams| which play a file "as microphone"
41 // with |num_local_streams| which play a file "locally", using a constant
42 // amplitude of |input_value|. The local streams manifest as "anonymous"
43 // mixing participants, meaning they will be mixed regardless of the number
44 // of participants. (A stream is a VoiceEngine "channel").
46 // The mixed output is verified to always fall between |max_output_value| and
47 // |min_output_value|, after a startup phase.
49 // |num_remote_streams_using_mono| of the remote streams use mono, with the
50 // remainder using stereo.
51 void RunMixingTest(int num_remote_streams,
52 int num_local_streams,
53 int num_remote_streams_using_mono,
56 int16_t max_output_value,
57 int16_t min_output_value) {
58 ASSERT_LE(num_remote_streams_using_mono, num_remote_streams);
61 input_filename_ = test::ResourcePath("voice_engine/audio_long16", "pcm");
63 input_filename_ = test::OutputPath() + "mixing_test_input.pcm";
64 GenerateInputFile(input_value);
67 std::vector<int> local_streams(num_local_streams);
68 for (size_t i = 0; i < local_streams.size(); ++i) {
69 local_streams[i] = voe_base_->CreateChannel();
70 EXPECT_NE(-1, local_streams[i]);
72 StartLocalStreams(local_streams);
73 TEST_LOG("Playing %d local streams.\n", num_local_streams);
75 std::vector<int> remote_streams(num_remote_streams);
76 for (size_t i = 0; i < remote_streams.size(); ++i) {
77 remote_streams[i] = voe_base_->CreateChannel();
78 EXPECT_NE(-1, remote_streams[i]);
80 StartRemoteStreams(remote_streams, num_remote_streams_using_mono);
81 TEST_LOG("Playing %d remote streams.\n", num_remote_streams);
83 // Give it plenty of time to get started.
86 // Start recording the mixed output and wait.
87 EXPECT_EQ(0, voe_file_->StartRecordingPlayout(-1 /* record meeting */,
88 output_filename_.c_str()));
89 SleepMs(kTestDurationMs);
90 EXPECT_EQ(0, voe_file_->StopRecordingPlayout(-1));
92 StopLocalStreams(local_streams);
93 StopRemoteStreams(remote_streams);
96 VerifyMixedOutput(max_output_value, min_output_value);
101 // Generate input file with constant values equal to |input_value|. The file
102 // will be twice the duration of the test.
103 void GenerateInputFile(int16_t input_value) {
104 FILE* input_file = fopen(input_filename_.c_str(), "wb");
105 ASSERT_TRUE(input_file != NULL);
106 for (int i = 0; i < kSampleRateHz / 1000 * (kTestDurationMs * 2); i++) {
107 ASSERT_EQ(1u, fwrite(&input_value, sizeof(input_value), 1, input_file));
109 ASSERT_EQ(0, fclose(input_file));
112 void VerifyMixedOutput(int16_t max_output_value, int16_t min_output_value) {
113 // Verify the mixed output.
114 FILE* output_file = fopen(output_filename_.c_str(), "rb");
115 ASSERT_TRUE(output_file != NULL);
116 int16_t output_value = 0;
117 int samples_read = 0;
118 while (fread(&output_value, sizeof(output_value), 1, output_file) == 1) {
120 std::ostringstream trace_stream;
121 trace_stream << samples_read << " samples read";
122 SCOPED_TRACE(trace_stream.str());
123 EXPECT_LE(output_value, max_output_value);
124 EXPECT_GE(output_value, min_output_value);
126 // Ensure we've at least recorded half as much file as the duration of the
127 // test. We have to use a relaxed tolerance here due to filesystem flakiness
129 ASSERT_GE((samples_read * 1000.0) / kSampleRateHz, 0.5 * kTestDurationMs);
130 // Ensure we read the entire file.
131 ASSERT_NE(0, feof(output_file));
132 ASSERT_EQ(0, fclose(output_file));
135 // Start up local streams ("anonymous" participants).
136 void StartLocalStreams(const std::vector<int>& streams) {
137 for (size_t i = 0; i < streams.size(); ++i) {
138 EXPECT_EQ(0, voe_base_->StartPlayout(streams[i]));
139 EXPECT_EQ(0, voe_file_->StartPlayingFileLocally(streams[i],
140 input_filename_.c_str(), true));
144 void StopLocalStreams(const std::vector<int>& streams) {
145 for (size_t i = 0; i < streams.size(); ++i) {
146 EXPECT_EQ(0, voe_base_->StopPlayout(streams[i]));
147 EXPECT_EQ(0, voe_base_->DeleteChannel(streams[i]));
151 // Start up remote streams ("normal" participants).
152 void StartRemoteStreams(const std::vector<int>& streams,
153 int num_remote_streams_using_mono) {
154 // Use L16 at 16kHz to minimize distortion (file recording is 16kHz and
155 // resampling will cause distortion).
156 CodecInst codec_inst;
157 strcpy(codec_inst.plname, "L16");
158 codec_inst.channels = 1;
159 codec_inst.plfreq = kSampleRateHz;
160 codec_inst.pltype = 105;
161 codec_inst.pacsize = codec_inst.plfreq / 100;
162 codec_inst.rate = codec_inst.plfreq * sizeof(int16_t) * 8; // 8 bits/byte.
164 for (int i = 0; i < num_remote_streams_using_mono; ++i) {
165 // Add some delay between starting up the channels in order to give them
166 // different energies in the "real audio" test and hopefully exercise
169 StartRemoteStream(streams[i], codec_inst, 1234 + 2 * i);
172 // The remainder of the streams will use stereo.
173 codec_inst.channels = 2;
175 for (size_t i = num_remote_streams_using_mono; i < streams.size(); ++i) {
176 StartRemoteStream(streams[i], codec_inst, 1234 + 2 * i);
180 // Start up a single remote stream.
181 void StartRemoteStream(int stream, const CodecInst& codec_inst, int port) {
182 EXPECT_EQ(0, voe_codec_->SetRecPayloadType(stream, codec_inst));
183 EXPECT_EQ(0, voe_network_->RegisterExternalTransport(stream, *transport_));
184 EXPECT_EQ(0, voe_base_->StartReceive(stream));
185 EXPECT_EQ(0, voe_base_->StartPlayout(stream));
186 EXPECT_EQ(0, voe_codec_->SetSendCodec(stream, codec_inst));
187 EXPECT_EQ(0, voe_base_->StartSend(stream));
188 EXPECT_EQ(0, voe_file_->StartPlayingFileAsMicrophone(stream,
189 input_filename_.c_str(), true));
192 void StopRemoteStreams(const std::vector<int>& streams) {
193 for (size_t i = 0; i < streams.size(); ++i) {
194 EXPECT_EQ(0, voe_base_->StopSend(streams[i]));
195 EXPECT_EQ(0, voe_base_->StopPlayout(streams[i]));
196 EXPECT_EQ(0, voe_base_->StopReceive(streams[i]));
197 EXPECT_EQ(0, voe_network_->DeRegisterExternalTransport(streams[i]));
198 EXPECT_EQ(0, voe_base_->DeleteChannel(streams[i]));
202 std::string input_filename_;
203 const std::string output_filename_;
204 LoopBackTransport* transport_;
207 // This test has no verification, but exercises additional code paths in a
208 // somewhat more realistic scenario using real audio. It can at least hunt for
209 // asserts and crashes.
210 TEST_F(MixingTest, MixManyChannelsForStress) {
211 RunMixingTest(10, 0, 10, true, 0, 0, 0);
214 // These tests assume a maximum of three mixed participants. We typically allow
215 // a +/- 10% range around the expected output level to account for distortion
216 // from coding and processing in the loopback chain.
217 TEST_F(MixingTest, FourChannelsWithOnlyThreeMixed) {
218 const int16_t kInputValue = 1000;
219 const int16_t kExpectedOutput = kInputValue * 3;
220 RunMixingTest(4, 0, 4, false, kInputValue, 1.1 * kExpectedOutput,
221 0.9 * kExpectedOutput);
224 // Ensure the mixing saturation protection is working. We can do this because
225 // the mixing limiter is given some headroom, so the expected output is less
227 TEST_F(MixingTest, VerifySaturationProtection) {
228 const int16_t kInputValue = 20000;
229 const int16_t kExpectedOutput = kLimiterHeadroom;
230 // If this isn't satisfied, we're not testing anything.
231 ASSERT_GT(kInputValue * 3, kInt16Max);
232 ASSERT_LT(1.1 * kExpectedOutput, kInt16Max);
233 RunMixingTest(3, 0, 3, false, kInputValue, 1.1 * kExpectedOutput,
234 0.9 * kExpectedOutput);
237 TEST_F(MixingTest, SaturationProtectionHasNoEffectOnOneChannel) {
238 const int16_t kInputValue = kInt16Max;
239 const int16_t kExpectedOutput = kInt16Max;
240 // If this isn't satisfied, we're not testing anything.
241 ASSERT_GT(0.95 * kExpectedOutput, kLimiterHeadroom);
242 // Tighter constraints are required here to properly test this.
243 RunMixingTest(1, 0, 1, false, kInputValue, kExpectedOutput,
244 0.95 * kExpectedOutput);
247 TEST_F(MixingTest, VerifyAnonymousAndNormalParticipantMixing) {
248 const int16_t kInputValue = 1000;
249 const int16_t kExpectedOutput = kInputValue * 2;
250 RunMixingTest(1, 1, 1, false, kInputValue, 1.1 * kExpectedOutput,
251 0.9 * kExpectedOutput);
254 TEST_F(MixingTest, AnonymousParticipantsAreAlwaysMixed) {
255 const int16_t kInputValue = 1000;
256 const int16_t kExpectedOutput = kInputValue * 4;
257 RunMixingTest(3, 1, 3, false, kInputValue, 1.1 * kExpectedOutput,
258 0.9 * kExpectedOutput);
261 TEST_F(MixingTest, VerifyStereoAndMonoMixing) {
262 const int16_t kInputValue = 1000;
263 const int16_t kExpectedOutput = kInputValue * 2;
264 RunMixingTest(2, 0, 1, false, kInputValue, 1.1 * kExpectedOutput,
265 // Lower than 0.9 due to observed flakiness on bots.
266 0.8 * kExpectedOutput);
269 } // namespace webrtc