• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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