1 /* 2 * Copyright (C) 2012 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 19 import android.media.cts.R; 20 21 import android.media.MediaRecorder; 22 import android.media.MediaPlayer; 23 import android.platform.test.annotations.AppModeFull; 24 import android.view.SurfaceHolder; 25 import android.test.ActivityInstrumentationTestCase2; 26 import android.os.Environment; 27 import android.content.res.AssetFileDescriptor; 28 import android.content.res.Resources; 29 import android.util.Log; 30 31 import java.util.Random; 32 33 /** 34 * Tests for the MediaPlayer.java and MediaRecorder.java APIs 35 * 36 * These testcases make randomized calls to the public APIs available, and 37 * the focus is on whether the randomized calls can lead to crash in 38 * mediaserver process and/or ANRs. 39 * 40 * The files in res/raw used by testLocalVideo* are (c) copyright 2008, 41 * Blender Foundation / www.bigbuckbunny.org, and are licensed under the Creative Commons 42 * Attribution 3.0 License at http://creativecommons.org/licenses/by/3.0/us/. 43 */ 44 @AppModeFull(reason = "TODO: evaluate and port to instant") 45 public class MediaRandomTest extends ActivityInstrumentationTestCase2<MediaStubActivity> { 46 private static final String TAG = "MediaRandomTest"; 47 48 private static final String OUTPUT_FILE = 49 Environment.getExternalStorageDirectory().toString() + "/record.3gp"; 50 51 private static final int NUMBER_OF_RECORDER_RANDOM_ACTIONS = 100000; 52 private static final int NUMBER_OF_PLAYER_RANDOM_ACTIONS = 100000; 53 54 private MediaRecorder mRecorder; 55 private MediaPlayer mPlayer; 56 private SurfaceHolder mSurfaceHolder; 57 private Resources mResources; 58 59 // Modified across multiple threads 60 private volatile boolean mMediaServerDied; 61 private volatile int mAction; 62 private volatile int mParam; 63 64 @Override setUp()65 protected void setUp() throws Exception { 66 super.setUp(); 67 getInstrumentation().waitForIdleSync(); 68 mMediaServerDied = false; 69 mSurfaceHolder = getActivity().getSurfaceHolder(); 70 mResources = getInstrumentation().getTargetContext().getResources(); 71 try { 72 // Running this on UI thread make sure that 73 // onError callback can be received. 74 runTestOnUiThread(new Runnable() { 75 public void run() { 76 mRecorder = new MediaRecorder(); 77 mPlayer = new MediaPlayer(); 78 } 79 }); 80 } catch (Throwable e) { 81 e.printStackTrace(); 82 fail(); 83 } 84 } 85 86 @Override tearDown()87 protected void tearDown() throws Exception { 88 if (mRecorder != null) { 89 mRecorder.release(); 90 mRecorder = null; 91 } 92 if (mPlayer != null) { 93 mPlayer.release(); 94 mPlayer = null; 95 } 96 super.tearDown(); 97 } 98 99 /** 100 * This is a watchdog used to stop the process if it hasn't been pinged 101 * for more than specified milli-seconds. It is used like: 102 * 103 * Watchdog w = new Watchdog(10000); // 10 seconds. 104 * w.start(); // start the watchdog. 105 * ... 106 * w.ping(); 107 * ... 108 * w.ping(); 109 * ... 110 * w.end(); // ask the watchdog to stop. 111 * w.join(); // join the thread. 112 */ 113 class Watchdog extends Thread { 114 private final long mTimeoutMs; 115 private boolean mWatchdogStop; 116 private boolean mWatchdogPinged; 117 Watchdog(long timeoutMs)118 public Watchdog(long timeoutMs) { 119 mTimeoutMs = timeoutMs; 120 mWatchdogStop = false; 121 mWatchdogPinged = false; 122 } 123 run()124 public synchronized void run() { 125 while (true) { 126 // avoid early termination by "spurious" waitup. 127 final long startTimeMs = System.currentTimeMillis(); 128 long remainingWaitTimeMs = mTimeoutMs; 129 do { 130 try { 131 wait(remainingWaitTimeMs); 132 } catch (InterruptedException ex) { 133 // ignore. 134 } 135 remainingWaitTimeMs = mTimeoutMs - (System.currentTimeMillis() - startTimeMs); 136 } while (remainingWaitTimeMs > 0); 137 138 if (mWatchdogStop) { 139 break; 140 } 141 142 if (!mWatchdogPinged) { 143 fail("Action " + mAction + " Param " + mParam 144 + " waited over " + (mTimeoutMs - remainingWaitTimeMs) + " ms"); 145 return; 146 } 147 mWatchdogPinged = false; 148 } 149 } 150 ping()151 public synchronized void ping() { 152 mWatchdogPinged = true; 153 this.notify(); 154 } 155 end()156 public synchronized void end() { 157 mWatchdogStop = true; 158 this.notify(); 159 } 160 } 161 MediaRandomTest()162 public MediaRandomTest() { 163 super("android.media.cts", MediaStubActivity.class); 164 } 165 loadSource(int resid)166 private void loadSource(int resid) throws Exception { 167 AssetFileDescriptor afd = mResources.openRawResourceFd(resid); 168 try { 169 mPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), 170 afd.getLength()); 171 } finally { 172 afd.close(); 173 } 174 } testPlayerRandomActionH264()175 public void testPlayerRandomActionH264() throws Exception { 176 testPlayerRandomAction(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz); 177 } testPlayerRandomActionHEVC()178 public void testPlayerRandomActionHEVC() throws Exception { 179 testPlayerRandomAction(R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz); 180 } testPlayerRandomAction(int resid)181 private void testPlayerRandomAction(int resid) throws Exception { 182 Watchdog watchdog = new Watchdog(5000); 183 try { 184 mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { 185 @Override 186 public boolean onError(MediaPlayer mp, int what, int extra) { 187 if (mPlayer == mp && 188 what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) { 189 Log.e(TAG, "mediaserver process died"); 190 mMediaServerDied = true; 191 } 192 return true; 193 } 194 }); 195 loadSource(resid); 196 mPlayer.setDisplay(mSurfaceHolder); 197 mPlayer.prepare(); 198 mPlayer.start(); 199 200 long seed = System.currentTimeMillis(); 201 Log.v(TAG, "seed = " + seed); 202 Random r = new Random(seed); 203 204 watchdog.start(); 205 for (int i = 0; i < NUMBER_OF_PLAYER_RANDOM_ACTIONS; i++){ 206 watchdog.ping(); 207 assertTrue(!mMediaServerDied); 208 209 mAction = (int)(r.nextInt() % 12); 210 mParam = (int)(r.nextInt() % 1000000); 211 try { 212 switch (mAction) { 213 case 0: 214 mPlayer.getCurrentPosition(); 215 break; 216 case 1: 217 mPlayer.getDuration(); 218 break; 219 case 2: 220 mPlayer.getVideoHeight(); 221 break; 222 case 3: 223 mPlayer.getVideoWidth(); 224 break; 225 case 4: 226 mPlayer.isPlaying(); 227 break; 228 case 5: 229 mPlayer.pause(); 230 break; 231 case 6: 232 // Don't add mPlayer.prepare() call here for two reasons: 233 // 1. calling prepare() is a bad idea since it is a blocking call, and 234 // 2. when prepare() is in progress, mediaserver died message will not be sent to apps 235 mPlayer.prepareAsync(); 236 break; 237 case 7: 238 mPlayer.seekTo((int)(mParam)); 239 break; 240 case 8: 241 mPlayer.setLooping(mParam % 2 == 0); 242 break; 243 case 9: 244 mPlayer.setVolume((mParam % 1000) / 500.0f, 245 (mParam / 1000) / 500.0f); 246 break; 247 case 10: 248 mPlayer.start(); 249 break; 250 case 11: 251 Thread.sleep(mParam % 20); 252 break; 253 } 254 } catch (Exception e) { 255 } 256 } 257 mPlayer.stop(); 258 } catch (Exception e) { 259 Log.v(TAG, e.toString()); 260 } finally { 261 watchdog.end(); 262 watchdog.join(); 263 } 264 } 265 testRecorderRandomAction()266 public void testRecorderRandomAction() throws Exception { 267 Watchdog watchdog = new Watchdog(5000); 268 try { 269 long seed = System.currentTimeMillis(); 270 Log.v(TAG, "seed = " + seed); 271 Random r = new Random(seed); 272 273 mMediaServerDied = false; 274 mRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() { 275 @Override 276 public void onError(MediaRecorder recorder, int what, int extra) { 277 if (mRecorder == recorder && 278 what == MediaRecorder.MEDIA_ERROR_SERVER_DIED) { 279 Log.e(TAG, "mediaserver process died"); 280 mMediaServerDied = true; 281 } 282 } 283 }); 284 285 final int[] width = {176, 352, 320, 640, 1280, 1920}; 286 final int[] height = {144, 288, 240, 480, 720, 1080}; 287 final int[] audioSource = { 288 MediaRecorder.AudioSource.DEFAULT, 289 MediaRecorder.AudioSource.MIC, 290 MediaRecorder.AudioSource.CAMCORDER, 291 }; 292 293 watchdog.start(); 294 for (int i = 0; i < NUMBER_OF_RECORDER_RANDOM_ACTIONS; i++) { 295 watchdog.ping(); 296 assertTrue(!mMediaServerDied); 297 298 mAction = (int)(r.nextInt(14)); 299 mParam = (int)(r.nextInt(1000000)); 300 try { 301 switch (mAction) { 302 case 0: { 303 // We restrict the audio sources because setting some sources 304 // may cause 2+ second delays because the input device may 305 // retry - loop (e.g. VOICE_UPLINK for voice call to be initiated). 306 final int index = mParam % audioSource.length; 307 mRecorder.setAudioSource(audioSource[index]); 308 break; 309 } 310 case 1: 311 // XXX: 312 // Fix gralloc source and change 313 // mRecorder.setVideoSource(mParam % 3); 314 mRecorder.setVideoSource(mParam % 2); 315 break; 316 case 2: 317 mRecorder.setOutputFormat(mParam % 5); 318 break; 319 case 3: 320 mRecorder.setAudioEncoder(mParam % 3); 321 break; 322 case 4: 323 mRecorder.setVideoEncoder(mParam % 5); 324 break; 325 case 5: 326 mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); 327 break; 328 case 6: 329 int index = mParam % width.length; 330 mRecorder.setVideoSize(width[index], height[index]); 331 break; 332 case 7: 333 mRecorder.setVideoFrameRate(mParam % 40 - 5); 334 break; 335 case 8: 336 mRecorder.setOutputFile(OUTPUT_FILE); 337 break; 338 case 9: 339 mRecorder.prepare(); 340 break; 341 case 10: 342 mRecorder.start(); 343 break; 344 case 11: 345 Thread.sleep(mParam % 20); 346 break; 347 case 12: 348 mRecorder.stop(); 349 break; 350 case 13: 351 mRecorder.reset(); 352 break; 353 default: 354 break; 355 } 356 } catch (Exception e) { 357 } 358 } 359 } catch (Exception e) { 360 Log.v(TAG, e.toString()); 361 } finally { 362 watchdog.end(); 363 watchdog.join(); 364 } 365 } 366 } 367