Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / webrtc / voice_engine / test / auto_test / standard / mixing_test.cc
1 /*
2  *  Copyright (c) 2012 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 <stdio.h>
12 #include <string>
13
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"
17
18 namespace webrtc {
19 namespace {
20
21 const int16_t kLimiterHeadroom = 29204;  // == -1 dbFS
22 const int16_t kInt16Max = 0x7fff;
23 const int kSampleRateHz = 16000;
24 const int kTestDurationMs = 3000;
25
26 }  // namespace
27
28 class MixingTest : public AfterInitializationFixture {
29  protected:
30   MixingTest()
31       : output_filename_(test::OutputPath() + "mixing_test_output.pcm") {
32   }
33   void SetUp() {
34     transport_ = new LoopBackTransport(voe_network_);
35   }
36   void TearDown() {
37     delete transport_;
38   }
39
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").
45   //
46   // The mixed output is verified to always fall between |max_output_value| and
47   // |min_output_value|, after a startup phase.
48   //
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,
54                      bool real_audio,
55                      int16_t input_value,
56                      int16_t max_output_value,
57                      int16_t min_output_value) {
58     ASSERT_LE(num_remote_streams_using_mono, num_remote_streams);
59
60     if (real_audio) {
61       input_filename_ = test::ResourcePath("voice_engine/audio_long16", "pcm");
62     } else {
63       input_filename_ = test::OutputPath() + "mixing_test_input.pcm";
64       GenerateInputFile(input_value);
65     }
66
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]);
71     }
72     StartLocalStreams(local_streams);
73     TEST_LOG("Playing %d local streams.\n", num_local_streams);
74
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]);
79     }
80     StartRemoteStreams(remote_streams, num_remote_streams_using_mono);
81     TEST_LOG("Playing %d remote streams.\n", num_remote_streams);
82
83     // Give it plenty of time to get started.
84     SleepMs(1000);
85
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));
91
92     StopLocalStreams(local_streams);
93     StopRemoteStreams(remote_streams);
94
95     if (!real_audio) {
96       VerifyMixedOutput(max_output_value, min_output_value);
97     }
98   }
99
100  private:
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));
108     }
109     ASSERT_EQ(0, fclose(input_file));
110   }
111
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) {
119       samples_read++;
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);
125     }
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
128     // on the bots.
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));
133   }
134
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));
141     }
142   }
143
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]));
148     }
149   }
150
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.
163
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
167       // more code paths.
168       SleepMs(50);
169       StartRemoteStream(streams[i], codec_inst, 1234 + 2 * i);
170     }
171
172     // The remainder of the streams will use stereo.
173     codec_inst.channels = 2;
174     codec_inst.pltype++;
175     for (size_t i = num_remote_streams_using_mono; i < streams.size(); ++i) {
176       StartRemoteStream(streams[i], codec_inst, 1234 + 2 * i);
177     }
178   }
179
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));
190   }
191
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]));
199     }
200   }
201
202   std::string input_filename_;
203   const std::string output_filename_;
204   LoopBackTransport* transport_;
205 };
206
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);
212 }
213
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);
222 }
223
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
226 // than full scale.
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);
235 }
236
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);
245 }
246
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);
252 }
253
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);
259 }
260
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);
267 }
268
269 }  // namespace webrtc