1# Copyright 2018 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 logging 5import os 6import time 7 8from autotest_lib.client.bin import utils 9from autotest_lib.client.common_lib import file_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_VideoPlayback(power_test.power_Test): 17 """class for power_VideoPlayback test. 18 """ 19 version = 1 20 21 _BASE_URL='http://commondatastorage.googleapis.com/chromiumos-test-assets-public/tast/cros/video/perf/' 22 23 # list of video name and url. 24 _VIDEOS = [ 25 ('h264_1080_30fps', 26 _BASE_URL + 'h264/1080p_30fps_300frames_20181225.h264.mp4' 27 ), 28 ('h264_1080_60fps', 29 _BASE_URL + 'h264/1080p_60fps_600frames_20181225.h264.mp4' 30 ), 31 ('h264_4k_30fps', 32 _BASE_URL + 'h264/2160p_30fps_300frames_20181225.h264.mp4' 33 ), 34 ('h264_4k_60fps', 35 _BASE_URL + 'h264/2160p_60fps_600frames_20181225.h264.mp4' 36 ), 37 ('vp8_1080_30fps', 38 _BASE_URL + 'vp8/1080p_30fps_300frames_20181225.vp8.webm' 39 ), 40 ('vp8_1080_60fps', 41 _BASE_URL + 'vp8/1080p_60fps_600frames_20181225.vp8.webm' 42 ), 43 ('vp8_4k_30fps', 44 _BASE_URL + 'vp8/2160p_30fps_300frames_20181225.vp8.webm' 45 ), 46 ('vp8_4k_60fps', 47 _BASE_URL + 'vp8/2160p_60fps_600frames_20181225.vp8.webm' 48 ), 49 ('vp9_1080_30fps', 50 _BASE_URL + 'vp9/1080p_30fps_300frames_20181225.vp9.webm' 51 ), 52 ('vp9_1080_60fps', 53 _BASE_URL + 'vp9/1080p_60fps_600frames_20181225.vp9.webm' 54 ), 55 ('vp9_4k_30fps', 56 _BASE_URL + 'vp9/2160p_30fps_300frames_20181225.vp9.webm' 57 ), 58 ('vp9_4k_60fps', 59 _BASE_URL + 'vp9/2160p_60fps_600frames_20181225.vp9.webm' 60 ), 61 ('av1_720_30fps', 62 _BASE_URL + 'av1/720p_30fps_300frames_20190305.av1.mp4' 63 ), 64 ('av1_720_60fps', 65 _BASE_URL + 'av1/720p_60fps_600frames_20190305.av1.mp4' 66 ), 67 ('av1_1080_30fps', 68 _BASE_URL + 'av1/1080p_30fps_300frames_20190305.av1.mp4' 69 ), 70 ('av1_1080_60fps', 71 _BASE_URL + 'av1/1080p_60fps_600frames_20190305.av1.mp4' 72 ), 73 ] 74 75 # Ram disk location to download video file. 76 # We use ram disk to avoid power hit from network / disk usage. 77 _RAMDISK = '/tmp/ramdisk' 78 79 # Time in seconds to wait after set up before starting test. 80 _WAIT_FOR_IDLE = 10 81 82 # Time in seconds to measure power per video file. 83 _MEASUREMENT_DURATION = 30 84 85 # Chrome arguemnts to disable HW video decode 86 _DISABLE_HW_VIDEO_DECODE_ARGS = '--disable-accelerated-video-decode' 87 88 def initialize(self, pdash_note='', seconds_period=5): 89 """Create and mount ram disk to download video.""" 90 super(power_VideoPlayback, self).initialize( 91 seconds_period=seconds_period, pdash_note=pdash_note) 92 utils.run('mkdir -p %s' % self._RAMDISK) 93 # Don't throw an exception on errors. 94 result = utils.run('mount -t ramfs -o context=u:object_r:tmpfs:s0 ' 95 'ramfs %s' % self._RAMDISK, ignore_status=True) 96 if result.exit_status: 97 logging.info('cannot mount ramfs with context=u:object_r:tmpfs:s0,' 98 ' trying plain mount') 99 # Try again without selinux options. This time fail on error. 100 utils.run('mount -t ramfs ramfs %s' % self._RAMDISK) 101 audio_helper.set_volume_levels(10, 10) 102 103 def _play_video(self, cr, local_path): 104 """Opens the video and plays it. 105 106 @param cr: Autotest Chrome instance. 107 @param local_path: path to the local video file to play. 108 """ 109 tab = cr.browser.tabs[0] 110 tab.Navigate(cr.browser.platform.http_server.UrlOf(local_path)) 111 tab.WaitForDocumentReadyStateToBeComplete() 112 tab.EvaluateJavaScript("document.getElementsByTagName('video')[0]." 113 "loop=true") 114 115 def _calculate_dropped_frame_percent(self, tab): 116 """Calculate percent of dropped frame. 117 118 @param tab: tab object that played video in Autotest Chrome instance. 119 """ 120 decoded_frame_count = tab.EvaluateJavaScript( 121 "document.getElementsByTagName" 122 "('video')[0].webkitDecodedFrameCount") 123 dropped_frame_count = tab.EvaluateJavaScript( 124 "document.getElementsByTagName" 125 "('video')[0].webkitDroppedFrameCount") 126 if decoded_frame_count != 0: 127 dropped_frame_percent = \ 128 100.0 * dropped_frame_count / decoded_frame_count 129 else: 130 logging.error("No frame is decoded. Set drop percent to 100.") 131 dropped_frame_percent = 100.0 132 133 logging.info("Decoded frames=%d, dropped frames=%d, percent=%f", 134 decoded_frame_count, dropped_frame_count, dropped_frame_percent) 135 return dropped_frame_percent 136 137 def run_once(self, videos=None, secs_per_video=_MEASUREMENT_DURATION, 138 use_hw_decode=True): 139 """run_once method. 140 141 @param videos: list of tuple of tagname and video url to test. 142 @param secs_per_video: time in seconds to play video and measure power. 143 @param use_hw_decode: if False, disable hw video decoding. 144 """ 145 videos_local = [] 146 loop = 0 147 148 if not videos: 149 videos = self._VIDEOS 150 151 # Download video to ramdisk 152 for name, url in videos: 153 local_path = os.path.join(self._RAMDISK, os.path.basename(url)) 154 logging.info('Downloading %s to %s', url, local_path) 155 file_utils.download_file(url, local_path) 156 videos_local.append((name, local_path)) 157 158 extra_browser_args = [] 159 if not use_hw_decode: 160 extra_browser_args.append(self._DISABLE_HW_VIDEO_DECODE_ARGS) 161 162 with chrome.Chrome(extra_browser_args=extra_browser_args, 163 init_network_controller=True) as self.cr: 164 self.cr.browser.platform.SetHTTPServerDirectories(self._RAMDISK) 165 tab = self.cr.browser.tabs.New() 166 tab.Activate() 167 168 # Just measure power in full-screen. 169 fullscreen = tab.EvaluateJavaScript('document.webkitIsFullScreen') 170 if not fullscreen: 171 with keyboard.Keyboard() as keys: 172 keys.press_key('f4') 173 174 time.sleep(self._WAIT_FOR_IDLE) 175 self.start_measurements() 176 177 for name, url in videos_local: 178 logging.info('Playing video: %s', name) 179 self._play_video(self.cr, url) 180 tagname = '%s_%s' % (self.tagged_testname, name) 181 loop_start = time.time() 182 self.loop_sleep(loop, secs_per_video) 183 self.keyvals[name + '_dropped_frame_percent'] = \ 184 self._calculate_dropped_frame_percent(tab) 185 self.checkpoint_measurements(tagname, loop_start) 186 loop += 1 187 188 def cleanup(self): 189 """Unmount ram disk.""" 190 utils.run('umount %s' % self._RAMDISK) 191 super(power_VideoPlayback, self).cleanup() 192