blob: eed3b9c33a7c27aa435aaa061270e4b52251fc94 [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__))
kjellander30292102017-02-09 01:05:54 -080033SRC_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, os.pardir, os.pardir,
34 os.pardir))
oprypinbed7a6b2017-06-19 01:16:45 -070035BAD_DEVICES_JSON = os.path.join(SRC_DIR,
36 os.environ.get('CHROMIUM_OUT_DIR', 'out'),
37 'bad_devices.json')
oprypin30cda5e2017-04-24 04:15:13 -070038
39
40class Error(Exception):
41 pass
42
43
44class VideoQualityTestError(Error):
45 pass
mandermoed582f72017-01-23 07:55:42 -080046
47
ehmaldonadod103f4b2017-02-16 07:20:26 -080048def _RunCommand(argv, cwd=SRC_DIR, **kwargs):
mandermoed582f72017-01-23 07:55:42 -080049 logging.info('Running %r', argv)
ehmaldonadod103f4b2017-02-16 07:20:26 -080050 subprocess.check_call(argv, cwd=cwd, **kwargs)
mandermoed582f72017-01-23 07:55:42 -080051
52
oprypin30cda5e2017-04-24 04:15:13 -070053def _RunCommandWithOutput(argv, cwd=SRC_DIR, **kwargs):
54 logging.info('Running %r', argv)
55 return subprocess.check_output(argv, cwd=cwd, **kwargs)
56
57
58def _RunBackgroundCommand(argv, cwd=SRC_DIR):
59 logging.info('Running %r', argv)
60 process = subprocess.Popen(argv, cwd=cwd)
oprypin30cda5e2017-04-24 04:15:13 -070061 time.sleep(0.5)
62 status = process.poll()
63 if status: # is not None or 0
64 raise subprocess.CalledProcessError(status, argv)
65 return process
66
67
mandermoed582f72017-01-23 07:55:42 -080068def _ParseArgs():
69 parser = argparse.ArgumentParser(description='Start loopback video analysis.')
mandermoed582f72017-01-23 07:55:42 -080070 parser.add_argument('build_dir_android',
71 help='The path to the build directory for Android.')
72 parser.add_argument('--build_dir_x86',
73 help='The path to the build directory for building locally.')
74 parser.add_argument('--temp_dir',
75 help='A temporary directory to put the output.')
oprypin30cda5e2017-04-24 04:15:13 -070076 parser.add_argument('--adb-path', help='Path to adb binary.', default='adb')
mandermoed582f72017-01-23 07:55:42 -080077
78 args = parser.parse_args()
79 return args
80
81
82def main():
83 logging.basicConfig(level=logging.INFO)
84
85 args = _ParseArgs()
86
mandermoed582f72017-01-23 07:55:42 -080087 build_dir_android = args.build_dir_android
88 build_dir_x86 = args.build_dir_x86
89 temp_dir = args.temp_dir
oprypin30cda5e2017-04-24 04:15:13 -070090 adb_path = args.adb_path
mandermoed582f72017-01-23 07:55:42 -080091 if not temp_dir:
92 temp_dir = tempfile.mkdtemp()
93 else:
94 if not os.path.exists(temp_dir):
95 os.makedirs(temp_dir)
96
97 if not build_dir_x86:
98 build_dir_x86 = os.path.join(temp_dir, 'LocalBuild')
99 _RunCommand(['gn', 'gen', build_dir_x86])
100 _RunCommand(['ninja', '-C', build_dir_x86, 'frame_analyzer'])
101
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200102 tools_dir = os.path.join(SRC_DIR, 'tools_webrtc')
oprypin3b2fb202017-03-06 02:23:34 -0800103 toolchain_dir = os.path.join(tools_dir, 'video_quality_toolchain')
mandermoed582f72017-01-23 07:55:42 -0800104
105 # Download ffmpeg and zxing.
oprypin1d7392a2017-05-16 05:36:15 -0700106 download_tools_script = os.path.join(tools_dir, 'download_tools.py')
107 _RunCommand([sys.executable, download_tools_script, toolchain_dir])
108
ehmaldonado3aba2d12017-06-30 05:12:09 -0700109 testing_tools_dir = os.path.join(SRC_DIR, 'webrtc', 'rtc_tools', 'testing')
oprypin1d7392a2017-05-16 05:36:15 -0700110
111 # Download, extract and build AppRTC.
112 setup_apprtc_script = os.path.join(testing_tools_dir, 'setup_apprtc.py')
113 _RunCommand([sys.executable, setup_apprtc_script, temp_dir])
mandermoed582f72017-01-23 07:55:42 -0800114
oprypin30cda5e2017-04-24 04:15:13 -0700115 # Select an Android device in case multiple are connected
oprypinbed7a6b2017-06-19 01:16:45 -0700116 try:
117 with open(BAD_DEVICES_JSON) as bad_devices_file:
118 bad_devices = json.load(bad_devices_file)
119 except IOError:
120 if os.environ.get('CHROME_HEADLESS'):
121 logging.warning('Cannot read %r', BAD_DEVICES_JSON)
122 bad_devices = {}
123
oprypin30cda5e2017-04-24 04:15:13 -0700124 for line in _RunCommandWithOutput([adb_path, 'devices']).splitlines():
125 if line.endswith('\tdevice'):
126 android_device = line.split('\t')[0]
oprypinbed7a6b2017-06-19 01:16:45 -0700127 if android_device not in bad_devices:
128 break
oprypin30cda5e2017-04-24 04:15:13 -0700129 else:
130 raise VideoQualityTestError('Cannot find any connected Android device.')
131
oprypin1d7392a2017-05-16 05:36:15 -0700132 processes = []
133 try:
134 # Start AppRTC Server
135 dev_appserver = os.path.join(temp_dir, 'apprtc', 'temp', 'google-cloud-sdk',
136 'bin', 'dev_appserver.py')
137 appengine_dir = os.path.join(temp_dir, 'apprtc', 'out', 'app_engine')
138 processes.append(_RunBackgroundCommand([
139 'python', dev_appserver, appengine_dir,
140 '--port=9999', '--admin_port=9998',
141 '--skip_sdk_update_check', '--clear_datastore=yes']))
oprypin30cda5e2017-04-24 04:15:13 -0700142
oprypin1d7392a2017-05-16 05:36:15 -0700143 # Start Collider
144 collider_path = os.path.join(temp_dir, 'collider', 'collidermain')
145 processes.append(_RunBackgroundCommand([
146 collider_path, '-tls=false', '-port=8089',
147 '-room-server=http://localhost:9999']))
oprypin30cda5e2017-04-24 04:15:13 -0700148
oprypin1d7392a2017-05-16 05:36:15 -0700149 # Start adb reverse forwarder
150 reverseforwarder_path = os.path.join(
151 SRC_DIR, 'build', 'android', 'adb_reverse_forwarder.py')
152 processes.append(_RunBackgroundCommand([
153 reverseforwarder_path, '--device', android_device,
154 '9999', '9999', '8089', '8089']))
oprypin30cda5e2017-04-24 04:15:13 -0700155
oprypin1d7392a2017-05-16 05:36:15 -0700156 # Run the Espresso code.
157 test_script = os.path.join(build_dir_android,
158 'bin', 'run_AppRTCMobileTestStubbedVideoIO')
159 _RunCommand([test_script, '--device', android_device])
mandermoed582f72017-01-23 07:55:42 -0800160
oprypin1d7392a2017-05-16 05:36:15 -0700161 # Pull the output video.
162 test_video = os.path.join(temp_dir, 'test_video.y4m')
163 _RunCommand([adb_path, '-s', android_device,
164 'pull', '/sdcard/output.y4m', test_video])
mandermoed582f72017-01-23 07:55:42 -0800165
oprypin1d7392a2017-05-16 05:36:15 -0700166 test_video_yuv = os.path.join(temp_dir, 'test_video.yuv')
mandermoed582f72017-01-23 07:55:42 -0800167
oprypin1d7392a2017-05-16 05:36:15 -0700168 ffmpeg_path = os.path.join(toolchain_dir, 'linux', 'ffmpeg')
mandermoed582f72017-01-23 07:55:42 -0800169
oprypin1d7392a2017-05-16 05:36:15 -0700170 def ConvertVideo(input_video, output_video):
171 _RunCommand([ffmpeg_path, '-y', '-i', input_video, output_video])
mandermoed582f72017-01-23 07:55:42 -0800172
oprypin1d7392a2017-05-16 05:36:15 -0700173 ConvertVideo(test_video, test_video_yuv)
mandermoed582f72017-01-23 07:55:42 -0800174
oprypin1d7392a2017-05-16 05:36:15 -0700175 reference_video = os.path.join(SRC_DIR,
176 'resources', 'reference_video_640x360_30fps.y4m')
mandermoed582f72017-01-23 07:55:42 -0800177
oprypin1d7392a2017-05-16 05:36:15 -0700178 reference_video_yuv = os.path.join(temp_dir,
179 'reference_video_640x360_30fps.yuv')
mandermoed582f72017-01-23 07:55:42 -0800180
oprypin1d7392a2017-05-16 05:36:15 -0700181 ConvertVideo(reference_video, reference_video_yuv)
mandermoed582f72017-01-23 07:55:42 -0800182
oprypin1d7392a2017-05-16 05:36:15 -0700183 # Run compare script.
ehmaldonado3aba2d12017-06-30 05:12:09 -0700184 compare_script = os.path.join(SRC_DIR, 'webrtc', 'rtc_tools',
oprypin1d7392a2017-05-16 05:36:15 -0700185 'compare_videos.py')
186 zxing_path = os.path.join(toolchain_dir, 'linux', 'zxing')
mandermoed582f72017-01-23 07:55:42 -0800187
oprypin1d7392a2017-05-16 05:36:15 -0700188 # The frame_analyzer binary should be built for local computer and not for
189 # Android
190 frame_analyzer = os.path.join(build_dir_x86, 'frame_analyzer')
mandermoed582f72017-01-23 07:55:42 -0800191
oprypin1d7392a2017-05-16 05:36:15 -0700192 frame_width = 640
193 frame_height = 360
mandermoed582f72017-01-23 07:55:42 -0800194
oprypin1d7392a2017-05-16 05:36:15 -0700195 stats_file_ref = os.path.join(temp_dir, 'stats_ref.txt')
196 stats_file_test = os.path.join(temp_dir, 'stats_test.txt')
mandermoed582f72017-01-23 07:55:42 -0800197
oprypin1d7392a2017-05-16 05:36:15 -0700198 _RunCommand([
199 sys.executable, compare_script, '--ref_video', reference_video_yuv,
200 '--test_video', test_video_yuv, '--yuv_frame_width', str(frame_width),
201 '--yuv_frame_height', str(frame_height),
202 '--stats_file_ref', stats_file_ref,
203 '--stats_file_test', stats_file_test,
204 '--frame_analyzer', frame_analyzer,
205 '--ffmpeg_path', ffmpeg_path, '--zxing_path', zxing_path])
mandermoed582f72017-01-23 07:55:42 -0800206
oprypin1d7392a2017-05-16 05:36:15 -0700207 finally:
208 for process in processes:
209 if process:
210 process.terminate()
211 process.wait()
212
213 shutil.rmtree(temp_dir)
mandermoed582f72017-01-23 07:55:42 -0800214
215
216if __name__ == '__main__':
217 sys.exit(main())
218