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