blob: 8f4068199be7342e521eeb2e27f5bbe1b4083610 [file] [log] [blame]
kjellander8f8d1a02017-03-06 04:01:16 -08001#!/usr/bin/env python
2# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
3#
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
6# tree. An additional intellectual property rights grant can be found
7# in the file PATENTS. All contributing project authors may
8# be found in the AUTHORS file in the root of the source tree.
9
10"""
11This script is the wrapper that runs the low-bandwidth audio test.
12
13After running the test, post-process steps for calculating audio quality of the
14output files will be performed.
15"""
16
17import argparse
18import logging
19import os
oprypin92220ff2017-03-23 03:40:03 -070020import re
oprypin6d305ba2017-03-30 04:01:30 -070021import shutil
kjellander8f8d1a02017-03-06 04:01:16 -080022import subprocess
23import sys
24
25
26SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
27SRC_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, os.pardir, os.pardir,
28 os.pardir))
29
30
oprypin92220ff2017-03-23 03:40:03 -070031def _LogCommand(command):
32 logging.info('Running %r', command)
33 return command
kjellander8f8d1a02017-03-06 04:01:16 -080034
35
36def _ParseArgs():
37 parser = argparse.ArgumentParser(description='Run low-bandwidth audio tests.')
38 parser.add_argument('build_dir',
39 help='Path to the build directory (e.g. out/Release).')
oprypin92220ff2017-03-23 03:40:03 -070040 parser.add_argument('--remove', action='store_true',
41 help='Remove output audio files after testing.')
oprypin6d305ba2017-03-30 04:01:30 -070042 parser.add_argument('--android', action='store_true',
43 help='Perform the test on a connected Android device instead.')
44 parser.add_argument('--adb-path', help='Path to adb binary.', default='adb')
kjellander8f8d1a02017-03-06 04:01:16 -080045 args = parser.parse_args()
46 return args
47
48
oprypin92220ff2017-03-23 03:40:03 -070049def _GetPlatform():
50 if sys.platform == 'win32':
51 return 'win'
52 elif sys.platform == 'darwin':
53 return 'mac'
54 elif sys.platform.startswith('linux'):
55 return 'linux'
56
57
oprypin92220ff2017-03-23 03:40:03 -070058def _DownloadTools():
59 tools_dir = os.path.join(SRC_DIR, 'tools-webrtc')
60 toolchain_dir = os.path.join(tools_dir, 'audio_quality')
61
62 # Download pesq.
63 download_script = os.path.join(tools_dir, 'download_tools.py')
64 command = [sys.executable, download_script, toolchain_dir]
65 subprocess.check_call(_LogCommand(command))
66
oprypin6d305ba2017-03-30 04:01:30 -070067 pesq_path = os.path.join(toolchain_dir, _GetPlatform(), 'pesq')
oprypin92220ff2017-03-23 03:40:03 -070068 return pesq_path
69
70
oprypinabd101b2017-04-06 23:21:30 -070071def ExtractTestRuns(lines, echo=False):
72 """Extracts information about tests from the output of a test runner.
73
74 Produces tuples (android_device, test_name, reference_file, degraded_file).
75 """
76 for line in lines:
77 if echo:
78 sys.stdout.write(line)
79
80 # Output from Android has a prefix with the device name.
81 android_prefix_re = r'(?:I\b.+\brun_tests_on_device\((.+?)\)\s*)?'
82 test_re = r'^' + android_prefix_re + r'TEST (\w+) ([^ ]+?) ([^ ]+?)\s*$'
83
84 match = re.search(test_re, line)
85 if match:
86 yield match.groups()
87
88
89def _GetFile(file_path, out_dir, move=False,
90 android=False, adb_prefix=('adb',)):
oprypin6d305ba2017-03-30 04:01:30 -070091 out_file_name = os.path.basename(file_path)
92 out_file_path = os.path.join(out_dir, out_file_name)
93
94 if android:
oprypinabd101b2017-04-06 23:21:30 -070095 # Pull the file from the connected Android device.
96 adb_command = adb_prefix + ('pull', file_path, out_dir)
oprypin6d305ba2017-03-30 04:01:30 -070097 subprocess.check_call(_LogCommand(adb_command))
oprypinabd101b2017-04-06 23:21:30 -070098 if move:
99 # Remove that file.
100 adb_command = adb_prefix + ('shell', 'rm', file_path)
101 subprocess.check_call(_LogCommand(adb_command))
oprypin6d305ba2017-03-30 04:01:30 -0700102 elif os.path.abspath(file_path) != os.path.abspath(out_file_path):
oprypinabd101b2017-04-06 23:21:30 -0700103 if move:
104 shutil.move(file_path, out_file_path)
105 else:
106 shutil.copy(file_path, out_file_path)
oprypin6d305ba2017-03-30 04:01:30 -0700107
108 return out_file_path
109
110
kjellander8f8d1a02017-03-06 04:01:16 -0800111def main():
112 # pylint: disable=W0101
113 logging.basicConfig(level=logging.INFO)
114
115 args = _ParseArgs()
116
oprypin92220ff2017-03-23 03:40:03 -0700117 pesq_path = _DownloadTools()
kjellander8f8d1a02017-03-06 04:01:16 -0800118
oprypin6d305ba2017-03-30 04:01:30 -0700119 out_dir = os.path.join(args.build_dir, '..')
120 if args.android:
121 test_command = [os.path.join(args.build_dir, 'bin',
122 'run_low_bandwidth_audio_test'), '-v']
123 else:
124 test_command = [os.path.join(args.build_dir, 'low_bandwidth_audio_test')]
oprypin92220ff2017-03-23 03:40:03 -0700125
126 # Start the test executable that produces audio files.
oprypin6d305ba2017-03-30 04:01:30 -0700127 test_process = subprocess.Popen(_LogCommand(test_command),
128 stdout=subprocess.PIPE)
oprypin92220ff2017-03-23 03:40:03 -0700129
oprypinabd101b2017-04-06 23:21:30 -0700130 try:
131 lines = iter(test_process.stdout.readline, '')
132 for result in ExtractTestRuns(lines, echo=True):
133 (android_device, test_name, reference_file, degraded_file) = result
oprypin92220ff2017-03-23 03:40:03 -0700134
oprypinabd101b2017-04-06 23:21:30 -0700135 adb_prefix = (args.adb_path,)
136 if android_device:
137 adb_prefix += ('-s', android_device)
oprypin92220ff2017-03-23 03:40:03 -0700138
oprypinabd101b2017-04-06 23:21:30 -0700139 reference_file = _GetFile(reference_file, out_dir,
140 android=args.android, adb_prefix=adb_prefix)
141 degraded_file = _GetFile(degraded_file, out_dir, move=True,
142 android=args.android, adb_prefix=adb_prefix)
oprypin92220ff2017-03-23 03:40:03 -0700143
oprypinabd101b2017-04-06 23:21:30 -0700144 # Analyze audio.
145 pesq_command = [pesq_path, '+16000',
146 os.path.basename(reference_file),
147 os.path.basename(degraded_file)]
148 # Need to provide paths in the current directory due to a bug in PESQ:
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)
oprypin92220ff2017-03-23 03:40:03 -0700153
oprypinabd101b2017-04-06 23:21:30 -0700154 # Find the scores in stdout of pesq.
155 match = re.search(
156 r'Prediction \(Raw MOS, MOS-LQO\):\s+=\s+([\d.]+)\s+([\d.]+)',
157 pesq_output)
158 if match:
159 raw_mos, _ = match.groups()
oprypin92220ff2017-03-23 03:40:03 -0700160
oprypinabd101b2017-04-06 23:21:30 -0700161 # 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()
oprypin6d305ba2017-03-30 04:01:30 -0700171
oprypin92220ff2017-03-23 03:40:03 -0700172 return test_process.wait()
kjellander8f8d1a02017-03-06 04:01:16 -0800173
174
175if __name__ == '__main__':
176 sys.exit(main())