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