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