1 /* 2 * Copyright 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media.cujcommon.cts; 18 19 import static android.media.cujcommon.cts.CujTestBase.ORIENTATIONS; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertTrue; 23 24 import android.app.Activity; 25 import android.content.pm.ActivityInfo; 26 import android.os.Looper; 27 import android.util.DisplayMetrics; 28 29 import androidx.annotation.NonNull; 30 import androidx.media3.common.C; 31 import androidx.media3.common.Format; 32 import androidx.media3.common.Player; 33 import androidx.media3.common.Player.Events; 34 import androidx.media3.common.TrackSelectionOverride; 35 import androidx.media3.common.TrackSelectionParameters; 36 import androidx.media3.common.Tracks; 37 38 import java.util.ArrayList; 39 import java.util.List; 40 41 public abstract class PlayerListener implements Player.Listener { 42 43 public static final long NOTIFICATIONTEST_PLAYBACK_DELTA_TIME_US = 6000; 44 public static final Object LISTENER_LOCK = new Object(); 45 public static int CURRENT_MEDIA_INDEX = 0; 46 47 // Enum Declared for Test Type 48 public enum TestType { 49 PLAYBACK_TEST, 50 SEEK_TEST, 51 ORIENTATION_TEST, 52 ADAPTIVE_PLAYBACK_TEST, 53 SCROLL_TEST, 54 SWITCH_AUDIO_TRACK_TEST, 55 SWITCH_SUBTITLE_TRACK_TEST, 56 CALL_NOTIFICATION_TEST, 57 MESSAGE_NOTIFICATION_TEST, 58 PINCH_TO_ZOOM_TEST, 59 SPEED_CHANGE_TEST, 60 PIP_MODE_TEST, 61 SPLIT_SCREEN_TEST, 62 DEVICE_LOCK_TEST, 63 LOCK_PLAYBACK_CONTROLLER_TEST 64 } 65 66 public static boolean mPlaybackEnded; 67 protected long mExpectedTotalTime; 68 protected MainActivity mActivity; 69 protected ScrollTestActivity mScrollActivity; 70 protected long mSendMessagePosition; 71 protected int mPreviousOrientation; 72 protected int mOrientationIndex; 73 protected boolean mScrollRequested; 74 protected boolean mTrackChangeRequested; 75 protected List<Tracks.Group> mTrackGroups; 76 protected Format mStartTrackFormat; 77 protected Format mCurrentTrackFormat; 78 protected Format mConfiguredTrackFormat; 79 protected long mStartTime; 80 PlayerListener()81 public PlayerListener() { 82 this.mSendMessagePosition = 0; 83 } 84 85 /** 86 * Returns the type of test. 87 */ getTestType()88 public abstract TestType getTestType(); 89 90 /** 91 * Returns True for Orientation test. 92 */ isOrientationTest()93 public final boolean isOrientationTest() { 94 return getTestType().equals(TestType.ORIENTATION_TEST); 95 } 96 97 /** 98 * Returns True for Scroll test. 99 */ isScrollTest()100 public final boolean isScrollTest() { 101 return getTestType().equals(TestType.SCROLL_TEST); 102 } 103 104 /** 105 * Returns True for Call Notification test. 106 */ isCallNotificationTest()107 public final boolean isCallNotificationTest() { 108 return getTestType().equals(TestType.CALL_NOTIFICATION_TEST); 109 } 110 111 /** 112 * Returns True for PinchToZoom test. 113 */ isPinchToZoomTest()114 public final boolean isPinchToZoomTest() { 115 return getTestType().equals(TestType.PINCH_TO_ZOOM_TEST); 116 } 117 118 /** 119 * Returns True for PIP Minimized Playback Mode test. 120 */ isPipTest()121 public final boolean isPipTest() { 122 return getTestType().equals(TestType.PIP_MODE_TEST); 123 } 124 125 /** 126 * Returns True for Split Screen test. 127 */ isSplitScreenTest()128 public final boolean isSplitScreenTest() { 129 return getTestType().equals(TestType.SPLIT_SCREEN_TEST); 130 } 131 132 /** 133 * Returns expected playback time for the playlist. 134 */ getExpectedTotalTime()135 public final long getExpectedTotalTime() { 136 return mExpectedTotalTime; 137 } 138 139 /** 140 * Sets activity for test. 141 */ setActivity(MainActivity activity)142 public final void setActivity(MainActivity activity) { 143 this.mActivity = activity; 144 if (isOrientationTest()) { 145 mOrientationIndex = 0; 146 mActivity.setRequestedOrientation( 147 ORIENTATIONS[mOrientationIndex] /* SCREEN_ORIENTATION_PORTRAIT */); 148 } 149 } 150 151 /** 152 * Get Orientation of the device. 153 */ getDeviceOrientation(final Activity activity)154 protected static int getDeviceOrientation(final Activity activity) { 155 final DisplayMetrics displayMetrics = new DisplayMetrics(); 156 activity.getDisplay().getRealMetrics(displayMetrics); 157 if (displayMetrics.widthPixels < displayMetrics.heightPixels) { 158 return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 159 } else { 160 return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 161 } 162 } 163 164 /** 165 * Sets activity for scroll test. 166 */ setScrollActivity(ScrollTestActivity activity)167 public final void setScrollActivity(ScrollTestActivity activity) { 168 this.mScrollActivity = activity; 169 } 170 171 /** 172 * Check if two formats are similar. 173 * 174 * @param refFormat Reference format 175 * @param testFormat Test format 176 * @return True, if two formats are similar, false otherwise 177 */ isFormatSimilar(Format refFormat, Format testFormat)178 protected final boolean isFormatSimilar(Format refFormat, Format testFormat) { 179 String refMediaType = refFormat.sampleMimeType; 180 String testMediaType = testFormat.sampleMimeType; 181 if (getTestType().equals(TestType.SWITCH_AUDIO_TRACK_TEST)) { 182 assertTrue(refMediaType.startsWith("audio/") && testMediaType.startsWith("audio/")); 183 if ((refFormat.channelCount != testFormat.channelCount) || (refFormat.sampleRate 184 != testFormat.sampleRate)) { 185 return false; 186 } 187 } else if (getTestType().equals(TestType.SWITCH_SUBTITLE_TRACK_TEST)) { 188 assertTrue((refMediaType.startsWith("text/") && testMediaType.startsWith("text/")) || ( 189 refMediaType.startsWith("application/") && testMediaType.startsWith("application/"))); 190 } 191 if (!refMediaType.equals(testMediaType)) { 192 return false; 193 } 194 if (!refFormat.id.equals(testFormat.id)) { 195 return false; 196 } 197 return true; 198 } 199 200 /** 201 * Called when player states changed. 202 * 203 * @param player The {@link Player} whose state changed. Use the getters to obtain the latest 204 * states. 205 * @param events The {@link Events} that happened in this iteration, indicating which player 206 * states changed. 207 */ onEvents(@onNull Player player, Events events)208 public final void onEvents(@NonNull Player player, Events events) { 209 if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)) { 210 onEventsPlaybackStateChanged(player); 211 synchronized (LISTENER_LOCK) { 212 if (player.getPlaybackState() == Player.STATE_ENDED) { 213 if (mPlaybackEnded) { 214 throw new RuntimeException("mPlaybackEnded already set, player could be ended"); 215 } 216 if (!isScrollTest()) { 217 mActivity.removePlayerListener(); 218 } else { 219 assertTrue(mScrollRequested); 220 mScrollActivity.removePlayerListener(); 221 } 222 // Verify the total time taken by the notification test 223 if (getTestType().equals(TestType.CALL_NOTIFICATION_TEST) || getTestType().equals( 224 TestType.MESSAGE_NOTIFICATION_TEST)) { 225 long actualTime = System.currentTimeMillis() - mStartTime; 226 assertEquals((float) mExpectedTotalTime, (float) actualTime, 227 NOTIFICATIONTEST_PLAYBACK_DELTA_TIME_US); 228 } 229 mPlaybackEnded = true; 230 LISTENER_LOCK.notify(); 231 } 232 } 233 } 234 if (events.contains(Player.EVENT_MEDIA_ITEM_TRANSITION)) { 235 onEventsMediaItemTransition(player); 236 // Add duration on media transition. 237 long duration = player.getDuration(); 238 if (duration != C.TIME_UNSET) { 239 mExpectedTotalTime += duration; 240 } 241 } 242 } 243 244 /** 245 * Called when the value returned from {@link Player#getPlaybackState()} changes. 246 * 247 * @param player The {@link Player} whose state changed. Use the getters to obtain the latest 248 * states. 249 */ onEventsPlaybackStateChanged(@onNull Player player)250 public abstract void onEventsPlaybackStateChanged(@NonNull Player player); 251 252 /** 253 * Called when the value returned from {@link Player#getCurrentMediaItem()} changes or the player 254 * starts repeating the current item. 255 * 256 * @param player The {@link Player} whose state changed. Use the getters to obtain the latest 257 * states. 258 */ onEventsMediaItemTransition(@onNull Player player)259 public abstract void onEventsMediaItemTransition(@NonNull Player player); 260 261 /** 262 * Create a message at given position to change the audio or the subtitle track 263 * 264 * @param sendMessagePosition Position at which message needs to be executed 265 * @param trackGroupIndex Index of the current track group 266 * @param trackIndex Index of the current track 267 */ createSwitchTrackMessage(long sendMessagePosition, int trackGroupIndex, int trackIndex)268 protected final void createSwitchTrackMessage(long sendMessagePosition, int trackGroupIndex, 269 int trackIndex) { 270 mActivity.mPlayer.createMessage((messageType, payload) -> { 271 TrackSelectionParameters currentParameters = 272 mActivity.mPlayer.getTrackSelectionParameters(); 273 TrackSelectionParameters newParameters = currentParameters 274 .buildUpon() 275 .setOverrideForType( 276 new TrackSelectionOverride( 277 mTrackGroups.get(trackGroupIndex).getMediaTrackGroup(), 278 trackIndex)) 279 .build(); 280 mActivity.mPlayer.setTrackSelectionParameters(newParameters); 281 mConfiguredTrackFormat = mTrackGroups.get(trackGroupIndex) 282 .getTrackFormat(trackIndex); 283 mTrackChangeRequested = true; 284 }).setLooper(Looper.getMainLooper()).setPosition(sendMessagePosition) 285 .setDeleteAfterDelivery(true).send(); 286 } 287 288 /** 289 * Called when the value of getCurrentTracks() changes. onEvents(Player, Player.Events) will also 290 * be called to report this event along with other events that happen in the same Looper message 291 * queue iteration. 292 * 293 * @param tracks The available tracks information. Never null, but may be of length zero. 294 */ 295 @Override onTracksChanged(Tracks tracks)296 public final void onTracksChanged(Tracks tracks) { 297 for (Tracks.Group currentTrackGroup : tracks.getGroups()) { 298 if (currentTrackGroup.isSelected() && ( 299 (getTestType().equals(TestType.SWITCH_AUDIO_TRACK_TEST) && (currentTrackGroup.getType() 300 == C.TRACK_TYPE_AUDIO)) || (getTestType().equals(TestType.SWITCH_SUBTITLE_TRACK_TEST) 301 && (currentTrackGroup.getType() == C.TRACK_TYPE_TEXT)))) { 302 for (int trackIndex = 0; trackIndex < currentTrackGroup.length; trackIndex++) { 303 if (currentTrackGroup.isTrackSelected(trackIndex)) { 304 if (!mTrackChangeRequested) { 305 mStartTrackFormat = currentTrackGroup.getTrackFormat(trackIndex); 306 } else { 307 mCurrentTrackFormat = currentTrackGroup.getTrackFormat(trackIndex); 308 } 309 } 310 } 311 } 312 } 313 } 314 315 /** 316 * Get all audio/subtitle tracks group from the player's Tracks. 317 */ getTrackGroups()318 protected final List<Tracks.Group> getTrackGroups() { 319 List<Tracks.Group> trackGroups = new ArrayList<>(); 320 Tracks currentTracks = mActivity.mPlayer.getCurrentTracks(); 321 for (Tracks.Group currentTrackGroup : currentTracks.getGroups()) { 322 if ((currentTrackGroup.getType() == C.TRACK_TYPE_AUDIO) || (currentTrackGroup.getType() 323 == C.TRACK_TYPE_TEXT)) { 324 trackGroups.add(currentTrackGroup); 325 } 326 } 327 return trackGroups; 328 } 329 } 330