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