Upstream version 9.38.198.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 kPayloadType = 105;
24 const int kInSampleRateHz = 16000;  // Input file taken as 16 kHz by default.
25 const int kRecSampleRateHz = 16000;  // Recorded with 16 kHz L16.
26 const int kTestDurationMs = 3000;
27 const CodecInst kCodecL16 = {kPayloadType, "L16", 16000, 160, 1, 256000};
28 const CodecInst kCodecOpus = {kPayloadType, "opus", 48000, 960, 1, 32000};
29
30 }  // namespace
31
32 class MixingTest : public AfterInitializationFixture {
33  protected:
34   MixingTest()
35       : output_filename_(test::OutputPath() + "mixing_test_output.pcm") {
36   }
37   void SetUp() {
38     transport_ = new LoopBackTransport(voe_network_);
39   }
40   void TearDown() {
41     delete transport_;
42   }
43
44   // Creates and mixes |num_remote_streams| which play a file "as microphone"
45   // with |num_local_streams| which play a file "locally", using a constant
46   // amplitude of |input_value|. The local streams manifest as "anonymous"
47   // mixing participants, meaning they will be mixed regardless of the number
48   // of participants. (A stream is a VoiceEngine "channel").
49   //
50   // The mixed output is verified to always fall between |max_output_value| and
51   // |min_output_value|, after a startup phase.
52   //
53   // |num_remote_streams_using_mono| of the remote streams use mono, with the
54   // remainder using stereo.
55   void RunMixingTest(int num_remote_streams,
56                      int num_local_streams,
57                      int num_remote_streams_using_mono,
58                      bool real_audio,
59                      int16_t input_value,
60                      int16_t max_output_value,
61                      int16_t min_output_value,
62                      const CodecInst& codec_inst) {
63     ASSERT_LE(num_remote_streams_using_mono, num_remote_streams);
64
65     if (real_audio) {
66       input_filename_ = test::ResourcePath("voice_engine/audio_long16", "pcm");
67     } else {
68       input_filename_ = test::OutputPath() + "mixing_test_input.pcm";
69       GenerateInputFile(input_value);
70     }
71
72     std::vector<int> local_streams(num_local_streams);
73     for (size_t i = 0; i < local_streams.size(); ++i) {
74       local_streams[i] = voe_base_->CreateChannel();
75       EXPECT_NE(-1, local_streams[i]);
76     }
77     StartLocalStreams(local_streams);
78     TEST_LOG("Playing %d local streams.\n", num_local_streams);
79
80     std::vector<int> remote_streams(num_remote_streams);
81     for (size_t i = 0; i < remote_streams.size(); ++i) {
82       remote_streams[i] = voe_base_->CreateChannel();
83       EXPECT_NE(-1, remote_streams[i]);
84     }
85     StartRemoteStreams(remote_streams, num_remote_streams_using_mono,
86                        codec_inst);
87     TEST_LOG("Playing %d remote streams.\n", num_remote_streams);
88
89     // Give it plenty of time to get started.
90     SleepMs(1000);
91
92     // Start recording the mixed output and wait.
93     EXPECT_EQ(0, voe_file_->StartRecordingPlayout(-1 /* record meeting */,
94         output_filename_.c_str()));
95     SleepMs(kTestDurationMs);
96     while (GetFileDurationMs(output_filename_.c_str()) < kTestDurationMs) {
97       SleepMs(200);
98     }
99     EXPECT_EQ(0, voe_file_->StopRecordingPlayout(-1));
100
101     StopLocalStreams(local_streams);
102     StopRemoteStreams(remote_streams);
103
104     if (!real_audio) {
105       VerifyMixedOutput(max_output_value, min_output_value);
106     }
107   }
108
109  private:
110   // Generate input file with constant values equal to |input_value|. The file
111   // will be twice the duration of the test.
112   void GenerateInputFile(int16_t input_value) {
113     FILE* input_file = fopen(input_filename_.c_str(), "wb");
114     ASSERT_TRUE(input_file != NULL);
115     for (int i = 0; i < kInSampleRateHz / 1000 * (kTestDurationMs * 2); i++) {
116       ASSERT_EQ(1u, fwrite(&input_value, sizeof(input_value), 1, input_file));
117     }
118     ASSERT_EQ(0, fclose(input_file));
119   }
120
121   void VerifyMixedOutput(int16_t max_output_value, int16_t min_output_value) {
122     // Verify the mixed output.
123     FILE* output_file = fopen(output_filename_.c_str(), "rb");
124     ASSERT_TRUE(output_file != NULL);
125     int16_t output_value = 0;
126     int samples_read = 0;
127     while (fread(&output_value, sizeof(output_value), 1, output_file) == 1) {
128       samples_read++;
129       std::ostringstream trace_stream;
130       trace_stream << samples_read << " samples read";
131       SCOPED_TRACE(trace_stream.str());
132       EXPECT_LE(output_value, max_output_value);
133       EXPECT_GE(output_value, min_output_value);
134     }
135     // Ensure we've at least recorded half as much file as the duration of the
136     // test. We have to use a relaxed tolerance here due to filesystem flakiness
137     // on the bots.
138     ASSERT_GE((samples_read * 1000.0) / kRecSampleRateHz, kTestDurationMs);
139     // Ensure we read the entire file.
140     ASSERT_NE(0, feof(output_file));
141     ASSERT_EQ(0, fclose(output_file));
142   }
143
144   // Start up local streams ("anonymous" participants).
145   void StartLocalStreams(const std::vector<int>& streams) {
146     for (size_t i = 0; i < streams.size(); ++i) {
147       EXPECT_EQ(0, voe_base_->StartPlayout(streams[i]));
148       EXPECT_EQ(0, voe_file_->StartPlayingFileLocally(streams[i],
149           input_filename_.c_str(), true));
150     }
151   }
152
153   void StopLocalStreams(const std::vector<int>& streams) {
154     for (size_t i = 0; i < streams.size(); ++i) {
155       EXPECT_EQ(0, voe_base_->StopPlayout(streams[i]));
156       EXPECT_EQ(0, voe_base_->DeleteChannel(streams[i]));
157     }
158   }
159
160   // Start up remote streams ("normal" participants).
161   void StartRemoteStreams(const std::vector<int>& streams,
162                           int num_remote_streams_using_mono,
163                           const CodecInst& codec_inst) {
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     CodecInst codec_inst_stereo = codec_inst;
174     codec_inst_stereo.channels = 2;
175     codec_inst_stereo.pltype++;
176     for (size_t i = num_remote_streams_using_mono; i < streams.size(); ++i) {
177       StartRemoteStream(streams[i], codec_inst_stereo, 1234 + 2 * i);
178     }
179   }
180
181   // Start up a single remote stream.
182   void StartRemoteStream(int stream, const CodecInst& codec_inst, int port) {
183     EXPECT_EQ(0, voe_codec_->SetRecPayloadType(stream, codec_inst));
184     EXPECT_EQ(0, voe_network_->RegisterExternalTransport(stream, *transport_));
185     EXPECT_EQ(0, voe_base_->StartReceive(stream));
186     EXPECT_EQ(0, voe_base_->StartPlayout(stream));
187     EXPECT_EQ(0, voe_codec_->SetSendCodec(stream, codec_inst));
188     EXPECT_EQ(0, voe_base_->StartSend(stream));
189     EXPECT_EQ(0, voe_file_->StartPlayingFileAsMicrophone(stream,
190         input_filename_.c_str(), true));
191   }
192
193   void StopRemoteStreams(const std::vector<int>& streams) {
194     for (size_t i = 0; i < streams.size(); ++i) {
195       EXPECT_EQ(0, voe_base_->StopSend(streams[i]));
196       EXPECT_EQ(0, voe_base_->StopPlayout(streams[i]));
197       EXPECT_EQ(0, voe_base_->StopReceive(streams[i]));
198       EXPECT_EQ(0, voe_network_->DeRegisterExternalTransport(streams[i]));
199       EXPECT_EQ(0, voe_base_->DeleteChannel(streams[i]));
200     }
201   }
202
203   int GetFileDurationMs(const char* file_name) {
204     FILE* fid = fopen(file_name, "rb");
205     EXPECT_FALSE(fid == NULL);
206     fseek(fid, 0, SEEK_END);
207     int size = ftell(fid);
208     EXPECT_NE(-1, size);
209     fclose(fid);
210     // Divided by 2 due to 2 bytes/sample.
211     return size * 1000 / kRecSampleRateHz / 2;
212   }
213
214   std::string input_filename_;
215   const std::string output_filename_;
216   LoopBackTransport* transport_;
217 };
218
219 // This test has no verification, but exercises additional code paths in a
220 // somewhat more realistic scenario using real audio. It can at least hunt for
221 // asserts and crashes.
222 TEST_F(MixingTest, MixManyChannelsForStress) {
223   RunMixingTest(10, 0, 10, true, 0, 0, 0, kCodecL16);
224 }
225
226 TEST_F(MixingTest, MixManyChannelsForStressOpus) {
227   RunMixingTest(10, 0, 10, true, 0, 0, 0, kCodecOpus);
228 }
229
230 // These tests assume a maximum of three mixed participants. We typically allow
231 // a +/- 10% range around the expected output level to account for distortion
232 // from coding and processing in the loopback chain.
233 TEST_F(MixingTest, FourChannelsWithOnlyThreeMixed) {
234   const int16_t kInputValue = 1000;
235   const int16_t kExpectedOutput = kInputValue * 3;
236   RunMixingTest(4, 0, 4, false, kInputValue, 1.1 * kExpectedOutput,
237                 0.9 * kExpectedOutput, kCodecL16);
238 }
239
240 // Ensure the mixing saturation protection is working. We can do this because
241 // the mixing limiter is given some headroom, so the expected output is less
242 // than full scale.
243 TEST_F(MixingTest, VerifySaturationProtection) {
244   const int16_t kInputValue = 20000;
245   const int16_t kExpectedOutput = kLimiterHeadroom;
246   // If this isn't satisfied, we're not testing anything.
247   ASSERT_GT(kInputValue * 3, kInt16Max);
248   ASSERT_LT(1.1 * kExpectedOutput, kInt16Max);
249   RunMixingTest(3, 0, 3, false, kInputValue, 1.1 * kExpectedOutput,
250                0.9 * kExpectedOutput, kCodecL16);
251 }
252
253 TEST_F(MixingTest, SaturationProtectionHasNoEffectOnOneChannel) {
254   const int16_t kInputValue = kInt16Max;
255   const int16_t kExpectedOutput = kInt16Max;
256   // If this isn't satisfied, we're not testing anything.
257   ASSERT_GT(0.95 * kExpectedOutput, kLimiterHeadroom);
258   // Tighter constraints are required here to properly test this.
259   RunMixingTest(1, 0, 1, false, kInputValue, kExpectedOutput,
260                 0.95 * kExpectedOutput, kCodecL16);
261 }
262
263 TEST_F(MixingTest, VerifyAnonymousAndNormalParticipantMixing) {
264   const int16_t kInputValue = 1000;
265   const int16_t kExpectedOutput = kInputValue * 2;
266   RunMixingTest(1, 1, 1, false, kInputValue, 1.1 * kExpectedOutput,
267                 0.9 * kExpectedOutput, kCodecL16);
268 }
269
270 TEST_F(MixingTest, AnonymousParticipantsAreAlwaysMixed) {
271   const int16_t kInputValue = 1000;
272   const int16_t kExpectedOutput = kInputValue * 4;
273   RunMixingTest(3, 1, 3, false, kInputValue, 1.1 * kExpectedOutput,
274                 0.9 * kExpectedOutput, kCodecL16);
275 }
276
277 TEST_F(MixingTest, VerifyStereoAndMonoMixing) {
278   const int16_t kInputValue = 1000;
279   const int16_t kExpectedOutput = kInputValue * 2;
280   RunMixingTest(2, 0, 1, false, kInputValue, 1.1 * kExpectedOutput,
281                 // Lower than 0.9 due to observed flakiness on bots.
282                 0.8 * kExpectedOutput, kCodecL16);
283 }
284
285 }  // namespace webrtc