androidx.media.MediaPlayer2Test Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of android-all Show documentation
Show all versions of android-all Show documentation
A library jar that provides APIs for Applications written for the Google Android Platform.
/*
* Copyright 2018 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 androidx.media;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.hardware.Camera;
import android.media.AudioManager;
import android.media.MediaMetadataRetriever;
import android.media.MediaRecorder;
import android.media.MediaTimestamp;
import android.media.PlaybackParams;
import android.media.SubtitleData;
import android.media.SyncParams;
import android.media.audiofx.AudioEffect;
import android.media.audiofx.Visualizer;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.support.test.filters.LargeTest;
import android.support.test.filters.MediumTest;
import android.support.test.filters.SdkSuppress;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
import androidx.media.MediaPlayerInterface.PlayerEventCallback;
import androidx.media.test.R;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@RunWith(AndroidJUnit4.class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
public class MediaPlayer2Test extends MediaPlayer2TestBase {
private static final String LOG_TAG = "MediaPlayer2Test";
private static final int RECORDED_VIDEO_WIDTH = 176;
private static final int RECORDED_VIDEO_HEIGHT = 144;
private static final long RECORDED_DURATION_MS = 3000;
private static final float FLOAT_TOLERANCE = .0001f;
private String mRecordedFilePath;
private final Vector mSubtitleTrackIndex = new Vector<>();
private final Monitor mOnSubtitleDataCalled = new Monitor();
private int mSelectedSubtitleIndex;
private File mOutFile;
private Camera mCamera;
@Before
@Override
public void setUp() throws Throwable {
super.setUp();
mRecordedFilePath = new File(Environment.getExternalStorageDirectory(),
"mediaplayer_record.out").getAbsolutePath();
mOutFile = new File(mRecordedFilePath);
}
@After
@Override
public void tearDown() throws Exception {
super.tearDown();
if (mOutFile != null && mOutFile.exists()) {
mOutFile.delete();
}
}
@Test
@MediumTest
public void testPlayNullSourcePath() throws Exception {
final Monitor onSetDataSourceCalled = new Monitor();
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE) {
assertTrue(status != MediaPlayer2.CALL_STATUS_NO_ERROR);
onSetDataSourceCalled.signal();
}
}
};
synchronized (mEventCbLock) {
mEventCallbacks.add(ecb);
}
onSetDataSourceCalled.reset();
mPlayer.setDataSource((DataSourceDesc) null);
onSetDataSourceCalled.waitForSignal();
}
@Test
@LargeTest
public void testPlayAudioFromDataURI() throws Exception {
final int mp3Duration = 34909;
final int tolerance = 70;
final int seekDuration = 100;
// This is "R.raw.testmp3_2", base64-encoded.
final int resid = R.raw.testmp3_3;
InputStream is = mContext.getResources().openRawResource(resid);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder builder = new StringBuilder();
builder.append("data:;base64,");
builder.append(reader.readLine());
Uri uri = Uri.parse(builder.toString());
MediaPlayer2 mp = createMediaPlayer2(mContext, uri);
final Monitor onPrepareCalled = new Monitor();
final Monitor onPlayCalled = new Monitor();
final Monitor onSeekToCalled = new Monitor();
final Monitor onLoopCurrentCalled = new Monitor();
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
onPrepareCalled.signal();
}
}
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
onPlayCalled.signal();
} else if (what == MediaPlayer2.CALL_COMPLETED_LOOP_CURRENT) {
onLoopCurrentCalled.signal();
} else if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
onSeekToCalled.signal();
}
}
};
mp.setMediaPlayer2EventCallback(mExecutor, ecb);
try {
AudioAttributesCompat attributes = new AudioAttributesCompat.Builder()
.setLegacyStreamType(AudioManager.STREAM_MUSIC)
.build();
mp.setAudioAttributes(attributes);
assertFalse(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
onPlayCalled.reset();
mp.play();
onPlayCalled.waitForSignal();
assertTrue(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
/* FIXME: what's API for checking loop state?
assertFalse(mp.isLooping());
*/
onLoopCurrentCalled.reset();
mp.loopCurrent(true);
onLoopCurrentCalled.waitForSignal();
/* FIXME: what's API for checking loop state?
assertTrue(mp.isLooping());
*/
assertEquals(mp3Duration, mp.getDuration(), tolerance);
long pos = mp.getCurrentPosition();
assertTrue(pos >= 0);
assertTrue(pos < mp3Duration - seekDuration);
onSeekToCalled.reset();
mp.seekTo(pos + seekDuration, MediaPlayer2.SEEK_PREVIOUS_SYNC);
onSeekToCalled.waitForSignal();
assertEquals(pos + seekDuration, mp.getCurrentPosition(), tolerance);
// test pause and restart
mp.pause();
Thread.sleep(SLEEP_TIME);
assertFalse(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
onPlayCalled.reset();
mp.play();
onPlayCalled.waitForSignal();
assertTrue(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
// test stop and restart
mp.reset();
mp.setMediaPlayer2EventCallback(mExecutor, ecb);
mp.setDataSource(new DataSourceDesc.Builder()
.setDataSource(mContext, uri)
.build());
onPrepareCalled.reset();
mp.prepare();
onPrepareCalled.waitForSignal();
assertFalse(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
onPlayCalled.reset();
mp.play();
onPlayCalled.waitForSignal();
assertTrue(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
// waiting to complete
while (mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING) {
Thread.sleep(SLEEP_TIME);
}
} finally {
mp.close();
}
}
@Test
@LargeTest
public void testPlayAudio() throws Exception {
final int resid = R.raw.testmp3_2;
final int mp3Duration = 34909;
final int tolerance = 70;
final int seekDuration = 100;
MediaPlayer2 mp = createMediaPlayer2(mContext, resid);
final Monitor onPrepareCalled = new Monitor();
final Monitor onPlayCalled = new Monitor();
final Monitor onSeekToCalled = new Monitor();
final Monitor onLoopCurrentCalled = new Monitor();
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
onPrepareCalled.signal();
}
}
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
onPlayCalled.signal();
} else if (what == MediaPlayer2.CALL_COMPLETED_LOOP_CURRENT) {
onLoopCurrentCalled.signal();
} else if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
onSeekToCalled.signal();
}
}
};
mp.setMediaPlayer2EventCallback(mExecutor, ecb);
try {
AudioAttributesCompat attributes = new AudioAttributesCompat.Builder()
.setLegacyStreamType(AudioManager.STREAM_MUSIC)
.build();
mp.setAudioAttributes(attributes);
assertFalse(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
onPlayCalled.reset();
mp.play();
onPlayCalled.waitForSignal();
assertTrue(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
//assertFalse(mp.isLooping());
onLoopCurrentCalled.reset();
mp.loopCurrent(true);
onLoopCurrentCalled.waitForSignal();
//assertTrue(mp.isLooping());
assertEquals(mp3Duration, mp.getDuration(), tolerance);
long pos = mp.getCurrentPosition();
assertTrue(pos >= 0);
assertTrue(pos < mp3Duration - seekDuration);
onSeekToCalled.reset();
mp.seekTo(pos + seekDuration, MediaPlayer2.SEEK_PREVIOUS_SYNC);
onSeekToCalled.waitForSignal();
assertEquals(pos + seekDuration, mp.getCurrentPosition(), tolerance);
// test pause and restart
mp.pause();
Thread.sleep(SLEEP_TIME);
assertFalse(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
onPlayCalled.reset();
mp.play();
onPlayCalled.waitForSignal();
assertTrue(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
// test stop and restart
mp.reset();
AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
mp.setDataSource(new DataSourceDesc.Builder()
.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
.build());
mp.setMediaPlayer2EventCallback(mExecutor, ecb);
onPrepareCalled.reset();
mp.prepare();
onPrepareCalled.waitForSignal();
afd.close();
assertFalse(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
onPlayCalled.reset();
mp.play();
onPlayCalled.waitForSignal();
assertTrue(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
// waiting to complete
while (mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING) {
Thread.sleep(SLEEP_TIME);
}
} catch (Exception e) {
throw e;
} finally {
mp.close();
}
}
/*
public void testConcurentPlayAudio() throws Exception {
final int resid = R.raw.test1m1s; // MP3 longer than 1m are usualy offloaded
final int tolerance = 70;
List mps = Stream.generate(() -> createMediaPlayer2(mContext, resid))
.limit(5).collect(Collectors.toList());
try {
for (MediaPlayer2 mp : mps) {
Monitor onPlayCalled = new Monitor();
Monitor onLoopCurrentCalled = new Monitor();
MediaPlayer2.MediaPlayer2EventCallback ecb =
new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
onPlayCalled.signal();
} else if (what == MediaPlayer2.CALL_COMPLETED_LOOP_CURRENT) {
onLoopCurrentCalled.signal();
}
}
};
mp.setMediaPlayer2EventCallback(mExecutor, ecb);
AudioAttributes attributes = new AudioAttributes.Builder()
.setInternalLegacyStreamType(AudioManager.STREAM_MUSIC)
.build();
mp.setAudioAttributes(attributes);
assertFalse(mp.isPlaying());
onPlayCalled.reset();
mp.play();
onPlayCalled.waitForSignal();
assertTrue(mp.isPlaying());
assertFalse(mp.isLooping());
onLoopCurrentCalled.reset();
mp.loopCurrent(true);
onLoopCurrentCalled.waitForSignal();
assertTrue(mp.isLooping());
long pos = mp.getCurrentPosition();
assertTrue(pos >= 0);
Thread.sleep(SLEEP_TIME); // Delay each track to be able to ear them
}
// Check that all mp3 are playing concurrently here
for (MediaPlayer2 mp : mps) {
long pos = mp.getCurrentPosition();
Thread.sleep(SLEEP_TIME);
assertEquals(pos + SLEEP_TIME, mp.getCurrentPosition(), tolerance);
}
} finally {
mps.forEach(MediaPlayer2::close);
}
}
*/
@Test
@LargeTest
public void testPlayAudioLooping() throws Exception {
final int resid = R.raw.testmp3;
MediaPlayer2 mp = createMediaPlayer2(mContext, resid);
try {
AudioAttributesCompat attributes = new AudioAttributesCompat.Builder()
.setLegacyStreamType(AudioManager.STREAM_MUSIC)
.build();
mp.setAudioAttributes(attributes);
mp.loopCurrent(true);
final Monitor onCompletionCalled = new Monitor();
final Monitor onPlayCalled = new Monitor();
MediaPlayer2.MediaPlayer2EventCallback ecb =
new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd,
int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
Log.i("@@@", "got oncompletion");
onCompletionCalled.signal();
}
}
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
onPlayCalled.signal();
}
}
};
mp.setMediaPlayer2EventCallback(mExecutor, ecb);
assertFalse(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
onPlayCalled.reset();
mp.play();
onPlayCalled.waitForSignal();
assertTrue(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
long duration = mp.getDuration();
Thread.sleep(duration * 4); // allow for several loops
assertTrue(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
assertEquals("wrong number of completion signals", 0,
onCompletionCalled.getNumSignal());
mp.loopCurrent(false);
// wait for playback to finish
while (mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING) {
Thread.sleep(SLEEP_TIME);
}
assertEquals("wrong number of completion signals", 1,
onCompletionCalled.getNumSignal());
} finally {
mp.close();
}
}
@Test
@LargeTest
public void testPlayMidi() throws Exception {
final int resid = R.raw.midi8sec;
final int midiDuration = 8000;
final int tolerance = 70;
final int seekDuration = 1000;
MediaPlayer2 mp = createMediaPlayer2(mContext, resid);
final Monitor onPrepareCalled = new Monitor();
final Monitor onSeekToCalled = new Monitor();
final Monitor onLoopCurrentCalled = new Monitor();
MediaPlayer2.MediaPlayer2EventCallback ecb =
new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
onPrepareCalled.signal();
}
}
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_LOOP_CURRENT) {
onLoopCurrentCalled.signal();
} else if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
onSeekToCalled.signal();
}
}
};
mp.setMediaPlayer2EventCallback(mExecutor, ecb);
try {
AudioAttributesCompat attributes = new AudioAttributesCompat.Builder()
.setLegacyStreamType(AudioManager.STREAM_MUSIC)
.build();
mp.setAudioAttributes(attributes);
mp.play();
/* FIXME: what's API for checking loop state?
assertFalse(mp.isLooping());
*/
onLoopCurrentCalled.reset();
mp.loopCurrent(true);
onLoopCurrentCalled.waitForSignal();
/* FIXME: what's API for checking loop state?
assertTrue(mp.isLooping());
*/
assertEquals(midiDuration, mp.getDuration(), tolerance);
long pos = mp.getCurrentPosition();
assertTrue(pos >= 0);
assertTrue(pos < midiDuration - seekDuration);
onSeekToCalled.reset();
mp.seekTo(pos + seekDuration, MediaPlayer2.SEEK_PREVIOUS_SYNC);
onSeekToCalled.waitForSignal();
assertEquals(pos + seekDuration, mp.getCurrentPosition(), tolerance);
// test stop and restart
mp.reset();
AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
mp.setDataSource(new DataSourceDesc.Builder()
.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
.build());
mp.setMediaPlayer2EventCallback(mExecutor, ecb);
onPrepareCalled.reset();
mp.prepare();
onPrepareCalled.waitForSignal();
afd.close();
mp.play();
Thread.sleep(SLEEP_TIME);
} finally {
mp.close();
}
}
static class OutputListener {
int mSession;
AudioEffect mVc;
Visualizer mVis;
byte [] mVisData;
boolean mSoundDetected;
OutputListener(int session) {
mSession = session;
/* FIXME: find out a public API for replacing AudioEffect contructor.
// creating a volume controller on output mix ensures that ro.audio.silent mutes
// audio after the effects and not before
mVc = new AudioEffect(
AudioEffect.EFFECT_TYPE_NULL,
UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
0,
session);
mVc.setEnabled(true);
*/
mVis = new Visualizer(session);
int size = 256;
int[] range = Visualizer.getCaptureSizeRange();
if (size < range[0]) {
size = range[0];
}
if (size > range[1]) {
size = range[1];
}
assertTrue(mVis.setCaptureSize(size) == Visualizer.SUCCESS);
mVis.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
@Override
public void onWaveFormDataCapture(Visualizer visualizer,
byte[] waveform, int samplingRate) {
if (!mSoundDetected) {
for (int i = 0; i < waveform.length; i++) {
// 8 bit unsigned PCM, zero level is at 128, which is -128 when
// seen as a signed byte
if (waveform[i] != -128) {
mSoundDetected = true;
break;
}
}
}
}
@Override
public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
}
}, 10000 /* milliHertz */, true /* PCM */, false /* FFT */);
assertTrue(mVis.setEnabled(true) == Visualizer.SUCCESS);
}
void reset() {
mSoundDetected = false;
}
boolean heardSound() {
return mSoundDetected;
}
void release() {
mVis.release();
/* FIXME: find out a public API for replacing AudioEffect contructor.
mVc.release();
*/
}
}
public void testPlayAudioTwice() throws Exception {
final int resid = R.raw.camera_click;
MediaPlayer2 mp = createMediaPlayer2(mContext, resid);
try {
AudioAttributesCompat attributes = new AudioAttributesCompat.Builder()
.setLegacyStreamType(AudioManager.STREAM_MUSIC)
.build();
mp.setAudioAttributes(attributes);
OutputListener listener = new OutputListener(mp.getAudioSessionId());
Thread.sleep(SLEEP_TIME);
assertFalse("noise heard before test started", listener.heardSound());
mp.play();
Thread.sleep(SLEEP_TIME);
assertFalse("player was still playing after " + SLEEP_TIME + " ms",
mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
assertTrue("nothing heard while test ran", listener.heardSound());
listener.reset();
mp.seekTo(0, MediaPlayer2.SEEK_PREVIOUS_SYNC);
mp.play();
Thread.sleep(SLEEP_TIME);
assertTrue("nothing heard when sound was replayed", listener.heardSound());
listener.release();
} finally {
mp.close();
}
}
@Test
@LargeTest
public void testPlayVideo() throws Exception {
playVideoTest(R.raw.testvideo, 352, 288);
}
/**
* Test for reseting a surface during video playback
* After reseting, the video should continue playing
* from the time setDisplay() was called
*/
@Test
@LargeTest
public void testVideoSurfaceResetting() throws Exception {
final int tolerance = 150;
final int audioLatencyTolerance = 1000; /* covers audio path latency variability */
final int seekPos = 4760; // This is the I-frame position
final CountDownLatch seekDone = new CountDownLatch(1);
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
seekDone.countDown();
}
}
};
synchronized (mEventCbLock) {
mEventCallbacks.add(ecb);
}
if (!checkLoadResource(R.raw.testvideo)) {
return; // skip;
}
playLoadedVideo(352, 288, -1);
Thread.sleep(SLEEP_TIME);
long posBefore = mPlayer.getCurrentPosition();
mPlayer.setSurface(mActivity.getSurfaceHolder2().getSurface());
long posAfter = mPlayer.getCurrentPosition();
/* temporarily disable timestamp checking because MediaPlayer2 now seeks to I-frame
* position, instead of requested position. setDisplay invovles a seek operation
* internally.
*/
// TODO: uncomment out line below when MediaPlayer2 can seek to requested position.
// assertEquals(posAfter, posBefore, tolerance);
assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
Thread.sleep(SLEEP_TIME);
mPlayer.seekTo(seekPos, MediaPlayer2.SEEK_PREVIOUS_SYNC);
seekDone.await();
posAfter = mPlayer.getCurrentPosition();
assertEquals(seekPos, posAfter, tolerance + audioLatencyTolerance);
Thread.sleep(SLEEP_TIME / 2);
posBefore = mPlayer.getCurrentPosition();
mPlayer.setSurface(null);
posAfter = mPlayer.getCurrentPosition();
// TODO: uncomment out line below when MediaPlayer2 can seek to requested position.
// assertEquals(posAfter, posBefore, tolerance);
assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
Thread.sleep(SLEEP_TIME);
posBefore = mPlayer.getCurrentPosition();
mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
posAfter = mPlayer.getCurrentPosition();
// TODO: uncomment out line below when MediaPlayer2 can seek to requested position.
// assertEquals(posAfter, posBefore, tolerance);
assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
Thread.sleep(SLEEP_TIME);
}
public void testRecordedVideoPlayback0() throws Exception {
testRecordedVideoPlaybackWithAngle(0);
}
public void testRecordedVideoPlayback90() throws Exception {
testRecordedVideoPlaybackWithAngle(90);
}
public void testRecordedVideoPlayback180() throws Exception {
testRecordedVideoPlaybackWithAngle(180);
}
public void testRecordedVideoPlayback270() throws Exception {
testRecordedVideoPlaybackWithAngle(270);
}
private boolean hasCamera() {
return mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
}
private void testRecordedVideoPlaybackWithAngle(int angle) throws Exception {
int width = RECORDED_VIDEO_WIDTH;
int height = RECORDED_VIDEO_HEIGHT;
final String file = mRecordedFilePath;
final long durationMs = RECORDED_DURATION_MS;
if (!hasCamera()) {
return;
}
boolean isSupported = false;
mCamera = Camera.open(0);
Camera.Parameters parameters = mCamera.getParameters();
List videoSizes = parameters.getSupportedVideoSizes();
// getSupportedVideoSizes returns null when separate video/preview size
// is not supported.
if (videoSizes == null) {
videoSizes = parameters.getSupportedPreviewSizes();
}
for (Camera.Size size : videoSizes) {
if (size.width == width && size.height == height) {
isSupported = true;
break;
}
}
mCamera.release();
mCamera = null;
if (!isSupported) {
width = videoSizes.get(0).width;
height = videoSizes.get(0).height;
}
checkOrientation(angle);
recordVideo(width, height, angle, file, durationMs);
checkDisplayedVideoSize(width, height, angle, file);
checkVideoRotationAngle(angle, file);
}
private void checkOrientation(int angle) throws Exception {
assertTrue(angle >= 0);
assertTrue(angle < 360);
assertTrue((angle % 90) == 0);
}
private void recordVideo(
int w, int h, int angle, String file, long durationMs) throws Exception {
MediaRecorder recorder = new MediaRecorder();
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
recorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
recorder.setOutputFile(file);
recorder.setOrientationHint(angle);
recorder.setVideoSize(w, h);
recorder.setPreviewDisplay(mActivity.getSurfaceHolder2().getSurface());
recorder.prepare();
recorder.start();
Thread.sleep(durationMs);
recorder.stop();
recorder.release();
recorder = null;
}
private void checkDisplayedVideoSize(
int w, int h, int angle, String file) throws Exception {
int displayWidth = w;
int displayHeight = h;
if ((angle % 180) != 0) {
displayWidth = h;
displayHeight = w;
}
playVideoTest(file, displayWidth, displayHeight);
}
private void checkVideoRotationAngle(int angle, String file) {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(file);
String rotation = retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
retriever.release();
retriever = null;
assertNotNull(rotation);
assertEquals(Integer.parseInt(rotation), angle);
}
@Test
@LargeTest
public void testSkipToNext() throws Exception {
testPlaylist(true);
}
@Test
@LargeTest
public void testPlaylist() throws Exception {
testPlaylist(false);
}
private void testPlaylist(boolean skip) throws Exception {
if (!checkLoadResource(
R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz)) {
return; // skip
}
final DataSourceDesc dsd1 = createDataSourceDesc(
R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz);
final DataSourceDesc dsd2 = createDataSourceDesc(
R.raw.testvideo);
ArrayList nextDSDs = new ArrayList(2);
nextDSDs.add(dsd2);
nextDSDs.add(dsd1);
mPlayer.setNextDataSources(nextDSDs);
final Monitor onCompletion1Called = new Monitor();
final Monitor onCompletion2Called = new Monitor();
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
Log.i(LOG_TAG, "testPlaylist: prepared dsd MediaId=" + dsd.getMediaId());
mOnPrepareCalled.signal();
} else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
if (dsd == dsd1) {
onCompletion1Called.signal();
} else if (dsd == dsd2) {
onCompletion2Called.signal();
} else {
mOnCompletionCalled.signal();
}
}
}
};
synchronized (mEventCbLock) {
mEventCallbacks.add(ecb);
}
mOnCompletionCalled.reset();
onCompletion1Called.reset();
onCompletion2Called.reset();
mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
mPlayer.prepare();
mPlayer.play();
if (skip) {
mPlayer.skipToNext();
mPlayer.skipToNext();
} else {
mOnCompletionCalled.waitForSignal();
onCompletion2Called.waitForSignal();
}
onCompletion1Called.waitForSignal();
if (skip) {
assertFalse("first dsd completed", mOnCompletionCalled.isSignalled());
assertFalse("second dsd completed", onCompletion2Called.isSignalled());
}
mPlayer.reset();
}
// setPlaybackParams() with non-zero speed should NOT start playback.
// TODO: enable this test when MediaPlayer2.setPlaybackParams() is fixed
/*
public void testSetPlaybackParamsPositiveSpeed() throws Exception {
if (!checkLoadResource(
R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz)) {
return; // skip
}
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
mOnPrepareCalled.signal();
} else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
mOnCompletionCalled.signal();
}
}
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
mOnSeekCompleteCalled.signal();
}
}
};
synchronized (mEventCbLock) {
mEventCallbacks.add(ecb);
}
mOnCompletionCalled.reset();
mPlayer.setDisplay(mActivity.getSurfaceHolder());
mOnPrepareCalled.reset();
mPlayer.prepare();
mOnPrepareCalled.waitForSignal();
mOnSeekCompleteCalled.reset();
mPlayer.seekTo(0, MediaPlayer2.SEEK_PREVIOUS_SYNC);
mOnSeekCompleteCalled.waitForSignal();
final float playbackRate = 1.0f;
int playTime = 2000; // The testing clip is about 10 second long.
mPlayer.setPlaybackParams(new PlaybackParams().setSpeed(playbackRate));
assertTrue("MediaPlayer2 should be playing", mPlayer.isPlaying());
Thread.sleep(playTime);
assertTrue("MediaPlayer2 should still be playing",
mPlayer.getCurrentPosition() > 0);
long duration = mPlayer.getDuration();
mOnSeekCompleteCalled.reset();
mPlayer.seekTo(duration - 1000, MediaPlayer2.SEEK_PREVIOUS_SYNC);
mOnSeekCompleteCalled.waitForSignal();
mOnCompletionCalled.waitForSignal();
assertFalse("MediaPlayer2 should not be playing", mPlayer.isPlaying());
long eosPosition = mPlayer.getCurrentPosition();
mPlayer.setPlaybackParams(new PlaybackParams().setSpeed(playbackRate));
assertTrue("MediaPlayer2 should be playing after EOS", mPlayer.isPlaying());
Thread.sleep(playTime);
long position = mPlayer.getCurrentPosition();
assertTrue("MediaPlayer2 should still be playing after EOS",
position > 0 && position < eosPosition);
mPlayer.reset();
}
*/
@Test
@LargeTest
public void testPlaybackRate() throws Exception {
final int toleranceMs = 1000;
if (!checkLoadResource(
R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz)) {
return; // skip
}
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
mOnPrepareCalled.signal();
}
}
};
synchronized (mEventCbLock) {
mEventCallbacks.add(ecb);
}
mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
mOnPrepareCalled.reset();
mPlayer.prepare();
mOnPrepareCalled.waitForSignal();
SyncParams sync = new SyncParams().allowDefaults();
mPlayer.setSyncParams(sync);
sync = mPlayer.getSyncParams();
float[] rates = { 0.25f, 0.5f, 1.0f, 2.0f };
for (float playbackRate : rates) {
mPlayer.seekTo(0, MediaPlayer2.SEEK_PREVIOUS_SYNC);
Thread.sleep(1000);
int playTime = 4000; // The testing clip is about 10 second long.
mPlayer.setPlaybackParams(new PlaybackParams().setSpeed(playbackRate));
mPlayer.play();
Thread.sleep(playTime);
PlaybackParams pbp = mPlayer.getPlaybackParams();
assertEquals(
playbackRate, pbp.getSpeed(),
FLOAT_TOLERANCE + playbackRate * sync.getTolerance());
assertTrue("MediaPlayer2 should still be playing",
mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
long playedMediaDurationMs = mPlayer.getCurrentPosition();
int diff = Math.abs((int) (playedMediaDurationMs / playbackRate) - playTime);
if (diff > toleranceMs) {
fail("Media player had error in playback rate " + playbackRate
+ ", play time is " + playTime + " vs expected " + playedMediaDurationMs);
}
mPlayer.pause();
pbp = mPlayer.getPlaybackParams();
// TODO: pause() should NOT change PlaybackParams.
// assertEquals(0.f, pbp.getSpeed(), FLOAT_TOLERANCE);
}
mPlayer.reset();
}
@Test
@LargeTest
public void testSeekModes() throws Exception {
// This clip has 2 I frames at 66687us and 4299687us.
if (!checkLoadResource(
R.raw.bbb_s1_320x240_mp4_h264_mp2_800kbps_30fps_aac_lc_5ch_240kbps_44100hz)) {
return; // skip
}
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
mOnPrepareCalled.signal();
}
}
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
mOnSeekCompleteCalled.signal();
}
}
};
synchronized (mEventCbLock) {
mEventCallbacks.add(ecb);
}
mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
mOnPrepareCalled.reset();
mPlayer.prepare();
mOnPrepareCalled.waitForSignal();
mOnSeekCompleteCalled.reset();
mPlayer.play();
final long seekPosMs = 3000;
final long timeToleranceMs = 100;
final long syncTime1Ms = 67;
final long syncTime2Ms = 4300;
// TODO: tighten checking range. For now, ensure mediaplayer doesn't
// seek to previous sync or next sync.
long cp = runSeekMode(MediaPlayer2.SEEK_CLOSEST, seekPosMs);
assertTrue("MediaPlayer2 did not seek to closest position",
cp > seekPosMs && cp < syncTime2Ms);
// TODO: tighten checking range. For now, ensure mediaplayer doesn't
// seek to closest position or next sync.
cp = runSeekMode(MediaPlayer2.SEEK_PREVIOUS_SYNC, seekPosMs);
assertTrue("MediaPlayer2 did not seek to preivous sync position",
cp < seekPosMs - timeToleranceMs);
// TODO: tighten checking range. For now, ensure mediaplayer doesn't
// seek to closest position or previous sync.
cp = runSeekMode(MediaPlayer2.SEEK_NEXT_SYNC, seekPosMs);
assertTrue("MediaPlayer2 did not seek to next sync position",
cp > syncTime2Ms - timeToleranceMs);
// TODO: tighten checking range. For now, ensure mediaplayer doesn't
// seek to closest position or previous sync.
cp = runSeekMode(MediaPlayer2.SEEK_CLOSEST_SYNC, seekPosMs);
assertTrue("MediaPlayer2 did not seek to closest sync position",
cp > syncTime2Ms - timeToleranceMs);
mPlayer.reset();
}
private long runSeekMode(int seekMode, long seekPosMs) throws Exception {
final int sleepIntervalMs = 100;
int timeRemainedMs = 10000; // total time for testing
final int timeToleranceMs = 100;
mPlayer.seekTo(seekPosMs, seekMode);
mOnSeekCompleteCalled.waitForSignal();
mOnSeekCompleteCalled.reset();
long cp = -seekPosMs;
while (timeRemainedMs > 0) {
cp = mPlayer.getCurrentPosition();
// Wait till MediaPlayer2 starts rendering since MediaPlayer2 caches
// seek position as current position.
if (cp < seekPosMs - timeToleranceMs || cp > seekPosMs + timeToleranceMs) {
break;
}
timeRemainedMs -= sleepIntervalMs;
Thread.sleep(sleepIntervalMs);
}
assertTrue("MediaPlayer2 did not finish seeking in time for mode " + seekMode,
timeRemainedMs > 0);
return cp;
}
@Test
@LargeTest
public void testGetTimestamp() throws Exception {
final int toleranceUs = 100000;
final float playbackRate = 1.0f;
if (!checkLoadResource(
R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz)) {
return; // skip
}
final Monitor onPauseCalled = new Monitor();
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
mOnPrepareCalled.signal();
}
}
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_PAUSE) {
onPauseCalled.signal();
}
}
};
synchronized (mEventCbLock) {
mEventCallbacks.add(ecb);
}
mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
mOnPrepareCalled.reset();
mPlayer.prepare();
mOnPrepareCalled.waitForSignal();
mPlayer.play();
mPlayer.setPlaybackParams(new PlaybackParams().setSpeed(playbackRate));
Thread.sleep(SLEEP_TIME); // let player get into stable state.
long nt1 = System.nanoTime();
MediaTimestamp ts1 = mPlayer.getTimestamp();
long nt2 = System.nanoTime();
assertTrue("Media player should return a valid time stamp", ts1 != null);
assertEquals("MediaPlayer2 had error in clockRate " + ts1.getMediaClockRate(),
playbackRate, ts1.getMediaClockRate(), 0.001f);
assertTrue("The nanoTime of Media timestamp should be taken when getTimestamp is called.",
nt1 <= ts1.getAnchorSytemNanoTime() && ts1.getAnchorSytemNanoTime() <= nt2);
onPauseCalled.reset();
mPlayer.pause();
onPauseCalled.waitForSignal();
ts1 = mPlayer.getTimestamp();
assertTrue("Media player should return a valid time stamp", ts1 != null);
assertTrue("Media player should have play rate of 0.0f when paused",
ts1.getMediaClockRate() == 0.0f);
mPlayer.seekTo(0, MediaPlayer2.SEEK_PREVIOUS_SYNC);
mPlayer.play();
Thread.sleep(SLEEP_TIME); // let player get into stable state.
int playTime = 4000; // The testing clip is about 10 second long.
ts1 = mPlayer.getTimestamp();
assertTrue("Media player should return a valid time stamp", ts1 != null);
Thread.sleep(playTime);
MediaTimestamp ts2 = mPlayer.getTimestamp();
assertTrue("Media player should return a valid time stamp", ts2 != null);
assertTrue("The clockRate should not be changed.",
ts1.getMediaClockRate() == ts2.getMediaClockRate());
assertEquals("MediaPlayer2 had error in timestamp.",
ts1.getAnchorMediaTimeUs() + (long) (playTime * ts1.getMediaClockRate() * 1000),
ts2.getAnchorMediaTimeUs(), toleranceUs);
mPlayer.reset();
}
public void testLocalVideo_MKV_H265_1280x720_500kbps_25fps_AAC_Stereo_128kbps_44100Hz()
throws Exception {
playVideoTest(
R.raw.video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz, 1280, 720);
}
public void testLocalVideo_MP4_H264_480x360_500kbps_25fps_AAC_Stereo_128kbps_44110Hz()
throws Exception {
playVideoTest(
R.raw.video_480x360_mp4_h264_500kbps_25fps_aac_stereo_128kbps_44100hz, 480, 360);
}
public void testLocalVideo_MP4_H264_480x360_500kbps_30fps_AAC_Stereo_128kbps_44110Hz()
throws Exception {
playVideoTest(
R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz, 480, 360);
}
public void testLocalVideo_MP4_H264_480x360_1000kbps_25fps_AAC_Stereo_128kbps_44110Hz()
throws Exception {
playVideoTest(
R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 480, 360);
}
public void testLocalVideo_MP4_H264_480x360_1000kbps_30fps_AAC_Stereo_128kbps_44110Hz()
throws Exception {
playVideoTest(
R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz, 480, 360);
}
public void testLocalVideo_MP4_H264_480x360_1350kbps_25fps_AAC_Stereo_128kbps_44110Hz()
throws Exception {
playVideoTest(
R.raw.video_480x360_mp4_h264_1350kbps_25fps_aac_stereo_128kbps_44100hz, 480, 360);
}
public void testLocalVideo_MP4_H264_480x360_1350kbps_30fps_AAC_Stereo_128kbps_44110Hz()
throws Exception {
playVideoTest(
R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz, 480, 360);
}
public void testLocalVideo_MP4_H264_480x360_1350kbps_30fps_AAC_Stereo_128kbps_44110Hz_frag()
throws Exception {
playVideoTest(
R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented,
480, 360);
}
public void testLocalVideo_MP4_H264_480x360_1350kbps_30fps_AAC_Stereo_192kbps_44110Hz()
throws Exception {
playVideoTest(
R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz, 480, 360);
}
public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Mono_24kbps_11025Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_mono_24kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Mono_24kbps_22050Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_mono_24kbps_22050hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Stereo_24kbps_11025Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_stereo_24kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Stereo_24kbps_22050Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_stereo_24kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Stereo_128kbps_11025Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_stereo_128kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Stereo_128kbps_22050Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_stereo_128kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Mono_24kbps_11025Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_mono_24kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Mono_24kbps_22050Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_mono_24kbps_22050hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Stereo_24kbps_11025Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_stereo_24kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Stereo_24kbps_22050Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_stereo_24kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Stereo_128kbps_11025Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_stereo_128kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Stereo_128kbps_22050Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_stereo_128kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Mono_24kbps_11025Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Mono_24kbps_22050Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_22050hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Stereo_24kbps_11025Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_24kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Stereo_24kbps_22050Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_24kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Stereo_128kbps_11025Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Stereo_128kbps_22050Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Mono_24kbps_11025Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_mono_24kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Mono_24kbps_22050Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_mono_24kbps_22050hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Stereo_24kbps_11025Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_stereo_24kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Stereo_24kbps_22050Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_stereo_24kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Stereo_128kbps_11025Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_11025hz, 176, 144);
}
public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Stereo_128kbps_22050Hz()
throws Exception {
playVideoTest(
R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_22050hz, 176, 144);
}
private void readSubtitleTracks() throws Exception {
mSubtitleTrackIndex.clear();
List trackInfos = mPlayer.getTrackInfo();
if (trackInfos == null || trackInfos.size() == 0) {
return;
}
Vector subtitleTrackIndex = new Vector<>();
for (int i = 0; i < trackInfos.size(); ++i) {
assertTrue(trackInfos.get(i) != null);
if (trackInfos.get(i).getTrackType()
== MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
subtitleTrackIndex.add(i);
}
}
mSubtitleTrackIndex.addAll(subtitleTrackIndex);
}
private void selectSubtitleTrack(int index) throws Exception {
int trackIndex = mSubtitleTrackIndex.get(index);
mPlayer.selectTrack(trackIndex);
mSelectedSubtitleIndex = index;
}
private void deselectSubtitleTrack(int index) throws Exception {
int trackIndex = mSubtitleTrackIndex.get(index);
mOnDeselectTrackCalled.reset();
mPlayer.deselectTrack(trackIndex);
mOnDeselectTrackCalled.waitForSignal();
if (mSelectedSubtitleIndex == index) {
mSelectedSubtitleIndex = -1;
}
}
@Test
@LargeTest
public void testDeselectTrackForSubtitleTracks() throws Throwable {
if (!checkLoadResource(R.raw.testvideo_with_2_subtitle_tracks)) {
return; // skip;
}
mInstrumentation.waitForIdleSync();
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
mOnPrepareCalled.signal();
} else if (what == MediaPlayer2.MEDIA_INFO_METADATA_UPDATE) {
mOnInfoCalled.signal();
}
}
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
mOnSeekCompleteCalled.signal();
} else if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
mOnPlayCalled.signal();
} else if (what == MediaPlayer2.CALL_COMPLETED_DESELECT_TRACK) {
mCallStatus = status;
mOnDeselectTrackCalled.signal();
}
}
@Override
public void onSubtitleData(
MediaPlayer2 mp, DataSourceDesc dsd, SubtitleData data) {
if (data != null && data.getData() != null) {
mOnSubtitleDataCalled.signal();
}
}
};
synchronized (mEventCbLock) {
mEventCallbacks.add(ecb);
}
mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
mOnPrepareCalled.reset();
mPlayer.prepare();
mOnPrepareCalled.waitForSignal();
mOnPlayCalled.reset();
mPlayer.play();
mOnPlayCalled.waitForSignal();
assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
// Closed caption tracks are in-band.
// So, those tracks will be found after processing a number of frames.
mOnInfoCalled.waitForSignal(1500);
mOnInfoCalled.reset();
mOnInfoCalled.waitForSignal(1500);
readSubtitleTracks();
// Run twice to check if repeated selection-deselection on the same track works well.
for (int i = 0; i < 2; i++) {
// Waits until at least one subtitle is fired. Timeout is 2.5 seconds.
selectSubtitleTrack(i);
mOnSubtitleDataCalled.reset();
assertTrue(mOnSubtitleDataCalled.waitForSignal(2500));
// Try deselecting track.
deselectSubtitleTrack(i);
mOnSubtitleDataCalled.reset();
assertFalse(mOnSubtitleDataCalled.waitForSignal(1500));
}
// Deselecting unselected track: expected error status
mCallStatus = MediaPlayer2.CALL_STATUS_NO_ERROR;
deselectSubtitleTrack(0);
assertTrue(mCallStatus != MediaPlayer2.CALL_STATUS_NO_ERROR);
mPlayer.reset();
}
@Test
@LargeTest
public void testChangeSubtitleTrack() throws Throwable {
if (!checkLoadResource(R.raw.testvideo_with_2_subtitle_tracks)) {
return; // skip;
}
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
mOnPrepareCalled.signal();
} else if (what == MediaPlayer2.MEDIA_INFO_METADATA_UPDATE) {
mOnInfoCalled.signal();
}
}
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
mOnPlayCalled.signal();
}
}
@Override
public void onSubtitleData(
MediaPlayer2 mp, DataSourceDesc dsd, SubtitleData data) {
if (data != null && data.getData() != null) {
mOnSubtitleDataCalled.signal();
}
}
};
synchronized (mEventCbLock) {
mEventCallbacks.add(ecb);
}
mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
mOnPrepareCalled.reset();
mPlayer.prepare();
mOnPrepareCalled.waitForSignal();
mOnPlayCalled.reset();
mPlayer.play();
mOnPlayCalled.waitForSignal();
assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
// Closed caption tracks are in-band.
// So, those tracks will be found after processing a number of frames.
mOnInfoCalled.waitForSignal(1500);
mOnInfoCalled.reset();
mOnInfoCalled.waitForSignal(1500);
readSubtitleTracks();
// Waits until at least two captions are fired. Timeout is 2.5 sec.
selectSubtitleTrack(0);
assertTrue(mOnSubtitleDataCalled.waitForCountedSignals(2, 2500) >= 2);
mOnSubtitleDataCalled.reset();
selectSubtitleTrack(1);
assertTrue(mOnSubtitleDataCalled.waitForCountedSignals(2, 2500) >= 2);
mPlayer.reset();
}
@Test
@LargeTest
public void testGetTrackInfoForVideoWithSubtitleTracks() throws Throwable {
if (!checkLoadResource(R.raw.testvideo_with_2_subtitle_tracks)) {
return; // skip;
}
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
mOnPrepareCalled.signal();
} else if (what == MediaPlayer2.MEDIA_INFO_METADATA_UPDATE) {
mOnInfoCalled.signal();
}
}
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
mOnPlayCalled.signal();
}
}
};
synchronized (mEventCbLock) {
mEventCallbacks.add(ecb);
}
mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
mOnPrepareCalled.reset();
mPlayer.prepare();
mOnPrepareCalled.waitForSignal();
mOnPlayCalled.reset();
mPlayer.play();
mOnPlayCalled.waitForSignal();
assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
// The media metadata will be changed while playing since closed caption tracks are in-band
// and those tracks will be found after processing a number of frames. These tracks will be
// found within one second.
mOnInfoCalled.waitForSignal(1500);
mOnInfoCalled.reset();
mOnInfoCalled.waitForSignal(1500);
readSubtitleTracks();
assertEquals(2, mSubtitleTrackIndex.size());
mPlayer.reset();
}
@Test
@LargeTest
public void testMediaTimeDiscontinuity() throws Exception {
if (!checkLoadResource(
R.raw.bbb_s1_320x240_mp4_h264_mp2_800kbps_30fps_aac_lc_5ch_240kbps_44100hz)) {
return; // skip
}
final BlockingDeque timestamps = new LinkedBlockingDeque<>();
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
mOnSeekCompleteCalled.signal();
}
}
@Override
public void onMediaTimeDiscontinuity(
MediaPlayer2 mp, DataSourceDesc dsd, MediaTimestamp timestamp) {
timestamps.add(timestamp);
mOnMediaTimeDiscontinuityCalled.signal();
}
};
synchronized (mEventCbLock) {
mEventCallbacks.add(ecb);
}
mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
mPlayer.prepare();
// Timestamp needs to be reported when playback starts.
mOnMediaTimeDiscontinuityCalled.reset();
mPlayer.play();
do {
assertTrue(mOnMediaTimeDiscontinuityCalled.waitForSignal(1000));
} while (Math.abs(timestamps.getLast().getMediaClockRate() - 1.0f) > 0.01f);
// Timestamp needs to be reported when seeking is done.
mOnSeekCompleteCalled.reset();
mOnMediaTimeDiscontinuityCalled.reset();
mPlayer.seekTo(3000);
mOnSeekCompleteCalled.waitForSignal();
do {
assertTrue(mOnMediaTimeDiscontinuityCalled.waitForSignal(1000));
} while (Math.abs(timestamps.getLast().getMediaClockRate() - 1.0f) > 0.01f);
// Timestamp needs to be updated when playback rate changes.
mOnMediaTimeDiscontinuityCalled.reset();
mPlayer.setPlaybackParams(new PlaybackParams().setSpeed(0.5f));
mOnMediaTimeDiscontinuityCalled.waitForSignal();
do {
assertTrue(mOnMediaTimeDiscontinuityCalled.waitForSignal(1000));
} while (Math.abs(timestamps.getLast().getMediaClockRate() - 0.5f) > 0.01f);
// Timestamp needs to be updated when player is paused.
mOnMediaTimeDiscontinuityCalled.reset();
mPlayer.pause();
mOnMediaTimeDiscontinuityCalled.waitForSignal();
do {
assertTrue(mOnMediaTimeDiscontinuityCalled.waitForSignal(1000));
} while (Math.abs(timestamps.getLast().getMediaClockRate() - 0.0f) > 0.01f);
mPlayer.reset();
}
/*
* This test assumes the resources being tested are between 8 and 14 seconds long
* The ones being used here are 10 seconds long.
*/
@Test
@LargeTest
public void testResumeAtEnd() throws Throwable {
int testsRun = testResumeAtEnd(R.raw.loudsoftmp3)
+ testResumeAtEnd(R.raw.loudsoftwav)
+ testResumeAtEnd(R.raw.loudsoftogg)
+ testResumeAtEnd(R.raw.loudsoftitunes)
+ testResumeAtEnd(R.raw.loudsoftfaac)
+ testResumeAtEnd(R.raw.loudsoftaac);
}
// returns 1 if test was run, 0 otherwise
private int testResumeAtEnd(int res) throws Throwable {
if (!loadResource(res)) {
Log.i(LOG_TAG, "testResumeAtEnd: No decoder found for "
+ mContext.getResources().getResourceEntryName(res) + " --- skipping.");
return 0; // skip
}
mOnCompletionCalled.reset();
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
mOnPrepareCalled.signal();
} else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
mOnCompletionCalled.signal();
mPlayer.play();
}
}
};
mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
mOnPrepareCalled.reset();
mPlayer.prepare();
mOnPrepareCalled.waitForSignal();
// skip the first part of the file so we reach EOF sooner
mPlayer.seekTo(5000, MediaPlayer2.SEEK_PREVIOUS_SYNC);
mPlayer.play();
// sleep long enough that we restart playback at least once, but no more
Thread.sleep(10000);
assertTrue("MediaPlayer2 should still be playing",
mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
mPlayer.reset();
assertEquals("wrong number of repetitions", 1, mOnCompletionCalled.getNumSignal());
return 1;
}
@Test
@LargeTest
public void testPositionAtEnd() throws Throwable {
int testsRun = testPositionAtEnd(R.raw.test1m1shighstereo)
+ testPositionAtEnd(R.raw.loudsoftmp3)
+ testPositionAtEnd(R.raw.loudsoftwav)
+ testPositionAtEnd(R.raw.loudsoftogg)
+ testPositionAtEnd(R.raw.loudsoftitunes)
+ testPositionAtEnd(R.raw.loudsoftfaac)
+ testPositionAtEnd(R.raw.loudsoftaac);
}
private int testPositionAtEnd(int res) throws Throwable {
if (!loadResource(res)) {
Log.i(LOG_TAG, "testPositionAtEnd: No decoder found for "
+ mContext.getResources().getResourceEntryName(res) + " --- skipping.");
return 0; // skip
}
AudioAttributesCompat attributes = new AudioAttributesCompat.Builder()
.setLegacyStreamType(AudioManager.STREAM_MUSIC)
.build();
mPlayer.setAudioAttributes(attributes);
mOnCompletionCalled.reset();
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
mOnPrepareCalled.signal();
} else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
mOnCompletionCalled.signal();
}
}
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
mOnPlayCalled.signal();
}
}
};
mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
mOnPrepareCalled.reset();
mPlayer.prepare();
mOnPrepareCalled.waitForSignal();
long duration = mPlayer.getDuration();
assertTrue("resource too short", duration > 6000);
mPlayer.seekTo(duration - 5000, MediaPlayer2.SEEK_PREVIOUS_SYNC);
mOnPlayCalled.reset();
mPlayer.play();
mOnPlayCalled.waitForSignal();
while (mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING) {
Log.i("@@@@", "position: " + mPlayer.getCurrentPosition());
Thread.sleep(500);
}
Log.i("@@@@", "final position: " + mPlayer.getCurrentPosition());
assertTrue(mPlayer.getCurrentPosition() > duration - 1000);
mPlayer.reset();
return 1;
}
@Test
@LargeTest
public void testMediaPlayer2Callback() throws Throwable {
final int mp4Duration = 8484;
if (!checkLoadResource(R.raw.testvideo)) {
return; // skip;
}
mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
mOnCompletionCalled.reset();
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd,
int width, int height) {
mOnVideoSizeChangedCalled.signal();
}
@Override
public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
mOnErrorCalled.signal();
}
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
mOnInfoCalled.signal();
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
mOnPrepareCalled.signal();
} else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
mOnCompletionCalled.signal();
}
}
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
mOnSeekCompleteCalled.signal();
} else if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
mOnPlayCalled.signal();
}
}
};
synchronized (mEventCbLock) {
mEventCallbacks.add(ecb);
}
assertFalse(mOnPrepareCalled.isSignalled());
assertFalse(mOnVideoSizeChangedCalled.isSignalled());
mPlayer.prepare();
mOnPrepareCalled.waitForSignal();
mOnVideoSizeChangedCalled.waitForSignal();
mOnSeekCompleteCalled.reset();
mPlayer.seekTo(mp4Duration >> 1, MediaPlayer2.SEEK_PREVIOUS_SYNC);
mOnSeekCompleteCalled.waitForSignal();
assertFalse(mOnCompletionCalled.isSignalled());
mPlayer.play();
mOnPlayCalled.waitForSignal();
while (mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING) {
Thread.sleep(SLEEP_TIME);
}
assertFalse(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
mOnCompletionCalled.waitForSignal();
assertFalse(mOnErrorCalled.isSignalled());
mPlayer.reset();
}
@Test
@LargeTest
public void testPlayerStates() throws Throwable {
final int mp4Duration = 8484;
if (!checkLoadResource(R.raw.testvideo)) {
return; // skip;
}
mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
final Monitor prepareCompleted = new Monitor();
final Monitor playCompleted = new Monitor();
final Monitor pauseCompleted = new Monitor();
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_PREPARE) {
prepareCompleted.signal();
} else if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
playCompleted.signal();
} else if (what == MediaPlayer2.CALL_COMPLETED_PAUSE) {
pauseCompleted.signal();
}
}
};
synchronized (mEventCbLock) {
mEventCallbacks.add(ecb);
}
MediaPlayerInterface playerBase = mPlayer.getMediaPlayerInterface();
assertEquals(MediaPlayerInterface.BUFFERING_STATE_UNKNOWN, playerBase.getBufferingState());
assertEquals(MediaPlayerInterface.PLAYER_STATE_IDLE, playerBase.getPlayerState());
prepareCompleted.reset();
playerBase.prepare();
prepareCompleted.waitForSignal();
assertEquals(MediaPlayerInterface.BUFFERING_STATE_BUFFERING_AND_PLAYABLE,
playerBase.getBufferingState());
assertEquals(MediaPlayerInterface.PLAYER_STATE_PAUSED, playerBase.getPlayerState());
assertEquals(MediaPlayer2.MEDIAPLAYER2_STATE_PREPARED, mPlayer.getMediaPlayer2State());
playCompleted.reset();
playerBase.play();
playCompleted.waitForSignal();
assertEquals(MediaPlayerInterface.BUFFERING_STATE_BUFFERING_AND_PLAYABLE,
playerBase.getBufferingState());
assertEquals(MediaPlayerInterface.PLAYER_STATE_PLAYING, playerBase.getPlayerState());
pauseCompleted.reset();
playerBase.pause();
pauseCompleted.waitForSignal();
assertEquals(MediaPlayerInterface.BUFFERING_STATE_BUFFERING_AND_PLAYABLE,
playerBase.getBufferingState());
assertEquals(MediaPlayerInterface.PLAYER_STATE_PAUSED, playerBase.getPlayerState());
playerBase.reset();
assertEquals(MediaPlayerInterface.BUFFERING_STATE_UNKNOWN, playerBase.getBufferingState());
assertEquals(MediaPlayerInterface.PLAYER_STATE_IDLE, playerBase.getPlayerState());
}
@Test
@LargeTest
public void testPlayerEventCallback() throws Throwable {
final int mp4Duration = 8484;
if (!checkLoadResource(R.raw.testvideo)) {
return; // skip;
}
final DataSourceDesc dsd2 = createDataSourceDesc(
R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz);
mPlayer.setNextDataSource(dsd2);
mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
final Monitor onDsdChangedCalled = new Monitor();
final Monitor onPrepareCalled = new Monitor();
final Monitor onSeekCompleteCalled = new Monitor();
final Monitor onPlayerStateChangedCalled = new Monitor();
final AtomicInteger playerState = new AtomicInteger();
final Monitor onBufferingStateChangedCalled = new Monitor();
final AtomicInteger bufferingState = new AtomicInteger();
final Monitor onPlaybackSpeedChanged = new Monitor();
final AtomicReference playbackSpeed = new AtomicReference<>();
PlayerEventCallback callback = new PlayerEventCallback() {
@Override
public void onCurrentDataSourceChanged(MediaPlayerInterface mpb, DataSourceDesc dsd) {
onDsdChangedCalled.signal();
}
@Override
public void onMediaPrepared(MediaPlayerInterface mpb, DataSourceDesc dsd) {
onPrepareCalled.signal();
}
@Override
public void onPlayerStateChanged(MediaPlayerInterface mpb, int state) {
playerState.set(state);
onPlayerStateChangedCalled.signal();
}
@Override
public void onBufferingStateChanged(MediaPlayerInterface mpb, DataSourceDesc dsd,
int state) {
bufferingState.set(state);
onBufferingStateChangedCalled.signal();
}
@Override
public void onPlaybackSpeedChanged(MediaPlayerInterface mpb, float speed) {
playbackSpeed.set(speed);
onPlaybackSpeedChanged.signal();
}
@Override
public void onSeekCompleted(MediaPlayerInterface mpb, long position) {
onSeekCompleteCalled.signal();
}
};
MediaPlayerInterface basePlayer = mPlayer.getMediaPlayerInterface();
ExecutorService executor = Executors.newFixedThreadPool(1);
basePlayer.registerPlayerEventCallback(executor, callback);
onPrepareCalled.reset();
onPlayerStateChangedCalled.reset();
onBufferingStateChangedCalled.reset();
basePlayer.prepare();
do {
assertTrue(onBufferingStateChangedCalled.waitForSignal(1000));
} while (bufferingState.get()
!= MediaPlayerInterface.BUFFERING_STATE_BUFFERING_AND_STARVED);
assertTrue(onPrepareCalled.waitForSignal(1000));
do {
assertTrue(onPlayerStateChangedCalled.waitForSignal(1000));
} while (playerState.get() != MediaPlayerInterface.PLAYER_STATE_PAUSED);
do {
assertTrue(onBufferingStateChangedCalled.waitForSignal(1000));
} while (bufferingState.get()
!= MediaPlayerInterface.BUFFERING_STATE_BUFFERING_AND_PLAYABLE);
onSeekCompleteCalled.reset();
basePlayer.seekTo(mp4Duration >> 1);
onSeekCompleteCalled.waitForSignal();
onPlaybackSpeedChanged.reset();
basePlayer.setPlaybackSpeed(0.5f);
do {
assertTrue(onPlaybackSpeedChanged.waitForSignal(1000));
} while (Math.abs(playbackSpeed.get() - 0.5f) > FLOAT_TOLERANCE);
basePlayer.skipToNext();
assertTrue(onDsdChangedCalled.waitForSignal(1000));
basePlayer.reset();
basePlayer.unregisterPlayerEventCallback(callback);
executor.shutdown();
}
public void testRecordAndPlay() throws Exception {
if (!hasMicrophone()) {
return;
}
/* FIXME: check the codec exists.
if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_AUDIO_AMR_NB)
|| !MediaUtils.checkEncoder(MediaFormat.MIMETYPE_AUDIO_AMR_NB)) {
return; // skip
}
*/
File outputFile = new File(Environment.getExternalStorageDirectory(),
"record_and_play.3gp");
String outputFileLocation = outputFile.getAbsolutePath();
try {
recordMedia(outputFileLocation);
Uri uri = Uri.parse(outputFileLocation);
MediaPlayer2 mp = MediaPlayer2.create();
try {
mp.setDataSource(new DataSourceDesc.Builder()
.setDataSource(mContext, uri)
.build());
mp.prepare();
Thread.sleep(SLEEP_TIME);
playAndStop(mp);
} finally {
mp.close();
}
try {
mp = createMediaPlayer2(mContext, uri);
playAndStop(mp);
} finally {
if (mp != null) {
mp.close();
}
}
try {
mp = createMediaPlayer2(mContext, uri, mActivity.getSurfaceHolder());
playAndStop(mp);
} finally {
if (mp != null) {
mp.close();
}
}
} finally {
outputFile.delete();
}
}
private void playAndStop(MediaPlayer2 mp) throws Exception {
mp.play();
Thread.sleep(SLEEP_TIME);
mp.reset();
}
private void recordMedia(String outputFile) throws Exception {
MediaRecorder mr = new MediaRecorder();
try {
mr.setAudioSource(MediaRecorder.AudioSource.MIC);
mr.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mr.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mr.setOutputFile(outputFile);
mr.prepare();
mr.start();
Thread.sleep(SLEEP_TIME);
mr.stop();
} finally {
mr.release();
}
}
private boolean hasMicrophone() {
return mActivity.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_MICROPHONE);
}
// Smoke test playback from a Media2DataSource.
@Test
@LargeTest
public void testPlaybackFromAMedia2DataSource() throws Exception {
final int resid = R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
final int duration = 10000;
/* FIXME: check the codec exists.
if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
return;
}
*/
TestMedia2DataSource dataSource =
TestMedia2DataSource.fromAssetFd(mResources.openRawResourceFd(resid));
// Test returning -1 from getSize() to indicate unknown size.
dataSource.returnFromGetSize(-1);
mPlayer.setDataSource(new DataSourceDesc.Builder()
.setDataSource(dataSource)
.build());
playLoadedVideo(null, null, -1);
assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
// Test pause and restart.
mPlayer.pause();
Thread.sleep(SLEEP_TIME);
assertFalse(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
mOnPrepareCalled.signal();
}
}
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
mOnPlayCalled.signal();
}
}
};
mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
mOnPlayCalled.reset();
mPlayer.play();
mOnPlayCalled.waitForSignal();
assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
// Test reset.
mPlayer.reset();
mPlayer.setDataSource(new DataSourceDesc.Builder()
.setDataSource(dataSource)
.build());
mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
mOnPrepareCalled.reset();
mPlayer.prepare();
mOnPrepareCalled.waitForSignal();
mOnPlayCalled.reset();
mPlayer.play();
mOnPlayCalled.waitForSignal();
assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
// Test seek. Note: the seek position is cached and returned as the
// current position so there's no point in comparing them.
mPlayer.seekTo(duration - SLEEP_TIME, MediaPlayer2.SEEK_PREVIOUS_SYNC);
while (mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING) {
Thread.sleep(SLEEP_TIME);
}
}
@Test
@LargeTest
public void testNullMedia2DataSourceIsRejected() throws Exception {
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE) {
mCallStatus = status;
mOnPlayCalled.signal();
}
}
};
mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
mCallStatus = MediaPlayer2.CALL_STATUS_NO_ERROR;
mPlayer.setDataSource((DataSourceDesc) null);
mOnPlayCalled.waitForSignal();
assertTrue(mCallStatus != MediaPlayer2.CALL_STATUS_NO_ERROR);
}
@Test
@LargeTest
public void testMedia2DataSourceIsClosedOnReset() throws Exception {
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
if (what == MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE) {
mCallStatus = status;
mOnPlayCalled.signal();
}
}
};
mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
TestMedia2DataSource dataSource = new TestMedia2DataSource(new byte[0]);
mPlayer.setDataSource(new DataSourceDesc.Builder()
.setDataSource(dataSource)
.build());
mOnPlayCalled.waitForSignal();
mPlayer.reset();
assertTrue(dataSource.isClosed());
}
@Test
@LargeTest
public void testPlaybackFailsIfMedia2DataSourceThrows() throws Exception {
final int resid = R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
/* FIXME: check the codec exists.
if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
return;
}
*/
setOnErrorListener();
TestMedia2DataSource dataSource =
TestMedia2DataSource.fromAssetFd(mResources.openRawResourceFd(resid));
mPlayer.setDataSource(new DataSourceDesc.Builder()
.setDataSource(dataSource)
.build());
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
mOnPrepareCalled.signal();
}
}
};
synchronized (mEventCbLock) {
mEventCallbacks.add(ecb);
}
mOnPrepareCalled.reset();
mPlayer.prepare();
mOnPrepareCalled.waitForSignal();
dataSource.throwFromReadAt();
mPlayer.play();
assertTrue(mOnErrorCalled.waitForSignal());
}
@Test
@LargeTest
public void testPlaybackFailsIfMedia2DataSourceReturnsAnError() throws Exception {
final int resid = R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
/* FIXME: check the codec exists.
if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
return;
}
*/
TestMedia2DataSource dataSource =
TestMedia2DataSource.fromAssetFd(mResources.openRawResourceFd(resid));
mPlayer.setDataSource(new DataSourceDesc.Builder()
.setDataSource(dataSource)
.build());
setOnErrorListener();
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
mOnPrepareCalled.signal();
}
}
};
synchronized (mEventCbLock) {
mEventCallbacks.add(ecb);
}
mOnPrepareCalled.reset();
mPlayer.prepare();
mOnPrepareCalled.waitForSignal();
dataSource.returnFromReadAt(-2);
mPlayer.play();
assertTrue(mOnErrorCalled.waitForSignal());
}
@Test
@SmallTest
public void testClearPendingCommands() throws Exception {
final Monitor readAllowed = new Monitor();
Media2DataSource dataSource = new Media2DataSource() {
@Override
public int readAt(long position, byte[] buffer, int offset, int size)
throws IOException {
try {
readAllowed.waitForSignal();
} catch (InterruptedException e) {
fail();
}
return -1;
}
@Override
public long getSize() throws IOException {
return -1; // Unknown size
}
@Override
public void close() throws IOException {}
};
final ArrayDeque commandsCompleted = new ArrayDeque<>();
setOnErrorListener();
MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
@Override
public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
mOnPrepareCalled.signal();
}
}
@Override
public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
commandsCompleted.add(what);
}
@Override
public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
mOnErrorCalled.signal();
}
};
synchronized (mEventCbLock) {
mEventCallbacks.add(ecb);
}
mOnPrepareCalled.reset();
mOnErrorCalled.reset();
mPlayer.setDataSource(new DataSourceDesc.Builder()
.setDataSource(dataSource)
.build());
// prepare() will be pending until readAllowed is signaled.
mPlayer.prepare();
mPlayer.play();
mPlayer.pause();
mPlayer.play();
mPlayer.pause();
mPlayer.play();
mPlayer.seekTo(1000);
// Cause a failure on the pending prepare operation.
readAllowed.signal();
mOnErrorCalled.waitForSignal();
assertEquals(0, mOnPrepareCalled.getNumSignal());
assertEquals(1, commandsCompleted.size());
assertEquals(MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE,
(int) commandsCompleted.peekFirst());
}
}