blob: 0b55878d046f3ebbaaddf0d96fff9b21bf8c7c79 [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')
oprypin30cda5e2017-04-24 04:15:13 -070037
38
39class Error(Exception):
40 pass
41
42
43class VideoQualityTestError(Error):
44 pass
mandermoed582f72017-01-23 07:55:42 -080045
46
ehmaldonadod103f4b2017-02-16 07:20:26 -080047def _RunCommand(argv, cwd=SRC_DIR, **kwargs):
mandermoed582f72017-01-23 07:55:42 -080048 logging.info('Running %r', argv)
ehmaldonadod103f4b2017-02-16 07:20:26 -080049 subprocess.check_call(argv, cwd=cwd, **kwargs)
mandermoed582f72017-01-23 07:55:42 -080050
51
oprypin30cda5e2017-04-24 04:15:13 -070052def _RunCommandWithOutput(argv, cwd=SRC_DIR, **kwargs):
53 logging.info('Running %r', argv)
54 return subprocess.check_output(argv, cwd=cwd, **kwargs)
55
56
57def _RunBackgroundCommand(argv, cwd=SRC_DIR):
58 logging.info('Running %r', argv)
59 process = subprocess.Popen(argv, cwd=cwd)
oprypin30cda5e2017-04-24 04:15:13 -070060 time.sleep(0.5)
61 status = process.poll()
62 if status: # is not None or 0
63 raise subprocess.CalledProcessError(status, argv)
64 return process
65
66
mandermoed582f72017-01-23 07:55:42 -080067def _ParseArgs():
68 parser = argparse.ArgumentParser(description='Start loopback video analysis.')
mandermoed582f72017-01-23 07:55:42 -080069 parser.add_argument('build_dir_android',
70 help='The path to the build directory for Android.')
71 parser.add_argument('--build_dir_x86',
72 help='The path to the build directory for building locally.')
73 parser.add_argument('--temp_dir',
74 help='A temporary directory to put the output.')
oprypin30cda5e2017-04-24 04:15:13 -070075 parser.add_argument('--adb-path', help='Path to adb binary.', default='adb')
mandermoed582f72017-01-23 07:55:42 -080076
77 args = parser.parse_args()
78 return args
79
80
81def main():
82 logging.basicConfig(level=logging.INFO)
83
84 args = _ParseArgs()
85
mandermoed582f72017-01-23 07:55:42 -080086 build_dir_android = args.build_dir_android
87 build_dir_x86 = args.build_dir_x86
88 temp_dir = args.temp_dir
oprypin30cda5e2017-04-24 04:15:13 -070089 adb_path = args.adb_path
mandermoed582f72017-01-23 07:55:42 -080090 if not temp_dir:
91 temp_dir = tempfile.mkdtemp()
92 else:
93 if not os.path.exists(temp_dir):
94 os.makedirs(temp_dir)
95
96 if not build_dir_x86:
97 build_dir_x86 = os.path.join(temp_dir, 'LocalBuild')
98 _RunCommand(['gn', 'gen', build_dir_x86])
99 _RunCommand(['ninja', '-C', build_dir_x86, 'frame_analyzer'])
100
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200101 tools_dir = os.path.join(SRC_DIR, 'tools_webrtc')
oprypin3b2fb202017-03-06 02:23:34 -0800102 toolchain_dir = os.path.join(tools_dir, 'video_quality_toolchain')
mandermoed582f72017-01-23 07:55:42 -0800103
104 # Download ffmpeg and zxing.
oprypin1d7392a2017-05-16 05:36:15 -0700105 download_tools_script = os.path.join(tools_dir, 'download_tools.py')
106 _RunCommand([sys.executable, download_tools_script, toolchain_dir])
107
Henrik Kjellander9d8ce7c2017-09-15 13:05:32 +0200108 testing_tools_dir = os.path.join(SRC_DIR, 'rtc_tools', 'testing')
oprypin1d7392a2017-05-16 05:36:15 -0700109
110 # Download, extract and build AppRTC.
111 setup_apprtc_script = os.path.join(testing_tools_dir, 'setup_apprtc.py')
112 _RunCommand([sys.executable, setup_apprtc_script, temp_dir])
mandermoed582f72017-01-23 07:55:42 -0800113
oprypin30cda5e2017-04-24 04:15:13 -0700114 # Select an Android device in case multiple are connected
oprypinbed7a6b2017-06-19 01:16:45 -0700115 try:
116 with open(BAD_DEVICES_JSON) as bad_devices_file:
117 bad_devices = json.load(bad_devices_file)
118 except IOError:
119 if os.environ.get('CHROME_HEADLESS'):
120 logging.warning('Cannot read %r', BAD_DEVICES_JSON)
121 bad_devices = {}
122
oprypin30cda5e2017-04-24 04:15:13 -0700123 for line in _RunCommandWithOutput([adb_path, 'devices']).splitlines():
124 if line.endswith('\tdevice'):
125 android_device = line.split('\t')[0]
oprypinbed7a6b2017-06-19 01:16:45 -0700126 if android_device not in bad_devices:
127 break
oprypin30cda5e2017-04-24 04:15:13 -0700128 else:
129 raise VideoQualityTestError('Cannot find any connected Android device.')
130
oprypin1d7392a2017-05-16 05:36:15 -0700131 processes = []
132 try:
133 # Start AppRTC Server
134 dev_appserver = os.path.join(temp_dir, 'apprtc', 'temp', 'google-cloud-sdk',
135 'bin', 'dev_appserver.py')
136 appengine_dir = os.path.join(temp_dir, 'apprtc', 'out', 'app_engine')
137 processes.append(_RunBackgroundCommand([
138 'python', dev_appserver, appengine_dir,
139 '--port=9999', '--admin_port=9998',
140 '--skip_sdk_update_check', '--clear_datastore=yes']))
oprypin30cda5e2017-04-24 04:15:13 -0700141
oprypin1d7392a2017-05-16 05:36:15 -0700142 # Start Collider
143 collider_path = os.path.join(temp_dir, 'collider', 'collidermain')
144 processes.append(_RunBackgroundCommand([
145 collider_path, '-tls=false', '-port=8089',
146 '-room-server=http://localhost:9999']))
oprypin30cda5e2017-04-24 04:15:13 -0700147
oprypin1d7392a2017-05-16 05:36:15 -0700148 # Start adb reverse forwarder
149 reverseforwarder_path = os.path.join(
150 SRC_DIR, 'build', 'android', 'adb_reverse_forwarder.py')
151 processes.append(_RunBackgroundCommand([
152 reverseforwarder_path, '--device', android_device,
153 '9999', '9999', '8089', '8089']))
oprypin30cda5e2017-04-24 04:15:13 -0700154
oprypin1d7392a2017-05-16 05:36:15 -0700155 # Run the Espresso code.
156 test_script = os.path.join(build_dir_android,
157 'bin', 'run_AppRTCMobileTestStubbedVideoIO')
158 _RunCommand([test_script, '--device', android_device])
mandermoed582f72017-01-23 07:55:42 -0800159
oprypin1d7392a2017-05-16 05:36:15 -0700160 # Pull the output video.
161 test_video = os.path.join(temp_dir, 'test_video.y4m')
162 _RunCommand([adb_path, '-s', android_device,
163 'pull', '/sdcard/output.y4m', test_video])
mandermoed582f72017-01-23 07:55:42 -0800164
oprypin1d7392a2017-05-16 05:36:15 -0700165 test_video_yuv = os.path.join(temp_dir, 'test_video.yuv')
mandermoed582f72017-01-23 07:55:42 -0800166
oprypin1d7392a2017-05-16 05:36:15 -0700167 ffmpeg_path = os.path.join(toolchain_dir, 'linux', 'ffmpeg')
mandermoed582f72017-01-23 07:55:42 -0800168
oprypin1d7392a2017-05-16 05:36:15 -0700169 def ConvertVideo(input_video, output_video):
170 _RunCommand([ffmpeg_path, '-y', '-i', input_video, output_video])
mandermoed582f72017-01-23 07:55:42 -0800171
oprypin1d7392a2017-05-16 05:36:15 -0700172 ConvertVideo(test_video, test_video_yuv)
mandermoed582f72017-01-23 07:55:42 -0800173
oprypin1d7392a2017-05-16 05:36:15 -0700174 reference_video = os.path.join(SRC_DIR,
175 'resources', 'reference_video_640x360_30fps.y4m')
mandermoed582f72017-01-23 07:55:42 -0800176
oprypin1d7392a2017-05-16 05:36:15 -0700177 reference_video_yuv = os.path.join(temp_dir,
178 'reference_video_640x360_30fps.yuv')
mandermoed582f72017-01-23 07:55:42 -0800179
oprypin1d7392a2017-05-16 05:36:15 -0700180 ConvertVideo(reference_video, reference_video_yuv)
mandermoed582f72017-01-23 07:55:42 -0800181
oprypin1d7392a2017-05-16 05:36:15 -0700182 # Run compare script.
Henrik Kjellander9d8ce7c2017-09-15 13:05:32 +0200183 compare_script = os.path.join(SRC_DIR, 'rtc_tools', 'compare_videos.py')
oprypin1d7392a2017-05-16 05:36:15 -0700184 zxing_path = os.path.join(toolchain_dir, 'linux', 'zxing')
mandermoed582f72017-01-23 07:55:42 -0800185
oprypin1d7392a2017-05-16 05:36:15 -0700186 # The frame_analyzer binary should be built for local computer and not for
187 # Android
188 frame_analyzer = os.path.join(build_dir_x86, 'frame_analyzer')
mandermoed582f72017-01-23 07:55:42 -0800189
oprypin1d7392a2017-05-16 05:36:15 -0700190 frame_width = 640
191 frame_height = 360
mandermoed582f72017-01-23 07:55:42 -0800192
oprypin1d7392a2017-05-16 05:36:15 -0700193 stats_file_ref = os.path.join(temp_dir, 'stats_ref.txt')
194 stats_file_test = os.path.join(temp_dir, 'stats_test.txt')
mandermoed582f72017-01-23 07:55:42 -0800195
oprypin1d7392a2017-05-16 05:36:15 -0700196 _RunCommand([
197 sys.executable, compare_script, '--ref_video', reference_video_yuv,
198 '--test_video', test_video_yuv, '--yuv_frame_width', str(frame_width),
199 '--yuv_frame_height', str(frame_height),
200 '--stats_file_ref', stats_file_ref,
201 '--stats_file_test', stats_file_test,
202 '--frame_analyzer', frame_analyzer,
203 '--ffmpeg_path', ffmpeg_path, '--zxing_path', zxing_path])
mandermoed582f72017-01-23 07:55:42 -0800204
oprypin1d7392a2017-05-16 05:36:15 -0700205 finally:
206 for process in processes:
207 if process:
208 process.terminate()
209 process.wait()
210
211 shutil.rmtree(temp_dir)
mandermoed582f72017-01-23 07:55:42 -0800212
213
214if __name__ == '__main__':
215 sys.exit(main())
216