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 android.content.Context; 19 import android.content.pm.PackageManager; 20 import android.content.res.AssetFileDescriptor; 21 import android.content.res.Resources; 22 import android.media.MediaPlayer; 23 import android.media.cts.TestUtils.Monitor; 24 import android.net.Uri; 25 import android.os.PersistableBundle; 26 import android.test.ActivityInstrumentationTestCase2; 27 28 import com.android.compatibility.common.util.MediaUtils; 29 30 import java.io.IOException; 31 import java.net.HttpCookie; 32 import java.util.List; 33 import java.util.logging.Logger; 34 import java.util.Map; 35 import java.util.Set; 36 37 /** 38 * Base class for tests which use MediaPlayer to play audio or video. 39 */ 40 public class MediaPlayerTestBase extends ActivityInstrumentationTestCase2<MediaStubActivity> { 41 private static final Logger LOG = Logger.getLogger(MediaPlayerTestBase.class.getName()); 42 43 protected static final int SLEEP_TIME = 1000; 44 protected static final int LONG_SLEEP_TIME = 6000; 45 protected static final int STREAM_RETRIES = 20; 46 protected static boolean sUseScaleToFitMode = false; 47 48 protected Monitor mOnVideoSizeChangedCalled = new Monitor(); 49 protected Monitor mOnVideoRenderingStartCalled = new Monitor(); 50 protected Monitor mOnBufferingUpdateCalled = new Monitor(); 51 protected Monitor mOnPrepareCalled = new Monitor(); 52 protected Monitor mOnSeekCompleteCalled = new Monitor(); 53 protected Monitor mOnCompletionCalled = new Monitor(); 54 protected Monitor mOnInfoCalled = new Monitor(); 55 protected Monitor mOnErrorCalled = new Monitor(); 56 57 protected Context mContext; 58 protected Resources mResources; 59 60 61 protected MediaPlayer mMediaPlayer = null; 62 protected MediaPlayer mMediaPlayer2 = null; 63 protected MediaStubActivity mActivity; 64 MediaPlayerTestBase()65 public MediaPlayerTestBase() { 66 super(MediaStubActivity.class); 67 } 68 69 @Override setUp()70 protected void setUp() throws Exception { 71 super.setUp(); 72 mActivity = getActivity(); 73 getInstrumentation().waitForIdleSync(); 74 try { 75 runTestOnUiThread(new Runnable() { 76 public void run() { 77 mMediaPlayer = new MediaPlayer(); 78 mMediaPlayer2 = new MediaPlayer(); 79 } 80 }); 81 } catch (Throwable e) { 82 e.printStackTrace(); 83 fail(); 84 } 85 mContext = getInstrumentation().getTargetContext(); 86 mResources = mContext.getResources(); 87 } 88 89 @Override tearDown()90 protected void tearDown() throws Exception { 91 if (mMediaPlayer != null) { 92 mMediaPlayer.release(); 93 mMediaPlayer = null; 94 } 95 if (mMediaPlayer2 != null) { 96 mMediaPlayer2.release(); 97 mMediaPlayer2 = null; 98 } 99 mActivity = null; 100 super.tearDown(); 101 } 102 103 // returns true on success loadResource(int resid)104 protected boolean loadResource(int resid) throws Exception { 105 if (!MediaUtils.hasCodecsForResource(mContext, resid)) { 106 return false; 107 } 108 109 AssetFileDescriptor afd = mResources.openRawResourceFd(resid); 110 try { 111 mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), 112 afd.getLength()); 113 114 // Although it is only meant for video playback, it should not 115 // cause issues for audio-only playback. 116 int videoScalingMode = sUseScaleToFitMode? 117 MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT 118 : MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING; 119 120 mMediaPlayer.setVideoScalingMode(videoScalingMode); 121 } finally { 122 afd.close(); 123 } 124 sUseScaleToFitMode = !sUseScaleToFitMode; // Alternate the scaling mode 125 return true; 126 } 127 checkLoadResource(int resid)128 protected boolean checkLoadResource(int resid) throws Exception { 129 return MediaUtils.check(loadResource(resid), "no decoder found"); 130 } 131 loadSubtitleSource(int resid)132 protected void loadSubtitleSource(int resid) throws Exception { 133 AssetFileDescriptor afd = mResources.openRawResourceFd(resid); 134 try { 135 mMediaPlayer.addTimedTextSource(afd.getFileDescriptor(), afd.getStartOffset(), 136 afd.getLength(), MediaPlayer.MEDIA_MIMETYPE_TEXT_SUBRIP); 137 } finally { 138 afd.close(); 139 } 140 } 141 playLiveVideoTest(String path, int playTime)142 protected void playLiveVideoTest(String path, int playTime) throws Exception { 143 playVideoWithRetries(path, null, null, playTime); 144 } 145 playLiveAudioOnlyTest(String path, int playTime)146 protected void playLiveAudioOnlyTest(String path, int playTime) throws Exception { 147 playVideoWithRetries(path, -1, -1, playTime); 148 } 149 playVideoTest(String path, int width, int height)150 protected void playVideoTest(String path, int width, int height) throws Exception { 151 playVideoWithRetries(path, width, height, 0); 152 } 153 playVideoWithRetries(String path, Integer width, Integer height, int playTime)154 protected void playVideoWithRetries(String path, Integer width, Integer height, int playTime) 155 throws Exception { 156 boolean playedSuccessfully = false; 157 for (int i = 0; i < STREAM_RETRIES; i++) { 158 try { 159 mMediaPlayer.reset(); 160 mMediaPlayer.setDataSource(path); 161 playLoadedVideo(width, height, playTime); 162 playedSuccessfully = true; 163 break; 164 } catch (PrepareFailedException e) { 165 // prepare() can fail because of network issues, so try again 166 LOG.warning("prepare() failed on try " + i + ", trying playback again"); 167 } 168 } 169 assertTrue("Stream did not play successfully after all attempts", playedSuccessfully); 170 } 171 playVideoTest(int resid, int width, int height)172 protected void playVideoTest(int resid, int width, int height) throws Exception { 173 if (!checkLoadResource(resid)) { 174 return; // skip 175 } 176 177 playLoadedVideo(width, height, 0); 178 } 179 playLiveVideoTest( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, int playTime)180 protected void playLiveVideoTest( 181 Uri uri, Map<String, String> headers, List<HttpCookie> cookies, 182 int playTime) throws Exception { 183 playVideoWithRetries(uri, headers, cookies, null /* width */, null /* height */, playTime); 184 } 185 playLiveAudioOnlyTest( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, int playTime)186 protected void playLiveAudioOnlyTest( 187 Uri uri, Map<String, String> headers, List<HttpCookie> cookies, 188 int playTime) throws Exception { 189 playVideoWithRetries(uri, headers, cookies, -1 /* width */, -1 /* height */, playTime); 190 } 191 playVideoWithRetries( Uri uri, Map<String, String> headers, List<HttpCookie> cookies, Integer width, Integer height, int playTime)192 protected void playVideoWithRetries( 193 Uri uri, Map<String, String> headers, List<HttpCookie> cookies, 194 Integer width, Integer height, int playTime) throws Exception { 195 boolean playedSuccessfully = false; 196 for (int i = 0; i < STREAM_RETRIES; i++) { 197 try { 198 mMediaPlayer.reset(); 199 mMediaPlayer.setDataSource(getInstrumentation().getTargetContext(), 200 uri, headers, cookies); 201 playLoadedVideo(width, height, playTime); 202 playedSuccessfully = true; 203 break; 204 } catch (PrepareFailedException e) { 205 // prepare() can fail because of network issues, so try again 206 // playLoadedVideo already has reset the player so we can try again safely. 207 LOG.warning("prepare() failed on try " + i + ", trying playback again"); 208 } 209 } 210 assertTrue("Stream did not play successfully after all attempts", playedSuccessfully); 211 } 212 213 /** 214 * Play a video which has already been loaded with setDataSource(). 215 * 216 * @param width width of the video to verify, or null to skip verification 217 * @param height height of the video to verify, or null to skip verification 218 * @param playTime length of time to play video, or 0 to play entire video. 219 * with a non-negative value, this method stops the playback after the length of 220 * time or the duration the video is elapsed. With a value of -1, 221 * this method simply starts the video and returns immediately without 222 * stoping the video playback. 223 */ playLoadedVideo(final Integer width, final Integer height, int playTime)224 protected void playLoadedVideo(final Integer width, final Integer height, int playTime) 225 throws Exception { 226 final float leftVolume = 0.5f; 227 final float rightVolume = 0.5f; 228 229 boolean audioOnly = (width != null && width.intValue() == -1) || 230 (height != null && height.intValue() == -1); 231 232 mMediaPlayer.setDisplay(mActivity.getSurfaceHolder()); 233 mMediaPlayer.setScreenOnWhilePlaying(true); 234 mMediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() { 235 @Override 236 public void onVideoSizeChanged(MediaPlayer mp, int w, int h) { 237 if (w == 0 && h == 0) { 238 // A size of 0x0 can be sent initially one time when using NuPlayer. 239 assertFalse(mOnVideoSizeChangedCalled.isSignalled()); 240 return; 241 } 242 mOnVideoSizeChangedCalled.signal(); 243 if (width != null) { 244 assertEquals(width.intValue(), w); 245 } 246 if (height != null) { 247 assertEquals(height.intValue(), h); 248 } 249 } 250 }); 251 mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { 252 @Override 253 public boolean onError(MediaPlayer mp, int what, int extra) { 254 fail("Media player had error " + what + " playing video"); 255 return true; 256 } 257 }); 258 mMediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() { 259 @Override 260 public boolean onInfo(MediaPlayer mp, int what, int extra) { 261 if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { 262 mOnVideoRenderingStartCalled.signal(); 263 } 264 return true; 265 } 266 }); 267 try { 268 mMediaPlayer.prepare(); 269 } catch (IOException e) { 270 mMediaPlayer.reset(); 271 throw new PrepareFailedException(); 272 } 273 274 mMediaPlayer.start(); 275 if (!audioOnly) { 276 mOnVideoSizeChangedCalled.waitForSignal(); 277 mOnVideoRenderingStartCalled.waitForSignal(); 278 } 279 mMediaPlayer.setVolume(leftVolume, rightVolume); 280 281 // waiting to complete 282 if (playTime == -1) { 283 return; 284 } else if (playTime == 0) { 285 while (mMediaPlayer.isPlaying()) { 286 Thread.sleep(SLEEP_TIME); 287 } 288 } else { 289 Thread.sleep(playTime); 290 } 291 292 // validate a few MediaMetrics. 293 PersistableBundle metrics = mMediaPlayer.getMetrics(); 294 if (metrics == null) { 295 fail("MediaPlayer.getMetrics() returned null metrics"); 296 } else if (metrics.isEmpty()) { 297 fail("MediaPlayer.getMetrics() returned empty metrics"); 298 } else { 299 300 int size = metrics.size(); 301 Set<String> keys = metrics.keySet(); 302 303 if (keys == null) { 304 fail("MediaMetricsSet returned no keys"); 305 } else if (keys.size() != size) { 306 fail("MediaMetricsSet.keys().size() mismatch MediaMetricsSet.size()"); 307 } 308 309 // we played something; so one of these should be non-null 310 String vmime = metrics.getString(MediaPlayer.MetricsConstants.MIME_TYPE_VIDEO, null); 311 String amime = metrics.getString(MediaPlayer.MetricsConstants.MIME_TYPE_AUDIO, null); 312 if (vmime == null && amime == null) { 313 fail("getMetrics() returned neither video nor audio mime value"); 314 } 315 316 long duration = metrics.getLong(MediaPlayer.MetricsConstants.DURATION, -2); 317 if (duration == -2) { 318 fail("getMetrics() didn't return a duration"); 319 } 320 long playing = metrics.getLong(MediaPlayer.MetricsConstants.PLAYING, -2); 321 if (playing == -2) { 322 fail("getMetrics() didn't return a playing time"); 323 } 324 if (!keys.contains(MediaPlayer.MetricsConstants.PLAYING)) { 325 fail("MediaMetricsSet.keys() missing: " + MediaPlayer.MetricsConstants.PLAYING); 326 } 327 } 328 329 mMediaPlayer.stop(); 330 } 331 332 private static class PrepareFailedException extends Exception {} 333 isTv()334 public boolean isTv() { 335 PackageManager pm = getInstrumentation().getTargetContext().getPackageManager(); 336 return pm.hasSystemFeature(pm.FEATURE_TELEVISION) 337 && pm.hasSystemFeature(pm.FEATURE_LEANBACK); 338 } 339 checkTv()340 public boolean checkTv() { 341 return MediaUtils.check(isTv(), "not a TV"); 342 } 343 setOnErrorListener()344 protected void setOnErrorListener() { 345 mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { 346 @Override 347 public boolean onError(MediaPlayer mp, int what, int extra) { 348 mOnErrorCalled.signal(); 349 return false; 350 } 351 }); 352 } 353 } 354