Add a framework for audio end-to-end quality testing.
The quality comparison step is still to be done. BUG=issue502 TEST=manual Review URL: https://webrtc-codereview.appspot.com/577005 git-svn-id: http://webrtc.googlecode.com/svn/trunk@2220 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
8aaf31d14f
commit
b06db96840
77
tools/e2e_quality/audio/audio_e2e_harness.cc
Normal file
77
tools/e2e_quality/audio/audio_e2e_harness.cc
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Sets up a simple VoiceEngine loopback call with the default audio devices
|
||||
// and runs forever. Some parameters can be configured through command-line
|
||||
// flags.
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "src/voice_engine/main/interface/voe_audio_processing.h"
|
||||
#include "src/voice_engine/main/interface/voe_base.h"
|
||||
#include "src/voice_engine/main/interface/voe_codec.h"
|
||||
|
||||
DEFINE_string(codec, "ISAC", "codec name");
|
||||
DEFINE_int32(rate, 16000, "codec sample rate in Hz");
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
void RunHarness() {
|
||||
VoiceEngine* voe = VoiceEngine::Create();
|
||||
ASSERT_TRUE(voe != NULL);
|
||||
VoEAudioProcessing* audio = VoEAudioProcessing::GetInterface(voe);
|
||||
ASSERT_TRUE(audio != NULL);
|
||||
VoEBase* base = VoEBase::GetInterface(voe);
|
||||
ASSERT_TRUE(base != NULL);
|
||||
VoECodec* codec = VoECodec::GetInterface(voe);
|
||||
ASSERT_TRUE(codec != NULL);
|
||||
|
||||
ASSERT_EQ(0, base->Init());
|
||||
int channel = base->CreateChannel();
|
||||
ASSERT_NE(-1, channel);
|
||||
ASSERT_EQ(0, base->SetSendDestination(channel, 1234, "127.0.0.1"));
|
||||
ASSERT_EQ(0, base->SetLocalReceiver(channel, 1234));
|
||||
|
||||
CodecInst codec_params = {0};
|
||||
bool codec_found = false;
|
||||
for (int i = 0; i < codec->NumOfCodecs(); i++) {
|
||||
ASSERT_EQ(0, codec->GetCodec(i, codec_params));
|
||||
if (FLAGS_codec.compare(codec_params.plname) == 0 &&
|
||||
FLAGS_rate == codec_params.plfreq) {
|
||||
codec_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(codec_found);
|
||||
ASSERT_EQ(0, codec->SetSendCodec(channel, codec_params));
|
||||
|
||||
// Disable all audio processing.
|
||||
ASSERT_EQ(0, audio->SetAgcStatus(false));
|
||||
ASSERT_EQ(0, audio->SetEcStatus(false));
|
||||
ASSERT_EQ(0, audio->EnableHighPassFilter(false));
|
||||
ASSERT_EQ(0, audio->SetNsStatus(false));
|
||||
|
||||
ASSERT_EQ(0, base->StartReceive(channel));
|
||||
ASSERT_EQ(0, base->StartPlayout(channel));
|
||||
ASSERT_EQ(0, base->StartSend(channel));
|
||||
|
||||
// Run forever...
|
||||
while (1);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||
webrtc::RunHarness();
|
||||
}
|
||||
6
tools/e2e_quality/audio/default.pa
Executable file
6
tools/e2e_quality/audio/default.pa
Executable file
@ -0,0 +1,6 @@
|
||||
# Place in ~/.pulse/ to add null sinks for the audio end-to-end quality test.
|
||||
|
||||
.include /etc/pulse/default.pa
|
||||
|
||||
load-module module-null-sink sink_name=render sink_properties=device.description=render format=s16 rate=48000 channels=1
|
||||
load-module module-null-sink sink_name=capture sink_properties=device.description=capture format=s16 rate=48000 channels=1
|
||||
94
tools/e2e_quality/audio/run_audio_test.py
Executable file
94
tools/e2e_quality/audio/run_audio_test.py
Executable file
@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style license
|
||||
# that can be found in the LICENSE file in the root of the source
|
||||
# tree. An additional intellectual property rights grant can be found
|
||||
# in the file PATENTS. All contributing project authors may
|
||||
# be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
__author__ = 'andrew@webrtc.org (Andrew MacDonald)'
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
"""Runs an end-to-end audio quality test on Linux.
|
||||
|
||||
Expects the presence of PulseAudio virtual devices (null sinks). These are
|
||||
configured as default devices for a VoiceEngine audio call. A PulseAudio
|
||||
utility (pacat) is used to play to and record from the virtual devices.
|
||||
|
||||
The input reference file is then compared to the output file.
|
||||
"""
|
||||
|
||||
def popen_and_call(popen_args, call_on_exit):
|
||||
"""Executes the arguments, and triggers the callback when finished."""
|
||||
|
||||
def run_in_thread(popen_args, call_on_exit):
|
||||
proc = subprocess.Popen(popen_args)
|
||||
proc.wait()
|
||||
call_on_exit()
|
||||
return
|
||||
thread = threading.Thread(target=run_in_thread,
|
||||
args=(popen_args, call_on_exit))
|
||||
thread.start()
|
||||
return thread
|
||||
|
||||
def main(argv):
|
||||
parser = optparse.OptionParser()
|
||||
usage = 'Usage: %prog [options]'
|
||||
parser.set_usage(usage)
|
||||
parser.add_option('--input', default='input.pcm', help='input PCM file')
|
||||
parser.add_option('--output', default='output.pcm', help='output PCM file')
|
||||
parser.add_option('--codec', default='ISAC', help='codec name')
|
||||
parser.add_option('--rate', default='16000', help='sample rate in Hz')
|
||||
parser.add_option('--channels', default='1', help='number of channels')
|
||||
parser.add_option('--play_sink', default='capture',
|
||||
help='name of PulseAudio sink to which to play audio')
|
||||
parser.add_option('--rec_sink', default='render',
|
||||
help='name of PulseAudio sink whose monitor will be recorded')
|
||||
parser.add_option('--harness',
|
||||
default=os.path.dirname(sys.argv[0]) +
|
||||
'/../../../out/Debug/audio_e2e_harness',
|
||||
help='path to audio harness executable')
|
||||
(options, args) = parser.parse_args(argv[1:])
|
||||
|
||||
# Set default devices to be used by VoiceEngine.
|
||||
subprocess.call(['pacmd', 'set-default-sink', options.rec_sink]);
|
||||
subprocess.call(['pacmd', 'set-default-source',
|
||||
options.play_sink + '.monitor']);
|
||||
|
||||
print 'Start an audio call'
|
||||
print options.harness
|
||||
voe_proc = subprocess.Popen([options.harness,
|
||||
'--codec=' + options.codec, '--rate=' + options.rate]);
|
||||
|
||||
print 'Start recording to ' + options.output
|
||||
format_args = ('-n --format=s16le --rate=' + options.rate + ' --channels=' +
|
||||
options.channels + ' --raw')
|
||||
command = ('pacat -r -d ' + options.rec_sink + '.monitor ' + format_args +
|
||||
' ' + options.output)
|
||||
record_proc = subprocess.Popen(shlex.split(command))
|
||||
def stop_recording():
|
||||
record_proc.kill()
|
||||
|
||||
print 'Start playing from ' + options.input
|
||||
command = ('pacat -p -d ' + options.play_sink + ' ' + format_args + ' ' +
|
||||
options.input)
|
||||
popen_and_call(shlex.split(command), stop_recording)
|
||||
|
||||
# record_proc will be killed after playout finishes.
|
||||
record_proc.wait()
|
||||
|
||||
print 'Shutdown audio call'
|
||||
voe_proc.kill()
|
||||
|
||||
# TODO(andrew): compare files.
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv))
|
||||
25
tools/e2e_quality/e2e_quality.gyp
Normal file
25
tools/e2e_quality/e2e_quality.gyp
Normal file
@ -0,0 +1,25 @@
|
||||
# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style license
|
||||
# that can be found in the LICENSE file in the root of the source
|
||||
# tree. An additional intellectual property rights grant can be found
|
||||
# in the file PATENTS. All contributing project authors may
|
||||
# be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
{
|
||||
'includes': [ '../../src/build/common.gypi'],
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'audio_e2e_harness',
|
||||
'type': 'executable',
|
||||
'dependencies': [
|
||||
'<(webrtc_root)/voice_engine/voice_engine.gyp:voice_engine_core',
|
||||
'<(DEPTH)/testing/gtest.gyp:gtest',
|
||||
'<(DEPTH)/third_party/google-gflags/google-gflags.gyp:google-gflags',
|
||||
],
|
||||
'sources': [
|
||||
'audio/audio_e2e_harness.cc',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -21,9 +21,8 @@
|
||||
'src/voice_engine/voice_engine.gyp:*',
|
||||
'test/metrics.gyp:*',
|
||||
'test/test.gyp:*',
|
||||
'tools/e2e_quality/e2e_quality.gyp:*',
|
||||
],
|
||||
},
|
||||
],
|
||||
'conditions': [
|
||||
], # conditions
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user