2012-05-10 20:45:11 +02:00
|
|
|
#!/usr/bin/env python
|
2012-05-23 04:47:58 +02:00
|
|
|
#
|
2012-05-10 20:45:11 +02:00
|
|
|
# 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.
|
|
|
|
|
|
|
|
"""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.
|
|
|
|
"""
|
|
|
|
|
2012-05-23 04:47:58 +02:00
|
|
|
import optparse
|
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import shlex
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import time
|
2012-05-10 20:45:11 +02:00
|
|
|
|
2012-12-10 11:08:00 +01:00
|
|
|
import perf.perf_utils
|
|
|
|
|
2012-05-10 20:45:11 +02:00
|
|
|
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',
|
2012-05-23 04:47:58 +02:00
|
|
|
default=os.path.abspath(os.path.dirname(sys.argv[0]) +
|
|
|
|
'/../../../out/Debug/audio_e2e_harness'),
|
2012-05-10 20:45:11 +02:00
|
|
|
help='path to audio harness executable')
|
2012-05-23 04:47:58 +02:00
|
|
|
parser.add_option('--compare',
|
|
|
|
help='command-line arguments for comparison tool')
|
|
|
|
parser.add_option('--regexp',
|
|
|
|
help='regular expression to extract the comparison metric')
|
2013-03-07 10:59:43 +01:00
|
|
|
options, _ = parser.parse_args(argv[1:])
|
2012-05-10 20:45:11 +02:00
|
|
|
|
2012-06-12 05:25:03 +02:00
|
|
|
# Get the initial default capture device, to restore later.
|
|
|
|
command = ['pacmd', 'list-sources']
|
|
|
|
print ' '.join(command)
|
|
|
|
proc = subprocess.Popen(command, stdout=subprocess.PIPE)
|
|
|
|
output = proc.communicate()[0]
|
|
|
|
if proc.returncode != 0:
|
|
|
|
return proc.returncode
|
|
|
|
default_source = re.search(r'(^ \* index: )([0-9]+$)', output,
|
|
|
|
re.MULTILINE).group(2)
|
|
|
|
|
2012-05-23 04:47:58 +02:00
|
|
|
# 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.
|
2012-06-07 06:00:15 +02:00
|
|
|
command = ['pacmd', 'set-default-source', options.play_sink + '.monitor']
|
|
|
|
print ' '.join(command)
|
|
|
|
retcode = subprocess.call(command, stdout=subprocess.PIPE)
|
2012-05-23 04:47:58 +02:00
|
|
|
if retcode != 0:
|
|
|
|
return retcode
|
|
|
|
|
|
|
|
command = [options.harness, '--render=' + options.rec_sink,
|
|
|
|
'--codec=' + options.codec, '--rate=' + options.rate]
|
|
|
|
print ' '.join(command)
|
|
|
|
voe_proc = subprocess.Popen(command)
|
|
|
|
|
2012-06-12 05:25:03 +02:00
|
|
|
# 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, because VoE often takes some time to startup a
|
|
|
|
# call.
|
|
|
|
time.sleep(5)
|
|
|
|
|
2012-06-07 06:00:15 +02:00
|
|
|
format_args = ['--format=s16le', '--rate=' + options.rate,
|
2012-05-23 04:47:58 +02:00
|
|
|
'--channels=' + options.channels, '--raw']
|
|
|
|
command = (['pacat', '-p', '-d', options.play_sink] + format_args +
|
|
|
|
[options.input])
|
|
|
|
print ' '.join(command)
|
|
|
|
play_proc = subprocess.Popen(command)
|
|
|
|
|
|
|
|
command = (['pacat', '-r', '-d', options.rec_sink + '.monitor'] +
|
|
|
|
format_args + [options.output])
|
|
|
|
print ' '.join(command)
|
|
|
|
record_proc = subprocess.Popen(command)
|
|
|
|
|
|
|
|
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
|
2012-05-10 20:45:11 +02:00
|
|
|
|
2012-06-12 05:25:03 +02:00
|
|
|
# Restore the initial default capture device.
|
|
|
|
command = ['pacmd', 'set-default-source', default_source]
|
|
|
|
print ' '.join(command)
|
|
|
|
retcode = subprocess.call(command, stdout=subprocess.PIPE)
|
|
|
|
if retcode != 0:
|
|
|
|
return retcode
|
|
|
|
|
2012-05-23 04:47:58 +02:00
|
|
|
if options.compare and options.regexp:
|
|
|
|
command = shlex.split(options.compare) + [options.input, options.output]
|
|
|
|
print ' '.join(command)
|
2012-06-12 05:25:03 +02:00
|
|
|
proc = subprocess.Popen(command, stdout=subprocess.PIPE)
|
|
|
|
output = proc.communicate()[0]
|
|
|
|
if proc.returncode != 0:
|
|
|
|
return proc.returncode
|
2012-05-10 20:45:11 +02:00
|
|
|
|
2012-05-23 04:47:58 +02:00
|
|
|
# The list should only contain one item.
|
2012-12-10 11:08:00 +01:00
|
|
|
value = ''.join(re.findall(options.regexp, output))
|
|
|
|
|
|
|
|
perf.perf_utils.PrintPerfResult(graph_name='audio_e2e_score',
|
2013-01-07 13:05:53 +01:00
|
|
|
series_name='e2e_score',
|
2012-12-10 11:08:00 +01:00
|
|
|
data_point=value,
|
|
|
|
units='MOS') # Assuming we run PESQ.
|
2012-05-10 20:45:11 +02:00
|
|
|
|
2012-05-23 04:47:58 +02:00
|
|
|
return 0
|
2012-05-10 20:45:11 +02:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
sys.exit(main(sys.argv))
|