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 // Refer to kUsage below for a description.
12
13 #include "gflags/gflags.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "webrtc/base/scoped_ptr.h"
16 #include "webrtc/system_wrappers/interface/sleep.h"
17 #include "webrtc/system_wrappers/interface/trace.h"
18 #include "webrtc/test/channel_transport/include/channel_transport.h"
19 #include "webrtc/test/testsupport/trace_to_stderr.h"
20 #include "webrtc/tools/agc/agc_manager.h"
21 #include "webrtc/voice_engine/include/voe_audio_processing.h"
22 #include "webrtc/voice_engine/include/voe_base.h"
23 #include "webrtc/voice_engine/include/voe_codec.h"
24 #include "webrtc/voice_engine/include/voe_external_media.h"
25 #include "webrtc/voice_engine/include/voe_file.h"
26 #include "webrtc/voice_engine/include/voe_hardware.h"
27 #include "webrtc/voice_engine/include/voe_network.h"
28 #include "webrtc/voice_engine/include/voe_volume_control.h"
29
30 DEFINE_bool(codecs, false, "print out available codecs");
31 DEFINE_int32(pt, 103, "codec payload type (defaults to ISAC/16000/1)");
32 DEFINE_bool(internal, true, "use the internal AGC in 'serial' mode, or as the "
33 "first voice engine's AGC in parallel mode");
34 DEFINE_bool(parallel, false, "run internal and public AGCs in parallel, with "
35 "left- and right-panning respectively. Not compatible with -aec.");
36 DEFINE_bool(devices, false, "print out capture devices and indexes to be used "
37 "with the capture flags");
38 DEFINE_int32(capture1, 0, "capture device index for the first voice engine");
39 DEFINE_int32(capture2, 0, "capture device index for second voice engine");
40 DEFINE_int32(render1, 0, "render device index for first voice engine");
41 DEFINE_int32(render2, 0, "render device index for second voice engine");
42 DEFINE_bool(aec, false, "runs two voice engines in parallel, with the first "
43 "playing out a file and sending its captured signal to the second voice "
44 "engine. Also enables echo cancellation.");
45 DEFINE_bool(ns, true, "enable noise suppression");
46 DEFINE_bool(highpass, true, "enable high pass filter");
47 DEFINE_string(filename, "", "filename for the -aec mode");
48
49 namespace webrtc {
50 namespace {
51
52 const char kUsage[] =
53 "\nWithout additional flags, sets up a simple VoiceEngine loopback call\n"
54 "with the default audio devices and runs forever. The internal AGC is\n"
55 "enabled and the public disabled.\n\n"
56
57 "It can also run the public AGC in parallel with the internal, panned to\n"
58 "opposite stereo channels on the default render device. The capture\n"
59 "devices for each can be selected (recommended, because otherwise they\n"
60 "will fight for the level on the same device).\n\n"
61
62 "Lastly, it can be used for local AEC testing. In this mode, the first\n"
63 "voice engine plays out a file over the selected render device (normally\n"
64 "loudspeakers) and records from the selected capture device. The second\n"
65 "voice engine receives the capture signal and plays it out over the\n"
66 "selected render device (normally headphones). This allows the user to\n"
67 "test an echo scenario with the first voice engine, while monitoring the\n"
68 "result with the second.";
69
70 class AgcVoiceEngine {
71 public:
72 enum Pan {
73 NoPan,
74 PanLeft,
75 PanRight
76 };
77
AgcVoiceEngine(bool internal,int tx_port,int rx_port,int capture_idx,int render_idx)78 AgcVoiceEngine(bool internal, int tx_port, int rx_port, int capture_idx,
79 int render_idx)
80 : voe_(VoiceEngine::Create()),
81 base_(VoEBase::GetInterface(voe_)),
82 hardware_(VoEHardware::GetInterface(voe_)),
83 codec_(VoECodec::GetInterface(voe_)),
84 manager_(new AgcManager(voe_)),
85 channel_(-1),
86 capture_idx_(capture_idx),
87 render_idx_(render_idx) {
88 SetUp(internal, tx_port, rx_port);
89 }
90
~AgcVoiceEngine()91 ~AgcVoiceEngine() {
92 TearDown();
93 }
94
SetUp(bool internal,int tx_port,int rx_port)95 void SetUp(bool internal, int tx_port, int rx_port) {
96 ASSERT_TRUE(voe_ != NULL);
97 ASSERT_TRUE(base_ != NULL);
98 ASSERT_TRUE(hardware_ != NULL);
99 ASSERT_TRUE(codec_ != NULL);
100 VoEAudioProcessing* audio = VoEAudioProcessing::GetInterface(voe_);
101 ASSERT_TRUE(audio != NULL);
102 VoENetwork* network = VoENetwork::GetInterface(voe_);
103 ASSERT_TRUE(network != NULL);
104
105 ASSERT_EQ(0, base_->Init());
106 channel_ = base_->CreateChannel();
107 ASSERT_NE(-1, channel_);
108
109 channel_transport_.reset(
110 new test::VoiceChannelTransport(network, channel_));
111 ASSERT_EQ(0, channel_transport_->SetSendDestination("127.0.0.1", tx_port));
112 ASSERT_EQ(0, channel_transport_->SetLocalReceiver(rx_port));
113
114 ASSERT_EQ(0, hardware_->SetRecordingDevice(capture_idx_));
115 ASSERT_EQ(0, hardware_->SetPlayoutDevice(render_idx_));
116
117 CodecInst codec_params = {0};
118 bool codec_found = false;
119 for (int i = 0; i < codec_->NumOfCodecs(); i++) {
120 ASSERT_EQ(0, codec_->GetCodec(i, codec_params));
121 if (FLAGS_pt == codec_params.pltype) {
122 codec_found = true;
123 break;
124 }
125 }
126 ASSERT_TRUE(codec_found);
127 ASSERT_EQ(0, codec_->SetSendCodec(channel_, codec_params));
128
129 ASSERT_EQ(0, audio->EnableHighPassFilter(FLAGS_highpass));
130 ASSERT_EQ(0, audio->SetNsStatus(FLAGS_ns));
131 ASSERT_EQ(0, audio->SetEcStatus(FLAGS_aec));
132
133 ASSERT_EQ(0, manager_->Enable(internal));
134 ASSERT_EQ(0, audio->SetAgcStatus(!internal));
135
136 audio->Release();
137 network->Release();
138 }
139
TearDown()140 void TearDown() {
141 Stop();
142 channel_transport_.reset(NULL);
143 ASSERT_EQ(0, base_->DeleteChannel(channel_));
144 ASSERT_EQ(0, base_->Terminate());
145 // Don't test; the manager hasn't released its interfaces.
146 hardware_->Release();
147 base_->Release();
148 codec_->Release();
149 delete manager_;
150 ASSERT_TRUE(VoiceEngine::Delete(voe_));
151 }
152
PrintDevices()153 void PrintDevices() {
154 int num_devices = 0;
155 char device_name[128] = {0};
156 char guid[128] = {0};
157 ASSERT_EQ(0, hardware_->GetNumOfRecordingDevices(num_devices));
158 printf("Capture devices:\n");
159 for (int i = 0; i < num_devices; i++) {
160 ASSERT_EQ(0, hardware_->GetRecordingDeviceName(i, device_name, guid));
161 printf("%d: %s\n", i, device_name);
162 }
163 ASSERT_EQ(0, hardware_->GetNumOfPlayoutDevices(num_devices));
164 printf("Render devices:\n");
165 for (int i = 0; i < num_devices; i++) {
166 ASSERT_EQ(0, hardware_->GetPlayoutDeviceName(i, device_name, guid));
167 printf("%d: %s\n", i, device_name);
168 }
169 }
170
PrintCodecs()171 void PrintCodecs() {
172 CodecInst params = {0};
173 printf("Codecs:\n");
174 for (int i = 0; i < codec_->NumOfCodecs(); i++) {
175 ASSERT_EQ(0, codec_->GetCodec(i, params));
176 printf("%d %s/%d/%d\n", params.pltype, params.plname, params.plfreq,
177 params.channels);
178 }
179 }
180
StartSending()181 void StartSending() {
182 ASSERT_EQ(0, base_->StartSend(channel_));
183 }
184
StartPlaying(Pan pan,const std::string & filename)185 void StartPlaying(Pan pan, const std::string& filename) {
186 VoEVolumeControl* volume = VoEVolumeControl::GetInterface(voe_);
187 VoEFile* file = VoEFile::GetInterface(voe_);
188 ASSERT_TRUE(volume != NULL);
189 ASSERT_TRUE(file != NULL);
190 if (pan == PanLeft) {
191 volume->SetOutputVolumePan(channel_, 1, 0);
192 } else if (pan == PanRight) {
193 volume->SetOutputVolumePan(channel_, 0, 1);
194 }
195 if (filename != "") {
196 printf("playing file\n");
197 ASSERT_EQ(0, file->StartPlayingFileLocally(channel_, filename.c_str(),
198 true, kFileFormatPcm16kHzFile, 1.0, 0, 0));
199 }
200 ASSERT_EQ(0, base_->StartReceive(channel_));
201 ASSERT_EQ(0, base_->StartPlayout(channel_));
202 volume->Release();
203 file->Release();
204 }
205
Stop()206 void Stop() {
207 ASSERT_EQ(0, base_->StopSend(channel_));
208 ASSERT_EQ(0, base_->StopPlayout(channel_));
209 }
210
211 private:
212 VoiceEngine* voe_;
213 VoEBase* base_;
214 VoEHardware* hardware_;
215 VoECodec* codec_;
216 AgcManager* manager_;
217 int channel_;
218 int capture_idx_;
219 int render_idx_;
220 rtc::scoped_ptr<test::VoiceChannelTransport> channel_transport_;
221 };
222
RunHarness()223 void RunHarness() {
224 rtc::scoped_ptr<AgcVoiceEngine> voe1(new AgcVoiceEngine(
225 FLAGS_internal, 2000, 2000, FLAGS_capture1, FLAGS_render1));
226 rtc::scoped_ptr<AgcVoiceEngine> voe2;
227 if (FLAGS_parallel) {
228 voe2.reset(new AgcVoiceEngine(!FLAGS_internal, 3000, 3000, FLAGS_capture2,
229 FLAGS_render2));
230 voe1->StartPlaying(AgcVoiceEngine::PanLeft, "");
231 voe1->StartSending();
232 voe2->StartPlaying(AgcVoiceEngine::PanRight, "");
233 voe2->StartSending();
234 } else if (FLAGS_aec) {
235 voe1.reset(new AgcVoiceEngine(FLAGS_internal, 2000, 4242, FLAGS_capture1,
236 FLAGS_render1));
237 voe2.reset(new AgcVoiceEngine(!FLAGS_internal, 4242, 2000, FLAGS_capture2,
238 FLAGS_render2));
239 voe1->StartPlaying(AgcVoiceEngine::NoPan, FLAGS_filename);
240 voe1->StartSending();
241 voe2->StartPlaying(AgcVoiceEngine::NoPan, "");
242 } else {
243 voe1->StartPlaying(AgcVoiceEngine::NoPan, "");
244 voe1->StartSending();
245 }
246
247 // Run forever...
248 SleepMs(0x7fffffff);
249 }
250
PrintDevices()251 void PrintDevices() {
252 AgcVoiceEngine device_voe(false, 4242, 4242, 0, 0);
253 device_voe.PrintDevices();
254 }
255
PrintCodecs()256 void PrintCodecs() {
257 AgcVoiceEngine codec_voe(false, 4242, 4242, 0, 0);
258 codec_voe.PrintCodecs();
259 }
260
261 } // namespace
262 } // namespace webrtc
263
main(int argc,char ** argv)264 int main(int argc, char** argv) {
265 google::SetUsageMessage(webrtc::kUsage);
266 google::ParseCommandLineFlags(&argc, &argv, true);
267 webrtc::test::TraceToStderr trace_to_stderr;
268
269 if (FLAGS_parallel && FLAGS_aec) {
270 printf("-parallel and -aec are not compatible\n");
271 return 1;
272 }
273 if (FLAGS_devices) {
274 webrtc::PrintDevices();
275 }
276 if (FLAGS_codecs) {
277 webrtc::PrintCodecs();
278 }
279 if (!FLAGS_devices && !FLAGS_codecs) {
280 webrtc::RunHarness();
281 }
282 return 0;
283 }
284