| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # | |
| 3 # Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | |
| 4 # | |
| 5 # Use of this source code is governed by a BSD-style license | |
| 6 # that can be found in the LICENSE file in the root of the source | |
| 7 # tree. An additional intellectual property rights grant can be found | |
| 8 # in the file PATENTS. All contributing project authors may | |
| 9 # be found in the AUTHORS file in the root of the source tree. | |
| 10 | |
| 11 """Runs an end-to-end audio quality test on Linux. | |
| 12 | |
| 13 Expects the presence of PulseAudio virtual devices (null sinks). These are | |
| 14 configured as default devices for a VoiceEngine audio call. A PulseAudio | |
| 15 utility (pacat) is used to play to and record from the virtual devices. | |
| 16 | |
| 17 The input reference file is then compared to the output file. | |
| 18 """ | |
| 19 | |
| 20 import optparse | |
| 21 import os | |
| 22 import re | |
| 23 import shlex | |
| 24 import subprocess | |
| 25 import sys | |
| 26 import time | |
| 27 | |
| 28 import perf.perf_utils | |
| 29 | |
| 30 def main(argv): | |
| 31 parser = optparse.OptionParser() | |
| 32 usage = 'Usage: %prog [options]' | |
| 33 parser.set_usage(usage) | |
| 34 parser.add_option('--input', default='input.pcm', help='input PCM file') | |
| 35 parser.add_option('--output', default='output.pcm', help='output PCM file') | |
| 36 parser.add_option('--codec', default='ISAC', help='codec name') | |
| 37 parser.add_option('--rate', default='16000', help='sample rate in Hz') | |
| 38 parser.add_option('--channels', default='1', help='number of channels') | |
| 39 parser.add_option('--play_sink', default='capture', | |
| 40 help='name of PulseAudio sink to which to play audio') | |
| 41 parser.add_option('--rec_sink', default='render', | |
| 42 help='name of PulseAudio sink whose monitor will be recorded') | |
| 43 parser.add_option('--harness', | |
| 44 default=os.path.abspath(os.path.dirname(sys.argv[0]) + | |
| 45 '/../../../out/Debug/audio_e2e_harness'), | |
| 46 help='path to audio harness executable') | |
| 47 parser.add_option('--compare', | |
| 48 help='command-line arguments for comparison tool') | |
| 49 parser.add_option('--regexp', | |
| 50 help='regular expression to extract the comparison metric') | |
| 51 options, _ = parser.parse_args(argv[1:]) | |
| 52 | |
| 53 # Get the initial default capture device, to restore later. | |
| 54 command = ['pacmd', 'list-sources'] | |
| 55 print ' '.join(command) | |
| 56 proc = subprocess.Popen(command, stdout=subprocess.PIPE) | |
| 57 output = proc.communicate()[0] | |
| 58 if proc.returncode != 0: | |
| 59 return proc.returncode | |
| 60 default_source = re.search(r'(^ \* index: )([0-9]+$)', output, | |
| 61 re.MULTILINE).group(2) | |
| 62 | |
| 63 # Set the default capture device to be used by VoiceEngine. We unfortunately | |
| 64 # need to do this rather than select the devices directly through the harness | |
| 65 # because monitor sources don't appear in VoiceEngine except as defaults. | |
| 66 # | |
| 67 # We pass the render device for VoiceEngine to select because (for unknown | |
| 68 # reasons) the virtual device is sometimes not used when the default. | |
| 69 command = ['pacmd', 'set-default-source', options.play_sink + '.monitor'] | |
| 70 print ' '.join(command) | |
| 71 retcode = subprocess.call(command, stdout=subprocess.PIPE) | |
| 72 if retcode != 0: | |
| 73 return retcode | |
| 74 | |
| 75 command = [options.harness, '--render=' + options.rec_sink, | |
| 76 '--codec=' + options.codec, '--rate=' + options.rate] | |
| 77 print ' '.join(command) | |
| 78 voe_proc = subprocess.Popen(command) | |
| 79 | |
| 80 # If recording starts before there is data available, pacat sometimes | |
| 81 # inexplicably adds a large delay to the start of the file. We wait here in | |
| 82 # an attempt to prevent that, because VoE often takes some time to startup a | |
| 83 # call. | |
| 84 time.sleep(5) | |
| 85 | |
| 86 format_args = ['--format=s16le', '--rate=' + options.rate, | |
| 87 '--channels=' + options.channels, '--raw'] | |
| 88 command = (['pacat', '-p', '-d', options.play_sink] + format_args + | |
| 89 [options.input]) | |
| 90 print ' '.join(command) | |
| 91 play_proc = subprocess.Popen(command) | |
| 92 | |
| 93 command = (['pacat', '-r', '-d', options.rec_sink + '.monitor'] + | |
| 94 format_args + [options.output]) | |
| 95 print ' '.join(command) | |
| 96 record_proc = subprocess.Popen(command) | |
| 97 | |
| 98 retcode = play_proc.wait() | |
| 99 # If these ended early, an exception will be thrown here. | |
| 100 record_proc.kill() | |
| 101 voe_proc.kill() | |
| 102 if retcode != 0: | |
| 103 return retcode | |
| 104 | |
| 105 # Restore the initial default capture device. | |
| 106 command = ['pacmd', 'set-default-source', default_source] | |
| 107 print ' '.join(command) | |
| 108 retcode = subprocess.call(command, stdout=subprocess.PIPE) | |
| 109 if retcode != 0: | |
| 110 return retcode | |
| 111 | |
| 112 if options.compare and options.regexp: | |
| 113 command = shlex.split(options.compare) + [options.input, options.output] | |
| 114 print ' '.join(command) | |
| 115 proc = subprocess.Popen(command, stdout=subprocess.PIPE) | |
| 116 output = proc.communicate()[0] | |
| 117 if proc.returncode != 0: | |
| 118 return proc.returncode | |
| 119 | |
| 120 # The list should only contain one item. | |
| 121 value = ''.join(re.findall(options.regexp, output)) | |
| 122 | |
| 123 perf.perf_utils.PrintPerfResult(graph_name='audio_e2e_score', | |
| 124 series_name='e2e_score', | |
| 125 data_point=value, | |
| 126 units='MOS') # Assuming we run PESQ. | |
| 127 | |
| 128 return 0 | |
| 129 | |
| 130 if __name__ == '__main__': | |
| 131 sys.exit(main(sys.argv)) | |
| OLD | NEW |