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