1 /* 2 * Copyright (C) 2011 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 package android.media.cts; 17 18 import static org.junit.Assert.assertEquals; 19 import static org.junit.Assert.assertFalse; 20 import static org.junit.Assert.assertTrue; 21 import static org.junit.Assert.fail; 22 23 import android.media.MediaPlayer; 24 import android.media.cts.TestUtils.Monitor; 25 import android.net.Uri; 26 import android.os.PersistableBundle; 27 28 import org.junit.After; 29 import org.junit.Before; 30 31 import java.io.IOException; 32 import java.net.HttpCookie; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Set; 36 import java.util.logging.Logger; 37 38 /** 39 * Base class for tests which use MediaPlayer to play audio or video. 40 */ 41 public class MediaPlayerTestBase extends MediaTestBase { 42 private static final Logger LOG = Logger.getLogger(MediaPlayerTestBase.class.getName()); 43 44 protected Monitor mOnVideoSizeChangedCalled = new Monitor(); 45 protected Monitor mOnVideoRenderingStartCalled = new Monitor(); 46 protected Monitor mOnBufferingUpdateCalled = new Monitor(); 47 protected Monitor mOnPrepareCalled = new Monitor(); 48 protected Monitor mOnSeekCompleteCalled = new Monitor(); 49 protected Monitor mOnCompletionCalled = new Monitor(); 50 protected Monitor mOnInfoCalled = new Monitor(); 51 protected Monitor mOnErrorCalled = new Monitor(); 52 53 protected MediaPlayer mMediaPlayer = null; 54 protected MediaPlayer mMediaPlayer2 = null; 55 56 @Before 57 @Override setUp()58 public void setUp() throws Throwable { 59 super.setUp(); 60 runOnUiThread(() -> { 61 mMediaPlayer = new MediaPlayer(); 62 mMediaPlayer2 = new MediaPlayer(); 63 }); 64 } 65 @After 66 @Override tearDown()67 public void tearDown() { 68 if (mMediaPlayer != null) { 69 mMediaPlayer.release(); 70 mMediaPlayer = null; 71 } 72 if (mMediaPlayer2 != null) { 73 mMediaPlayer2.release(); 74 mMediaPlayer2 = null; 75 } 76 super.tearDown(); 77 } 78 runOnUiThread(Runnable runnable)79 protected void runOnUiThread(Runnable runnable) throws Throwable { 80 Throwable[] throwableHolder = new Throwable[1]; 81 getInstrumentation().runOnMainSync(() -> { 82 try { 83 runnable.run(); 84 } catch (Throwable throwable) { 85 throwableHolder[0] = throwable; 86 } 87 }); 88 if (throwableHolder[0] != null) { 89 throw throwableHolder[0]; 90 } 91 } 92 playLiveVideoTest(String path, int playTime)93 protected void playLiveVideoTest(String path, int playTime) throws Exception { 94 playVideoWithRetries(path, null, null, playTime); 95 } 96 playLiveAudioOnlyTest(String path, int playTime)97 protected void playLiveAudioOnlyTest(String path, int playTime) throws Exception { 98 playVideoWithRetries(path, -1, -1, playTime); 99 } 100 playVideoTest(String path, int width, int height)101 protected void playVideoTest(String path, int width, int height) throws Exception { 102 playVideoWithRetries(path, width, height, 0); 103 } 104 playVideoWithRetries(String path, Integer width, Integer height, int playTime)105 protected void playVideoWithRetries(String path, Integer width, Integer height, int playTime) 106 throws Exception { 107 boolean playedSuccessfully = false; 108 for (int i = 0; i < STREAM_RETRIES; i++) { 109 try { 110 mMediaPlayer.reset(); 111 mMediaPlayer.setDataSource(path); 112 playLoadedVideo(width, height, playTime); 113 playedSuccessfully = true; 114 break; 115 } catch (PrepareFailedException e) { 116 // prepare() can fail because of network issues, so try again 117 LOG.warning("prepare() failed on try " + i + ", trying playback again"); 118 } 119 } 120 assertTrue("Stream did not play successfully after all attempts", playedSuccessfully); 121 } 122 playLiveVideoTest( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, int playTime)123 protected void playLiveVideoTest( 124 Uri uri, Map<String, String> headers, List<HttpCookie> cookies, 125 int playTime) throws Exception { 126 playVideoWithRetries(uri, headers, cookies, null /* width */, null /* height */, playTime); 127 } 128 playLiveAudioOnlyTest( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, int playTime)129 protected void playLiveAudioOnlyTest( 130 Uri uri, Map<String, String> headers, List<HttpCookie> cookies, 131 int playTime) throws Exception { 132 playVideoWithRetries(uri, headers, cookies, -1 /* width */, -1 /* height */, playTime); 133 } 134 playVideoWithRetries( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, Integer width, Integer height, int playTime)135 protected void playVideoWithRetries( 136 Uri uri, Map<String, String> headers, List<HttpCookie> cookies, 137 Integer width, Integer height, int playTime) throws Exception { 138 boolean playedSuccessfully = false; 139 for (int i = 0; i < STREAM_RETRIES; i++) { 140 try { 141 mMediaPlayer.reset(); 142 mMediaPlayer.setDataSource( 143 getInstrumentation().getTargetContext(), 144 uri, 145 headers, 146 cookies); 147 playLoadedVideo(width, height, playTime); 148 playedSuccessfully = true; 149 break; 150 } catch (PrepareFailedException e) { 151 // prepare() can fail because of network issues, so try again 152 // playLoadedVideo already has reset the player so we can try again safely. 153 LOG.warning("prepare() failed on try " + i + ", trying playback again"); 154 } 155 } 156 assertTrue("Stream did not play successfully after all attempts", playedSuccessfully); 157 } 158 159 /** 160 * Play a video which has already been loaded with setDataSource(). 161 * 162 * @param width width of the video to verify, or null to skip verification 163 * @param height height of the video to verify, or null to skip verification 164 * @param playTime length of time to play video, or 0 to play entire video. 165 * with a non-negative value, this method stops the playback after the length of 166 * time or the duration the video is elapsed. With a value of -1, 167 * this method simply starts the video and returns immediately without 168 * stoping the video playback. 169 */ playLoadedVideo(final Integer width, final Integer height, int playTime)170 protected void playLoadedVideo(final Integer width, final Integer height, int playTime) 171 throws Exception { 172 final float leftVolume = 0.5f; 173 final float rightVolume = 0.5f; 174 175 boolean audioOnly = (width != null && width == -1) || 176 (height != null && height == -1); 177 178 mMediaPlayer.setDisplay(mActivity.getSurfaceHolder()); 179 mMediaPlayer.setScreenOnWhilePlaying(true); 180 mMediaPlayer.setOnVideoSizeChangedListener((mp, w, h) -> { 181 if (w == 0 && h == 0) { 182 // A size of 0x0 can be sent initially one time when using NuPlayer. 183 assertFalse(mOnVideoSizeChangedCalled.isSignalled()); 184 return; 185 } 186 mOnVideoSizeChangedCalled.signal(); 187 if (width != null) { 188 assertEquals(width.intValue(), w); 189 } 190 if (height != null) { 191 assertEquals(height.intValue(), h); 192 } 193 }); 194 mMediaPlayer.setOnErrorListener((mp, what, extra) -> { 195 fail("Media player had error " + what + " playing video"); 196 return true; 197 }); 198 mMediaPlayer.setOnInfoListener((mp, what, extra) -> { 199 if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { 200 mOnVideoRenderingStartCalled.signal(); 201 } 202 return true; 203 }); 204 try { 205 mMediaPlayer.prepare(); 206 } catch (IOException e) { 207 mMediaPlayer.reset(); 208 throw new PrepareFailedException(); 209 } 210 211 mMediaPlayer.start(); 212 if (!audioOnly) { 213 mOnVideoSizeChangedCalled.waitForSignal(); 214 mOnVideoRenderingStartCalled.waitForSignal(); 215 } 216 mMediaPlayer.setVolume(leftVolume, rightVolume); 217 218 // waiting to complete 219 if (playTime == -1) { 220 return; 221 } else if (playTime == 0) { 222 while (mMediaPlayer.isPlaying()) { 223 Thread.sleep(SLEEP_TIME); 224 } 225 } else { 226 Thread.sleep(playTime); 227 } 228 229 // validate a few MediaMetrics. 230 PersistableBundle metrics = mMediaPlayer.getMetrics(); 231 if (metrics == null) { 232 fail("MediaPlayer.getMetrics() returned null metrics"); 233 } else if (metrics.isEmpty()) { 234 fail("MediaPlayer.getMetrics() returned empty metrics"); 235 } else { 236 237 int size = metrics.size(); 238 Set<String> keys = metrics.keySet(); 239 240 if (keys == null) { 241 fail("MediaMetricsSet returned no keys"); 242 } else if (keys.size() != size) { 243 fail("MediaMetricsSet.keys().size() mismatch MediaMetricsSet.size()"); 244 } 245 246 // we played something; so one of these should be non-null 247 String vmime = metrics.getString(MediaPlayer.MetricsConstants.MIME_TYPE_VIDEO, null); 248 String amime = metrics.getString(MediaPlayer.MetricsConstants.MIME_TYPE_AUDIO, null); 249 if (vmime == null && amime == null) { 250 fail("getMetrics() returned neither video nor audio mime value"); 251 } 252 253 long duration = metrics.getLong(MediaPlayer.MetricsConstants.DURATION, -2); 254 if (duration == -2) { 255 fail("getMetrics() didn't return a duration"); 256 } 257 long playing = metrics.getLong(MediaPlayer.MetricsConstants.PLAYING, -2); 258 if (playing == -2) { 259 fail("getMetrics() didn't return a playing time"); 260 } 261 if (!keys.contains(MediaPlayer.MetricsConstants.PLAYING)) { 262 fail("MediaMetricsSet.keys() missing: " + MediaPlayer.MetricsConstants.PLAYING); 263 } 264 } 265 266 mMediaPlayer.stop(); 267 } 268 269 private static class PrepareFailedException extends Exception {} 270 setOnErrorListener()271 protected void setOnErrorListener() { 272 mMediaPlayer.setOnErrorListener((mp, what, extra) -> { 273 mOnErrorCalled.signal(); 274 return false; 275 }); 276 } 277 } 278