blob: 6991a0a57b6cb485cfe7402ce32c2b80383e9eb6 [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
25import shutil
26import subprocess
27import sys
28import tempfile
oprypin30cda5e2017-04-24 04:15:13 -070029import time
mandermoed582f72017-01-23 07:55:42 -080030
31
32SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
Henrik Kjellandercb3b1c12017-09-18 06:20:33 +020033SRC_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, os.pardir, os.pardir))
oprypinbed7a6b2017-06-19 01:16:45 -070034BAD_DEVICES_JSON = os.path.join(SRC_DIR,
35 os.environ.get('CHROMIUM_OUT_DIR', 'out'),
36 'bad_devices.json')
Henrik Kjellanderec57e052017-10-17 21:36:01 +020037sys.path.append(os.path.join(SRC_DIR, 'build'))
38import find_depot_tools
oprypin30cda5e2017-04-24 04:15:13 -070039
40
41class Error(Exception):
42 pass
43
44
45class VideoQualityTestError(Error):
46 pass
mandermoed582f72017-01-23 07:55:42 -080047
48
ehmaldonadod103f4b2017-02-16 07:20:26 -080049def _RunCommand(argv, cwd=SRC_DIR, **kwargs):
mandermoed582f72017-01-23 07:55:42 -080050 logging.info('Running %r', argv)
ehmaldonadod103f4b2017-02-16 07:20:26 -080051 subprocess.check_call(argv, cwd=cwd, **kwargs)
mandermoed582f72017-01-23 07:55:42 -080052
53
oprypin30cda5e2017-04-24 04:15:13 -070054def _RunCommandWithOutput(argv, cwd=SRC_DIR, **kwargs):
55 logging.info('Running %r', argv)
56 return subprocess.check_output(argv, cwd=cwd, **kwargs)
57
58
59def _RunBackgroundCommand(argv, cwd=SRC_DIR):
60 logging.info('Running %r', argv)
61 process = subprocess.Popen(argv, cwd=cwd)
oprypin30cda5e2017-04-24 04:15:13 -070062 time.sleep(0.5)
63 status = process.poll()
64 if status: # is not None or 0
65 raise subprocess.CalledProcessError(status, argv)
66 return process
67
68
mandermoed582f72017-01-23 07:55:42 -080069def _ParseArgs():
70 parser = argparse.ArgumentParser(description='Start loopback video analysis.')
mandermoed582f72017-01-23 07:55:42 -080071 parser.add_argument('build_dir_android',
72 help='The path to the build directory for Android.')
73 parser.add_argument('--build_dir_x86',
74 help='The path to the build directory for building locally.')
75 parser.add_argument('--temp_dir',
76 help='A temporary directory to put the output.')
oprypin30cda5e2017-04-24 04:15:13 -070077 parser.add_argument('--adb-path', help='Path to adb binary.', default='adb')
mandermoed582f72017-01-23 07:55:42 -080078
79 args = parser.parse_args()
80 return args
81
82
83def main():
84 logging.basicConfig(level=logging.INFO)
85
86 args = _ParseArgs()
87
mandermoed582f72017-01-23 07:55:42 -080088 build_dir_android = args.build_dir_android
89 build_dir_x86 = args.build_dir_x86
90 temp_dir = args.temp_dir
oprypin30cda5e2017-04-24 04:15:13 -070091 adb_path = args.adb_path
mandermoed582f72017-01-23 07:55:42 -080092 if not temp_dir:
93 temp_dir = tempfile.mkdtemp()
94 else:
95 if not os.path.exists(temp_dir):
96 os.makedirs(temp_dir)
97
98 if not build_dir_x86:
99 build_dir_x86 = os.path.join(temp_dir, 'LocalBuild')
Henrik Kjellanderec57e052017-10-17 21:36:01 +0200100
101 def DepotToolPath(*args):
102 return os.path.join(find_depot_tools.DEPOT_TOOLS_PATH, *args)
103
104 _RunCommand([sys.executable, DepotToolPath('gn.py'), 'gen', build_dir_x86])
105 _RunCommand([DepotToolPath('ninja'), '-C', build_dir_x86,
106 'frame_analyzer'])
mandermoed582f72017-01-23 07:55:42 -0800107
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200108 tools_dir = os.path.join(SRC_DIR, 'tools_webrtc')
oprypin3b2fb202017-03-06 02:23:34 -0800109 toolchain_dir = os.path.join(tools_dir, 'video_quality_toolchain')
mandermoed582f72017-01-23 07:55:42 -0800110
111 # Download ffmpeg and zxing.
oprypin1d7392a2017-05-16 05:36:15 -0700112 download_tools_script = os.path.join(tools_dir, 'download_tools.py')
113 _RunCommand([sys.executable, download_tools_script, toolchain_dir])
114
Henrik Kjellander9d8ce7c2017-09-15 13:05:32 +0200115 testing_tools_dir = os.path.join(SRC_DIR, 'rtc_tools', 'testing')
oprypin1d7392a2017-05-16 05:36:15 -0700116
117 # Download, extract and build AppRTC.
118 setup_apprtc_script = os.path.join(testing_tools_dir, 'setup_apprtc.py')
119 _RunCommand([sys.executable, setup_apprtc_script, temp_dir])
mandermoed582f72017-01-23 07:55:42 -0800120
oprypin30cda5e2017-04-24 04:15:13 -0700121 # Select an Android device in case multiple are connected
oprypinbed7a6b2017-06-19 01:16:45 -0700122 try:
123 with open(BAD_DEVICES_JSON) as bad_devices_file:
124 bad_devices = json.load(bad_devices_file)
125 except IOError:
126 if os.environ.get('CHROME_HEADLESS'):
127 logging.warning('Cannot read %r', BAD_DEVICES_JSON)
128 bad_devices = {}
129
oprypin30cda5e2017-04-24 04:15:13 -0700130 for line in _RunCommandWithOutput([adb_path, 'devices']).splitlines():
131 if line.endswith('\tdevice'):
132 android_device = line.split('\t')[0]
oprypinbed7a6b2017-06-19 01:16:45 -0700133 if android_device not in bad_devices:
134 break
oprypin30cda5e2017-04-24 04:15:13 -0700135 else:
136 raise VideoQualityTestError('Cannot find any connected Android device.')
137
oprypin1d7392a2017-05-16 05:36:15 -0700138 processes = []
139 try:
140 # Start AppRTC Server
141 dev_appserver = os.path.join(temp_dir, 'apprtc', 'temp', 'google-cloud-sdk',
142 'bin', 'dev_appserver.py')
143 appengine_dir = os.path.join(temp_dir, 'apprtc', 'out', 'app_engine')
144 processes.append(_RunBackgroundCommand([
145 'python', dev_appserver, appengine_dir,
146 '--port=9999', '--admin_port=9998',
147 '--skip_sdk_update_check', '--clear_datastore=yes']))
oprypin30cda5e2017-04-24 04:15:13 -0700148
oprypin1d7392a2017-05-16 05:36:15 -0700149 # 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']))
oprypin30cda5e2017-04-24 04:15:13 -0700154
oprypin1d7392a2017-05-16 05:36:15 -0700155 # 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,
160 '9999', '9999', '8089', '8089']))
oprypin30cda5e2017-04-24 04:15:13 -0700161
oprypin1d7392a2017-05-16 05:36:15 -0700162 # Run the Espresso code.
163 test_script = os.path.join(build_dir_android,
164 'bin', 'run_AppRTCMobileTestStubbedVideoIO')
165 _RunCommand([test_script, '--device', android_device])
mandermoed582f72017-01-23 07:55:42 -0800166
oprypin1d7392a2017-05-16 05:36:15 -0700167 # Pull the output video.
168 test_video = os.path.join(temp_dir, 'test_video.y4m')
169 _RunCommand([adb_path, '-s', android_device,
170 'pull', '/sdcard/output.y4m', test_video])
mandermoed582f72017-01-23 07:55:42 -0800171
oprypin1d7392a2017-05-16 05:36:15 -0700172 test_video_yuv = os.path.join(temp_dir, 'test_video.yuv')
mandermoed582f72017-01-23 07:55:42 -0800173
oprypin1d7392a2017-05-16 05:36:15 -0700174 ffmpeg_path = os.path.join(toolchain_dir, 'linux', 'ffmpeg')
mandermoed582f72017-01-23 07:55:42 -0800175
oprypin1d7392a2017-05-16 05:36:15 -0700176 def ConvertVideo(input_video, output_video):
177 _RunCommand([ffmpeg_path, '-y', '-i', input_video, output_video])
mandermoed582f72017-01-23 07:55:42 -0800178
oprypin1d7392a2017-05-16 05:36:15 -0700179 ConvertVideo(test_video, test_video_yuv)
mandermoed582f72017-01-23 07:55:42 -0800180
oprypin1d7392a2017-05-16 05:36:15 -0700181 reference_video = os.path.join(SRC_DIR,
182 'resources', 'reference_video_640x360_30fps.y4m')
mandermoed582f72017-01-23 07:55:42 -0800183
oprypin1d7392a2017-05-16 05:36:15 -0700184 reference_video_yuv = os.path.join(temp_dir,
185 'reference_video_640x360_30fps.yuv')
mandermoed582f72017-01-23 07:55:42 -0800186
oprypin1d7392a2017-05-16 05:36:15 -0700187 ConvertVideo(reference_video, reference_video_yuv)
mandermoed582f72017-01-23 07:55:42 -0800188
oprypin1d7392a2017-05-16 05:36:15 -0700189 # Run compare script.
Henrik Kjellander9d8ce7c2017-09-15 13:05:32 +0200190 compare_script = os.path.join(SRC_DIR, 'rtc_tools', 'compare_videos.py')
oprypin1d7392a2017-05-16 05:36:15 -0700191 zxing_path = os.path.join(toolchain_dir, 'linux', 'zxing')
mandermoed582f72017-01-23 07:55:42 -0800192
oprypin1d7392a2017-05-16 05:36:15 -0700193 # The frame_analyzer binary should be built for local computer and not for
194 # Android
195 frame_analyzer = os.path.join(build_dir_x86, 'frame_analyzer')
mandermoed582f72017-01-23 07:55:42 -0800196
oprypin1d7392a2017-05-16 05:36:15 -0700197 frame_width = 640
198 frame_height = 360
mandermoed582f72017-01-23 07:55:42 -0800199
oprypin1d7392a2017-05-16 05:36:15 -0700200 stats_file_ref = os.path.join(temp_dir, 'stats_ref.txt')
201 stats_file_test = os.path.join(temp_dir, 'stats_test.txt')
mandermoed582f72017-01-23 07:55:42 -0800202
oprypin1d7392a2017-05-16 05:36:15 -0700203 _RunCommand([
204 sys.executable, compare_script, '--ref_video', reference_video_yuv,
205 '--test_video', test_video_yuv, '--yuv_frame_width', str(frame_width),
206 '--yuv_frame_height', str(frame_height),
207 '--stats_file_ref', stats_file_ref,
208 '--stats_file_test', stats_file_test,
209 '--frame_analyzer', frame_analyzer,
210 '--ffmpeg_path', ffmpeg_path, '--zxing_path', zxing_path])
mandermoed582f72017-01-23 07:55:42 -0800211
oprypin1d7392a2017-05-16 05:36:15 -0700212 finally:
213 for process in processes:
214 if process:
215 process.terminate()
216 process.wait()
217
218 shutil.rmtree(temp_dir)
mandermoed582f72017-01-23 07:55:42 -0800219
220
221if __name__ == '__main__':
222 sys.exit(main())
223