Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(320)

Side by Side Diff: webrtc/audio/test/low_bandwidth_audio_test.py

Issue 2804083003: Add POLQA to low bandwidth audio test (Closed)
Patch Set: Rebase Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. 2 # Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
3 # 3 #
4 # Use of this source code is governed by a BSD-style license 4 # Use of this source code is governed by a BSD-style license
5 # that can be found in the LICENSE file in the root of the source 5 # that can be found in the LICENSE file in the root of the source
6 # tree. An additional intellectual property rights grant can be found 6 # tree. An additional intellectual property rights grant can be found
7 # in the file PATENTS. All contributing project authors may 7 # in the file PATENTS. All contributing project authors may
8 # be found in the AUTHORS file in the root of the source tree. 8 # be found in the AUTHORS file in the root of the source tree.
9 9
10 """ 10 """
11 This script is the wrapper that runs the low-bandwidth audio test. 11 This script is the wrapper that runs the low-bandwidth audio test.
12 12
13 After running the test, post-process steps for calculating audio quality of the 13 After running the test, post-process steps for calculating audio quality of the
14 output files will be performed. 14 output files will be performed.
15 """ 15 """
16 16
17 import argparse 17 import argparse
18 import collections
18 import logging 19 import logging
19 import os 20 import os
20 import re 21 import re
21 import shutil 22 import shutil
22 import subprocess 23 import subprocess
23 import sys 24 import sys
24 25
25 26
26 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 27 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
27 SRC_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, os.pardir, os.pardir, 28 SRC_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, os.pardir, os.pardir,
(...skipping 24 matching lines...) Expand all
52 elif sys.platform == 'darwin': 53 elif sys.platform == 'darwin':
53 return 'mac' 54 return 'mac'
54 elif sys.platform.startswith('linux'): 55 elif sys.platform.startswith('linux'):
55 return 'linux' 56 return 'linux'
56 57
57 58
58 def _DownloadTools(): 59 def _DownloadTools():
59 tools_dir = os.path.join(SRC_DIR, 'tools-webrtc') 60 tools_dir = os.path.join(SRC_DIR, 'tools-webrtc')
60 toolchain_dir = os.path.join(tools_dir, 'audio_quality') 61 toolchain_dir = os.path.join(tools_dir, 'audio_quality')
61 62
62 # Download pesq. 63 # Download PESQ and POLQA.
63 download_script = os.path.join(tools_dir, 'download_tools.py') 64 download_script = os.path.join(tools_dir, 'download_tools.py')
64 command = [sys.executable, download_script, toolchain_dir] 65 command = [sys.executable, download_script, toolchain_dir]
65 subprocess.check_call(_LogCommand(command)) 66 subprocess.check_call(_LogCommand(command))
66 67
67 pesq_path = os.path.join(toolchain_dir, _GetPlatform(), 'pesq') 68 pesq_path = os.path.join(toolchain_dir, _GetPlatform(), 'pesq')
68 return pesq_path 69 polqa_path = os.path.join(toolchain_dir, _GetPlatform(), 'PolqaOem64')
70 return pesq_path, polqa_path
69 71
70 72
71 def ExtractTestRuns(lines, echo=False): 73 def ExtractTestRuns(lines, echo=False):
72 """Extracts information about tests from the output of a test runner. 74 """Extracts information about tests from the output of a test runner.
73 75
74 Produces tuples (android_device, test_name, reference_file, degraded_file). 76 Produces tuples (android_device, test_name, reference_file, degraded_file).
75 """ 77 """
76 for line in lines: 78 for line in lines:
77 if echo: 79 if echo:
78 sys.stdout.write(line) 80 sys.stdout.write(line)
(...skipping 22 matching lines...) Expand all
101 subprocess.check_call(_LogCommand(adb_command)) 103 subprocess.check_call(_LogCommand(adb_command))
102 elif os.path.abspath(file_path) != os.path.abspath(out_file_path): 104 elif os.path.abspath(file_path) != os.path.abspath(out_file_path):
103 if move: 105 if move:
104 shutil.move(file_path, out_file_path) 106 shutil.move(file_path, out_file_path)
105 else: 107 else:
106 shutil.copy(file_path, out_file_path) 108 shutil.copy(file_path, out_file_path)
107 109
108 return out_file_path 110 return out_file_path
109 111
110 112
113 def _RunPesq(executable_path, reference_file, degraded_file,
114 sampling_frequency_hz=16000):
115 directory = os.path.dirname(reference_file)
116 assert os.path.dirname(degraded_file) == directory
117
118 # Analyze audio.
119 command = [executable_path, '+%d' % sampling_frequency_hz,
120 os.path.basename(reference_file),
121 os.path.basename(degraded_file)]
122 # Need to provide paths in the current directory due to a bug in PESQ:
123 # On Mac, for some 'path/to/file.wav', if 'file.wav' is longer than
124 # 'path/to', PESQ crashes.
125 out = subprocess.check_output(_LogCommand(command),
126 cwd=directory, stderr=subprocess.STDOUT)
127
128 # Find the scores in stdout of PESQ.
129 match = re.search(
130 r'Prediction \(Raw MOS, MOS-LQO\):\s+=\s+([\d.]+)\s+([\d.]+)', out)
131 if match:
132 raw_mos, _ = match.groups()
133
134 return {'pesq_mos': (raw_mos, 'score')}
135 else:
136 logging.error('PESQ: %s', out.splitlines()[-1])
137 return {}
138
139
140 def _RunPolqa(executable_path, reference_file, degraded_file):
141 # Analyze audio.
142 command = [executable_path, '-q', '-LC', 'NB',
143 '-Ref', reference_file, '-Test', degraded_file]
144 try:
145 process = subprocess.Popen(_LogCommand(command),
146 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
147 except OSError as e:
148 if e.errno == os.errno.ENOENT:
149 logging.warning('POLQA executable missing, skipping test.')
150 return {}
151 else:
152 raise
153 out, err = process.communicate()
154
155 # Find the scores in stdout of POLQA.
156 match = re.search(r'\bMOS-LQO:\s+([\d.]+)', out)
157
158 if process.returncode != 0 or not match:
159 if process.returncode == 2:
160 logging.warning('%s (2)', err.strip())
161 logging.warning('POLQA license error, skipping test.')
162 else:
163 logging.error('%s (%d)', err.strip(), process.returncode)
164 return {}
165
166 mos_lqo, = match.groups()
167 return {'polqa_mos_lqo': (mos_lqo, 'score')}
168
169
170 Analyzer = collections.namedtuple('Analyzer', ['func', 'executable',
171 'sampling_frequency_hz'])
172
173
111 def main(): 174 def main():
112 # pylint: disable=W0101 175 # pylint: disable=W0101
113 logging.basicConfig(level=logging.INFO) 176 logging.basicConfig(level=logging.INFO)
114 177
115 args = _ParseArgs() 178 args = _ParseArgs()
116 179
117 pesq_path = _DownloadTools() 180 pesq_path, polqa_path = _DownloadTools()
118 181
119 out_dir = os.path.join(args.build_dir, '..') 182 out_dir = os.path.join(args.build_dir, '..')
120 if args.android: 183 if args.android:
121 test_command = [os.path.join(args.build_dir, 'bin', 184 test_command = [os.path.join(args.build_dir, 'bin',
122 'run_low_bandwidth_audio_test'), '-v'] 185 'run_low_bandwidth_audio_test'), '-v']
123 else: 186 else:
124 test_command = [os.path.join(args.build_dir, 'low_bandwidth_audio_test')] 187 test_command = [os.path.join(args.build_dir, 'low_bandwidth_audio_test')]
125 188
126 # Start the test executable that produces audio files. 189 analyzers = [Analyzer(_RunPesq, pesq_path, 16000)]
127 test_process = subprocess.Popen(_LogCommand(test_command), 190 # Check if POLQA can run at all, or skip the 48 kHz tests entirely.
128 stdout=subprocess.PIPE) 191 example_path = os.path.join(SRC_DIR, 'resources',
192 'voice_engine', 'audio_tiny48.wav')
193 if _RunPolqa(polqa_path, example_path, example_path):
194 analyzers.append(Analyzer(_RunPolqa, polqa_path, 48000))
129 195
130 try: 196 for analyzer in analyzers:
131 lines = iter(test_process.stdout.readline, '') 197 # Start the test executable that produces audio files.
132 for result in ExtractTestRuns(lines, echo=True): 198 test_process = subprocess.Popen(
133 (android_device, test_name, reference_file, degraded_file) = result 199 _LogCommand(test_command + ['--sampling_frequency=%d' %
200 analyzer.sampling_frequency_hz]),
201 stdout=subprocess.PIPE)
202 try:
203 lines = iter(test_process.stdout.readline, '')
204 for result in ExtractTestRuns(lines, echo=True):
205 (android_device, test_name, reference_file, degraded_file) = result
134 206
135 adb_prefix = (args.adb_path,) 207 adb_prefix = (args.adb_path,)
136 if android_device: 208 if android_device:
137 adb_prefix += ('-s', android_device) 209 adb_prefix += ('-s', android_device)
138 210
139 reference_file = _GetFile(reference_file, out_dir, 211 reference_file = _GetFile(reference_file, out_dir,
140 android=args.android, adb_prefix=adb_prefix) 212 android=args.android, adb_prefix=adb_prefix)
141 degraded_file = _GetFile(degraded_file, out_dir, move=True, 213 degraded_file = _GetFile(degraded_file, out_dir, move=True,
142 android=args.android, adb_prefix=adb_prefix) 214 android=args.android, adb_prefix=adb_prefix)
143 215
144 # Analyze audio. 216 analyzer_results = analyzer.func(analyzer.executable,
145 pesq_command = [pesq_path, '+16000', 217 reference_file, degraded_file)
146 os.path.basename(reference_file), 218 for metric, (value, units) in analyzer_results.items():
147 os.path.basename(degraded_file)] 219 # Output a result for the perf dashboard.
148 # Need to provide paths in the current directory due to a bug in PESQ: 220 print 'RESULT %s: %s= %s %s' % (metric, test_name, value, units)
149 # On Mac, for some 'path/to/file.wav', if 'file.wav' is longer than
150 # 'path/to', PESQ crashes.
151 pesq_output = subprocess.check_output(_LogCommand(pesq_command),
152 cwd=out_dir)
153 221
154 # Find the scores in stdout of pesq. 222 if args.remove:
155 match = re.search( 223 os.remove(reference_file)
156 r'Prediction \(Raw MOS, MOS-LQO\):\s+=\s+([\d.]+)\s+([\d.]+)', 224 os.remove(degraded_file)
157 pesq_output) 225 finally:
158 if match: 226 test_process.terminate()
159 raw_mos, _ = match.groups()
160
161 # Output a result for the perf dashboard.
162 print 'RESULT pesq_mos: %s= %s score' % (test_name, raw_mos)
163 else:
164 logging.error('PESQ: %s', pesq_output.splitlines()[-1])
165
166 if args.remove:
167 os.remove(reference_file)
168 os.remove(degraded_file)
169 finally:
170 test_process.terminate()
171 227
172 return test_process.wait() 228 return test_process.wait()
173 229
174 230
175 if __name__ == '__main__': 231 if __name__ == '__main__':
176 sys.exit(main()) 232 sys.exit(main())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698