From 2b004655b523ced98aac43b45d31019914e671ce Mon Sep 17 00:00:00 2001 From: "andrew@webrtc.org" Date: Wed, 23 May 2012 02:47:58 +0000 Subject: [PATCH] Add comparison to e2e and clean up. BUG=issue502 TEST=manual Review URL: https://webrtc-codereview.appspot.com/577006 git-svn-id: http://webrtc.googlecode.com/svn/trunk@2274 4adac7df-926f-26a2-2b94-8c16560cd09d --- tools/e2e_quality/audio/audio_e2e_harness.cc | 21 ++++ tools/e2e_quality/audio/run_audio_test.py | 109 ++++++++++--------- 2 files changed, 81 insertions(+), 49 deletions(-) diff --git a/tools/e2e_quality/audio/audio_e2e_harness.cc b/tools/e2e_quality/audio/audio_e2e_harness.cc index 2899b42140..fe63286758 100644 --- a/tools/e2e_quality/audio/audio_e2e_harness.cc +++ b/tools/e2e_quality/audio/audio_e2e_harness.cc @@ -18,7 +18,9 @@ #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" +#include "src/voice_engine/main/interface/voe_hardware.h" +DEFINE_string(render, "render", "render device name"); DEFINE_string(codec, "ISAC", "codec name"); DEFINE_int32(rate, 16000, "codec sample rate in Hz"); @@ -34,6 +36,8 @@ void RunHarness() { ASSERT_TRUE(base != NULL); VoECodec* codec = VoECodec::GetInterface(voe); ASSERT_TRUE(codec != NULL); + VoEHardware* hardware = VoEHardware::GetInterface(voe); + ASSERT_TRUE(hardware != NULL); ASSERT_EQ(0, base->Init()); int channel = base->CreateChannel(); @@ -54,6 +58,23 @@ void RunHarness() { ASSERT_TRUE(codec_found); ASSERT_EQ(0, codec->SetSendCodec(channel, codec_params)); + int num_devices = 0; + ASSERT_EQ(0, hardware->GetNumOfPlayoutDevices(num_devices)); + char device_name[128] = {0}; + char guid[128] = {0}; + bool device_found = false; + int device_index; + for (device_index = 0; device_index < num_devices; device_index++) { + ASSERT_EQ(0, hardware->GetPlayoutDeviceName(device_index, device_name, + guid)); + if (FLAGS_render.compare(device_name) == 0) { + device_found = true; + break; + } + } + ASSERT_TRUE(device_found); + ASSERT_EQ(0, hardware->SetPlayoutDevice(device_index)); + // Disable all audio processing. ASSERT_EQ(0, audio->SetAgcStatus(false)); ASSERT_EQ(0, audio->SetEcStatus(false)); diff --git a/tools/e2e_quality/audio/run_audio_test.py b/tools/e2e_quality/audio/run_audio_test.py index 14895f9aee..b32ac2513a 100755 --- a/tools/e2e_quality/audio/run_audio_test.py +++ b/tools/e2e_quality/audio/run_audio_test.py @@ -1,4 +1,5 @@ #!/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 @@ -7,16 +8,6 @@ # 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 @@ -26,18 +17,14 @@ 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 +import optparse +import os +import re +import shlex +import subprocess +import sys +import threading +import time def main(argv): parser = optparse.OptionParser() @@ -53,42 +40,66 @@ def main(argv): 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', + default=os.path.abspath(os.path.dirname(sys.argv[0]) + + '/../../../out/Debug/audio_e2e_harness'), help='path to audio harness executable') + parser.add_option('--compare', + help='command-line arguments for comparison tool') + parser.add_option('--regexp', + help='regular expression to extract the comparison metric') (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']); + # Set the default capture device to be used by VoiceEngine. We unfortunately + # need to do this rather than select the devices directly through the harness + # because monitor sources don't appear in VoiceEngine except as defaults. + # + # We pass the render device for VoiceEngine to select because (for unknown + # reasons) the virtual device is sometimes not used when the default. + retcode = subprocess.call(['pacmd', 'set-default-source', + options.play_sink + '.monitor'], stdout=subprocess.PIPE); + if retcode != 0: + return retcode - print 'Start an audio call' - print options.harness - voe_proc = subprocess.Popen([options.harness, - '--codec=' + options.codec, '--rate=' + options.rate]); + command = [options.harness, '--render=' + options.rec_sink, + '--codec=' + options.codec, '--rate=' + options.rate] + print ' '.join(command) + voe_proc = subprocess.Popen(command) - 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() + format_args = ['-n', '--format=s16le', '--rate=' + options.rate, + '--channels=' + options.channels, '--raw'] + command = (['pacat', '-p', '-d', options.play_sink] + format_args + + [options.input]) + print ' '.join(command) + play_proc = subprocess.Popen(command) - 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) + # If recording starts before there is data available, pacat sometimes + # inexplicably adds a large delay to the start of the file. We wait here in + # an attempt to prevent that. + time.sleep(0.2) + command = (['pacat', '-r', '-d', options.rec_sink + '.monitor'] + + format_args + [options.output]) + print ' '.join(command) + record_proc = subprocess.Popen(command) - # record_proc will be killed after playout finishes. - record_proc.wait() - - print 'Shutdown audio call' + retcode = play_proc.wait() + # If these ended early, an exception will be thrown here. + record_proc.kill() voe_proc.kill() + if retcode != 0: + return retcode - # TODO(andrew): compare files. + if options.compare and options.regexp: + command = shlex.split(options.compare) + [options.input, options.output] + print ' '.join(command) + compare_proc = subprocess.Popen(command, stdout=subprocess.PIPE) + compare_output = compare_proc.communicate()[0] + if compare_proc.returncode != 0: + return compare_proc.returncode + + # The list should only contain one item. + print ''.join(re.findall(options.regexp, compare_output)) + + return 0 if __name__ == '__main__': sys.exit(main(sys.argv))