blob: 58243b1c9804402849ceb89738943178e4b9a9be [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
oprypin6d305ba2017-03-30 04:01:30 -070071def _GetFile(file_path, out_dir, android=False, adb_path=None):
72 out_file_name = os.path.basename(file_path)
73 out_file_path = os.path.join(out_dir, out_file_name)
74
75 if android:
76 # Pull the file from the connected Android device
77 adb_command = [adb_path, 'pull', file_path, out_dir]
78 subprocess.check_call(_LogCommand(adb_command))
79 elif os.path.abspath(file_path) != os.path.abspath(out_file_path):
80 shutil.copy(file_path, out_file_path)
81
82 return out_file_path
83
84
kjellander8f8d1a02017-03-06 04:01:16 -080085def main():
86 # pylint: disable=W0101
87 logging.basicConfig(level=logging.INFO)
88
89 args = _ParseArgs()
90
oprypin92220ff2017-03-23 03:40:03 -070091 pesq_path = _DownloadTools()
kjellander8f8d1a02017-03-06 04:01:16 -080092
oprypin6d305ba2017-03-30 04:01:30 -070093 out_dir = os.path.join(args.build_dir, '..')
94 if args.android:
95 test_command = [os.path.join(args.build_dir, 'bin',
96 'run_low_bandwidth_audio_test'), '-v']
97 else:
98 test_command = [os.path.join(args.build_dir, 'low_bandwidth_audio_test')]
oprypin92220ff2017-03-23 03:40:03 -070099
100 # Start the test executable that produces audio files.
oprypin6d305ba2017-03-30 04:01:30 -0700101 test_process = subprocess.Popen(_LogCommand(test_command),
102 stdout=subprocess.PIPE)
oprypin92220ff2017-03-23 03:40:03 -0700103
104 for line in iter(test_process.stdout.readline, ''):
105 # Echo the output to screen.
106 sys.stdout.write(line)
107
108 # Extract specific lines that contain information about produced files.
oprypin6d305ba2017-03-30 04:01:30 -0700109 # Output from Android has a prefix, need to skip it.
110 match = re.search(r'^(?:I\b.+\b)?TEST (\w+) ([^ ]+?) ([^ ]+?)\s*$', line)
oprypin92220ff2017-03-23 03:40:03 -0700111 if not match:
112 continue
oprypin6d305ba2017-03-30 04:01:30 -0700113 test_name = match.group(1)
114 reference_file = _GetFile(match.group(2), out_dir,
115 args.android, args.adb_path)
116 degraded_file = _GetFile(match.group(3), out_dir,
117 args.android, args.adb_path)
oprypin92220ff2017-03-23 03:40:03 -0700118
oprypin6d305ba2017-03-30 04:01:30 -0700119 # Analyze audio.
120 pesq_command = [pesq_path, '+16000',
121 os.path.basename(reference_file),
122 os.path.basename(degraded_file)]
123 # Need to provide paths in the current directory due to a bug in PESQ:
124 # On Mac, for some 'path/to/file.wav', if 'file.wav' is longer than
125 # 'path/to', PESQ crashes.
126 pesq_output = subprocess.check_output(_LogCommand(pesq_command),
127 cwd=out_dir)
oprypin92220ff2017-03-23 03:40:03 -0700128
129 # Find the scores in stdout of pesq.
130 match = re.search(
131 r'Prediction \(Raw MOS, MOS-LQO\):\s+=\s+([\d.]+)\s+([\d.]+)',
132 pesq_output)
133 if match:
134 raw_mos, _ = match.groups()
135
136 # Output a result for the perf dashboard.
137 print 'RESULT pesq_mos: %s= %s score' % (test_name, raw_mos)
138 else:
139 logging.error('PESQ: %s', pesq_output.splitlines()[-1])
140
oprypin6d305ba2017-03-30 04:01:30 -0700141 if args.remove:
142 os.remove(reference_file)
143 os.remove(degraded_file)
144
oprypin92220ff2017-03-23 03:40:03 -0700145 return test_process.wait()
kjellander8f8d1a02017-03-06 04:01:16 -0800146
147
148if __name__ == '__main__':
149 sys.exit(main())