blob: a74acf177a08d2077e35091b7bcc8e4cf700d22b [file] [log] [blame]
mandermoed582f72017-01-23 07:55:42 -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 starts a loopback call with stubbed video in
12and out. It then analyses the video quality of the output video against the
13reference input video.
14
15It expect to be given the webrtc output build directory as the first argument
16all other arguments are optional.
17
18It assumes you have a Android device plugged in.
19"""
20
21import argparse
oprypinbed7a6b2017-06-19 01:16:45 -070022import json
mandermoed582f72017-01-23 07:55:42 -080023import logging
24import os
mandermoed582f72017-01-23 07:55:42 -080025import subprocess
26import sys
27import tempfile
oprypin30cda5e2017-04-24 04:15:13 -070028import time
mandermoed582f72017-01-23 07:55:42 -080029
30
31SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
Henrik Kjellandercb3b1c12017-09-18 06:20:33 +020032SRC_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, os.pardir, os.pardir))
Edward Lemur70ba9b42018-01-22 13:46:58 +010033RTC_TOOLS_DIR = os.path.join(SRC_DIR, 'rtc_tools', 'testing')
34TOOLCHAIN_DIR = os.path.join(SRC_DIR, 'tools_webrtc', 'video_quality_toolchain',
35 'linux')
oprypinbed7a6b2017-06-19 01:16:45 -070036BAD_DEVICES_JSON = os.path.join(SRC_DIR,
37 os.environ.get('CHROMIUM_OUT_DIR', 'out'),
38 'bad_devices.json')
Edward Lemur70ba9b42018-01-22 13:46:58 +010039
40sys.path.append(RTC_TOOLS_DIR)
41import utils
oprypin30cda5e2017-04-24 04:15:13 -070042
43
44class Error(Exception):
45 pass
46
47
48class VideoQualityTestError(Error):
49 pass
mandermoed582f72017-01-23 07:55:42 -080050
51
Edward Lemurd5e17d62018-01-31 17:28:28 +010052def _RunCommand(argv, **kwargs):
mandermoed582f72017-01-23 07:55:42 -080053 logging.info('Running %r', argv)
Edward Lemurd5e17d62018-01-31 17:28:28 +010054 subprocess.check_call(argv, **kwargs)
mandermoed582f72017-01-23 07:55:42 -080055
56
Edward Lemurd5e17d62018-01-31 17:28:28 +010057def _RunCommandWithOutput(argv, **kwargs):
oprypin30cda5e2017-04-24 04:15:13 -070058 logging.info('Running %r', argv)
Edward Lemurd5e17d62018-01-31 17:28:28 +010059 return subprocess.check_output(argv, **kwargs)
oprypin30cda5e2017-04-24 04:15:13 -070060
61
Edward Lemurd5e17d62018-01-31 17:28:28 +010062def _RunBackgroundCommand(argv):
oprypin30cda5e2017-04-24 04:15:13 -070063 logging.info('Running %r', argv)
Edward Lemurd5e17d62018-01-31 17:28:28 +010064 process = subprocess.Popen(argv)
oprypin30cda5e2017-04-24 04:15:13 -070065 time.sleep(0.5)
66 status = process.poll()
67 if status: # is not None or 0
68 raise subprocess.CalledProcessError(status, argv)
69 return process
70
71
Edward Lemur70ba9b42018-01-22 13:46:58 +010072def CreateEmptyDir(suggested_dir):
73 if not suggested_dir:
74 return tempfile.mkdtemp()
75 utils.RemoveDirectory(suggested_dir)
76 os.makedirs(suggested_dir)
77 return suggested_dir
78
79
mandermoed582f72017-01-23 07:55:42 -080080def _ParseArgs():
81 parser = argparse.ArgumentParser(description='Start loopback video analysis.')
mandermoed582f72017-01-23 07:55:42 -080082 parser.add_argument('build_dir_android',
83 help='The path to the build directory for Android.')
mandermoed582f72017-01-23 07:55:42 -080084 parser.add_argument('--temp_dir',
85 help='A temporary directory to put the output.')
oprypin30cda5e2017-04-24 04:15:13 -070086 parser.add_argument('--adb-path', help='Path to adb binary.', default='adb')
Edward Lesmes9599fd42018-03-12 16:43:05 -040087 parser.add_argument('--num-retries', default='0',
Edward Lesmes5b9c6842018-03-09 13:07:22 -050088 help='Number of times to retry the test on Android.')
Edward Lemur71d766e2018-02-05 21:59:36 +010089 parser.add_argument('--isolated-script-test-perf-output',
Edward Lemur2e5966b2018-01-30 15:33:02 +010090 help='Where to store perf results in chartjson format.', default=None)
mandermoed582f72017-01-23 07:55:42 -080091
Edward Lemur21a35bc2018-01-31 13:46:50 +010092 args, unknown_args = parser.parse_known_args()
93
94 # Ignore Chromium-specific flags
95 parser = argparse.ArgumentParser()
96 parser.add_argument('--isolated-script-test-output',
97 type=str, default=None)
Edward Lemur21a35bc2018-01-31 13:46:50 +010098 parser.add_argument('--test-launcher-summary-output',
99 type=str, default=None)
100
101 parser.parse_args(unknown_args)
102
mandermoed582f72017-01-23 07:55:42 -0800103 return args
104
105
Edward Lemur70ba9b42018-01-22 13:46:58 +0100106def SelectAndroidDevice(adb_path):
107 # Select an Android device in case multiple are connected.
oprypinbed7a6b2017-06-19 01:16:45 -0700108 try:
109 with open(BAD_DEVICES_JSON) as bad_devices_file:
110 bad_devices = json.load(bad_devices_file)
111 except IOError:
112 if os.environ.get('CHROME_HEADLESS'):
113 logging.warning('Cannot read %r', BAD_DEVICES_JSON)
114 bad_devices = {}
115
oprypin30cda5e2017-04-24 04:15:13 -0700116 for line in _RunCommandWithOutput([adb_path, 'devices']).splitlines():
117 if line.endswith('\tdevice'):
118 android_device = line.split('\t')[0]
oprypinbed7a6b2017-06-19 01:16:45 -0700119 if android_device not in bad_devices:
Edward Lemur70ba9b42018-01-22 13:46:58 +0100120 return android_device
121 raise VideoQualityTestError('Cannot find any connected Android device.')
122
123
124def SetUpTools(android_device, temp_dir, processes):
125 # Extract AppRTC.
126 apprtc_archive = os.path.join(RTC_TOOLS_DIR, 'prebuilt_apprtc.zip')
127 golang_archive = os.path.join(RTC_TOOLS_DIR, 'golang', 'linux', 'go.tar.gz')
128
129 utils.UnpackArchiveTo(apprtc_archive, temp_dir)
130 utils.UnpackArchiveTo(golang_archive, temp_dir)
131
132 # Build AppRTC.
133 build_apprtc_script = os.path.join(RTC_TOOLS_DIR, 'build_apprtc.py')
Oleh Prypin1e908452018-04-04 14:31:58 +0200134 apprtc_dir = os.path.join(temp_dir, 'apprtc')
Edward Lemur70ba9b42018-01-22 13:46:58 +0100135 go_dir = os.path.join(temp_dir, 'go')
136 collider_dir = os.path.join(temp_dir, 'collider')
137
Oleh Prypin1e908452018-04-04 14:31:58 +0200138 _RunCommand([sys.executable, build_apprtc_script, apprtc_dir, go_dir,
139 collider_dir])
Edward Lemur70ba9b42018-01-22 13:46:58 +0100140
141 # Start AppRTC Server.
142 dev_appserver = os.path.join(temp_dir, 'apprtc', 'temp', 'google-cloud-sdk',
143 'bin', 'dev_appserver.py')
144 appengine_dir = os.path.join(temp_dir, 'apprtc', 'out', 'app_engine')
145 processes.append(_RunBackgroundCommand([
146 sys.executable, dev_appserver, appengine_dir, '--port=9999',
147 '--admin_port=9998', '--skip_sdk_update_check', '--clear_datastore=yes']))
148
149 # Start Collider.
150 collider_path = os.path.join(temp_dir, 'collider', 'collidermain')
151 processes.append(_RunBackgroundCommand([
152 collider_path, '-tls=false', '-port=8089',
153 '-room-server=http://localhost:9999']))
154
155 # Start adb reverse forwarder.
156 reverseforwarder_path = os.path.join(
157 SRC_DIR, 'build', 'android', 'adb_reverse_forwarder.py')
158 processes.append(_RunBackgroundCommand([
159 reverseforwarder_path, '--device', android_device, '9999', '9999', '8089',
160 '8089']))
161
162
Edward Lesmes5b9c6842018-03-09 13:07:22 -0500163def RunTest(android_device, adb_path, build_dir, temp_dir, num_retries,
Edward Lemur2e5966b2018-01-30 15:33:02 +0100164 chartjson_result_file):
Edward Lemur70ba9b42018-01-22 13:46:58 +0100165 ffmpeg_path = os.path.join(TOOLCHAIN_DIR, 'ffmpeg')
Sami Kalliomäki0673bc92018-08-27 17:58:13 +0200166 def ConvertVideo(input_video, output_video):
167 _RunCommand([ffmpeg_path, '-y', '-i', input_video, output_video])
Edward Lemur70ba9b42018-01-22 13:46:58 +0100168
169 # Start loopback call and record video.
170 test_script = os.path.join(
Sami Kalliomäkid54f5f52018-08-03 13:23:05 +0200171 build_dir, 'bin', 'run_AppRTCMobile_stubbed_video_io_test_apk')
Edward Lesmes5b9c6842018-03-09 13:07:22 -0500172 _RunCommand([test_script, '--device', android_device,
173 '--num-retries', num_retries])
Edward Lemur70ba9b42018-01-22 13:46:58 +0100174
175 # Pull the recorded video.
176 test_video = os.path.join(temp_dir, 'test_video.y4m')
177 _RunCommand([adb_path, '-s', android_device,
178 'pull', '/sdcard/output.y4m', test_video])
179
180 # Convert the recorded and reference videos to YUV.
181 reference_video = os.path.join(SRC_DIR,
182 'resources', 'reference_video_640x360_30fps.y4m')
183
Sami Kalliomäki0673bc92018-08-27 17:58:13 +0200184 test_video_yuv = os.path.join(temp_dir, 'test_video.yuv')
185 reference_video_yuv = os.path.join(
186 temp_dir, 'reference_video_640x360_30fps.yuv')
187
188 ConvertVideo(test_video, test_video_yuv)
189 ConvertVideo(reference_video, reference_video_yuv)
190
Edward Lemur70ba9b42018-01-22 13:46:58 +0100191 # Run comparison script.
192 compare_script = os.path.join(SRC_DIR, 'rtc_tools', 'compare_videos.py')
Mirko Bonadeib56706f2018-09-18 11:03:39 +0200193 frame_analyzer = os.path.join(build_dir, 'frame_analyzer_host')
Edward Lemur70ba9b42018-01-22 13:46:58 +0100194 zxing_path = os.path.join(TOOLCHAIN_DIR, 'zxing')
195 stats_file_ref = os.path.join(temp_dir, 'stats_ref.txt')
196 stats_file_test = os.path.join(temp_dir, 'stats_test.txt')
197
Edward Lemur2e5966b2018-01-30 15:33:02 +0100198 args = [
Sami Kalliomäki0673bc92018-08-27 17:58:13 +0200199 '--ref_video', reference_video_yuv,
200 '--test_video', test_video_yuv,
Edward Lemur70ba9b42018-01-22 13:46:58 +0100201 '--yuv_frame_width', '640',
202 '--yuv_frame_height', '360',
203 '--stats_file_ref', stats_file_ref,
204 '--stats_file_test', stats_file_test,
205 '--frame_analyzer', frame_analyzer,
206 '--ffmpeg_path', ffmpeg_path,
Edward Lemur2e5966b2018-01-30 15:33:02 +0100207 '--zxing_path', zxing_path,
208 ]
209 if chartjson_result_file:
210 args.extend(['--chartjson_result_file', chartjson_result_file])
211
212 _RunCommand([sys.executable, compare_script] + args)
Edward Lemur70ba9b42018-01-22 13:46:58 +0100213
214
215def main():
216 logging.basicConfig(level=logging.INFO)
217
218 args = _ParseArgs()
219
220 temp_dir = args.temp_dir
221 build_dir = args.build_dir_android
222 adb_path = args.adb_path
oprypin30cda5e2017-04-24 04:15:13 -0700223
oprypin1d7392a2017-05-16 05:36:15 -0700224 processes = []
Edward Lemur70ba9b42018-01-22 13:46:58 +0100225 temp_dir = CreateEmptyDir(temp_dir)
oprypin1d7392a2017-05-16 05:36:15 -0700226 try:
Edward Lemur70ba9b42018-01-22 13:46:58 +0100227 android_device = SelectAndroidDevice(adb_path)
228 SetUpTools(android_device, temp_dir, processes)
Edward Lesmes5b9c6842018-03-09 13:07:22 -0500229 RunTest(android_device, adb_path, build_dir, temp_dir, args.num_retries,
Edward Lemur71d766e2018-02-05 21:59:36 +0100230 args.isolated_script_test_perf_output)
oprypin1d7392a2017-05-16 05:36:15 -0700231 finally:
232 for process in processes:
233 if process:
234 process.terminate()
235 process.wait()
236
Edward Lemur70ba9b42018-01-22 13:46:58 +0100237 utils.RemoveDirectory(temp_dir)
mandermoed582f72017-01-23 07:55:42 -0800238
239
240if __name__ == '__main__':
241 sys.exit(main())