blob: a47fbe7e25bc3cbea19753db02b0a814563cb1ca [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
ehmaldonadod103f4b2017-02-16 07:20:26 -080052def _RunCommand(argv, cwd=SRC_DIR, **kwargs):
mandermoed582f72017-01-23 07:55:42 -080053 logging.info('Running %r', argv)
ehmaldonadod103f4b2017-02-16 07:20:26 -080054 subprocess.check_call(argv, cwd=cwd, **kwargs)
mandermoed582f72017-01-23 07:55:42 -080055
56
oprypin30cda5e2017-04-24 04:15:13 -070057def _RunCommandWithOutput(argv, cwd=SRC_DIR, **kwargs):
58 logging.info('Running %r', argv)
59 return subprocess.check_output(argv, cwd=cwd, **kwargs)
60
61
62def _RunBackgroundCommand(argv, cwd=SRC_DIR):
63 logging.info('Running %r', argv)
64 process = subprocess.Popen(argv, cwd=cwd)
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.')
84 parser.add_argument('--build_dir_x86',
85 help='The path to the build directory for building locally.')
86 parser.add_argument('--temp_dir',
87 help='A temporary directory to put the output.')
oprypin30cda5e2017-04-24 04:15:13 -070088 parser.add_argument('--adb-path', help='Path to adb binary.', default='adb')
Edward Lemur2e5966b2018-01-30 15:33:02 +010089 parser.add_argument('--chartjson-result-file',
90 help='Where to store perf results in chartjson format.', default=None)
mandermoed582f72017-01-23 07:55:42 -080091
92 args = parser.parse_args()
93 return args
94
95
Edward Lemur70ba9b42018-01-22 13:46:58 +010096def SelectAndroidDevice(adb_path):
97 # Select an Android device in case multiple are connected.
oprypinbed7a6b2017-06-19 01:16:45 -070098 try:
99 with open(BAD_DEVICES_JSON) as bad_devices_file:
100 bad_devices = json.load(bad_devices_file)
101 except IOError:
102 if os.environ.get('CHROME_HEADLESS'):
103 logging.warning('Cannot read %r', BAD_DEVICES_JSON)
104 bad_devices = {}
105
oprypin30cda5e2017-04-24 04:15:13 -0700106 for line in _RunCommandWithOutput([adb_path, 'devices']).splitlines():
107 if line.endswith('\tdevice'):
108 android_device = line.split('\t')[0]
oprypinbed7a6b2017-06-19 01:16:45 -0700109 if android_device not in bad_devices:
Edward Lemur70ba9b42018-01-22 13:46:58 +0100110 return android_device
111 raise VideoQualityTestError('Cannot find any connected Android device.')
112
113
114def SetUpTools(android_device, temp_dir, processes):
115 # Extract AppRTC.
116 apprtc_archive = os.path.join(RTC_TOOLS_DIR, 'prebuilt_apprtc.zip')
117 golang_archive = os.path.join(RTC_TOOLS_DIR, 'golang', 'linux', 'go.tar.gz')
118
119 utils.UnpackArchiveTo(apprtc_archive, temp_dir)
120 utils.UnpackArchiveTo(golang_archive, temp_dir)
121
122 # Build AppRTC.
123 build_apprtc_script = os.path.join(RTC_TOOLS_DIR, 'build_apprtc.py')
124 apprtc_src_dir = os.path.join(temp_dir, 'apprtc', 'src')
125 go_dir = os.path.join(temp_dir, 'go')
126 collider_dir = os.path.join(temp_dir, 'collider')
127
128 _RunCommand([sys.executable, build_apprtc_script, apprtc_src_dir, go_dir,
129 collider_dir])
130
131 # Start AppRTC Server.
132 dev_appserver = os.path.join(temp_dir, 'apprtc', 'temp', 'google-cloud-sdk',
133 'bin', 'dev_appserver.py')
134 appengine_dir = os.path.join(temp_dir, 'apprtc', 'out', 'app_engine')
135 processes.append(_RunBackgroundCommand([
136 sys.executable, dev_appserver, appengine_dir, '--port=9999',
137 '--admin_port=9998', '--skip_sdk_update_check', '--clear_datastore=yes']))
138
139 # Start Collider.
140 collider_path = os.path.join(temp_dir, 'collider', 'collidermain')
141 processes.append(_RunBackgroundCommand([
142 collider_path, '-tls=false', '-port=8089',
143 '-room-server=http://localhost:9999']))
144
145 # Start adb reverse forwarder.
146 reverseforwarder_path = os.path.join(
147 SRC_DIR, 'build', 'android', 'adb_reverse_forwarder.py')
148 processes.append(_RunBackgroundCommand([
149 reverseforwarder_path, '--device', android_device, '9999', '9999', '8089',
150 '8089']))
151
152
Edward Lemur2e5966b2018-01-30 15:33:02 +0100153def RunTest(android_device, adb_path, build_dir, temp_dir,
154 chartjson_result_file):
Edward Lemur70ba9b42018-01-22 13:46:58 +0100155 ffmpeg_path = os.path.join(TOOLCHAIN_DIR, 'ffmpeg')
156 def ConvertVideo(input_video, output_video):
157 _RunCommand([ffmpeg_path, '-y', '-i', input_video, output_video])
158
159 # Start loopback call and record video.
160 test_script = os.path.join(
161 build_dir, 'bin', 'run_AppRTCMobileTestStubbedVideoIO')
162 _RunCommand([test_script, '--device', android_device])
163
164 # Pull the recorded video.
165 test_video = os.path.join(temp_dir, 'test_video.y4m')
166 _RunCommand([adb_path, '-s', android_device,
167 'pull', '/sdcard/output.y4m', test_video])
168
169 # Convert the recorded and reference videos to YUV.
170 reference_video = os.path.join(SRC_DIR,
171 'resources', 'reference_video_640x360_30fps.y4m')
172
173 test_video_yuv = os.path.join(temp_dir, 'test_video.yuv')
174 reference_video_yuv = os.path.join(
175 temp_dir, 'reference_video_640x360_30fps.yuv')
176
177 ConvertVideo(test_video, test_video_yuv)
178 ConvertVideo(reference_video, reference_video_yuv)
179
180 # Run comparison script.
181 compare_script = os.path.join(SRC_DIR, 'rtc_tools', 'compare_videos.py')
182 frame_analyzer = os.path.join(TOOLCHAIN_DIR, 'frame_analyzer')
183 zxing_path = os.path.join(TOOLCHAIN_DIR, 'zxing')
184 stats_file_ref = os.path.join(temp_dir, 'stats_ref.txt')
185 stats_file_test = os.path.join(temp_dir, 'stats_test.txt')
186
Edward Lemur2e5966b2018-01-30 15:33:02 +0100187 args = [
Edward Lemur70ba9b42018-01-22 13:46:58 +0100188 '--ref_video', reference_video_yuv,
189 '--test_video', test_video_yuv,
190 '--yuv_frame_width', '640',
191 '--yuv_frame_height', '360',
192 '--stats_file_ref', stats_file_ref,
193 '--stats_file_test', stats_file_test,
194 '--frame_analyzer', frame_analyzer,
195 '--ffmpeg_path', ffmpeg_path,
Edward Lemur2e5966b2018-01-30 15:33:02 +0100196 '--zxing_path', zxing_path,
197 ]
198 if chartjson_result_file:
199 args.extend(['--chartjson_result_file', chartjson_result_file])
200
201 _RunCommand([sys.executable, compare_script] + args)
Edward Lemur70ba9b42018-01-22 13:46:58 +0100202
203
204def main():
205 logging.basicConfig(level=logging.INFO)
206
207 args = _ParseArgs()
208
209 temp_dir = args.temp_dir
210 build_dir = args.build_dir_android
211 adb_path = args.adb_path
oprypin30cda5e2017-04-24 04:15:13 -0700212
oprypin1d7392a2017-05-16 05:36:15 -0700213 processes = []
Edward Lemur70ba9b42018-01-22 13:46:58 +0100214 temp_dir = CreateEmptyDir(temp_dir)
oprypin1d7392a2017-05-16 05:36:15 -0700215 try:
Edward Lemur70ba9b42018-01-22 13:46:58 +0100216 android_device = SelectAndroidDevice(adb_path)
217 SetUpTools(android_device, temp_dir, processes)
Edward Lemur2e5966b2018-01-30 15:33:02 +0100218 RunTest(android_device, adb_path, build_dir, temp_dir,
219 args.chartjson_result_file)
oprypin1d7392a2017-05-16 05:36:15 -0700220 finally:
221 for process in processes:
222 if process:
223 process.terminate()
224 process.wait()
225
Edward Lemur70ba9b42018-01-22 13:46:58 +0100226 utils.RemoveDirectory(temp_dir)
mandermoed582f72017-01-23 07:55:42 -0800227
228
229if __name__ == '__main__':
230 sys.exit(main())
231