blob: bd4e94e4f39ea18c89bfba15340608ef9b25f3a6 [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 Lemur1175ecd2018-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 Lemur1175ecd2018-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 Lemur1175ecd2018-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')
mandermoed582f72017-01-23 07:55:42 -080089
90 args = parser.parse_args()
91 return args
92
93
Edward Lemur1175ecd2018-01-22 13:46:58 +010094def SelectAndroidDevice(adb_path):
95 # Select an Android device in case multiple are connected.
oprypinbed7a6b2017-06-19 01:16:45 -070096 try:
97 with open(BAD_DEVICES_JSON) as bad_devices_file:
98 bad_devices = json.load(bad_devices_file)
99 except IOError:
100 if os.environ.get('CHROME_HEADLESS'):
101 logging.warning('Cannot read %r', BAD_DEVICES_JSON)
102 bad_devices = {}
103
oprypin30cda5e2017-04-24 04:15:13 -0700104 for line in _RunCommandWithOutput([adb_path, 'devices']).splitlines():
105 if line.endswith('\tdevice'):
106 android_device = line.split('\t')[0]
oprypinbed7a6b2017-06-19 01:16:45 -0700107 if android_device not in bad_devices:
Edward Lemur1175ecd2018-01-22 13:46:58 +0100108 return android_device
109 raise VideoQualityTestError('Cannot find any connected Android device.')
110
111
112def SetUpTools(android_device, temp_dir, processes):
113 # Extract AppRTC.
114 apprtc_archive = os.path.join(RTC_TOOLS_DIR, 'prebuilt_apprtc.zip')
115 golang_archive = os.path.join(RTC_TOOLS_DIR, 'golang', 'linux', 'go.tar.gz')
116
117 utils.UnpackArchiveTo(apprtc_archive, temp_dir)
118 utils.UnpackArchiveTo(golang_archive, temp_dir)
119
120 # Build AppRTC.
121 build_apprtc_script = os.path.join(RTC_TOOLS_DIR, 'build_apprtc.py')
122 apprtc_src_dir = os.path.join(temp_dir, 'apprtc', 'src')
123 go_dir = os.path.join(temp_dir, 'go')
124 collider_dir = os.path.join(temp_dir, 'collider')
125
126 _RunCommand([sys.executable, build_apprtc_script, apprtc_src_dir, go_dir,
127 collider_dir])
128
129 # Start AppRTC Server.
130 dev_appserver = os.path.join(temp_dir, 'apprtc', 'temp', 'google-cloud-sdk',
131 'bin', 'dev_appserver.py')
132 appengine_dir = os.path.join(temp_dir, 'apprtc', 'out', 'app_engine')
133 processes.append(_RunBackgroundCommand([
134 sys.executable, dev_appserver, appengine_dir, '--port=9999',
135 '--admin_port=9998', '--skip_sdk_update_check', '--clear_datastore=yes']))
136
137 # Start Collider.
138 collider_path = os.path.join(temp_dir, 'collider', 'collidermain')
139 processes.append(_RunBackgroundCommand([
140 collider_path, '-tls=false', '-port=8089',
141 '-room-server=http://localhost:9999']))
142
143 # Start adb reverse forwarder.
144 reverseforwarder_path = os.path.join(
145 SRC_DIR, 'build', 'android', 'adb_reverse_forwarder.py')
146 processes.append(_RunBackgroundCommand([
147 reverseforwarder_path, '--device', android_device, '9999', '9999', '8089',
148 '8089']))
149
150
151def RunTest(android_device, adb_path, build_dir, temp_dir):
152 ffmpeg_path = os.path.join(TOOLCHAIN_DIR, 'ffmpeg')
153 def ConvertVideo(input_video, output_video):
154 _RunCommand([ffmpeg_path, '-y', '-i', input_video, output_video])
155
156 # Start loopback call and record video.
157 test_script = os.path.join(
158 build_dir, 'bin', 'run_AppRTCMobileTestStubbedVideoIO')
159 _RunCommand([test_script, '--device', android_device])
160
161 # Pull the recorded 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])
165
166 # Convert the recorded and reference videos to YUV.
167 reference_video = os.path.join(SRC_DIR,
168 'resources', 'reference_video_640x360_30fps.y4m')
169
170 test_video_yuv = os.path.join(temp_dir, 'test_video.yuv')
171 reference_video_yuv = os.path.join(
172 temp_dir, 'reference_video_640x360_30fps.yuv')
173
174 ConvertVideo(test_video, test_video_yuv)
175 ConvertVideo(reference_video, reference_video_yuv)
176
177 # Run comparison script.
178 compare_script = os.path.join(SRC_DIR, 'rtc_tools', 'compare_videos.py')
179 frame_analyzer = os.path.join(TOOLCHAIN_DIR, 'frame_analyzer')
180 zxing_path = os.path.join(TOOLCHAIN_DIR, 'zxing')
181 stats_file_ref = os.path.join(temp_dir, 'stats_ref.txt')
182 stats_file_test = os.path.join(temp_dir, 'stats_test.txt')
183
184 _RunCommand([
185 sys.executable, compare_script,
186 '--ref_video', reference_video_yuv,
187 '--test_video', test_video_yuv,
188 '--yuv_frame_width', '640',
189 '--yuv_frame_height', '360',
190 '--stats_file_ref', stats_file_ref,
191 '--stats_file_test', stats_file_test,
192 '--frame_analyzer', frame_analyzer,
193 '--ffmpeg_path', ffmpeg_path,
194 '--zxing_path', zxing_path])
195
196
197def main():
198 logging.basicConfig(level=logging.INFO)
199
200 args = _ParseArgs()
201
202 temp_dir = args.temp_dir
203 build_dir = args.build_dir_android
204 adb_path = args.adb_path
oprypin30cda5e2017-04-24 04:15:13 -0700205
oprypin1d7392a2017-05-16 05:36:15 -0700206 processes = []
Edward Lemur1175ecd2018-01-22 13:46:58 +0100207 temp_dir = CreateEmptyDir(temp_dir)
oprypin1d7392a2017-05-16 05:36:15 -0700208 try:
Edward Lemur1175ecd2018-01-22 13:46:58 +0100209 android_device = SelectAndroidDevice(adb_path)
210 SetUpTools(android_device, temp_dir, processes)
211 RunTest(android_device, adb_path, build_dir, temp_dir)
oprypin1d7392a2017-05-16 05:36:15 -0700212 finally:
213 for process in processes:
214 if process:
215 process.terminate()
216 process.wait()
217
Edward Lemur1175ecd2018-01-22 13:46:58 +0100218 utils.RemoveDirectory(temp_dir)
mandermoed582f72017-01-23 07:55:42 -0800219
220
221if __name__ == '__main__':
222 sys.exit(main())
223