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