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