blob: 8300b70350d34c76c1d7c8fd9b2d762aa20b1a18 [file] [log] [blame]
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +00001/*
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
13#include <string>
14
pbos@webrtc.org471ae722013-05-21 13:52:32 +000015#include "webrtc/test/testsupport/fileutils.h"
16#include "webrtc/voice_engine/test/auto_test/fixtures/after_initialization_fixture.h"
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000017
18namespace webrtc {
19namespace {
20
21const int16_t kLimiterHeadroom = 29204; // == -1 dbFS
22const int16_t kInt16Max = 0x7fff;
23const int kSampleRateHz = 16000;
24const int kTestDurationMs = 3000;
25const int kSkipOutputMs = 500;
26
27} // namespace
28
29class MixingTest : public AfterInitializationFixture {
30 protected:
31 MixingTest()
32 : input_filename_(test::OutputPath() + "mixing_test_input.pcm"),
33 output_filename_(test::OutputPath() + "mixing_test_output.pcm") {
34 }
pwestin@webrtc.orge4932182013-04-03 15:43:57 +000035 void SetUp() {
36 transport_ = new LoopBackTransport(voe_network_);
37 }
38 void TearDown() {
39 delete transport_;
40 }
pwestin@webrtc.org912b7f72013-03-13 23:20:57 +000041
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +000042 // Creates and mixes |num_remote_streams| which play a file "as microphone"
43 // with |num_local_streams| which play a file "locally", using a constant
44 // amplitude of |input_value|. The local streams manifest as "anonymous"
45 // mixing participants, meaning they will be mixed regardless of the number
46 // of participants. (A stream is a VoiceEngine "channel").
47 //
48 // The mixed output is verified to always fall between |max_output_value| and
49 // |min_output_value|, after a startup phase.
50 //
51 // |num_remote_streams_using_mono| of the remote streams use mono, with the
52 // remainder using stereo.
53 void RunMixingTest(int num_remote_streams,
54 int num_local_streams,
55 int num_remote_streams_using_mono,
56 int16_t input_value,
57 int16_t max_output_value,
58 int16_t min_output_value) {
59 ASSERT_LE(num_remote_streams_using_mono, num_remote_streams);
60
61 GenerateInputFile(input_value);
62
63 std::vector<int> local_streams(num_local_streams);
64 for (size_t i = 0; i < local_streams.size(); ++i) {
65 local_streams[i] = voe_base_->CreateChannel();
66 EXPECT_NE(-1, local_streams[i]);
67 }
68 StartLocalStreams(local_streams);
69 TEST_LOG("Playing %d local streams.\n", num_local_streams);
70
71 std::vector<int> remote_streams(num_remote_streams);
72 for (size_t i = 0; i < remote_streams.size(); ++i) {
73 remote_streams[i] = voe_base_->CreateChannel();
74 EXPECT_NE(-1, remote_streams[i]);
75 }
76 StartRemoteStreams(remote_streams, num_remote_streams_using_mono);
77 TEST_LOG("Playing %d remote streams.\n", num_remote_streams);
78
79 // Start recording the mixed output and wait.
80 EXPECT_EQ(0, voe_file_->StartRecordingPlayout(-1 /* record meeting */,
81 output_filename_.c_str()));
82 Sleep(kTestDurationMs);
83 EXPECT_EQ(0, voe_file_->StopRecordingPlayout(-1));
84
85 StopLocalStreams(local_streams);
86 StopRemoteStreams(remote_streams);
87
88 VerifyMixedOutput(max_output_value, min_output_value);
89 }
90
91 private:
92 // Generate input file with constant values equal to |input_value|. The file
93 // will be one second longer than the duration of the test.
94 void GenerateInputFile(int16_t input_value) {
95 FILE* input_file = fopen(input_filename_.c_str(), "wb");
96 ASSERT_TRUE(input_file != NULL);
97 for (int i = 0; i < kSampleRateHz / 1000 * (kTestDurationMs + 1000); i++) {
98 ASSERT_EQ(1u, fwrite(&input_value, sizeof(input_value), 1, input_file));
99 }
100 ASSERT_EQ(0, fclose(input_file));
101 }
102
103 void VerifyMixedOutput(int16_t max_output_value, int16_t min_output_value) {
104 // Verify the mixed output.
105 FILE* output_file = fopen(output_filename_.c_str(), "rb");
106 ASSERT_TRUE(output_file != NULL);
107 int16_t output_value = 0;
108 // Skip the first segment to avoid initialization and ramping-in effects.
109 EXPECT_EQ(0, fseek(output_file, sizeof(output_value) *
110 kSampleRateHz / 1000 * kSkipOutputMs, SEEK_SET));
111 int samples_read = 0;
112 while (fread(&output_value, sizeof(output_value), 1, output_file) == 1) {
113 samples_read++;
114 std::ostringstream trace_stream;
115 trace_stream << samples_read << " samples read";
116 SCOPED_TRACE(trace_stream.str());
117 EXPECT_LE(output_value, max_output_value);
118 EXPECT_GE(output_value, min_output_value);
119 }
120 // Ensure the recording length is close to the duration of the test.
121 // We have to use a relaxed tolerance here due to filesystem flakiness on
122 // the bots.
123 ASSERT_GE((samples_read * 1000.0) / kSampleRateHz,
124 0.7 * (kTestDurationMs - kSkipOutputMs));
125 // Ensure we read the entire file.
126 ASSERT_NE(0, feof(output_file));
127 ASSERT_EQ(0, fclose(output_file));
128 }
129
130 // Start up local streams ("anonymous" participants).
131 void StartLocalStreams(const std::vector<int>& streams) {
132 for (size_t i = 0; i < streams.size(); ++i) {
133 EXPECT_EQ(0, voe_base_->StartPlayout(streams[i]));
134 EXPECT_EQ(0, voe_file_->StartPlayingFileLocally(streams[i],
135 input_filename_.c_str(), true));
136 }
137 }
138
139 void StopLocalStreams(const std::vector<int>& streams) {
140 for (size_t i = 0; i < streams.size(); ++i) {
141 EXPECT_EQ(0, voe_base_->StopPlayout(streams[i]));
142 EXPECT_EQ(0, voe_base_->DeleteChannel(streams[i]));
143 }
144 }
145
146 // Start up remote streams ("normal" participants).
147 void StartRemoteStreams(const std::vector<int>& streams,
148 int num_remote_streams_using_mono) {
149 // Use L16 at 16kHz to minimize distortion (file recording is 16kHz and
150 // resampling will cause distortion).
151 CodecInst codec_inst;
152 strcpy(codec_inst.plname, "L16");
153 codec_inst.channels = 1;
154 codec_inst.plfreq = kSampleRateHz;
155 codec_inst.pltype = 105;
156 codec_inst.pacsize = codec_inst.plfreq / 100;
157 codec_inst.rate = codec_inst.plfreq * sizeof(int16_t) * 8; // 8 bits/byte.
158
159 for (int i = 0; i < num_remote_streams_using_mono; ++i) {
160 StartRemoteStream(streams[i], codec_inst, 1234 + 2 * i);
161 }
162
163 // The remainder of the streams will use stereo.
164 codec_inst.channels = 2;
165 codec_inst.pltype++;
166 for (size_t i = num_remote_streams_using_mono; i < streams.size(); ++i) {
167 StartRemoteStream(streams[i], codec_inst, 1234 + 2 * i);
168 }
169 }
170
171 // Start up a single remote stream.
172 void StartRemoteStream(int stream, const CodecInst& codec_inst, int port) {
173 EXPECT_EQ(0, voe_codec_->SetRecPayloadType(stream, codec_inst));
pwestin@webrtc.orge4932182013-04-03 15:43:57 +0000174 EXPECT_EQ(0, voe_network_->RegisterExternalTransport(stream, *transport_));
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000175 EXPECT_EQ(0, voe_base_->StartReceive(stream));
176 EXPECT_EQ(0, voe_base_->StartPlayout(stream));
177 EXPECT_EQ(0, voe_codec_->SetSendCodec(stream, codec_inst));
178 EXPECT_EQ(0, voe_base_->StartSend(stream));
179 EXPECT_EQ(0, voe_file_->StartPlayingFileAsMicrophone(stream,
180 input_filename_.c_str(), true));
181 }
182
183 void StopRemoteStreams(const std::vector<int>& streams) {
184 for (size_t i = 0; i < streams.size(); ++i) {
185 EXPECT_EQ(0, voe_base_->StopSend(streams[i]));
186 EXPECT_EQ(0, voe_base_->StopPlayout(streams[i]));
187 EXPECT_EQ(0, voe_base_->StopReceive(streams[i]));
pwestin@webrtc.orge4932182013-04-03 15:43:57 +0000188 EXPECT_EQ(0, voe_network_->DeRegisterExternalTransport(streams[i]));
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000189 EXPECT_EQ(0, voe_base_->DeleteChannel(streams[i]));
190 }
191 }
192
193 const std::string input_filename_;
194 const std::string output_filename_;
pwestin@webrtc.orge4932182013-04-03 15:43:57 +0000195 LoopBackTransport* transport_;
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000196};
197
198// These tests assume a maximum of three mixed participants. We typically allow
phoglund@webrtc.orgc62e7502013-01-03 14:33:00 +0000199// a +/- 10% range around the expected output level to account for distortion
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000200// from coding and processing in the loopback chain.
henrika@webrtc.org84423e92013-04-04 15:19:10 +0000201TEST_F(MixingTest, DISABLED_FourChannelsWithOnlyThreeMixed) {
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000202 const int16_t kInputValue = 1000;
203 const int16_t kExpectedOutput = kInputValue * 3;
phoglund@webrtc.orgc62e7502013-01-03 14:33:00 +0000204 RunMixingTest(4, 0, 4, kInputValue, 1.1 * kExpectedOutput,
205 0.9 * kExpectedOutput);
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000206}
207
208// Ensure the mixing saturation protection is working. We can do this because
209// the mixing limiter is given some headroom, so the expected output is less
210// than full scale.
henrika@webrtc.org84423e92013-04-04 15:19:10 +0000211TEST_F(MixingTest, DISABLED_VerifySaturationProtection) {
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000212 const int16_t kInputValue = 20000;
213 const int16_t kExpectedOutput = kLimiterHeadroom;
214 // If this isn't satisfied, we're not testing anything.
215 ASSERT_GT(kInputValue * 3, kInt16Max);
216 ASSERT_LT(1.1 * kExpectedOutput, kInt16Max);
phoglund@webrtc.orgc62e7502013-01-03 14:33:00 +0000217 RunMixingTest(3, 0, 3, kInputValue, 1.1 * kExpectedOutput,
218 0.9 * kExpectedOutput);
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000219}
220
henrika@webrtc.org84423e92013-04-04 15:19:10 +0000221TEST_F(MixingTest, DISABLED_SaturationProtectionHasNoEffectOnOneChannel) {
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000222 const int16_t kInputValue = kInt16Max;
223 const int16_t kExpectedOutput = kInt16Max;
224 // If this isn't satisfied, we're not testing anything.
225 ASSERT_GT(0.95 * kExpectedOutput, kLimiterHeadroom);
226 // Tighter constraints are required here to properly test this.
227 RunMixingTest(1, 0, 1, kInputValue, kExpectedOutput,
phoglund@webrtc.orgc62e7502013-01-03 14:33:00 +0000228 0.95 * kExpectedOutput);
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000229}
230
henrika@webrtc.org84423e92013-04-04 15:19:10 +0000231TEST_F(MixingTest, DISABLED_VerifyAnonymousAndNormalParticipantMixing) {
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000232 const int16_t kInputValue = 1000;
233 const int16_t kExpectedOutput = kInputValue * 2;
phoglund@webrtc.orgc62e7502013-01-03 14:33:00 +0000234 RunMixingTest(1, 1, 1, kInputValue, 1.1 * kExpectedOutput,
235 0.9 * kExpectedOutput);
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000236}
237
henrika@webrtc.org84423e92013-04-04 15:19:10 +0000238TEST_F(MixingTest, DISABLED_AnonymousParticipantsAreAlwaysMixed) {
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000239 const int16_t kInputValue = 1000;
240 const int16_t kExpectedOutput = kInputValue * 4;
phoglund@webrtc.orgc62e7502013-01-03 14:33:00 +0000241 RunMixingTest(3, 1, 3, kInputValue, 1.1 * kExpectedOutput,
242 0.9 * kExpectedOutput);
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000243}
244
henrika@webrtc.org84423e92013-04-04 15:19:10 +0000245TEST_F(MixingTest, DISABLED_VerifyStereoAndMonoMixing) {
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000246 const int16_t kInputValue = 1000;
247 const int16_t kExpectedOutput = kInputValue * 2;
phoglund@webrtc.orgc62e7502013-01-03 14:33:00 +0000248 RunMixingTest(2, 0, 1, kInputValue, 1.1 * kExpectedOutput,
249 0.9 * kExpectedOutput);
andrew@webrtc.orgb015cbe2012-10-22 18:19:23 +0000250}
251
252} // namespace webrtc