1# Lint as: python2, python3 2# Copyright 2019 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5import abc 6import logging 7import time 8 9from autotest_lib.client.bin import utils 10from autotest_lib.client.common_lib.cros import chrome 11from autotest_lib.client.cros.audio import audio_helper 12from autotest_lib.client.cros.input_playback import keyboard 13from autotest_lib.client.cros.power import power_test 14 15 16class power_VideoTest(power_test.power_Test): 17 """Optional base class for power related video tests.""" 18 version = 1 19 20 # Ram disk location to download video file. 21 # We use ram disk to avoid power hit from network / disk usage. 22 _RAMDISK = '/tmp/ramdisk' 23 24 # Time in seconds to wait after set up before starting each video. 25 _WAIT_FOR_IDLE = 15 26 27 # Time in seconds to measure power per video file. 28 _MEASUREMENT_DURATION = 120 29 30 # Chrome arguments to disable HW video decode 31 _DISABLE_HW_VIDEO_DECODE_ARGS = '--disable-accelerated-video-decode' 32 33 34 def initialize(self, seconds_period=3, pdash_note='', 35 force_discharge=False, run_arc=True): 36 """Create and mount ram disk to download video.""" 37 super(power_VideoTest, self).initialize( 38 seconds_period=seconds_period, pdash_note=pdash_note, 39 force_discharge=force_discharge, run_arc=run_arc) 40 utils.run('mkdir -p %s' % self._RAMDISK) 41 # Don't throw an exception on errors. 42 result = utils.run('mount -t ramfs -o context=u:object_r:tmpfs:s0 ' 43 'ramfs %s' % self._RAMDISK, ignore_status=True) 44 if result.exit_status: 45 logging.info('cannot mount ramfs with context=u:object_r:tmpfs:s0,' 46 ' trying plain mount') 47 # Try again without selinux options. This time fail on error. 48 utils.run('mount -t ramfs ramfs %s' % self._RAMDISK) 49 audio_helper.set_volume_levels(10, 10) 50 51 @abc.abstractmethod 52 def _prepare_video(self, cr, url): 53 """Prepare browser session before playing video. 54 55 @param cr: Autotest Chrome instance. 56 @param url: url of video file to play. 57 """ 58 raise NotImplementedError() 59 60 @abc.abstractmethod 61 def _start_video(self, cr, url): 62 """Open the video and play it. 63 64 @param cr: Autotest Chrome instance. 65 @param url: url of video file to play. 66 """ 67 raise NotImplementedError() 68 69 @abc.abstractmethod 70 def _teardown_video(self, cr, url): 71 """Teardown browser session after playing video. 72 73 @param cr: Autotest Chrome instance. 74 @param url: url of video file to play. 75 """ 76 raise NotImplementedError() 77 78 def _calculate_dropped_frame_percent(self, tab): 79 """Calculate percent of dropped frame. 80 81 @param tab: tab object that played video in Autotest Chrome instance. 82 """ 83 decoded_frame_count = tab.EvaluateJavaScript( 84 "document.getElementsByTagName" 85 "('video')[0].webkitDecodedFrameCount") 86 dropped_frame_count = tab.EvaluateJavaScript( 87 "document.getElementsByTagName" 88 "('video')[0].webkitDroppedFrameCount") 89 if decoded_frame_count != 0: 90 dropped_frame_percent = \ 91 100.0 * dropped_frame_count / decoded_frame_count 92 else: 93 logging.error("No frame is decoded. Set drop percent to 100.") 94 dropped_frame_percent = 100.0 95 96 logging.info("Decoded frames=%d, dropped frames=%d, percent=%f", 97 decoded_frame_count, dropped_frame_count, dropped_frame_percent) 98 return dropped_frame_percent 99 100 def run_once(self, videos=None, secs_per_video=_MEASUREMENT_DURATION, 101 use_hw_decode=True): 102 """run_once method. 103 104 @param videos: list of tuple of tagname and video url to test. 105 @param secs_per_video: time in seconds to play video and measure power. 106 @param use_hw_decode: if False, disable hw video decoding. 107 """ 108 # --disable-sync disables test account info sync, eg. Wi-Fi credentials, 109 # so that each test run does not remember info from last test run. 110 extra_browser_args = ['--disable-sync'] 111 # b/228256145 to avoid powerd restart 112 extra_browser_args.append('--disable-features=FirmwareUpdaterApp') 113 if not use_hw_decode: 114 extra_browser_args.append(self._DISABLE_HW_VIDEO_DECODE_ARGS) 115 116 with chrome.Chrome(extra_browser_args=extra_browser_args, 117 init_network_controller=True, 118 arc_mode=self._arc_mode) as self.cr: 119 # Chrome always starts with an empty tab, so we just use that one. 120 tab = self.cr.browser.tabs[0] 121 tab.Activate() 122 123 # Stop services again as Chrome might have restarted them. 124 self._services.stop_services() 125 126 # Just measure power in full-screen. 127 fullscreen = tab.EvaluateJavaScript('document.webkitIsFullScreen') 128 if not fullscreen: 129 with keyboard.Keyboard() as keys: 130 keys.press_key('f4') 131 132 self.start_measurements() 133 idle_start = time.time() 134 135 for name, url in videos: 136 self._prepare_video(self.cr, url) 137 time.sleep(self._WAIT_FOR_IDLE) 138 139 logging.info('Playing video: %s', name) 140 self._start_video(self.cr, url) 141 self.checkpoint_measurements('idle', idle_start) 142 143 loop_start = time.time() 144 time.sleep(secs_per_video) 145 self.checkpoint_measurements(name, loop_start) 146 147 idle_start = time.time() 148 self.keyvals[name + '_dropped_frame_percent'] = \ 149 self._calculate_dropped_frame_percent(tab) 150 self._teardown_video(self.cr, url) 151 152 def cleanup(self): 153 """Unmount ram disk.""" 154 utils.run('umount %s' % self._RAMDISK) 155 super(power_VideoTest, self).cleanup() 156