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 |