| /* |
| * 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.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.SdkSuppress; |
| import android.support.test.runner.AndroidJUnit4; |
| import android.util.Log; |
| |
| 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.InputStream; |
| import java.io.InputStreamReader; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Vector; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Executors; |
| 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<Integer> mSubtitleTrackIndex = new Vector<>(); |
| private final Monitor mOnSubtitleDataCalled = new Monitor(); |
| private int mSelectedSubtitleIndex; |
| |
| private File mOutFile; |
| private Camera mCamera; |
| |
| @Before |
| @Override |
| public void setUp() throws Exception { |
| 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(); |
| } |
| } |
| |
| // Bug 13652927 |
| public void testVorbisCrash() throws Exception { |
| MediaPlayer2 mp = mPlayer; |
| MediaPlayer2 mp2 = mPlayer2; |
| AssetFileDescriptor afd2 = mResources.openRawResourceFd(R.raw.testmp3_2); |
| mp2.setDataSource(new DataSourceDesc.Builder() |
| .setDataSource(afd2.getFileDescriptor(), afd2.getStartOffset(), afd2.getLength()) |
| .build()); |
| final Monitor onPrepareCalled = new Monitor(); |
| final Monitor onErrorCalled = 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 onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) { |
| onErrorCalled.signal(); |
| } |
| }; |
| mp2.setMediaPlayer2EventCallback(mExecutor, ecb); |
| mp2.prepare(); |
| onPrepareCalled.waitForSignal(); |
| afd2.close(); |
| mp2.clearMediaPlayer2EventCallback(); |
| |
| mp2.loopCurrent(true); |
| mp2.play(); |
| |
| for (int i = 0; i < 20; i++) { |
| try { |
| AssetFileDescriptor afd = mResources.openRawResourceFd(R.raw.bug13652927); |
| mp.setDataSource(new DataSourceDesc.Builder() |
| .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), |
| afd.getLength()) |
| .build()); |
| mp.setMediaPlayer2EventCallback(mExecutor, ecb); |
| onPrepareCalled.reset(); |
| mp.prepare(); |
| onErrorCalled.waitForSignal(); |
| afd.close(); |
| } catch (Exception e) { |
| // expected to fail |
| Log.i("@@@", "failed: " + e); |
| } |
| Thread.sleep(500); |
| assertTrue("media player died", |
| mp2.getPlayerState() == MediaPlayerBase.PLAYER_STATE_PLAYING); |
| mp.reset(); |
| } |
| } |
| |
| 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(); |
| } |
| |
| 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); |
| /* FIXME: ensure screen is on while testing. |
| mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK); |
| */ |
| |
| assertFalse(mp.getPlayerState() == MediaPlayerBase.PLAYER_STATE_PLAYING); |
| onPlayCalled.reset(); |
| mp.play(); |
| onPlayCalled.waitForSignal(); |
| assertTrue(mp.getPlayerState() == MediaPlayerBase.PLAYER_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.getPlayerState() == MediaPlayerBase.PLAYER_STATE_PLAYING); |
| onPlayCalled.reset(); |
| mp.play(); |
| onPlayCalled.waitForSignal(); |
| assertTrue(mp.getPlayerState() == MediaPlayerBase.PLAYER_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.getPlayerState() == MediaPlayerBase.PLAYER_STATE_PLAYING); |
| onPlayCalled.reset(); |
| mp.play(); |
| onPlayCalled.waitForSignal(); |
| assertTrue(mp.getPlayerState() == MediaPlayerBase.PLAYER_STATE_PLAYING); |
| |
| // waiting to complete |
| while (mp.getPlayerState() == MediaPlayerBase.PLAYER_STATE_PLAYING) { |
| Thread.sleep(SLEEP_TIME); |
| } |
| } finally { |
| mp.close(); |
| } |
| } |
| |
| 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.getPlayerState() == MediaPlayerBase.PLAYER_STATE_PLAYING); |
| onPlayCalled.reset(); |
| mp.play(); |
| onPlayCalled.waitForSignal(); |
| assertTrue(mp.getPlayerState() == MediaPlayerBase.PLAYER_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.getPlayerState() == MediaPlayerBase.PLAYER_STATE_PLAYING); |
| onPlayCalled.reset(); |
| mp.play(); |
| onPlayCalled.waitForSignal(); |
| assertTrue(mp.getPlayerState() == MediaPlayerBase.PLAYER_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.getPlayerState() == MediaPlayerBase.PLAYER_STATE_PLAYING); |
| onPlayCalled.reset(); |
| mp.play(); |
| onPlayCalled.waitForSignal(); |
| assertTrue(mp.getPlayerState() == MediaPlayerBase.PLAYER_STATE_PLAYING); |
| |
| // waiting to complete |
| while (mp.getPlayerState() == MediaPlayerBase.PLAYER_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<MediaPlayer2> 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); |
| mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK); |
| |
| 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); |
| } |
| } |
| */ |
| |
| 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) { |
| 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.getPlayerState() == MediaPlayerBase.PLAYER_STATE_PLAYING); |
| onPlayCalled.reset(); |
| mp.play(); |
| onPlayCalled.waitForSignal(); |
| assertTrue(mp.getPlayerState() == MediaPlayerBase.PLAYER_STATE_PLAYING); |
| |
| long duration = mp.getDuration(); |
| Thread.sleep(duration * 4); // allow for several loops |
| assertTrue(mp.getPlayerState() == MediaPlayerBase.PLAYER_STATE_PLAYING); |
| assertEquals("wrong number of completion signals", 0, |
| onCompletionCalled.getNumSignal()); |
| mp.loopCurrent(false); |
| |
| // wait for playback to finish |
| while (mp.getPlayerState() == MediaPlayerBase.PLAYER_STATE_PLAYING) { |
| Thread.sleep(SLEEP_TIME); |
| } |
| assertEquals("wrong number of completion signals", 1, |
| onCompletionCalled.getNumSignal()); |
| } finally { |
| mp.close(); |
| } |
| } |
| |
| 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.getPlayerState() == MediaPlayerBase.PLAYER_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.getPlayerState() == MediaPlayerBase.PLAYER_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.getPlayerState() == MediaPlayerBase.PLAYER_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.getPlayerState() == MediaPlayerBase.PLAYER_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<Camera.Size> 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); |
| } |
| |
| public void testPlaylist() 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<DataSourceDesc> nextDSDs = new ArrayList<DataSourceDesc>(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(); |
| |
| mOnCompletionCalled.waitForSignal(); |
| onCompletion2Called.waitForSignal(); |
| onCompletion1Called.waitForSignal(); |
| |
| 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.getPlayerState() == MediaPlayerBase.PLAYER_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<MediaPlayer2.TrackInfo> trackInfos = mPlayer.getTrackInfo(); |
| if (trackInfos == null || trackInfos.size() == 0) { |
| return; |
| } |
| |
| Vector<Integer> 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; |
| } |
| } |
| |
| public void testDeselectTrackForSubtitleTracks() throws Throwable { |
| if (!checkLoadResource(R.raw.testvideo_with_2_subtitle_tracks)) { |
| return; // skip; |
| } |
| |
| /* FIXME: find out counter part of waitForIdleSync. |
| getInstrumentation().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(); |
| } |
| } |
| }; |
| synchronized (mEventCbLock) { |
| mEventCallbacks.add(ecb); |
| } |
| |
| /* TODO: uncomment once API is available in supportlib. |
| mPlayer.setOnSubtitleDataListener(new MediaPlayer2.OnSubtitleDataListener() { |
| @Override |
| public void onSubtitleData(MediaPlayer2 mp, SubtitleData data) { |
| if (data != null && data.getData() != null) { |
| mOnSubtitleDataCalled.signal(); |
| } |
| } |
| }); |
| */ |
| |
| mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface()); |
| |
| mOnPrepareCalled.reset(); |
| mPlayer.prepare(); |
| mOnPrepareCalled.waitForSignal(); |
| |
| mOnPlayCalled.reset(); |
| mPlayer.play(); |
| mOnPlayCalled.waitForSignal(); |
| assertTrue(mPlayer.getPlayerState() == MediaPlayerBase.PLAYER_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(); |
| } |
| |
| public void testChangeSubtitleTrack() throws Throwable { |
| if (!checkLoadResource(R.raw.testvideo_with_2_subtitle_tracks)) { |
| return; // skip; |
| } |
| |
| /* TODO: uncomment once API is available in supportlib. |
| mPlayer.setOnSubtitleDataListener(new MediaPlayer2.OnSubtitleDataListener() { |
| @Override |
| public void onSubtitleData(MediaPlayer2 mp, SubtitleData data) { |
| if (data != null && data.getData() != null) { |
| mOnSubtitleDataCalled.signal(); |
| } |
| } |
| }); |
| */ |
| |
| 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.getPlayerState() == MediaPlayerBase.PLAYER_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.getPlayerState() == MediaPlayerBase.PLAYER_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(); |
| } |
| |
| /* |
| * This test assumes the resources being tested are between 8 and 14 seconds long |
| * The ones being used here are 10 seconds long. |
| */ |
| 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.getPlayerState() == MediaPlayerBase.PLAYER_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.getPlayerState() == MediaPlayerBase.PLAYER_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.getPlayerState() == MediaPlayerBase.PLAYER_STATE_PLAYING) { |
| Thread.sleep(SLEEP_TIME); |
| } |
| assertFalse(mPlayer.getPlayerState() == MediaPlayerBase.PLAYER_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); |
| } |
| |
| assertEquals(MediaPlayerBase.BUFFERING_STATE_UNKNOWN, mPlayer.getBufferingState()); |
| assertEquals(MediaPlayerBase.PLAYER_STATE_IDLE, mPlayer.getPlayerState()); |
| prepareCompleted.reset(); |
| mPlayer.prepare(); |
| prepareCompleted.waitForSignal(); |
| assertEquals(MediaPlayerBase.BUFFERING_STATE_BUFFERING_AND_PLAYABLE, |
| mPlayer.getBufferingState()); |
| assertEquals(MediaPlayerBase.PLAYER_STATE_PAUSED, mPlayer.getPlayerState()); |
| |
| playCompleted.reset(); |
| mPlayer.play(); |
| playCompleted.waitForSignal(); |
| assertEquals(MediaPlayerBase.BUFFERING_STATE_BUFFERING_AND_PLAYABLE, |
| mPlayer.getBufferingState()); |
| assertEquals(MediaPlayerBase.PLAYER_STATE_PLAYING, mPlayer.getPlayerState()); |
| |
| pauseCompleted.reset(); |
| mPlayer.pause(); |
| pauseCompleted.waitForSignal(); |
| assertEquals(MediaPlayerBase.BUFFERING_STATE_BUFFERING_AND_PLAYABLE, |
| mPlayer.getBufferingState()); |
| assertEquals(MediaPlayerBase.PLAYER_STATE_PAUSED, mPlayer.getPlayerState()); |
| |
| mPlayer.reset(); |
| assertEquals(MediaPlayerBase.BUFFERING_STATE_UNKNOWN, mPlayer.getBufferingState()); |
| assertEquals(MediaPlayerBase.PLAYER_STATE_IDLE, mPlayer.getPlayerState()); |
| } |
| |
| @Test |
| @LargeTest |
| public void testPlayerEventCallback() throws Throwable { |
| final int mp4Duration = 8484; |
| |
| if (!checkLoadResource(R.raw.testvideo)) { |
| return; // skip; |
| } |
| |
| mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface()); |
| |
| 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<Float> playbackSpeed = new AtomicReference<>(); |
| |
| MediaPlayerBase.PlayerEventCallback callback = new MediaPlayerBase.PlayerEventCallback() { |
| // TODO: implement and add test case for onCurrentDataSourceChanged() callback. |
| @Override |
| public void onMediaPrepared(MediaPlayerBase mpb, DataSourceDesc dsd) { |
| onPrepareCalled.signal(); |
| } |
| |
| @Override |
| public void onPlayerStateChanged(MediaPlayerBase mpb, int state) { |
| playerState.set(state); |
| onPlayerStateChangedCalled.signal(); |
| } |
| |
| @Override |
| public void onBufferingStateChanged(MediaPlayerBase mpb, DataSourceDesc dsd, |
| int state) { |
| bufferingState.set(state); |
| onBufferingStateChangedCalled.signal(); |
| } |
| |
| @Override |
| public void onPlaybackSpeedChanged(MediaPlayerBase mpb, float speed) { |
| playbackSpeed.set(speed); |
| onPlaybackSpeedChanged.signal(); |
| } |
| |
| @Override |
| public void onSeekCompleted(MediaPlayerBase mpb, long position) { |
| onSeekCompleteCalled.signal(); |
| } |
| }; |
| ExecutorService executor = Executors.newFixedThreadPool(1); |
| mPlayer.registerPlayerEventCallback(executor, callback); |
| |
| onPrepareCalled.reset(); |
| onPlayerStateChangedCalled.reset(); |
| onBufferingStateChangedCalled.reset(); |
| mPlayer.prepare(); |
| do { |
| assertTrue(onBufferingStateChangedCalled.waitForSignal(1000)); |
| } while (bufferingState.get() != MediaPlayerBase.BUFFERING_STATE_BUFFERING_AND_STARVED); |
| |
| assertTrue(onPrepareCalled.waitForSignal(1000)); |
| do { |
| assertTrue(onPlayerStateChangedCalled.waitForSignal(1000)); |
| } while (playerState.get() != MediaPlayerBase.PLAYER_STATE_PAUSED); |
| do { |
| assertTrue(onBufferingStateChangedCalled.waitForSignal(1000)); |
| } while (bufferingState.get() != MediaPlayerBase.BUFFERING_STATE_BUFFERING_AND_PLAYABLE); |
| |
| onSeekCompleteCalled.reset(); |
| mPlayer.seekTo(mp4Duration >> 1, MediaPlayer2.SEEK_PREVIOUS_SYNC); |
| onSeekCompleteCalled.waitForSignal(); |
| |
| onPlaybackSpeedChanged.reset(); |
| mPlayer.setPlaybackSpeed(0.5f); |
| do { |
| assertTrue(onPlaybackSpeedChanged.waitForSignal(1000)); |
| } while (Math.abs(playbackSpeed.get() - 0.5f) > FLOAT_TOLERANCE); |
| |
| mPlayer.reset(); |
| |
| mPlayer.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.getPlayerState() == MediaPlayerBase.PLAYER_STATE_PLAYING); |
| |
| // Test pause and restart. |
| mPlayer.pause(); |
| Thread.sleep(SLEEP_TIME); |
| assertFalse(mPlayer.getPlayerState() == MediaPlayerBase.PLAYER_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.getPlayerState() == MediaPlayerBase.PLAYER_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.getPlayerState() == MediaPlayerBase.PLAYER_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.getPlayerState() == MediaPlayerBase.PLAYER_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()); |
| } |
| } |