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