blob: f99c9278831b42a2e4f9dd3f1f44b46567b25327 [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media.cts;
import com.android.cts.media.R;
import android.media.MediaRecorder;
import android.media.MediaPlayer;
import android.view.SurfaceHolder;
import android.test.ActivityInstrumentationTestCase2;
import android.os.Environment;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.util.Log;
import java.util.Random;
/**
* Tests for the MediaPlayer.java and MediaRecorder.java APIs
*
* These testcases make randomized calls to the public APIs available, and
* the focus is on whether the randomized calls can lead to crash in
* mediaserver process and/or ANRs.
*
* The files in res/raw used by testLocalVideo* are (c) copyright 2008,
* Blender Foundation / www.bigbuckbunny.org, and are licensed under the Creative Commons
* Attribution 3.0 License at http://creativecommons.org/licenses/by/3.0/us/.
*/
public class MediaRandomTest extends ActivityInstrumentationTestCase2<MediaStubActivity> {
private static final String TAG = "MediaRandomTest";
private static final String OUTPUT_FILE =
Environment.getExternalStorageDirectory().toString() + "/record.3gp";
private static final int NUMBER_OF_RECORDER_RANDOM_ACTIONS = 100000;
private static final int NUMBER_OF_PLAYER_RANDOM_ACTIONS = 100000;
private MediaRecorder mRecorder;
private MediaPlayer mPlayer;
private SurfaceHolder mSurfaceHolder;
private Resources mResources;
// Modified across multiple threads
private volatile boolean mMediaServerDied;
private volatile int mAction;
private volatile int mParam;
@Override
protected void setUp() throws Exception {
super.setUp();
getInstrumentation().waitForIdleSync();
mMediaServerDied = false;
mSurfaceHolder = getActivity().getSurfaceHolder();
mResources = getInstrumentation().getTargetContext().getResources();
try {
// Running this on UI thread make sure that
// onError callback can be received.
runTestOnUiThread(new Runnable() {
public void run() {
mRecorder = new MediaRecorder();
mPlayer = new MediaPlayer();
}
});
} catch (Throwable e) {
e.printStackTrace();
fail();
}
}
@Override
protected void tearDown() throws Exception {
if (mRecorder != null) {
mRecorder.release();
mRecorder = null;
}
if (mPlayer != null) {
mPlayer.release();
mPlayer = null;
}
super.tearDown();
}
/**
* This is a watchdog used to stop the process if it hasn't been pinged
* for more than specified milli-seconds. It is used like:
*
* Watchdog w = new Watchdog(10000); // 10 seconds.
* w.start(); // start the watchdog.
* ...
* w.ping();
* ...
* w.ping();
* ...
* w.end(); // ask the watchdog to stop.
* w.join(); // join the thread.
*/
class Watchdog extends Thread {
private final long mTimeoutMs;
private boolean mWatchdogStop;
private boolean mWatchdogPinged;
public Watchdog(long timeoutMs) {
mTimeoutMs = timeoutMs;
mWatchdogStop = false;
mWatchdogPinged = false;
}
public synchronized void run() {
while (true) {
// avoid early termination by "spurious" waitup.
final long startTimeMs = System.currentTimeMillis();
long remainingWaitTimeMs = mTimeoutMs;
do {
try {
wait(remainingWaitTimeMs);
} catch (InterruptedException ex) {
// ignore.
}
remainingWaitTimeMs = mTimeoutMs - (System.currentTimeMillis() - startTimeMs);
} while (remainingWaitTimeMs > 0);
if (mWatchdogStop) {
break;
}
if (!mWatchdogPinged) {
fail("Action " + mAction + " Param " + mParam
+ " waited over " + (mTimeoutMs - remainingWaitTimeMs) + " ms");
return;
}
mWatchdogPinged = false;
}
}
public synchronized void ping() {
mWatchdogPinged = true;
this.notify();
}
public synchronized void end() {
mWatchdogStop = true;
this.notify();
}
}
public MediaRandomTest() {
super("com.android.cts.media", MediaStubActivity.class);
}
private void loadSource(int resid) throws Exception {
AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
try {
mPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
afd.getLength());
} finally {
afd.close();
}
}
public void testPlayerRandomAction() throws Exception {
Watchdog watchdog = new Watchdog(5000);
try {
mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
if (mPlayer == mp &&
what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
Log.e(TAG, "mediaserver process died");
mMediaServerDied = true;
}
return true;
}
});
loadSource(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
mPlayer.setDisplay(mSurfaceHolder);
mPlayer.prepare();
mPlayer.start();
long seed = System.currentTimeMillis();
Log.v(TAG, "seed = " + seed);
Random r = new Random(seed);
watchdog.start();
for (int i = 0; i < NUMBER_OF_PLAYER_RANDOM_ACTIONS; i++){
watchdog.ping();
assertTrue(!mMediaServerDied);
mAction = (int)(r.nextInt() % 12);
mParam = (int)(r.nextInt() % 1000000);
try {
switch (mAction) {
case 0:
mPlayer.getCurrentPosition();
break;
case 1:
mPlayer.getDuration();
break;
case 2:
mPlayer.getVideoHeight();
break;
case 3:
mPlayer.getVideoWidth();
break;
case 4:
mPlayer.isPlaying();
break;
case 5:
mPlayer.pause();
break;
case 6:
// Don't add mPlayer.prepare() call here for two reasons:
// 1. calling prepare() is a bad idea since it is a blocking call, and
// 2. when prepare() is in progress, mediaserver died message will not be sent to apps
mPlayer.prepareAsync();
break;
case 7:
mPlayer.seekTo((int)(mParam));
break;
case 8:
mPlayer.setLooping(mParam % 2 == 0);
break;
case 9:
mPlayer.setVolume((mParam % 1000) / 500.0f,
(mParam / 1000) / 500.0f);
break;
case 10:
mPlayer.start();
break;
case 11:
Thread.sleep(mParam % 20);
break;
}
} catch (Exception e) {
}
}
mPlayer.stop();
} catch (Exception e) {
Log.v(TAG, e.toString());
} finally {
watchdog.end();
watchdog.join();
}
}
public void testRecorderRandomAction() throws Exception {
Watchdog watchdog = new Watchdog(5000);
try {
long seed = System.currentTimeMillis();
Log.v(TAG, "seed = " + seed);
Random r = new Random(seed);
mMediaServerDied = false;
mRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() {
@Override
public void onError(MediaRecorder recorder, int what, int extra) {
if (mRecorder == recorder &&
what == MediaRecorder.MEDIA_ERROR_SERVER_DIED) {
Log.e(TAG, "mediaserver process died");
mMediaServerDied = true;
}
}
});
final int[] width = {176, 352, 320, 640, 1280, 1920};
final int[] height = {144, 288, 240, 480, 720, 1080};
watchdog.start();
for (int i = 0; i < NUMBER_OF_RECORDER_RANDOM_ACTIONS; i++) {
watchdog.ping();
assertTrue(!mMediaServerDied);
mAction = (int)(r.nextInt(14));
mParam = (int)(r.nextInt(1000000));
try {
switch (mAction) {
case 0:
mRecorder.setAudioSource(mParam % 3);
break;
case 1:
// XXX:
// Fix gralloc source and change
// mRecorder.setVideoSource(mParam % 3);
mRecorder.setVideoSource(mParam % 2);
break;
case 2:
mRecorder.setOutputFormat(mParam % 5);
break;
case 3:
mRecorder.setAudioEncoder(mParam % 3);
break;
case 4:
mRecorder.setVideoEncoder(mParam % 5);
break;
case 5:
mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
break;
case 6:
int index = mParam % width.length;
mRecorder.setVideoSize(width[index], height[index]);
break;
case 7:
mRecorder.setVideoFrameRate(mParam % 40 - 5);
break;
case 8:
mRecorder.setOutputFile(OUTPUT_FILE);
break;
case 9:
mRecorder.prepare();
break;
case 10:
mRecorder.start();
break;
case 11:
Thread.sleep(mParam % 20);
break;
case 12:
mRecorder.stop();
break;
case 13:
mRecorder.reset();
break;
default:
break;
}
} catch (Exception e) {
}
}
} catch (Exception e) {
Log.v(TAG, e.toString());
} finally {
watchdog.end();
watchdog.join();
}
}
}