# Copyright 2018 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import logging import os import time from autotest_lib.client.bin import utils from autotest_lib.client.common_lib import file_utils from autotest_lib.client.common_lib.cros import chrome from autotest_lib.client.cros.audio import audio_helper from autotest_lib.client.cros.input_playback import keyboard from autotest_lib.client.cros.power import power_test class power_VideoPlayback(power_test.power_Test): """class for power_VideoPlayback test. """ version = 1 _BASE_URL='http://commondatastorage.googleapis.com/chromiumos-test-assets-public/tast/cros/video/perf/' # list of video name and url. _VIDEOS = [ ('h264_1080_30fps', _BASE_URL + 'h264/1080p_30fps_300frames_20181225.h264.mp4' ), ('h264_1080_60fps', _BASE_URL + 'h264/1080p_60fps_600frames_20181225.h264.mp4' ), ('h264_4k_30fps', _BASE_URL + 'h264/2160p_30fps_300frames_20181225.h264.mp4' ), ('h264_4k_60fps', _BASE_URL + 'h264/2160p_60fps_600frames_20181225.h264.mp4' ), ('vp8_1080_30fps', _BASE_URL + 'vp8/1080p_30fps_300frames_20181225.vp8.webm' ), ('vp8_1080_60fps', _BASE_URL + 'vp8/1080p_60fps_600frames_20181225.vp8.webm' ), ('vp8_4k_30fps', _BASE_URL + 'vp8/2160p_30fps_300frames_20181225.vp8.webm' ), ('vp8_4k_60fps', _BASE_URL + 'vp8/2160p_60fps_600frames_20181225.vp8.webm' ), ('vp9_1080_30fps', _BASE_URL + 'vp9/1080p_30fps_300frames_20181225.vp9.webm' ), ('vp9_1080_60fps', _BASE_URL + 'vp9/1080p_60fps_600frames_20181225.vp9.webm' ), ('vp9_4k_30fps', _BASE_URL + 'vp9/2160p_30fps_300frames_20181225.vp9.webm' ), ('vp9_4k_60fps', _BASE_URL + 'vp9/2160p_60fps_600frames_20181225.vp9.webm' ), ('av1_720_30fps', _BASE_URL + 'av1/720p_30fps_300frames_20190305.av1.mp4' ), ('av1_720_60fps', _BASE_URL + 'av1/720p_60fps_600frames_20190305.av1.mp4' ), ('av1_1080_30fps', _BASE_URL + 'av1/1080p_30fps_300frames_20190305.av1.mp4' ), ('av1_1080_60fps', _BASE_URL + 'av1/1080p_60fps_600frames_20190305.av1.mp4' ), ] # Ram disk location to download video file. # We use ram disk to avoid power hit from network / disk usage. _RAMDISK = '/tmp/ramdisk' # Time in seconds to wait after set up before starting test. _WAIT_FOR_IDLE = 10 # Time in seconds to measure power per video file. _MEASUREMENT_DURATION = 30 # Chrome arguemnts to disable HW video decode _DISABLE_HW_VIDEO_DECODE_ARGS = '--disable-accelerated-video-decode' def initialize(self, pdash_note='', seconds_period=5): """Create and mount ram disk to download video.""" super(power_VideoPlayback, self).initialize( seconds_period=seconds_period, pdash_note=pdash_note) utils.run('mkdir -p %s' % self._RAMDISK) # Don't throw an exception on errors. result = utils.run('mount -t ramfs -o context=u:object_r:tmpfs:s0 ' 'ramfs %s' % self._RAMDISK, ignore_status=True) if result.exit_status: logging.info('cannot mount ramfs with context=u:object_r:tmpfs:s0,' ' trying plain mount') # Try again without selinux options. This time fail on error. utils.run('mount -t ramfs ramfs %s' % self._RAMDISK) audio_helper.set_volume_levels(10, 10) def _play_video(self, cr, local_path): """Opens the video and plays it. @param cr: Autotest Chrome instance. @param local_path: path to the local video file to play. """ tab = cr.browser.tabs[0] tab.Navigate(cr.browser.platform.http_server.UrlOf(local_path)) tab.WaitForDocumentReadyStateToBeComplete() tab.EvaluateJavaScript("document.getElementsByTagName('video')[0]." "loop=true") def _calculate_dropped_frame_percent(self, tab): """Calculate percent of dropped frame. @param tab: tab object that played video in Autotest Chrome instance. """ decoded_frame_count = tab.EvaluateJavaScript( "document.getElementsByTagName" "('video')[0].webkitDecodedFrameCount") dropped_frame_count = tab.EvaluateJavaScript( "document.getElementsByTagName" "('video')[0].webkitDroppedFrameCount") if decoded_frame_count != 0: dropped_frame_percent = \ 100.0 * dropped_frame_count / decoded_frame_count else: logging.error("No frame is decoded. Set drop percent to 100.") dropped_frame_percent = 100.0 logging.info("Decoded frames=%d, dropped frames=%d, percent=%f", decoded_frame_count, dropped_frame_count, dropped_frame_percent) return dropped_frame_percent def run_once(self, videos=None, secs_per_video=_MEASUREMENT_DURATION, use_hw_decode=True): """run_once method. @param videos: list of tuple of tagname and video url to test. @param secs_per_video: time in seconds to play video and measure power. @param use_hw_decode: if False, disable hw video decoding. """ videos_local = [] loop = 0 if not videos: videos = self._VIDEOS # Download video to ramdisk for name, url in videos: local_path = os.path.join(self._RAMDISK, os.path.basename(url)) logging.info('Downloading %s to %s', url, local_path) file_utils.download_file(url, local_path) videos_local.append((name, local_path)) extra_browser_args = [] if not use_hw_decode: extra_browser_args.append(self._DISABLE_HW_VIDEO_DECODE_ARGS) with chrome.Chrome(extra_browser_args=extra_browser_args, init_network_controller=True) as self.cr: self.cr.browser.platform.SetHTTPServerDirectories(self._RAMDISK) tab = self.cr.browser.tabs.New() tab.Activate() # Just measure power in full-screen. fullscreen = tab.EvaluateJavaScript('document.webkitIsFullScreen') if not fullscreen: with keyboard.Keyboard() as keys: keys.press_key('f4') time.sleep(self._WAIT_FOR_IDLE) self.start_measurements() for name, url in videos_local: logging.info('Playing video: %s', name) self._play_video(self.cr, url) tagname = '%s_%s' % (self.tagged_testname, name) loop_start = time.time() self.loop_sleep(loop, secs_per_video) self.keyvals[name + '_dropped_frame_percent'] = \ self._calculate_dropped_frame_percent(tab) self.checkpoint_measurements(tagname, loop_start) loop += 1 def cleanup(self): """Unmount ram disk.""" utils.run('umount %s' % self._RAMDISK) super(power_VideoPlayback, self).cleanup()