blob: 9ebe3a4a35ef651a53e5477c75294ecbd63dabe0 [file] [log] [blame]
/*
* Copyright (C) 2009 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.stub.R;
import dalvik.annotation.TestLevel;
import dalvik.annotation.TestTargetClass;
import dalvik.annotation.TestTargetNew;
import dalvik.annotation.TestTargets;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaPlayer.OnSeekCompleteListener;
import android.media.MediaPlayer.OnVideoSizeChangedListener;
import android.net.Uri;
import android.os.Environment;
import android.os.Looper;
import android.os.PowerManager;
import android.test.ActivityInstrumentationTestCase2;
import android.view.SurfaceHolder;
import android.webkit.cts.CtsTestServer;
import android.util.Log;
import java.io.File;
import java.io.FileDescriptor;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@TestTargetClass(MediaPlayer.class)
public class MediaPlayerTest extends ActivityInstrumentationTestCase2<MediaStubActivity> {
private static String TAG = "CtsMediaPlayerTest";
private static final int SLEEP_TIME = 1000;
private static final int LONG_SLEEP_TIME = 6000;
private final String mSourceMediaOnSdcard;
private Monitor mOnVideoSizeChangedCalled = new Monitor();
private Monitor mOnBufferingUpdateCalled = new Monitor();
private Monitor mOnPrepareCalled = new Monitor();
private Monitor mOnSeekCompleteCalled = new Monitor();
private Monitor mOnCompletionCalled = new Monitor();
private Monitor mOnInfoCalled = new Monitor();
private Monitor mOnErrorCalled = new Monitor();
private Context mContext;
private Resources mResources;
private CtsTestServer mServer;
private CountDownLatch mVideoSizeChanged = new CountDownLatch(1);
private CountDownLatch mLock = new CountDownLatch(1);
private static Looper sLooper = null;
private static final int WAIT_FOR_COMMAND_TO_COMPLETE = 60000; //1 min max.
/*
* InstrumentationTestRunner.onStart() calls Looper.prepare(), which creates a looper
* for the current thread. However, since we don't actually call loop() in the test,
* any messages queued with that looper will never be consumed. We instantiate the player
* in the constructor, before setUp(), so that its constructor does not see the
* nonfunctional Looper.
*/
private MediaPlayer mMediaPlayer = new MediaPlayer();
private static class Monitor {
private boolean signalled;
public synchronized void reset() {
signalled = false;
}
public synchronized void signal() {
signalled = true;
notifyAll();
}
public synchronized void waitForSignal() throws InterruptedException {
while (!signalled) {
wait();
}
}
}
public MediaPlayerTest() {
super("com.android.cts.stub", MediaStubActivity.class);
mSourceMediaOnSdcard = new File(Environment.getExternalStorageDirectory(),
"record_and_play.3gp").getAbsolutePath();
}
@Override
protected void setUp() throws Exception {
super.setUp();
mContext = getInstrumentation().getTargetContext();
mResources = mContext.getResources();
}
@Override
protected void tearDown() throws Exception {
if (mMediaPlayer != null) {
mMediaPlayer.release();
}
File file = new File(mSourceMediaOnSdcard);
if (file.exists()) {
file.delete();
}
if (mServer != null) {
mServer.shutdown();
}
super.tearDown();
}
@TestTargets({
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "create",
args = {Context.class, int.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setAudioStreamType",
args = {int.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setWakeMode",
args = {Context.class, int.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "isPlaying",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "start",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setLooping",
args = {boolean.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "isLooping",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "getCurrentPosition",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "seekTo",
args = {int.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "pause",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "stop",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "reset",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setDataSource",
args = {FileDescriptor.class, long.class, long.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "prepare",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "getDuration",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "release",
args = {}
)
})
public void testPlayAudio() throws Exception {
final int mp3Duration = 34909;
final int tolerance = 70;
final int seekDuration = 100;
final int resid = R.raw.testmp3_2;
MediaPlayer mp = MediaPlayer.create(mContext, resid);
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
assertFalse(mp.isPlaying());
mp.start();
assertTrue(mp.isPlaying());
assertFalse(mp.isLooping());
mp.setLooping(true);
assertTrue(mp.isLooping());
assertEquals(mp3Duration, mp.getDuration(), tolerance);
int pos = mp.getCurrentPosition();
assertTrue(pos >= 0);
assertTrue(pos < mp3Duration - seekDuration);
mp.seekTo(pos + seekDuration);
assertEquals(pos + seekDuration, mp.getCurrentPosition(), tolerance);
// test pause and restart
mp.pause();
Thread.sleep(SLEEP_TIME);
assertFalse(mp.isPlaying());
mp.start();
assertTrue(mp.isPlaying());
// test stop and restart
mp.stop();
mp.reset();
AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
afd.close();
mp.prepare();
assertFalse(mp.isPlaying());
mp.start();
assertTrue(mp.isPlaying());
// waiting to complete
while(mp.isPlaying()) {
Thread.sleep(SLEEP_TIME);
}
mp.release();
}
@TestTargets({
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "MediaPlayer",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setDisplay",
args = {SurfaceHolder.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setScreenOnWhilePlaying",
args = {boolean.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "getVideoHeight",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "getVideoWidth",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setVolume",
args = {float.class, float.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setDataSource",
args = {FileDescriptor.class}
)
})
/*
* Initializes the message looper so that the mediaPlayer object can
* receive the callback messages.
*/
private void initializeMessageLooper() {
new Thread() {
@Override
public void run() {
// Set up a looper to be used by camera.
Looper.prepare();
// Save the looper so that we can terminate this thread
// after we are done with it.
sLooper = Looper.myLooper();
mLock.countDown();
Looper.loop(); // Blocks forever until Looper.quit() is called.
Log.v(TAG, "initializeMessageLooper: quit.");
}
}.start();
}
/*
* Terminates the message looper thread.
*/
private static void terminateMessageLooper() {
sLooper.quit();
}
public void testPlayVideo() throws Exception {
final int expectedVideoWidth = 352; // width of R.raw.testvideo
final int expectedVideoHeight = 288; // height of R.raw.testvideo
final float leftVolume = 0.5f;
final float rightVolume = 0.5f;
final int resid = R.raw.testvideo;
mMediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
mVideoSizeChanged.countDown();
}
});
AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
afd.close();
mMediaPlayer.setDisplay(getActivity().getSurfaceHolder());
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.prepare();
int videoWidth = 0;
int videoHeight = 0;
initializeMessageLooper();
try {
mLock.await(WAIT_FOR_COMMAND_TO_COMPLETE, TimeUnit.MILLISECONDS);
} catch(Exception e) {
Log.v(TAG, "looper was interrupted.");
return;
}
try {
try {
mVideoSizeChanged.await(WAIT_FOR_COMMAND_TO_COMPLETE, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Log.v(TAG, "wait was interrupted");
}
videoWidth = mMediaPlayer.getVideoWidth();
videoHeight = mMediaPlayer.getVideoHeight();
terminateMessageLooper();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
assertEquals(expectedVideoWidth, videoWidth);
assertEquals(expectedVideoHeight, videoHeight);
mMediaPlayer.start();
mMediaPlayer.setVolume(leftVolume, rightVolume);
// waiting to complete
while (mMediaPlayer.isPlaying()) {
Thread.sleep(SLEEP_TIME);
}
mMediaPlayer.release();
}
@TestTargets({
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setOnBufferingUpdateListener",
args = {OnBufferingUpdateListener.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setDataSource",
args = {String.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setDisplay",
args = {SurfaceHolder.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "start",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "stop",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "prepare",
args = {}
)
})
public void testPlayMp3Stream1() throws Throwable {
streamTest("ringer.mp3", false, false);
}
public void testPlayMp3Stream2() throws Throwable {
streamTest("ringer.mp3", false, false);
}
public void testPlayMp3StreamRedirect() throws Throwable {
streamTest("ringer.mp3", true, false);
}
public void testPlayMp3StreamNoLength() throws Throwable {
streamTest("noiseandchirps.mp3", false, true);
}
public void testPlayOggStream() throws Throwable {
streamTest("noiseandchirps.ogg", false, false);
}
public void testPlayOggStreamRedirect() throws Throwable {
streamTest("noiseandchirps.ogg", true, false);
}
public void testPlayOggStreamNoLength() throws Throwable {
streamTest("noiseandchirps.ogg", false, true);
}
private void streamTest(String name, boolean redirect, boolean nolength) throws Throwable {
mServer = new CtsTestServer(mContext);
String stream_url = null;
if (redirect) {
// Stagefright doesn't have a limit, but we can't test support of infinite redirects
// Up to 4 redirects seems reasonable though.
stream_url = mServer.getRedirectingAssetUrl(name, 4);
} else {
stream_url = mServer.getAssetUrl(name);
}
if (nolength) {
stream_url = stream_url + "?" + CtsTestServer.NOLENGTH_POSTFIX;
}
mMediaPlayer.setDataSource(stream_url);
mMediaPlayer.setDisplay(getActivity().getSurfaceHolder());
mMediaPlayer.setScreenOnWhilePlaying(true);
mOnBufferingUpdateCalled.reset();
mMediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
public void onBufferingUpdate(MediaPlayer mp, int percent) {
mOnBufferingUpdateCalled.signal();
}
});
assertFalse(mOnBufferingUpdateCalled.signalled);
mMediaPlayer.prepare();
if (nolength) {
mMediaPlayer.start();
Thread.sleep(LONG_SLEEP_TIME);
assertFalse(mMediaPlayer.isPlaying());
} else {
mOnBufferingUpdateCalled.waitForSignal();
mMediaPlayer.start();
Thread.sleep(SLEEP_TIME);
}
mMediaPlayer.stop();
mMediaPlayer.reset();
}
@TestTargets({
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setOnCompletionListener",
args = {OnCompletionListener.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setOnErrorListener",
args = {OnErrorListener.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setOnPreparedListener",
args = {OnPreparedListener.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setOnSeekCompleteListener",
args = {OnSeekCompleteListener.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setOnVideoSizeChangedListener",
args = {OnVideoSizeChangedListener.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setOnInfoListener",
args = {MediaPlayer.OnInfoListener.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setDataSource",
args = {FileDescriptor.class, long.class, long.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "prepare",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "start",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "stop",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "isPlaying",
args = {}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "seekTo",
args = {int.class}
)
})
public void testCallback() throws Throwable {
final int mp4Duration = 8484;
AssetFileDescriptor afd = mResources.openRawResourceFd(R.raw.testvideo);
try {
mMediaPlayer.setDataSource(
afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
} finally {
afd.close();
}
mMediaPlayer.setDisplay(getActivity().getSurfaceHolder());
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
mOnVideoSizeChangedCalled.signal();
}
});
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
mOnPrepareCalled.signal();
}
});
mMediaPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() {
public void onSeekComplete(MediaPlayer mp) {
mOnSeekCompleteCalled.signal();
}
});
mOnCompletionCalled.reset();
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mOnCompletionCalled.signal();
}
});
mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
public boolean onError(MediaPlayer mp, int what, int extra) {
mOnErrorCalled.signal();
return false;
}
});
mMediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {
public boolean onInfo(MediaPlayer mp, int what, int extra) {
mOnInfoCalled.signal();
return false;
}
});
assertFalse(mOnPrepareCalled.signalled);
assertFalse(mOnVideoSizeChangedCalled.signalled);
mMediaPlayer.prepare();
mOnPrepareCalled.waitForSignal();
mOnVideoSizeChangedCalled.waitForSignal();
mOnSeekCompleteCalled.signalled = false;
mMediaPlayer.seekTo(mp4Duration >> 1);
mOnSeekCompleteCalled.waitForSignal();
assertFalse(mOnCompletionCalled.signalled);
mMediaPlayer.start();
while(mMediaPlayer.isPlaying()) {
Thread.sleep(SLEEP_TIME);
}
assertFalse(mMediaPlayer.isPlaying());
mOnCompletionCalled.waitForSignal();
assertFalse(mOnErrorCalled.signalled);
mMediaPlayer.stop();
mMediaPlayer.start();
mOnErrorCalled.waitForSignal();
}
@TestTargets({
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setDataSource",
args = {Context.class, Uri.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "setDataSource",
args = {String.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "create",
args = {Context.class, Uri.class}
),
@TestTargetNew(
level = TestLevel.COMPLETE,
method = "create",
args = {Context.class, Uri.class, SurfaceHolder.class}
),
@TestTargetNew(
level = TestLevel.PARTIAL,
method = "prepareAsync",
args = {}
)
})
public void testRecordAndPlay() throws Exception {
if (!hasMicrophone()) {
return;
}
recordMedia();
MediaPlayer mp = new MediaPlayer();
mp.setDataSource(mSourceMediaOnSdcard);
mp.prepareAsync();
Thread.sleep(SLEEP_TIME);
playAndStop(mp);
mp.reset();
Uri uri = Uri.parse(mSourceMediaOnSdcard);
mp = new MediaPlayer();
mp.setDataSource(mContext, uri);
mp.prepareAsync();
Thread.sleep(SLEEP_TIME);
playAndStop(mp);
mp.release();
mp = MediaPlayer.create(mContext, uri);
playAndStop(mp);
mp.release();
mp = MediaPlayer.create(mContext, uri, getActivity().getSurfaceHolder());
playAndStop(mp);
mp.release();
}
private void playAndStop(MediaPlayer mp) throws Exception {
mp.start();
Thread.sleep(SLEEP_TIME);
mp.stop();
}
private void recordMedia() throws Exception {
MediaRecorder mr = new MediaRecorder();
mr.setAudioSource(MediaRecorder.AudioSource.MIC);
mr.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mr.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mr.setOutputFile(mSourceMediaOnSdcard);
mr.prepare();
mr.start();
Thread.sleep(SLEEP_TIME);
mr.stop();
mr.release();
}
private boolean hasMicrophone() {
return getActivity().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_MICROPHONE);
}
}