1 # Copyright (c) 2013 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 time 7 8 from autotest_lib.client.bin import utils 9 from autotest_lib.client.common_lib import error 10 from autotest_lib.client.cros.graphics import graphics_utils 11 from autotest_lib.client.cros.power import power_utils, power_rapl, power_status 12 13 14 PLAYBACK_TEST_TIME_S = 10 15 PLAYER_ENDED_STATE = 'Ended' 16 PLAYER_PAUSE_STATE = 'Paused' 17 PLAYER_PLAYING_STATE = 'Playing' 18 WAIT_TIMEOUT_S = 20 19 20 21 class YouTubeHelper(object): 22 """A helper class contains YouTube related utility functions. 23 24 To use this class, please call wait_for_player_state(playing) as below 25 before calling set_video_duration. Please note that set_video_duration 26 must be called in order to access few functions which uses the video 27 length member variable. 28 29 yh = youtube_helper.YouTubeHelper(tab) 30 yh.wait_for_player_state(PLAYER_PLAYING_STATE) 31 yh.set_video_duration() 32 33 """ 34 35 def __init__(self, youtube_tab, power_logging=False): 36 self._tab = youtube_tab 37 self._video_duration = 0 38 self.power_logger = None 39 40 if power_logging and power_utils.has_rapl_support(): 41 self.power_logger = power_status.PowerLogger( 42 power_rapl.create_rapl()) 43 self.power_logger.start() 44 45 def set_video_duration(self): 46 """Sets the video duration.""" 47 self._video_duration = (int(self._tab.EvaluateJavaScript( 48 'player.getDuration()'))) 49 50 def get_power_measurement(self): 51 """Return power measurement. 52 53 @return: power readings. None if power_logging is not enabled. 54 """ 55 if self.power_logger: 56 return self.power_logger.calc() 57 return None 58 59 def video_current_time(self): 60 """Returns video's current playback time. 61 62 Returns: 63 returns the current playback location in seconds (int). 64 65 """ 66 return int(self._tab.EvaluateJavaScript('player.getCurrentTime()')) 67 68 def get_player_status(self): 69 """Returns the player status.""" 70 return self._tab.EvaluateJavaScript( 71 '(typeof playerStatus !== \'undefined\') && ' 72 'playerStatus.innerHTML') 73 74 def set_playback_quality(self, quality): 75 """Set the video quality to the quality passed in the arg. 76 77 @param quality: video quality to set. 78 79 """ 80 self._tab.ExecuteJavaScript( 81 'player.setPlaybackQuality("%s")' % quality) 82 83 def get_playback_quality(self): 84 """Returns the playback quality.""" 85 return self._tab.EvaluateJavaScript('player.getPlaybackQuality()') 86 87 def wait_for_player_state(self, expected_status): 88 """Wait till the player status changes to expected_status. 89 90 If the status doesn't change for long, the test will time out after 91 WAIT_TIMEOUT_S and fails. 92 93 @param expected_status: status which is expected for the test 94 to continue. 95 96 """ 97 utils.poll_for_condition( 98 lambda: self.get_player_status() == expected_status, 99 exception=error.TestError( 100 'Video failed to load. Player expected status: %s' 101 ' and current status: %s.' 102 % (expected_status, self.get_player_status())), 103 timeout=WAIT_TIMEOUT_S, 104 sleep_interval=1) 105 106 def verify_video_playback(self): 107 """Verify the video playback.""" 108 logging.info('Verifying the YouTube video playback.') 109 playback = 0 # seconds 110 prev_playback = 0 111 count = 0 112 while (self.video_current_time() < self._video_duration 113 and playback < PLAYBACK_TEST_TIME_S): 114 time.sleep(1) 115 if self.video_current_time() <= prev_playback: 116 if count < 2: 117 logging.info('Retrying to video playback test.') 118 self._tab.ExecuteJavaScript( 119 'player.seekTo(%d, true)' 120 % (self.video_current_time() + 2)) 121 time.sleep(1) 122 count = count + 1 123 else: 124 player_status = self.get_player_status() 125 raise error.TestError( 126 'Video is not playing. Player status: %s.' % 127 player_status) 128 prev_playback = self.video_current_time() 129 playback = playback + 1 130 131 def wait_for_expected_resolution(self, expected_quality): 132 """Wait for some time for getting expected resolution. 133 134 @param expected_quality: quality to be set. 135 136 """ 137 for _ in range(WAIT_TIMEOUT_S): 138 dummy_quality = self.get_playback_quality() 139 if dummy_quality == expected_quality: 140 logging.info('Expected resolution is set.') 141 break 142 else: 143 logging.info('Waiting for expected resolution.') 144 time.sleep(0.1) 145 146 def verify_video_resolutions(self, power_measurement=False): 147 """Verify available video resolutions. 148 149 Video resolution should be 360p, 480p, 720p and 1080p. 150 151 """ 152 logging.info('Verifying the video resolutions.') 153 video_qualities = self._tab.EvaluateJavaScript( 154 'player.getAvailableQualityLevels()') 155 logging.info('Available video resolutions: %s', video_qualities) 156 if not video_qualities: 157 raise error.TestError( 158 'Player failed to return available video qualities.') 159 video_qualities.reverse() 160 161 running_quality = self.get_playback_quality() 162 index = video_qualities.index(running_quality) 163 supporting_qualities = video_qualities[index:] 164 logging.info("new video quality %s ", supporting_qualities) 165 166 width, height = graphics_utils.get_internal_resolution() 167 logging.info('checking resolution: %d width %d height', width, height) 168 for quality in supporting_qualities: 169 logging.info('Playing video in %s quality.', quality) 170 171 if quality == "hd1080": 172 self._tab.ExecuteJavaScript('player.setSize(1920, 1080)') 173 if quality == "hd720": 174 self._tab.ExecuteJavaScript('player.setSize(1280, 720)') 175 if quality == "large": 176 self._tab.ExecuteJavaScript('player.setSize(853, 480)') 177 if quality == "medium": 178 self._tab.ExecuteJavaScript('player.setSize(640, 360)') 179 if quality == "small": 180 self._tab.ExecuteJavaScript('player.setSize(320, 240)') 181 182 self.set_playback_quality(quality) 183 self.wait_for_player_state(PLAYER_PLAYING_STATE) 184 self.wait_for_expected_resolution(quality) 185 current_quality = self.get_playback_quality() 186 187 if current_quality != quality: 188 raise error.TestError( 189 'Expected video quality: %s. Current video quality: %s' 190 % (quality, current_quality)) 191 192 if power_measurement and self.power_logger: 193 # Seeking to the beginning and ensure the player is playing. 194 self._tab.ExecuteJavaScript('player.seekTo(0, true)') 195 self._tab.ExecuteJavaScript('player.playVideo()') 196 self.wait_for_player_state(PLAYER_PLAYING_STATE) 197 with self.power_logger.checkblock('youtube-' + quality): 198 time.sleep(10) 199 else: 200 time.sleep(1) 201 202 def verify_player_states(self): 203 """Verify the player states like play, pause, ended and seek.""" 204 logging.info('Verifying the player states.') 205 self._tab.ExecuteJavaScript('player.pauseVideo()') 206 self.wait_for_player_state(PLAYER_PAUSE_STATE) 207 self._tab.ExecuteJavaScript('player.playVideo()') 208 self.wait_for_player_state(PLAYER_PLAYING_STATE) 209 # We are seeking the player position to (video length - 2 seconds). 210 # Since the player waits for WAIT_TIMEOUT_S for the status change, 211 # the video should be ended before we hit the timeout. 212 video_end_test_duration = (self._video_duration - 213 self.video_current_time() - 2) 214 if video_end_test_duration >= WAIT_TIMEOUT_S: 215 self._tab.ExecuteJavaScript( 216 'player.seekTo(%d, true)' % (self._video_duration - 5)) 217 self.wait_for_player_state(PLAYER_ENDED_STATE) 218 else: 219 raise error.TestError( 220 'Test video is not long enough for the video end test.') 221 # Verifying seek back from the end position. 222 self._tab.ExecuteJavaScript('player.seekTo(%d, true)' 223 % (self._video_duration / 2)) 224 self.wait_for_player_state(PLAYER_PLAYING_STATE) 225 # So the playback doesn't stay at the mid. 226 seek_test = False 227 for _ in range(WAIT_TIMEOUT_S): 228 logging.info('Waiting for seek position to change.') 229 time.sleep(1) 230 seek_position = self.video_current_time() 231 if (seek_position > self._video_duration / 2 232 and seek_position < self._video_duration): 233 seek_test = True 234 break 235 if not seek_test: 236 raise error.TestError( 237 'Seek location is wrong. ' 238 'Video length: %d, seek position: %d.' % 239 (self._video_duration, seek_position)) 240