1# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import os 7import time 8 9from autotest_lib.client.bin import site_utils, test, utils 10from autotest_lib.client.common_lib import error 11from autotest_lib.client.common_lib import file_utils 12from autotest_lib.client.common_lib.cros import chrome 13from autotest_lib.client.cros import power_status, power_utils 14from autotest_lib.client.cros import service_stopper 15from autotest_lib.client.cros.video import histogram_verifier 16from autotest_lib.client.cros.video import constants 17 18 19DISABLE_ACCELERATED_VIDEO_DECODE_BROWSER_ARGS = [ 20 '--disable-accelerated-video-decode'] 21DOWNLOAD_BASE = 'http://commondatastorage.googleapis.com/chromiumos-test-assets-public/' 22 23PLAYBACK_WITH_HW_ACCELERATION = 'playback_with_hw_acceleration' 24PLAYBACK_WITHOUT_HW_ACCELERATION = 'playback_without_hw_acceleration' 25 26# Measurement duration in seconds. 27MEASUREMENT_DURATION = 30 28# Time to exclude from calculation after playing a video [seconds]. 29STABILIZATION_DURATION = 10 30 31# List of thermal throttling services that should be disabled. 32# - temp_metrics for link. 33# - thermal for daisy, snow, pit etc. 34THERMAL_SERVICES = ['temp_metrics', 'thermal'] 35 36# Time in seconds to wait for cpu idle until giveup. 37WAIT_FOR_IDLE_CPU_TIMEOUT = 60.0 38# Maximum percent of cpu usage considered as idle. 39CPU_IDLE_USAGE = 0.1 40 41CPU_USAGE_DESCRIPTION = 'video_cpu_usage_' 42DROPPED_FRAMES_DESCRIPTION = 'video_dropped_frames_' 43DROPPED_FRAMES_PERCENT_DESCRIPTION = 'video_dropped_frames_percent_' 44POWER_DESCRIPTION = 'video_mean_energy_rate_' 45 46# Minimum battery charge percentage to run the test 47BATTERY_INITIAL_CHARGED_MIN = 10 48 49 50class video_PlaybackPerf(test.test): 51 """ 52 The test outputs the cpu usage, the dropped frame count and the power 53 consumption for video playback to performance dashboard. 54 """ 55 version = 1 56 arc_mode = None 57 58 59 def initialize(self): 60 self._service_stopper = None 61 self._original_governors = None 62 self._backlight = None 63 64 65 def start_playback(self, cr, local_path): 66 """ 67 Opens the video and plays it. 68 69 @param cr: Autotest Chrome instance. 70 @param local_path: path to the local video file to play. 71 """ 72 cr.browser.platform.SetHTTPServerDirectories(self.bindir) 73 74 tab = cr.browser.tabs[0] 75 tab.Navigate(cr.browser.platform.http_server.UrlOf(local_path)) 76 tab.WaitForDocumentReadyStateToBeComplete() 77 tab.EvaluateJavaScript("document.getElementsByTagName('video')[0]." 78 "loop=true") 79 80 81 def run_once(self, video_name, video_description, power_test=False, 82 arc_mode=None): 83 """ 84 Runs the video_PlaybackPerf test. 85 86 @param video_name: the name of video to play in the DOWNLOAD_BASE 87 @param video_description: a string describes the video to play which 88 will be part of entry name in dashboard. 89 @param power_test: True if this is a power test and it would only run 90 the power test. If False, it would run the cpu usage test and 91 the dropped frame count test. 92 @param arc_mode: if 'enabled', run the test with Android enabled. 93 """ 94 # Download test video. 95 url = DOWNLOAD_BASE + video_name 96 local_path = os.path.join(self.bindir, os.path.basename(video_name)) 97 logging.info("Downloading %s to %s", url, local_path); 98 file_utils.download_file(url, local_path) 99 self.arc_mode = arc_mode 100 101 if not power_test: 102 # Run the video playback dropped frame tests. 103 keyvals = self.test_dropped_frames(local_path) 104 105 # Every dictionary value is a tuple. The first element of the tuple 106 # is dropped frames. The second is dropped frames percent. 107 keyvals_dropped_frames = {k: v[0] for k, v in keyvals.iteritems()} 108 keyvals_dropped_frames_percent = { 109 k: v[1] for k, v in keyvals.iteritems()} 110 111 self.log_result(keyvals_dropped_frames, DROPPED_FRAMES_DESCRIPTION + 112 video_description, 'frames') 113 self.log_result(keyvals_dropped_frames_percent, 114 DROPPED_FRAMES_PERCENT_DESCRIPTION + 115 video_description, 'percent') 116 117 # Run the video playback cpu usage tests. 118 keyvals = self.test_cpu_usage(local_path) 119 self.log_result(keyvals, CPU_USAGE_DESCRIPTION + video_description, 120 'percent') 121 else: 122 keyvals = self.test_power(local_path) 123 self.log_result(keyvals, POWER_DESCRIPTION + video_description, 'W') 124 125 126 def test_dropped_frames(self, local_path): 127 """ 128 Runs the video dropped frame test. 129 130 @param local_path: the path to the video file. 131 132 @return a dictionary that contains the test result. 133 """ 134 def get_dropped_frames(cr): 135 time.sleep(MEASUREMENT_DURATION) 136 tab = cr.browser.tabs[0] 137 decoded_frame_count = tab.EvaluateJavaScript( 138 "document.getElementsByTagName" 139 "('video')[0].webkitDecodedFrameCount") 140 dropped_frame_count = tab.EvaluateJavaScript( 141 "document.getElementsByTagName" 142 "('video')[0].webkitDroppedFrameCount") 143 if decoded_frame_count != 0: 144 dropped_frame_percent = \ 145 100.0 * dropped_frame_count / decoded_frame_count 146 else: 147 logging.error("No frame is decoded. Set drop percent to 100.") 148 dropped_frame_percent = 100.0 149 logging.info("Decoded frames=%d, dropped frames=%d, percent=%f", 150 decoded_frame_count, 151 dropped_frame_count, 152 dropped_frame_percent) 153 return (dropped_frame_count, dropped_frame_percent) 154 return self.test_playback(local_path, get_dropped_frames) 155 156 157 def test_cpu_usage(self, local_path): 158 """ 159 Runs the video cpu usage test. 160 161 @param local_path: the path to the video file. 162 163 @return a dictionary that contains the test result. 164 """ 165 def get_cpu_usage(cr): 166 time.sleep(STABILIZATION_DURATION) 167 cpu_usage_start = site_utils.get_cpu_usage() 168 time.sleep(MEASUREMENT_DURATION) 169 cpu_usage_end = site_utils.get_cpu_usage() 170 return site_utils.compute_active_cpu_time(cpu_usage_start, 171 cpu_usage_end) * 100 172 if not utils.wait_for_idle_cpu(WAIT_FOR_IDLE_CPU_TIMEOUT, 173 CPU_IDLE_USAGE): 174 raise error.TestError('Could not get idle CPU.') 175 if not utils.wait_for_cool_machine(): 176 raise error.TestError('Could not get cold machine.') 177 # Stop the thermal service that may change the cpu frequency. 178 self._service_stopper = service_stopper.ServiceStopper(THERMAL_SERVICES) 179 self._service_stopper.stop_services() 180 # Set the scaling governor to performance mode to set the cpu to the 181 # highest frequency available. 182 self._original_governors = utils.set_high_performance_mode() 183 return self.test_playback(local_path, get_cpu_usage) 184 185 186 def test_power(self, local_path): 187 """ 188 Runs the video power consumption test. 189 190 @param local_path: the path to the video file. 191 192 @return a dictionary that contains the test result. 193 """ 194 195 self._backlight = power_utils.Backlight() 196 self._backlight.set_default() 197 198 self._service_stopper = service_stopper.ServiceStopper( 199 service_stopper.ServiceStopper.POWER_DRAW_SERVICES) 200 self._service_stopper.stop_services() 201 202 self._power_status = power_status.get_status() 203 # Verify that we are running on battery and the battery is sufficiently 204 # charged. 205 self._power_status.assert_battery_state(BATTERY_INITIAL_CHARGED_MIN) 206 207 measurements = [power_status.SystemPower( 208 self._power_status.battery_path)] 209 210 def get_power(cr): 211 power_logger = power_status.PowerLogger(measurements) 212 power_logger.start() 213 time.sleep(STABILIZATION_DURATION) 214 start_time = time.time() 215 time.sleep(MEASUREMENT_DURATION) 216 power_logger.checkpoint('result', start_time) 217 keyval = power_logger.calc() 218 return keyval['result_' + measurements[0].domain + '_pwr'] 219 220 return self.test_playback(local_path, get_power) 221 222 223 def test_playback(self, local_path, gather_result): 224 """ 225 Runs the video playback test with and without hardware acceleration. 226 227 @param local_path: the path to the video file. 228 @param gather_result: a function to run and return the test result 229 after chrome opens. The input parameter of the funciton is 230 Autotest chrome instance. 231 232 @return a dictionary that contains test the result. 233 """ 234 keyvals = {} 235 236 with chrome.Chrome(arc_mode=self.arc_mode, 237 init_network_controller=True) as cr: 238 # Open the video playback page and start playing. 239 self.start_playback(cr, local_path) 240 result = gather_result(cr) 241 242 # Check if decode is hardware accelerated. 243 if histogram_verifier.is_bucket_present( 244 cr, 245 constants.MEDIA_GVD_INIT_STATUS, 246 constants.MEDIA_GVD_BUCKET): 247 keyvals[PLAYBACK_WITH_HW_ACCELERATION] = result 248 else: 249 logging.info("Can not use hardware decoding.") 250 keyvals[PLAYBACK_WITHOUT_HW_ACCELERATION] = result 251 return keyvals 252 253 # Start chrome with disabled video hardware decode flag. 254 with chrome.Chrome(extra_browser_args= 255 DISABLE_ACCELERATED_VIDEO_DECODE_BROWSER_ARGS, 256 arc_mode=self.arc_mode) as cr: 257 # Open the video playback page and start playing. 258 self.start_playback(cr, local_path) 259 result = gather_result(cr) 260 261 # Make sure decode is not hardware accelerated. 262 if histogram_verifier.is_bucket_present( 263 cr, 264 constants.MEDIA_GVD_INIT_STATUS, 265 constants.MEDIA_GVD_BUCKET): 266 raise error.TestError( 267 'Video decode acceleration should not be working.') 268 keyvals[PLAYBACK_WITHOUT_HW_ACCELERATION] = result 269 270 return keyvals 271 272 273 def log_result(self, keyvals, description, units): 274 """ 275 Logs the test result output to the performance dashboard. 276 277 @param keyvals: a dictionary that contains results returned by 278 test_playback. 279 @param description: a string that describes the video and test result 280 and it will be part of the entry name in the dashboard. 281 @param units: the units of test result. 282 """ 283 result_with_hw = keyvals.get(PLAYBACK_WITH_HW_ACCELERATION) 284 if result_with_hw is not None: 285 self.output_perf_value( 286 description= 'hw_' + description, value=result_with_hw, 287 units=units, higher_is_better=False) 288 289 result_without_hw = keyvals[PLAYBACK_WITHOUT_HW_ACCELERATION] 290 self.output_perf_value( 291 description= 'sw_' + description, value=result_without_hw, 292 units=units, higher_is_better=False) 293 294 295 def cleanup(self): 296 # cleanup() is run by common_lib/test.py. 297 if self._backlight: 298 self._backlight.restore() 299 if self._service_stopper: 300 self._service_stopper.restore_services() 301 if self._original_governors: 302 utils.restore_scaling_governor_states(self._original_governors) 303 304 super(video_PlaybackPerf, self).cleanup() 305