1/*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package androidx.media;
17
18import static org.junit.Assert.assertEquals;
19import static org.junit.Assert.assertFalse;
20import static org.junit.Assert.assertNotNull;
21import static org.junit.Assert.assertTrue;
22import static org.junit.Assert.fail;
23
24import android.content.pm.PackageManager;
25import android.content.res.AssetFileDescriptor;
26import android.hardware.Camera;
27import android.media.AudioManager;
28import android.media.MediaMetadataRetriever;
29import android.media.MediaRecorder;
30import android.media.MediaTimestamp;
31import android.media.PlaybackParams;
32import android.media.SubtitleData;
33import android.media.SyncParams;
34import android.media.audiofx.AudioEffect;
35import android.media.audiofx.Visualizer;
36import android.net.Uri;
37import android.os.Build;
38import android.os.Environment;
39import android.support.test.filters.LargeTest;
40import android.support.test.filters.MediumTest;
41import android.support.test.filters.SdkSuppress;
42import android.support.test.filters.SmallTest;
43import android.support.test.runner.AndroidJUnit4;
44import android.util.Log;
45
46import androidx.media.MediaPlayerInterface.PlayerEventCallback;
47import androidx.media.test.R;
48
49import org.junit.After;
50import org.junit.Before;
51import org.junit.Test;
52import org.junit.runner.RunWith;
53
54import java.io.BufferedReader;
55import java.io.File;
56import java.io.IOException;
57import java.io.InputStream;
58import java.io.InputStreamReader;
59import java.util.ArrayDeque;
60import java.util.ArrayList;
61import java.util.List;
62import java.util.Vector;
63import java.util.concurrent.BlockingDeque;
64import java.util.concurrent.CountDownLatch;
65import java.util.concurrent.ExecutorService;
66import java.util.concurrent.Executors;
67import java.util.concurrent.LinkedBlockingDeque;
68import java.util.concurrent.atomic.AtomicInteger;
69import java.util.concurrent.atomic.AtomicReference;
70
71@RunWith(AndroidJUnit4.class)
72@SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
73public class MediaPlayer2Test extends MediaPlayer2TestBase {
74
75    private static final String LOG_TAG = "MediaPlayer2Test";
76
77    private static final int  RECORDED_VIDEO_WIDTH  = 176;
78    private static final int  RECORDED_VIDEO_HEIGHT = 144;
79    private static final long RECORDED_DURATION_MS  = 3000;
80    private static final float FLOAT_TOLERANCE = .0001f;
81
82    private String mRecordedFilePath;
83    private final Vector<Integer> mSubtitleTrackIndex = new Vector<>();
84    private final Monitor mOnSubtitleDataCalled = new Monitor();
85    private int mSelectedSubtitleIndex;
86
87    private File mOutFile;
88    private Camera mCamera;
89
90    @Before
91    @Override
92    public void setUp() throws Throwable {
93        super.setUp();
94        mRecordedFilePath = new File(Environment.getExternalStorageDirectory(),
95                "mediaplayer_record.out").getAbsolutePath();
96        mOutFile = new File(mRecordedFilePath);
97    }
98
99    @After
100    @Override
101    public void tearDown() throws Exception {
102        super.tearDown();
103        if (mOutFile != null && mOutFile.exists()) {
104            mOutFile.delete();
105        }
106    }
107
108    @Test
109    @MediumTest
110    public void testPlayNullSourcePath() throws Exception {
111        final Monitor onSetDataSourceCalled = new Monitor();
112        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
113            @Override
114            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
115                if (what == MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE) {
116                    assertTrue(status != MediaPlayer2.CALL_STATUS_NO_ERROR);
117                    onSetDataSourceCalled.signal();
118                }
119            }
120        };
121        synchronized (mEventCbLock) {
122            mEventCallbacks.add(ecb);
123        }
124
125        onSetDataSourceCalled.reset();
126        mPlayer.setDataSource((DataSourceDesc) null);
127        onSetDataSourceCalled.waitForSignal();
128    }
129
130    @Test
131    @LargeTest
132    public void testPlayAudioFromDataURI() throws Exception {
133        final int mp3Duration = 34909;
134        final int tolerance = 70;
135        final int seekDuration = 100;
136
137        // This is "R.raw.testmp3_2", base64-encoded.
138        final int resid = R.raw.testmp3_3;
139
140        InputStream is = mContext.getResources().openRawResource(resid);
141        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
142
143        StringBuilder builder = new StringBuilder();
144        builder.append("data:;base64,");
145        builder.append(reader.readLine());
146        Uri uri = Uri.parse(builder.toString());
147
148        MediaPlayer2 mp = createMediaPlayer2(mContext, uri);
149
150        final Monitor onPrepareCalled = new Monitor();
151        final Monitor onPlayCalled = new Monitor();
152        final Monitor onSeekToCalled = new Monitor();
153        final Monitor onLoopCurrentCalled = new Monitor();
154        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
155            @Override
156            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
157                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
158                    onPrepareCalled.signal();
159                }
160            }
161
162            @Override
163            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
164                if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
165                    onPlayCalled.signal();
166                } else if (what == MediaPlayer2.CALL_COMPLETED_LOOP_CURRENT) {
167                    onLoopCurrentCalled.signal();
168                } else if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
169                    onSeekToCalled.signal();
170                }
171            }
172        };
173        mp.setMediaPlayer2EventCallback(mExecutor, ecb);
174
175        try {
176            AudioAttributesCompat attributes = new AudioAttributesCompat.Builder()
177                    .setLegacyStreamType(AudioManager.STREAM_MUSIC)
178                    .build();
179            mp.setAudioAttributes(attributes);
180
181            assertFalse(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
182            onPlayCalled.reset();
183            mp.play();
184            onPlayCalled.waitForSignal();
185            assertTrue(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
186
187            /* FIXME: what's API for checking loop state?
188            assertFalse(mp.isLooping());
189            */
190            onLoopCurrentCalled.reset();
191            mp.loopCurrent(true);
192            onLoopCurrentCalled.waitForSignal();
193            /* FIXME: what's API for checking loop state?
194            assertTrue(mp.isLooping());
195            */
196
197            assertEquals(mp3Duration, mp.getDuration(), tolerance);
198            long pos = mp.getCurrentPosition();
199            assertTrue(pos >= 0);
200            assertTrue(pos < mp3Duration - seekDuration);
201
202            onSeekToCalled.reset();
203            mp.seekTo(pos + seekDuration, MediaPlayer2.SEEK_PREVIOUS_SYNC);
204            onSeekToCalled.waitForSignal();
205            assertEquals(pos + seekDuration, mp.getCurrentPosition(), tolerance);
206
207            // test pause and restart
208            mp.pause();
209            Thread.sleep(SLEEP_TIME);
210            assertFalse(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
211            onPlayCalled.reset();
212            mp.play();
213            onPlayCalled.waitForSignal();
214            assertTrue(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
215
216            // test stop and restart
217            mp.reset();
218            mp.setMediaPlayer2EventCallback(mExecutor, ecb);
219            mp.setDataSource(new DataSourceDesc.Builder()
220                    .setDataSource(mContext, uri)
221                    .build());
222            onPrepareCalled.reset();
223            mp.prepare();
224            onPrepareCalled.waitForSignal();
225
226            assertFalse(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
227            onPlayCalled.reset();
228            mp.play();
229            onPlayCalled.waitForSignal();
230            assertTrue(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
231
232            // waiting to complete
233            while (mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING) {
234                Thread.sleep(SLEEP_TIME);
235            }
236        } finally {
237            mp.close();
238        }
239    }
240
241    @Test
242    @LargeTest
243    public void testPlayAudio() throws Exception {
244        final int resid = R.raw.testmp3_2;
245        final int mp3Duration = 34909;
246        final int tolerance = 70;
247        final int seekDuration = 100;
248
249        MediaPlayer2 mp = createMediaPlayer2(mContext, resid);
250
251        final Monitor onPrepareCalled = new Monitor();
252        final Monitor onPlayCalled = new Monitor();
253        final Monitor onSeekToCalled = new Monitor();
254        final Monitor onLoopCurrentCalled = new Monitor();
255        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
256            @Override
257            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
258                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
259                    onPrepareCalled.signal();
260                }
261            }
262
263            @Override
264            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
265                if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
266                    onPlayCalled.signal();
267                } else if (what == MediaPlayer2.CALL_COMPLETED_LOOP_CURRENT) {
268                    onLoopCurrentCalled.signal();
269                } else if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
270                    onSeekToCalled.signal();
271                }
272            }
273        };
274        mp.setMediaPlayer2EventCallback(mExecutor, ecb);
275
276        try {
277            AudioAttributesCompat attributes = new AudioAttributesCompat.Builder()
278                    .setLegacyStreamType(AudioManager.STREAM_MUSIC)
279                    .build();
280            mp.setAudioAttributes(attributes);
281
282            assertFalse(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
283            onPlayCalled.reset();
284            mp.play();
285            onPlayCalled.waitForSignal();
286            assertTrue(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
287
288            //assertFalse(mp.isLooping());
289            onLoopCurrentCalled.reset();
290            mp.loopCurrent(true);
291            onLoopCurrentCalled.waitForSignal();
292            //assertTrue(mp.isLooping());
293
294            assertEquals(mp3Duration, mp.getDuration(), tolerance);
295            long pos = mp.getCurrentPosition();
296            assertTrue(pos >= 0);
297            assertTrue(pos < mp3Duration - seekDuration);
298
299            onSeekToCalled.reset();
300            mp.seekTo(pos + seekDuration, MediaPlayer2.SEEK_PREVIOUS_SYNC);
301            onSeekToCalled.waitForSignal();
302            assertEquals(pos + seekDuration, mp.getCurrentPosition(), tolerance);
303
304            // test pause and restart
305            mp.pause();
306            Thread.sleep(SLEEP_TIME);
307            assertFalse(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
308            onPlayCalled.reset();
309            mp.play();
310            onPlayCalled.waitForSignal();
311            assertTrue(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
312
313            // test stop and restart
314            mp.reset();
315            AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
316            mp.setDataSource(new DataSourceDesc.Builder()
317                    .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
318                    .build());
319
320            mp.setMediaPlayer2EventCallback(mExecutor, ecb);
321            onPrepareCalled.reset();
322            mp.prepare();
323            onPrepareCalled.waitForSignal();
324            afd.close();
325
326            assertFalse(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
327            onPlayCalled.reset();
328            mp.play();
329            onPlayCalled.waitForSignal();
330            assertTrue(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
331
332            // waiting to complete
333            while (mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING) {
334                Thread.sleep(SLEEP_TIME);
335            }
336        } catch (Exception e) {
337            throw e;
338        } finally {
339            mp.close();
340        }
341    }
342
343    /*
344    public void testConcurentPlayAudio() throws Exception {
345        final int resid = R.raw.test1m1s; // MP3 longer than 1m are usualy offloaded
346        final int tolerance = 70;
347
348        List<MediaPlayer2> mps = Stream.generate(() -> createMediaPlayer2(mContext, resid))
349                                      .limit(5).collect(Collectors.toList());
350
351        try {
352            for (MediaPlayer2 mp : mps) {
353                Monitor onPlayCalled = new Monitor();
354                Monitor onLoopCurrentCalled = new Monitor();
355                MediaPlayer2.MediaPlayer2EventCallback ecb =
356                    new MediaPlayer2.MediaPlayer2EventCallback() {
357                        @Override
358                        public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
359                                int what, int status) {
360                            if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
361                                onPlayCalled.signal();
362                            } else if (what == MediaPlayer2.CALL_COMPLETED_LOOP_CURRENT) {
363                                onLoopCurrentCalled.signal();
364                            }
365                        }
366                    };
367                mp.setMediaPlayer2EventCallback(mExecutor, ecb);
368
369                AudioAttributes attributes = new AudioAttributes.Builder()
370                        .setInternalLegacyStreamType(AudioManager.STREAM_MUSIC)
371                        .build();
372                mp.setAudioAttributes(attributes);
373
374                assertFalse(mp.isPlaying());
375                onPlayCalled.reset();
376                mp.play();
377                onPlayCalled.waitForSignal();
378                assertTrue(mp.isPlaying());
379
380                assertFalse(mp.isLooping());
381                onLoopCurrentCalled.reset();
382                mp.loopCurrent(true);
383                onLoopCurrentCalled.waitForSignal();
384                assertTrue(mp.isLooping());
385
386                long pos = mp.getCurrentPosition();
387                assertTrue(pos >= 0);
388
389                Thread.sleep(SLEEP_TIME); // Delay each track to be able to ear them
390            }
391            // Check that all mp3 are playing concurrently here
392            for (MediaPlayer2 mp : mps) {
393                long pos = mp.getCurrentPosition();
394                Thread.sleep(SLEEP_TIME);
395                assertEquals(pos + SLEEP_TIME, mp.getCurrentPosition(), tolerance);
396            }
397        } finally {
398            mps.forEach(MediaPlayer2::close);
399        }
400    }
401    */
402
403    @Test
404    @LargeTest
405    public void testPlayAudioLooping() throws Exception {
406        final int resid = R.raw.testmp3;
407
408        MediaPlayer2 mp = createMediaPlayer2(mContext, resid);
409        try {
410            AudioAttributesCompat attributes = new AudioAttributesCompat.Builder()
411                    .setLegacyStreamType(AudioManager.STREAM_MUSIC)
412                    .build();
413            mp.setAudioAttributes(attributes);
414            mp.loopCurrent(true);
415            final Monitor onCompletionCalled = new Monitor();
416            final Monitor onPlayCalled = new Monitor();
417            MediaPlayer2.MediaPlayer2EventCallback ecb =
418                    new MediaPlayer2.MediaPlayer2EventCallback() {
419                        @Override
420                        public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd,
421                                int what, int extra) {
422                            if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
423                                Log.i("@@@", "got oncompletion");
424                                onCompletionCalled.signal();
425                            }
426                        }
427
428                        @Override
429                        public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
430                                int what, int status) {
431                            if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
432                                onPlayCalled.signal();
433                            }
434                        }
435                    };
436            mp.setMediaPlayer2EventCallback(mExecutor, ecb);
437
438            assertFalse(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
439            onPlayCalled.reset();
440            mp.play();
441            onPlayCalled.waitForSignal();
442            assertTrue(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
443
444            long duration = mp.getDuration();
445            Thread.sleep(duration * 4); // allow for several loops
446            assertTrue(mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
447            assertEquals("wrong number of completion signals", 0,
448                    onCompletionCalled.getNumSignal());
449            mp.loopCurrent(false);
450
451            // wait for playback to finish
452            while (mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING) {
453                Thread.sleep(SLEEP_TIME);
454            }
455            assertEquals("wrong number of completion signals", 1,
456                    onCompletionCalled.getNumSignal());
457        } finally {
458            mp.close();
459        }
460    }
461
462    @Test
463    @LargeTest
464    public void testPlayMidi() throws Exception {
465        final int resid = R.raw.midi8sec;
466        final int midiDuration = 8000;
467        final int tolerance = 70;
468        final int seekDuration = 1000;
469
470        MediaPlayer2 mp = createMediaPlayer2(mContext, resid);
471
472        final Monitor onPrepareCalled = new Monitor();
473        final Monitor onSeekToCalled = new Monitor();
474        final Monitor onLoopCurrentCalled = new Monitor();
475        MediaPlayer2.MediaPlayer2EventCallback ecb =
476                new MediaPlayer2.MediaPlayer2EventCallback() {
477                    @Override
478                    public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
479                        if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
480                            onPrepareCalled.signal();
481                        }
482                    }
483
484                    @Override
485                    public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
486                            int what, int status) {
487                        if (what == MediaPlayer2.CALL_COMPLETED_LOOP_CURRENT) {
488                            onLoopCurrentCalled.signal();
489                        } else if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
490                            onSeekToCalled.signal();
491                        }
492                    }
493                };
494        mp.setMediaPlayer2EventCallback(mExecutor, ecb);
495
496        try {
497            AudioAttributesCompat attributes = new AudioAttributesCompat.Builder()
498                    .setLegacyStreamType(AudioManager.STREAM_MUSIC)
499                    .build();
500            mp.setAudioAttributes(attributes);
501
502            mp.play();
503
504            /* FIXME: what's API for checking loop state?
505            assertFalse(mp.isLooping());
506            */
507            onLoopCurrentCalled.reset();
508            mp.loopCurrent(true);
509            onLoopCurrentCalled.waitForSignal();
510            /* FIXME: what's API for checking loop state?
511            assertTrue(mp.isLooping());
512            */
513
514            assertEquals(midiDuration, mp.getDuration(), tolerance);
515            long pos = mp.getCurrentPosition();
516            assertTrue(pos >= 0);
517            assertTrue(pos < midiDuration - seekDuration);
518
519            onSeekToCalled.reset();
520            mp.seekTo(pos + seekDuration, MediaPlayer2.SEEK_PREVIOUS_SYNC);
521            onSeekToCalled.waitForSignal();
522            assertEquals(pos + seekDuration, mp.getCurrentPosition(), tolerance);
523
524            // test stop and restart
525            mp.reset();
526            AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
527            mp.setDataSource(new DataSourceDesc.Builder()
528                    .setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength())
529                    .build());
530
531            mp.setMediaPlayer2EventCallback(mExecutor, ecb);
532            onPrepareCalled.reset();
533            mp.prepare();
534            onPrepareCalled.waitForSignal();
535            afd.close();
536
537            mp.play();
538
539            Thread.sleep(SLEEP_TIME);
540        } finally {
541            mp.close();
542        }
543    }
544
545    static class OutputListener {
546        int mSession;
547        AudioEffect mVc;
548        Visualizer mVis;
549        byte [] mVisData;
550        boolean mSoundDetected;
551        OutputListener(int session) {
552            mSession = session;
553            /* FIXME: find out a public API for replacing AudioEffect contructor.
554            // creating a volume controller on output mix ensures that ro.audio.silent mutes
555            // audio after the effects and not before
556            mVc = new AudioEffect(
557                    AudioEffect.EFFECT_TYPE_NULL,
558                    UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
559                    0,
560                    session);
561            mVc.setEnabled(true);
562            */
563            mVis = new Visualizer(session);
564            int size = 256;
565            int[] range = Visualizer.getCaptureSizeRange();
566            if (size < range[0]) {
567                size = range[0];
568            }
569            if (size > range[1]) {
570                size = range[1];
571            }
572            assertTrue(mVis.setCaptureSize(size) == Visualizer.SUCCESS);
573
574            mVis.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
575                @Override
576                public void onWaveFormDataCapture(Visualizer visualizer,
577                        byte[] waveform, int samplingRate) {
578                    if (!mSoundDetected) {
579                        for (int i = 0; i < waveform.length; i++) {
580                            // 8 bit unsigned PCM, zero level is at 128, which is -128 when
581                            // seen as a signed byte
582                            if (waveform[i] != -128) {
583                                mSoundDetected = true;
584                                break;
585                            }
586                        }
587                    }
588                }
589
590                @Override
591                public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
592                }
593            }, 10000 /* milliHertz */, true /* PCM */, false /* FFT */);
594            assertTrue(mVis.setEnabled(true) == Visualizer.SUCCESS);
595        }
596
597        void reset() {
598            mSoundDetected = false;
599        }
600
601        boolean heardSound() {
602            return mSoundDetected;
603        }
604
605        void release() {
606            mVis.release();
607            /* FIXME: find out a public API for replacing AudioEffect contructor.
608            mVc.release();
609            */
610        }
611    }
612
613    public void testPlayAudioTwice() throws Exception {
614
615        final int resid = R.raw.camera_click;
616
617        MediaPlayer2 mp = createMediaPlayer2(mContext, resid);
618        try {
619            AudioAttributesCompat attributes = new AudioAttributesCompat.Builder()
620                    .setLegacyStreamType(AudioManager.STREAM_MUSIC)
621                    .build();
622            mp.setAudioAttributes(attributes);
623
624            OutputListener listener = new OutputListener(mp.getAudioSessionId());
625
626            Thread.sleep(SLEEP_TIME);
627            assertFalse("noise heard before test started", listener.heardSound());
628
629            mp.play();
630            Thread.sleep(SLEEP_TIME);
631            assertFalse("player was still playing after " + SLEEP_TIME + " ms",
632                    mp.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
633            assertTrue("nothing heard while test ran", listener.heardSound());
634            listener.reset();
635            mp.seekTo(0, MediaPlayer2.SEEK_PREVIOUS_SYNC);
636            mp.play();
637            Thread.sleep(SLEEP_TIME);
638            assertTrue("nothing heard when sound was replayed", listener.heardSound());
639            listener.release();
640        } finally {
641            mp.close();
642        }
643    }
644
645    @Test
646    @LargeTest
647    public void testPlayVideo() throws Exception {
648        playVideoTest(R.raw.testvideo, 352, 288);
649    }
650
651    /**
652     * Test for reseting a surface during video playback
653     * After reseting, the video should continue playing
654     * from the time setDisplay() was called
655     */
656    @Test
657    @LargeTest
658    public void testVideoSurfaceResetting() throws Exception {
659        final int tolerance = 150;
660        final int audioLatencyTolerance = 1000;  /* covers audio path latency variability */
661        final int seekPos = 4760;  // This is the I-frame position
662
663        final CountDownLatch seekDone = new CountDownLatch(1);
664
665        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
666            @Override
667            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
668                if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
669                    seekDone.countDown();
670                }
671            }
672        };
673        synchronized (mEventCbLock) {
674            mEventCallbacks.add(ecb);
675        }
676
677        if (!checkLoadResource(R.raw.testvideo)) {
678            return; // skip;
679        }
680        playLoadedVideo(352, 288, -1);
681
682        Thread.sleep(SLEEP_TIME);
683
684        long posBefore = mPlayer.getCurrentPosition();
685        mPlayer.setSurface(mActivity.getSurfaceHolder2().getSurface());
686        long posAfter = mPlayer.getCurrentPosition();
687
688        /* temporarily disable timestamp checking because MediaPlayer2 now seeks to I-frame
689         * position, instead of requested position. setDisplay invovles a seek operation
690         * internally.
691         */
692        // TODO: uncomment out line below when MediaPlayer2 can seek to requested position.
693        // assertEquals(posAfter, posBefore, tolerance);
694        assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
695
696        Thread.sleep(SLEEP_TIME);
697
698        mPlayer.seekTo(seekPos, MediaPlayer2.SEEK_PREVIOUS_SYNC);
699        seekDone.await();
700        posAfter = mPlayer.getCurrentPosition();
701        assertEquals(seekPos, posAfter, tolerance + audioLatencyTolerance);
702
703        Thread.sleep(SLEEP_TIME / 2);
704        posBefore = mPlayer.getCurrentPosition();
705        mPlayer.setSurface(null);
706        posAfter = mPlayer.getCurrentPosition();
707        // TODO: uncomment out line below when MediaPlayer2 can seek to requested position.
708        // assertEquals(posAfter, posBefore, tolerance);
709        assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
710
711        Thread.sleep(SLEEP_TIME);
712
713        posBefore = mPlayer.getCurrentPosition();
714        mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
715        posAfter = mPlayer.getCurrentPosition();
716
717        // TODO: uncomment out line below when MediaPlayer2 can seek to requested position.
718        // assertEquals(posAfter, posBefore, tolerance);
719        assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
720
721        Thread.sleep(SLEEP_TIME);
722    }
723
724    public void testRecordedVideoPlayback0() throws Exception {
725        testRecordedVideoPlaybackWithAngle(0);
726    }
727
728    public void testRecordedVideoPlayback90() throws Exception {
729        testRecordedVideoPlaybackWithAngle(90);
730    }
731
732    public void testRecordedVideoPlayback180() throws Exception {
733        testRecordedVideoPlaybackWithAngle(180);
734    }
735
736    public void testRecordedVideoPlayback270() throws Exception {
737        testRecordedVideoPlaybackWithAngle(270);
738    }
739
740    private boolean hasCamera() {
741        return mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
742    }
743
744    private void testRecordedVideoPlaybackWithAngle(int angle) throws Exception {
745        int width = RECORDED_VIDEO_WIDTH;
746        int height = RECORDED_VIDEO_HEIGHT;
747        final String file = mRecordedFilePath;
748        final long durationMs = RECORDED_DURATION_MS;
749
750        if (!hasCamera()) {
751            return;
752        }
753
754        boolean isSupported = false;
755        mCamera = Camera.open(0);
756        Camera.Parameters parameters = mCamera.getParameters();
757        List<Camera.Size> videoSizes = parameters.getSupportedVideoSizes();
758        // getSupportedVideoSizes returns null when separate video/preview size
759        // is not supported.
760        if (videoSizes == null) {
761            videoSizes = parameters.getSupportedPreviewSizes();
762        }
763        for (Camera.Size size : videoSizes) {
764            if (size.width == width && size.height == height) {
765                isSupported = true;
766                break;
767            }
768        }
769        mCamera.release();
770        mCamera = null;
771        if (!isSupported) {
772            width = videoSizes.get(0).width;
773            height = videoSizes.get(0).height;
774        }
775        checkOrientation(angle);
776        recordVideo(width, height, angle, file, durationMs);
777        checkDisplayedVideoSize(width, height, angle, file);
778        checkVideoRotationAngle(angle, file);
779    }
780
781    private void checkOrientation(int angle) throws Exception {
782        assertTrue(angle >= 0);
783        assertTrue(angle < 360);
784        assertTrue((angle % 90) == 0);
785    }
786
787    private void recordVideo(
788            int w, int h, int angle, String file, long durationMs) throws Exception {
789
790        MediaRecorder recorder = new MediaRecorder();
791        recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
792        recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
793        recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
794        recorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
795        recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
796        recorder.setOutputFile(file);
797        recorder.setOrientationHint(angle);
798        recorder.setVideoSize(w, h);
799        recorder.setPreviewDisplay(mActivity.getSurfaceHolder2().getSurface());
800        recorder.prepare();
801        recorder.start();
802        Thread.sleep(durationMs);
803        recorder.stop();
804        recorder.release();
805        recorder = null;
806    }
807
808    private void checkDisplayedVideoSize(
809            int w, int h, int angle, String file) throws Exception {
810
811        int displayWidth  = w;
812        int displayHeight = h;
813        if ((angle % 180) != 0) {
814            displayWidth  = h;
815            displayHeight = w;
816        }
817        playVideoTest(file, displayWidth, displayHeight);
818    }
819
820    private void checkVideoRotationAngle(int angle, String file) {
821        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
822        retriever.setDataSource(file);
823        String rotation = retriever.extractMetadata(
824                MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
825        retriever.release();
826        retriever = null;
827        assertNotNull(rotation);
828        assertEquals(Integer.parseInt(rotation), angle);
829    }
830
831    @Test
832    @LargeTest
833    public void testSkipToNext() throws Exception {
834        testPlaylist(true);
835    }
836
837    @Test
838    @LargeTest
839    public void testPlaylist() throws Exception {
840        testPlaylist(false);
841    }
842
843    private void testPlaylist(boolean skip) throws Exception {
844        if (!checkLoadResource(
845                R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz)) {
846            return; // skip
847        }
848        final DataSourceDesc dsd1 = createDataSourceDesc(
849                R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz);
850        final DataSourceDesc dsd2 = createDataSourceDesc(
851                R.raw.testvideo);
852        ArrayList<DataSourceDesc> nextDSDs = new ArrayList<DataSourceDesc>(2);
853        nextDSDs.add(dsd2);
854        nextDSDs.add(dsd1);
855
856        mPlayer.setNextDataSources(nextDSDs);
857
858        final Monitor onCompletion1Called = new Monitor();
859        final Monitor onCompletion2Called = new Monitor();
860        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
861            @Override
862            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
863                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
864                    Log.i(LOG_TAG, "testPlaylist: prepared dsd MediaId=" + dsd.getMediaId());
865                    mOnPrepareCalled.signal();
866                } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
867                    if (dsd == dsd1) {
868                        onCompletion1Called.signal();
869                    } else if (dsd == dsd2) {
870                        onCompletion2Called.signal();
871                    } else {
872                        mOnCompletionCalled.signal();
873                    }
874                }
875            }
876        };
877        synchronized (mEventCbLock) {
878            mEventCallbacks.add(ecb);
879        }
880
881        mOnCompletionCalled.reset();
882        onCompletion1Called.reset();
883        onCompletion2Called.reset();
884
885        mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
886
887        mPlayer.prepare();
888
889        mPlayer.play();
890
891        if (skip) {
892            mPlayer.skipToNext();
893            mPlayer.skipToNext();
894        } else {
895            mOnCompletionCalled.waitForSignal();
896            onCompletion2Called.waitForSignal();
897        }
898        onCompletion1Called.waitForSignal();
899        if (skip) {
900            assertFalse("first dsd completed", mOnCompletionCalled.isSignalled());
901            assertFalse("second dsd completed", onCompletion2Called.isSignalled());
902        }
903
904        mPlayer.reset();
905    }
906
907    // setPlaybackParams() with non-zero speed should NOT start playback.
908    // TODO: enable this test when MediaPlayer2.setPlaybackParams() is fixed
909    /*
910    public void testSetPlaybackParamsPositiveSpeed() throws Exception {
911        if (!checkLoadResource(
912                R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz)) {
913            return; // skip
914        }
915
916        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
917            @Override
918            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
919                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
920                    mOnPrepareCalled.signal();
921                } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
922                    mOnCompletionCalled.signal();
923                }
924            }
925
926            @Override
927            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
928                if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
929                    mOnSeekCompleteCalled.signal();
930                }
931            }
932        };
933        synchronized (mEventCbLock) {
934            mEventCallbacks.add(ecb);
935        }
936
937        mOnCompletionCalled.reset();
938        mPlayer.setDisplay(mActivity.getSurfaceHolder());
939
940        mOnPrepareCalled.reset();
941        mPlayer.prepare();
942        mOnPrepareCalled.waitForSignal();
943
944        mOnSeekCompleteCalled.reset();
945        mPlayer.seekTo(0, MediaPlayer2.SEEK_PREVIOUS_SYNC);
946        mOnSeekCompleteCalled.waitForSignal();
947
948        final float playbackRate = 1.0f;
949
950        int playTime = 2000;  // The testing clip is about 10 second long.
951        mPlayer.setPlaybackParams(new PlaybackParams().setSpeed(playbackRate));
952        assertTrue("MediaPlayer2 should be playing", mPlayer.isPlaying());
953        Thread.sleep(playTime);
954        assertTrue("MediaPlayer2 should still be playing",
955                mPlayer.getCurrentPosition() > 0);
956
957        long duration = mPlayer.getDuration();
958        mOnSeekCompleteCalled.reset();
959        mPlayer.seekTo(duration - 1000, MediaPlayer2.SEEK_PREVIOUS_SYNC);
960        mOnSeekCompleteCalled.waitForSignal();
961
962        mOnCompletionCalled.waitForSignal();
963        assertFalse("MediaPlayer2 should not be playing", mPlayer.isPlaying());
964        long eosPosition = mPlayer.getCurrentPosition();
965
966        mPlayer.setPlaybackParams(new PlaybackParams().setSpeed(playbackRate));
967        assertTrue("MediaPlayer2 should be playing after EOS", mPlayer.isPlaying());
968        Thread.sleep(playTime);
969        long position = mPlayer.getCurrentPosition();
970        assertTrue("MediaPlayer2 should still be playing after EOS",
971                position > 0 && position < eosPosition);
972
973        mPlayer.reset();
974    }
975    */
976
977    @Test
978    @LargeTest
979    public void testPlaybackRate() throws Exception {
980        final int toleranceMs = 1000;
981        if (!checkLoadResource(
982                R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz)) {
983            return; // skip
984        }
985
986        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
987            @Override
988            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
989                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
990                    mOnPrepareCalled.signal();
991                }
992            }
993        };
994        synchronized (mEventCbLock) {
995            mEventCallbacks.add(ecb);
996        }
997        mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
998
999        mOnPrepareCalled.reset();
1000        mPlayer.prepare();
1001        mOnPrepareCalled.waitForSignal();
1002
1003        SyncParams sync = new SyncParams().allowDefaults();
1004        mPlayer.setSyncParams(sync);
1005        sync = mPlayer.getSyncParams();
1006
1007        float[] rates = { 0.25f, 0.5f, 1.0f, 2.0f };
1008        for (float playbackRate : rates) {
1009            mPlayer.seekTo(0, MediaPlayer2.SEEK_PREVIOUS_SYNC);
1010            Thread.sleep(1000);
1011            int playTime = 4000;  // The testing clip is about 10 second long.
1012            mPlayer.setPlaybackParams(new PlaybackParams().setSpeed(playbackRate));
1013            mPlayer.play();
1014            Thread.sleep(playTime);
1015            PlaybackParams pbp = mPlayer.getPlaybackParams();
1016            assertEquals(
1017                    playbackRate, pbp.getSpeed(),
1018                    FLOAT_TOLERANCE + playbackRate * sync.getTolerance());
1019            assertTrue("MediaPlayer2 should still be playing",
1020                    mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
1021
1022            long playedMediaDurationMs = mPlayer.getCurrentPosition();
1023            int diff = Math.abs((int) (playedMediaDurationMs / playbackRate) - playTime);
1024            if (diff > toleranceMs) {
1025                fail("Media player had error in playback rate " + playbackRate
1026                        + ", play time is " + playTime + " vs expected " + playedMediaDurationMs);
1027            }
1028            mPlayer.pause();
1029            pbp = mPlayer.getPlaybackParams();
1030            // TODO: pause() should NOT change PlaybackParams.
1031            // assertEquals(0.f, pbp.getSpeed(), FLOAT_TOLERANCE);
1032        }
1033        mPlayer.reset();
1034    }
1035
1036    @Test
1037    @LargeTest
1038    public void testSeekModes() throws Exception {
1039        // This clip has 2 I frames at 66687us and 4299687us.
1040        if (!checkLoadResource(
1041                R.raw.bbb_s1_320x240_mp4_h264_mp2_800kbps_30fps_aac_lc_5ch_240kbps_44100hz)) {
1042            return; // skip
1043        }
1044
1045        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
1046            @Override
1047            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
1048                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
1049                    mOnPrepareCalled.signal();
1050                }
1051            }
1052
1053            @Override
1054            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
1055                if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
1056                    mOnSeekCompleteCalled.signal();
1057                }
1058            }
1059        };
1060        synchronized (mEventCbLock) {
1061            mEventCallbacks.add(ecb);
1062        }
1063
1064        mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
1065
1066        mOnPrepareCalled.reset();
1067        mPlayer.prepare();
1068        mOnPrepareCalled.waitForSignal();
1069
1070        mOnSeekCompleteCalled.reset();
1071        mPlayer.play();
1072
1073        final long seekPosMs = 3000;
1074        final long timeToleranceMs = 100;
1075        final long syncTime1Ms = 67;
1076        final long syncTime2Ms = 4300;
1077
1078        // TODO: tighten checking range. For now, ensure mediaplayer doesn't
1079        // seek to previous sync or next sync.
1080        long cp = runSeekMode(MediaPlayer2.SEEK_CLOSEST, seekPosMs);
1081        assertTrue("MediaPlayer2 did not seek to closest position",
1082                cp > seekPosMs && cp < syncTime2Ms);
1083
1084        // TODO: tighten checking range. For now, ensure mediaplayer doesn't
1085        // seek to closest position or next sync.
1086        cp = runSeekMode(MediaPlayer2.SEEK_PREVIOUS_SYNC, seekPosMs);
1087        assertTrue("MediaPlayer2 did not seek to preivous sync position",
1088                cp < seekPosMs - timeToleranceMs);
1089
1090        // TODO: tighten checking range. For now, ensure mediaplayer doesn't
1091        // seek to closest position or previous sync.
1092        cp = runSeekMode(MediaPlayer2.SEEK_NEXT_SYNC, seekPosMs);
1093        assertTrue("MediaPlayer2 did not seek to next sync position",
1094                cp > syncTime2Ms - timeToleranceMs);
1095
1096        // TODO: tighten checking range. For now, ensure mediaplayer doesn't
1097        // seek to closest position or previous sync.
1098        cp = runSeekMode(MediaPlayer2.SEEK_CLOSEST_SYNC, seekPosMs);
1099        assertTrue("MediaPlayer2 did not seek to closest sync position",
1100                cp > syncTime2Ms - timeToleranceMs);
1101
1102        mPlayer.reset();
1103    }
1104
1105    private long runSeekMode(int seekMode, long seekPosMs) throws Exception {
1106        final int sleepIntervalMs = 100;
1107        int timeRemainedMs = 10000;  // total time for testing
1108        final int timeToleranceMs = 100;
1109
1110        mPlayer.seekTo(seekPosMs, seekMode);
1111        mOnSeekCompleteCalled.waitForSignal();
1112        mOnSeekCompleteCalled.reset();
1113        long cp = -seekPosMs;
1114        while (timeRemainedMs > 0) {
1115            cp = mPlayer.getCurrentPosition();
1116            // Wait till MediaPlayer2 starts rendering since MediaPlayer2 caches
1117            // seek position as current position.
1118            if (cp < seekPosMs - timeToleranceMs || cp > seekPosMs + timeToleranceMs) {
1119                break;
1120            }
1121            timeRemainedMs -= sleepIntervalMs;
1122            Thread.sleep(sleepIntervalMs);
1123        }
1124        assertTrue("MediaPlayer2 did not finish seeking in time for mode " + seekMode,
1125                timeRemainedMs > 0);
1126        return cp;
1127    }
1128
1129    @Test
1130    @LargeTest
1131    public void testGetTimestamp() throws Exception {
1132        final int toleranceUs = 100000;
1133        final float playbackRate = 1.0f;
1134        if (!checkLoadResource(
1135                R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz)) {
1136            return; // skip
1137        }
1138
1139        final Monitor onPauseCalled = new Monitor();
1140        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
1141            @Override
1142            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
1143                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
1144                    mOnPrepareCalled.signal();
1145                }
1146            }
1147
1148            @Override
1149            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
1150                if (what == MediaPlayer2.CALL_COMPLETED_PAUSE) {
1151                    onPauseCalled.signal();
1152                }
1153            }
1154        };
1155        synchronized (mEventCbLock) {
1156            mEventCallbacks.add(ecb);
1157        }
1158
1159        mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
1160
1161        mOnPrepareCalled.reset();
1162        mPlayer.prepare();
1163        mOnPrepareCalled.waitForSignal();
1164
1165        mPlayer.play();
1166        mPlayer.setPlaybackParams(new PlaybackParams().setSpeed(playbackRate));
1167        Thread.sleep(SLEEP_TIME);  // let player get into stable state.
1168        long nt1 = System.nanoTime();
1169        MediaTimestamp ts1 = mPlayer.getTimestamp();
1170        long nt2 = System.nanoTime();
1171        assertTrue("Media player should return a valid time stamp", ts1 != null);
1172        assertEquals("MediaPlayer2 had error in clockRate " + ts1.getMediaClockRate(),
1173                playbackRate, ts1.getMediaClockRate(), 0.001f);
1174        assertTrue("The nanoTime of Media timestamp should be taken when getTimestamp is called.",
1175                nt1 <= ts1.getAnchorSytemNanoTime() && ts1.getAnchorSytemNanoTime() <= nt2);
1176
1177        onPauseCalled.reset();
1178        mPlayer.pause();
1179        onPauseCalled.waitForSignal();
1180        ts1 = mPlayer.getTimestamp();
1181        assertTrue("Media player should return a valid time stamp", ts1 != null);
1182        assertTrue("Media player should have play rate of 0.0f when paused",
1183                ts1.getMediaClockRate() == 0.0f);
1184
1185        mPlayer.seekTo(0, MediaPlayer2.SEEK_PREVIOUS_SYNC);
1186        mPlayer.play();
1187        Thread.sleep(SLEEP_TIME);  // let player get into stable state.
1188        int playTime = 4000;  // The testing clip is about 10 second long.
1189        ts1 = mPlayer.getTimestamp();
1190        assertTrue("Media player should return a valid time stamp", ts1 != null);
1191        Thread.sleep(playTime);
1192        MediaTimestamp ts2 = mPlayer.getTimestamp();
1193        assertTrue("Media player should return a valid time stamp", ts2 != null);
1194        assertTrue("The clockRate should not be changed.",
1195                ts1.getMediaClockRate() == ts2.getMediaClockRate());
1196        assertEquals("MediaPlayer2 had error in timestamp.",
1197                ts1.getAnchorMediaTimeUs() + (long) (playTime * ts1.getMediaClockRate() * 1000),
1198                ts2.getAnchorMediaTimeUs(), toleranceUs);
1199
1200        mPlayer.reset();
1201    }
1202
1203    public void testLocalVideo_MKV_H265_1280x720_500kbps_25fps_AAC_Stereo_128kbps_44100Hz()
1204            throws Exception {
1205        playVideoTest(
1206                R.raw.video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz, 1280, 720);
1207    }
1208
1209    public void testLocalVideo_MP4_H264_480x360_500kbps_25fps_AAC_Stereo_128kbps_44110Hz()
1210            throws Exception {
1211        playVideoTest(
1212                R.raw.video_480x360_mp4_h264_500kbps_25fps_aac_stereo_128kbps_44100hz, 480, 360);
1213    }
1214
1215    public void testLocalVideo_MP4_H264_480x360_500kbps_30fps_AAC_Stereo_128kbps_44110Hz()
1216            throws Exception {
1217        playVideoTest(
1218                R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz, 480, 360);
1219    }
1220
1221    public void testLocalVideo_MP4_H264_480x360_1000kbps_25fps_AAC_Stereo_128kbps_44110Hz()
1222            throws Exception {
1223        playVideoTest(
1224                R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 480, 360);
1225    }
1226
1227    public void testLocalVideo_MP4_H264_480x360_1000kbps_30fps_AAC_Stereo_128kbps_44110Hz()
1228            throws Exception {
1229        playVideoTest(
1230                R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz, 480, 360);
1231    }
1232
1233    public void testLocalVideo_MP4_H264_480x360_1350kbps_25fps_AAC_Stereo_128kbps_44110Hz()
1234            throws Exception {
1235        playVideoTest(
1236                R.raw.video_480x360_mp4_h264_1350kbps_25fps_aac_stereo_128kbps_44100hz, 480, 360);
1237    }
1238
1239    public void testLocalVideo_MP4_H264_480x360_1350kbps_30fps_AAC_Stereo_128kbps_44110Hz()
1240            throws Exception {
1241        playVideoTest(
1242                R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz, 480, 360);
1243    }
1244
1245    public void testLocalVideo_MP4_H264_480x360_1350kbps_30fps_AAC_Stereo_128kbps_44110Hz_frag()
1246            throws Exception {
1247        playVideoTest(
1248                R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented,
1249                480, 360);
1250    }
1251
1252    public void testLocalVideo_MP4_H264_480x360_1350kbps_30fps_AAC_Stereo_192kbps_44110Hz()
1253            throws Exception {
1254        playVideoTest(
1255                R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz, 480, 360);
1256    }
1257
1258    public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Mono_24kbps_11025Hz()
1259            throws Exception {
1260        playVideoTest(
1261                R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_mono_24kbps_11025hz, 176, 144);
1262    }
1263
1264    public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Mono_24kbps_22050Hz()
1265            throws Exception {
1266        playVideoTest(
1267                R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_mono_24kbps_22050hz, 176, 144);
1268    }
1269
1270    public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Stereo_24kbps_11025Hz()
1271            throws Exception {
1272        playVideoTest(
1273                R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_stereo_24kbps_11025hz, 176, 144);
1274    }
1275
1276    public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Stereo_24kbps_22050Hz()
1277            throws Exception {
1278        playVideoTest(
1279                R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_stereo_24kbps_11025hz, 176, 144);
1280    }
1281
1282    public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Stereo_128kbps_11025Hz()
1283            throws Exception {
1284        playVideoTest(
1285                R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_stereo_128kbps_11025hz, 176, 144);
1286    }
1287
1288    public void testLocalVideo_3gp_H263_176x144_56kbps_12fps_AAC_Stereo_128kbps_22050Hz()
1289            throws Exception {
1290        playVideoTest(
1291                R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_stereo_128kbps_11025hz, 176, 144);
1292    }
1293
1294    public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Mono_24kbps_11025Hz()
1295            throws Exception {
1296        playVideoTest(
1297                R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_mono_24kbps_11025hz, 176, 144);
1298    }
1299
1300    public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Mono_24kbps_22050Hz()
1301            throws Exception {
1302        playVideoTest(
1303                R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_mono_24kbps_22050hz, 176, 144);
1304    }
1305
1306    public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Stereo_24kbps_11025Hz()
1307            throws Exception {
1308        playVideoTest(
1309                R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_stereo_24kbps_11025hz, 176, 144);
1310    }
1311
1312    public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Stereo_24kbps_22050Hz()
1313            throws Exception {
1314        playVideoTest(
1315                R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_stereo_24kbps_11025hz, 176, 144);
1316    }
1317
1318    public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Stereo_128kbps_11025Hz()
1319            throws Exception {
1320        playVideoTest(
1321                R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_stereo_128kbps_11025hz, 176, 144);
1322    }
1323
1324    public void testLocalVideo_3gp_H263_176x144_56kbps_25fps_AAC_Stereo_128kbps_22050Hz()
1325            throws Exception {
1326        playVideoTest(
1327                R.raw.video_176x144_3gp_h263_56kbps_25fps_aac_stereo_128kbps_11025hz, 176, 144);
1328    }
1329
1330    public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Mono_24kbps_11025Hz()
1331            throws Exception {
1332        playVideoTest(
1333                R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz, 176, 144);
1334    }
1335
1336    public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Mono_24kbps_22050Hz()
1337            throws Exception {
1338        playVideoTest(
1339                R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_22050hz, 176, 144);
1340    }
1341
1342    public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Stereo_24kbps_11025Hz()
1343            throws Exception {
1344        playVideoTest(
1345                R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_24kbps_11025hz, 176, 144);
1346    }
1347
1348    public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Stereo_24kbps_22050Hz()
1349            throws Exception {
1350        playVideoTest(
1351                R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_24kbps_11025hz, 176, 144);
1352    }
1353
1354    public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Stereo_128kbps_11025Hz()
1355            throws Exception {
1356        playVideoTest(
1357                R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_11025hz, 176, 144);
1358    }
1359
1360    public void testLocalVideo_3gp_H263_176x144_300kbps_12fps_AAC_Stereo_128kbps_22050Hz()
1361            throws Exception {
1362        playVideoTest(
1363                R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_11025hz, 176, 144);
1364    }
1365
1366    public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Mono_24kbps_11025Hz()
1367            throws Exception {
1368        playVideoTest(
1369                R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_mono_24kbps_11025hz, 176, 144);
1370    }
1371
1372    public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Mono_24kbps_22050Hz()
1373            throws Exception {
1374        playVideoTest(
1375                R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_mono_24kbps_22050hz, 176, 144);
1376    }
1377
1378    public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Stereo_24kbps_11025Hz()
1379            throws Exception {
1380        playVideoTest(
1381                R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_stereo_24kbps_11025hz, 176, 144);
1382    }
1383
1384    public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Stereo_24kbps_22050Hz()
1385            throws Exception {
1386        playVideoTest(
1387                R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_stereo_24kbps_11025hz, 176, 144);
1388    }
1389
1390    public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Stereo_128kbps_11025Hz()
1391            throws Exception {
1392        playVideoTest(
1393                R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_11025hz, 176, 144);
1394    }
1395
1396    public void testLocalVideo_3gp_H263_176x144_300kbps_25fps_AAC_Stereo_128kbps_22050Hz()
1397            throws Exception {
1398        playVideoTest(
1399                R.raw.video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_22050hz, 176, 144);
1400    }
1401
1402    private void readSubtitleTracks() throws Exception {
1403        mSubtitleTrackIndex.clear();
1404        List<MediaPlayer2.TrackInfo> trackInfos = mPlayer.getTrackInfo();
1405        if (trackInfos == null || trackInfos.size() == 0) {
1406            return;
1407        }
1408
1409        Vector<Integer> subtitleTrackIndex = new Vector<>();
1410        for (int i = 0; i < trackInfos.size(); ++i) {
1411            assertTrue(trackInfos.get(i) != null);
1412            if (trackInfos.get(i).getTrackType()
1413                    == MediaPlayer2.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
1414                subtitleTrackIndex.add(i);
1415            }
1416        }
1417
1418        mSubtitleTrackIndex.addAll(subtitleTrackIndex);
1419    }
1420
1421    private void selectSubtitleTrack(int index) throws Exception {
1422        int trackIndex = mSubtitleTrackIndex.get(index);
1423        mPlayer.selectTrack(trackIndex);
1424        mSelectedSubtitleIndex = index;
1425    }
1426
1427    private void deselectSubtitleTrack(int index) throws Exception {
1428        int trackIndex = mSubtitleTrackIndex.get(index);
1429        mOnDeselectTrackCalled.reset();
1430        mPlayer.deselectTrack(trackIndex);
1431        mOnDeselectTrackCalled.waitForSignal();
1432        if (mSelectedSubtitleIndex == index) {
1433            mSelectedSubtitleIndex = -1;
1434        }
1435    }
1436
1437    @Test
1438    @LargeTest
1439    public void testDeselectTrackForSubtitleTracks() throws Throwable {
1440        if (!checkLoadResource(R.raw.testvideo_with_2_subtitle_tracks)) {
1441            return; // skip;
1442        }
1443
1444        mInstrumentation.waitForIdleSync();
1445
1446        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
1447            @Override
1448            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
1449                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
1450                    mOnPrepareCalled.signal();
1451                } else if (what == MediaPlayer2.MEDIA_INFO_METADATA_UPDATE) {
1452                    mOnInfoCalled.signal();
1453                }
1454            }
1455
1456            @Override
1457            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
1458                if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
1459                    mOnSeekCompleteCalled.signal();
1460                } else if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
1461                    mOnPlayCalled.signal();
1462                } else if (what == MediaPlayer2.CALL_COMPLETED_DESELECT_TRACK) {
1463                    mCallStatus = status;
1464                    mOnDeselectTrackCalled.signal();
1465                }
1466            }
1467
1468            @Override
1469            public void onSubtitleData(
1470                    MediaPlayer2 mp, DataSourceDesc dsd, SubtitleData data) {
1471                if (data != null && data.getData() != null) {
1472                    mOnSubtitleDataCalled.signal();
1473                }
1474            }
1475        };
1476        synchronized (mEventCbLock) {
1477            mEventCallbacks.add(ecb);
1478        }
1479
1480        mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
1481
1482        mOnPrepareCalled.reset();
1483        mPlayer.prepare();
1484        mOnPrepareCalled.waitForSignal();
1485
1486        mOnPlayCalled.reset();
1487        mPlayer.play();
1488        mOnPlayCalled.waitForSignal();
1489        assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
1490
1491        // Closed caption tracks are in-band.
1492        // So, those tracks will be found after processing a number of frames.
1493        mOnInfoCalled.waitForSignal(1500);
1494
1495        mOnInfoCalled.reset();
1496        mOnInfoCalled.waitForSignal(1500);
1497
1498        readSubtitleTracks();
1499
1500        // Run twice to check if repeated selection-deselection on the same track works well.
1501        for (int i = 0; i < 2; i++) {
1502            // Waits until at least one subtitle is fired. Timeout is 2.5 seconds.
1503            selectSubtitleTrack(i);
1504            mOnSubtitleDataCalled.reset();
1505            assertTrue(mOnSubtitleDataCalled.waitForSignal(2500));
1506
1507            // Try deselecting track.
1508            deselectSubtitleTrack(i);
1509            mOnSubtitleDataCalled.reset();
1510            assertFalse(mOnSubtitleDataCalled.waitForSignal(1500));
1511        }
1512
1513        // Deselecting unselected track: expected error status
1514        mCallStatus = MediaPlayer2.CALL_STATUS_NO_ERROR;
1515        deselectSubtitleTrack(0);
1516        assertTrue(mCallStatus != MediaPlayer2.CALL_STATUS_NO_ERROR);
1517
1518        mPlayer.reset();
1519    }
1520
1521    @Test
1522    @LargeTest
1523    public void testChangeSubtitleTrack() throws Throwable {
1524        if (!checkLoadResource(R.raw.testvideo_with_2_subtitle_tracks)) {
1525            return; // skip;
1526        }
1527
1528        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
1529            @Override
1530            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
1531                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
1532                    mOnPrepareCalled.signal();
1533                } else if (what == MediaPlayer2.MEDIA_INFO_METADATA_UPDATE) {
1534                    mOnInfoCalled.signal();
1535                }
1536            }
1537
1538            @Override
1539            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
1540                if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
1541                    mOnPlayCalled.signal();
1542                }
1543            }
1544
1545            @Override
1546            public void onSubtitleData(
1547                    MediaPlayer2 mp, DataSourceDesc dsd, SubtitleData data) {
1548                if (data != null && data.getData() != null) {
1549                    mOnSubtitleDataCalled.signal();
1550                }
1551            }
1552        };
1553        synchronized (mEventCbLock) {
1554            mEventCallbacks.add(ecb);
1555        }
1556
1557        mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
1558
1559        mOnPrepareCalled.reset();
1560        mPlayer.prepare();
1561        mOnPrepareCalled.waitForSignal();
1562
1563        mOnPlayCalled.reset();
1564        mPlayer.play();
1565        mOnPlayCalled.waitForSignal();
1566        assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
1567
1568        // Closed caption tracks are in-band.
1569        // So, those tracks will be found after processing a number of frames.
1570        mOnInfoCalled.waitForSignal(1500);
1571
1572        mOnInfoCalled.reset();
1573        mOnInfoCalled.waitForSignal(1500);
1574
1575        readSubtitleTracks();
1576
1577        // Waits until at least two captions are fired. Timeout is 2.5 sec.
1578        selectSubtitleTrack(0);
1579        assertTrue(mOnSubtitleDataCalled.waitForCountedSignals(2, 2500) >= 2);
1580
1581        mOnSubtitleDataCalled.reset();
1582        selectSubtitleTrack(1);
1583        assertTrue(mOnSubtitleDataCalled.waitForCountedSignals(2, 2500) >= 2);
1584
1585        mPlayer.reset();
1586    }
1587
1588    @Test
1589    @LargeTest
1590    public void testGetTrackInfoForVideoWithSubtitleTracks() throws Throwable {
1591        if (!checkLoadResource(R.raw.testvideo_with_2_subtitle_tracks)) {
1592            return; // skip;
1593        }
1594
1595        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
1596            @Override
1597            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
1598                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
1599                    mOnPrepareCalled.signal();
1600                } else if (what == MediaPlayer2.MEDIA_INFO_METADATA_UPDATE) {
1601                    mOnInfoCalled.signal();
1602                }
1603            }
1604
1605            @Override
1606            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
1607                if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
1608                    mOnPlayCalled.signal();
1609                }
1610            }
1611        };
1612        synchronized (mEventCbLock) {
1613            mEventCallbacks.add(ecb);
1614        }
1615
1616        mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
1617
1618        mOnPrepareCalled.reset();
1619        mPlayer.prepare();
1620        mOnPrepareCalled.waitForSignal();
1621
1622        mOnPlayCalled.reset();
1623        mPlayer.play();
1624        mOnPlayCalled.waitForSignal();
1625        assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
1626
1627        // The media metadata will be changed while playing since closed caption tracks are in-band
1628        // and those tracks will be found after processing a number of frames. These tracks will be
1629        // found within one second.
1630        mOnInfoCalled.waitForSignal(1500);
1631
1632        mOnInfoCalled.reset();
1633        mOnInfoCalled.waitForSignal(1500);
1634
1635        readSubtitleTracks();
1636        assertEquals(2, mSubtitleTrackIndex.size());
1637
1638        mPlayer.reset();
1639    }
1640
1641    @Test
1642    @LargeTest
1643    public void testMediaTimeDiscontinuity() throws Exception {
1644        if (!checkLoadResource(
1645                R.raw.bbb_s1_320x240_mp4_h264_mp2_800kbps_30fps_aac_lc_5ch_240kbps_44100hz)) {
1646            return; // skip
1647        }
1648
1649        final BlockingDeque<MediaTimestamp> timestamps = new LinkedBlockingDeque<>();
1650        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
1651            @Override
1652            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
1653                if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
1654                    mOnSeekCompleteCalled.signal();
1655                }
1656            }
1657            @Override
1658            public void onMediaTimeDiscontinuity(
1659                    MediaPlayer2 mp, DataSourceDesc dsd, MediaTimestamp timestamp) {
1660                timestamps.add(timestamp);
1661                mOnMediaTimeDiscontinuityCalled.signal();
1662            }
1663        };
1664        synchronized (mEventCbLock) {
1665            mEventCallbacks.add(ecb);
1666        }
1667
1668        mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
1669        mPlayer.prepare();
1670
1671        // Timestamp needs to be reported when playback starts.
1672        mOnMediaTimeDiscontinuityCalled.reset();
1673        mPlayer.play();
1674        do {
1675            assertTrue(mOnMediaTimeDiscontinuityCalled.waitForSignal(1000));
1676        } while (Math.abs(timestamps.getLast().getMediaClockRate() - 1.0f) > 0.01f);
1677
1678        // Timestamp needs to be reported when seeking is done.
1679        mOnSeekCompleteCalled.reset();
1680        mOnMediaTimeDiscontinuityCalled.reset();
1681        mPlayer.seekTo(3000);
1682        mOnSeekCompleteCalled.waitForSignal();
1683        do {
1684            assertTrue(mOnMediaTimeDiscontinuityCalled.waitForSignal(1000));
1685        } while (Math.abs(timestamps.getLast().getMediaClockRate() - 1.0f) > 0.01f);
1686
1687        // Timestamp needs to be updated when playback rate changes.
1688        mOnMediaTimeDiscontinuityCalled.reset();
1689        mPlayer.setPlaybackParams(new PlaybackParams().setSpeed(0.5f));
1690        mOnMediaTimeDiscontinuityCalled.waitForSignal();
1691        do {
1692            assertTrue(mOnMediaTimeDiscontinuityCalled.waitForSignal(1000));
1693        } while (Math.abs(timestamps.getLast().getMediaClockRate() - 0.5f) > 0.01f);
1694
1695        // Timestamp needs to be updated when player is paused.
1696        mOnMediaTimeDiscontinuityCalled.reset();
1697        mPlayer.pause();
1698        mOnMediaTimeDiscontinuityCalled.waitForSignal();
1699        do {
1700            assertTrue(mOnMediaTimeDiscontinuityCalled.waitForSignal(1000));
1701        } while (Math.abs(timestamps.getLast().getMediaClockRate() - 0.0f) > 0.01f);
1702
1703        mPlayer.reset();
1704    }
1705
1706    /*
1707     *  This test assumes the resources being tested are between 8 and 14 seconds long
1708     *  The ones being used here are 10 seconds long.
1709     */
1710    @Test
1711    @LargeTest
1712    public void testResumeAtEnd() throws Throwable {
1713        int testsRun = testResumeAtEnd(R.raw.loudsoftmp3)
1714                + testResumeAtEnd(R.raw.loudsoftwav)
1715                + testResumeAtEnd(R.raw.loudsoftogg)
1716                + testResumeAtEnd(R.raw.loudsoftitunes)
1717                + testResumeAtEnd(R.raw.loudsoftfaac)
1718                + testResumeAtEnd(R.raw.loudsoftaac);
1719    }
1720
1721    // returns 1 if test was run, 0 otherwise
1722    private int testResumeAtEnd(int res) throws Throwable {
1723        if (!loadResource(res)) {
1724            Log.i(LOG_TAG, "testResumeAtEnd: No decoder found for "
1725                    + mContext.getResources().getResourceEntryName(res) + " --- skipping.");
1726            return 0; // skip
1727        }
1728        mOnCompletionCalled.reset();
1729        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
1730            @Override
1731            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
1732                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
1733                    mOnPrepareCalled.signal();
1734                } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
1735                    mOnCompletionCalled.signal();
1736                    mPlayer.play();
1737                }
1738            }
1739        };
1740        mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
1741
1742        mOnPrepareCalled.reset();
1743        mPlayer.prepare();
1744        mOnPrepareCalled.waitForSignal();
1745
1746        // skip the first part of the file so we reach EOF sooner
1747        mPlayer.seekTo(5000, MediaPlayer2.SEEK_PREVIOUS_SYNC);
1748        mPlayer.play();
1749        // sleep long enough that we restart playback at least once, but no more
1750        Thread.sleep(10000);
1751        assertTrue("MediaPlayer2 should still be playing",
1752                mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
1753        mPlayer.reset();
1754        assertEquals("wrong number of repetitions", 1, mOnCompletionCalled.getNumSignal());
1755        return 1;
1756    }
1757
1758    @Test
1759    @LargeTest
1760    public void testPositionAtEnd() throws Throwable {
1761        int testsRun = testPositionAtEnd(R.raw.test1m1shighstereo)
1762                + testPositionAtEnd(R.raw.loudsoftmp3)
1763                + testPositionAtEnd(R.raw.loudsoftwav)
1764                + testPositionAtEnd(R.raw.loudsoftogg)
1765                + testPositionAtEnd(R.raw.loudsoftitunes)
1766                + testPositionAtEnd(R.raw.loudsoftfaac)
1767                + testPositionAtEnd(R.raw.loudsoftaac);
1768    }
1769
1770    private int testPositionAtEnd(int res) throws Throwable {
1771        if (!loadResource(res)) {
1772            Log.i(LOG_TAG, "testPositionAtEnd: No decoder found for "
1773                    + mContext.getResources().getResourceEntryName(res) + " --- skipping.");
1774            return 0; // skip
1775        }
1776        AudioAttributesCompat attributes = new AudioAttributesCompat.Builder()
1777                .setLegacyStreamType(AudioManager.STREAM_MUSIC)
1778                .build();
1779        mPlayer.setAudioAttributes(attributes);
1780
1781        mOnCompletionCalled.reset();
1782        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
1783            @Override
1784            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
1785                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
1786                    mOnPrepareCalled.signal();
1787                } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
1788                    mOnCompletionCalled.signal();
1789                }
1790            }
1791
1792            @Override
1793            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
1794                if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
1795                    mOnPlayCalled.signal();
1796                }
1797            }
1798        };
1799        mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
1800
1801        mOnPrepareCalled.reset();
1802        mPlayer.prepare();
1803        mOnPrepareCalled.waitForSignal();
1804
1805        long duration = mPlayer.getDuration();
1806        assertTrue("resource too short", duration > 6000);
1807        mPlayer.seekTo(duration - 5000, MediaPlayer2.SEEK_PREVIOUS_SYNC);
1808        mOnPlayCalled.reset();
1809        mPlayer.play();
1810        mOnPlayCalled.waitForSignal();
1811        while (mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING) {
1812            Log.i("@@@@", "position: " + mPlayer.getCurrentPosition());
1813            Thread.sleep(500);
1814        }
1815        Log.i("@@@@", "final position: " + mPlayer.getCurrentPosition());
1816        assertTrue(mPlayer.getCurrentPosition() > duration - 1000);
1817        mPlayer.reset();
1818        return 1;
1819    }
1820
1821    @Test
1822    @LargeTest
1823    public void testMediaPlayer2Callback() throws Throwable {
1824        final int mp4Duration = 8484;
1825
1826        if (!checkLoadResource(R.raw.testvideo)) {
1827            return; // skip;
1828        }
1829
1830        mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
1831
1832        mOnCompletionCalled.reset();
1833        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
1834            @Override
1835            public void onVideoSizeChanged(MediaPlayer2 mp, DataSourceDesc dsd,
1836                    int width, int height) {
1837                mOnVideoSizeChangedCalled.signal();
1838            }
1839
1840            @Override
1841            public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
1842                mOnErrorCalled.signal();
1843            }
1844
1845            @Override
1846            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
1847                mOnInfoCalled.signal();
1848
1849                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
1850                    mOnPrepareCalled.signal();
1851                } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
1852                    mOnCompletionCalled.signal();
1853                }
1854            }
1855
1856            @Override
1857            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
1858                if (what == MediaPlayer2.CALL_COMPLETED_SEEK_TO) {
1859                    mOnSeekCompleteCalled.signal();
1860                } else if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
1861                    mOnPlayCalled.signal();
1862                }
1863            }
1864        };
1865        synchronized (mEventCbLock) {
1866            mEventCallbacks.add(ecb);
1867        }
1868
1869        assertFalse(mOnPrepareCalled.isSignalled());
1870        assertFalse(mOnVideoSizeChangedCalled.isSignalled());
1871        mPlayer.prepare();
1872        mOnPrepareCalled.waitForSignal();
1873        mOnVideoSizeChangedCalled.waitForSignal();
1874
1875        mOnSeekCompleteCalled.reset();
1876        mPlayer.seekTo(mp4Duration >> 1, MediaPlayer2.SEEK_PREVIOUS_SYNC);
1877        mOnSeekCompleteCalled.waitForSignal();
1878
1879        assertFalse(mOnCompletionCalled.isSignalled());
1880        mPlayer.play();
1881        mOnPlayCalled.waitForSignal();
1882        while (mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING) {
1883            Thread.sleep(SLEEP_TIME);
1884        }
1885        assertFalse(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
1886        mOnCompletionCalled.waitForSignal();
1887        assertFalse(mOnErrorCalled.isSignalled());
1888        mPlayer.reset();
1889    }
1890
1891    @Test
1892    @LargeTest
1893    public void testPlayerStates() throws Throwable {
1894        final int mp4Duration = 8484;
1895
1896        if (!checkLoadResource(R.raw.testvideo)) {
1897            return; // skip;
1898        }
1899        mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
1900
1901        final Monitor prepareCompleted = new Monitor();
1902        final Monitor playCompleted = new Monitor();
1903        final Monitor pauseCompleted = new Monitor();
1904
1905        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
1906            @Override
1907            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
1908                if (what == MediaPlayer2.CALL_COMPLETED_PREPARE) {
1909                    prepareCompleted.signal();
1910                } else if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
1911                    playCompleted.signal();
1912                } else if (what == MediaPlayer2.CALL_COMPLETED_PAUSE) {
1913                    pauseCompleted.signal();
1914                }
1915            }
1916        };
1917        synchronized (mEventCbLock) {
1918            mEventCallbacks.add(ecb);
1919        }
1920
1921        MediaPlayerInterface playerBase = mPlayer.getMediaPlayerInterface();
1922        assertEquals(MediaPlayerInterface.BUFFERING_STATE_UNKNOWN, playerBase.getBufferingState());
1923        assertEquals(MediaPlayerInterface.PLAYER_STATE_IDLE, playerBase.getPlayerState());
1924        prepareCompleted.reset();
1925        playerBase.prepare();
1926        prepareCompleted.waitForSignal();
1927        assertEquals(MediaPlayerInterface.BUFFERING_STATE_BUFFERING_AND_PLAYABLE,
1928                playerBase.getBufferingState());
1929        assertEquals(MediaPlayerInterface.PLAYER_STATE_PAUSED, playerBase.getPlayerState());
1930        assertEquals(MediaPlayer2.MEDIAPLAYER2_STATE_PREPARED, mPlayer.getMediaPlayer2State());
1931
1932        playCompleted.reset();
1933        playerBase.play();
1934        playCompleted.waitForSignal();
1935        assertEquals(MediaPlayerInterface.BUFFERING_STATE_BUFFERING_AND_PLAYABLE,
1936                playerBase.getBufferingState());
1937        assertEquals(MediaPlayerInterface.PLAYER_STATE_PLAYING, playerBase.getPlayerState());
1938
1939        pauseCompleted.reset();
1940        playerBase.pause();
1941        pauseCompleted.waitForSignal();
1942        assertEquals(MediaPlayerInterface.BUFFERING_STATE_BUFFERING_AND_PLAYABLE,
1943                playerBase.getBufferingState());
1944        assertEquals(MediaPlayerInterface.PLAYER_STATE_PAUSED, playerBase.getPlayerState());
1945
1946        playerBase.reset();
1947        assertEquals(MediaPlayerInterface.BUFFERING_STATE_UNKNOWN, playerBase.getBufferingState());
1948        assertEquals(MediaPlayerInterface.PLAYER_STATE_IDLE, playerBase.getPlayerState());
1949    }
1950
1951    @Test
1952    @LargeTest
1953    public void testPlayerEventCallback() throws Throwable {
1954        final int mp4Duration = 8484;
1955
1956        if (!checkLoadResource(R.raw.testvideo)) {
1957            return; // skip;
1958        }
1959        final DataSourceDesc dsd2 = createDataSourceDesc(
1960                R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz);
1961        mPlayer.setNextDataSource(dsd2);
1962
1963        mPlayer.setSurface(mActivity.getSurfaceHolder().getSurface());
1964
1965        final Monitor onDsdChangedCalled = new Monitor();
1966        final Monitor onPrepareCalled = new Monitor();
1967        final Monitor onSeekCompleteCalled = new Monitor();
1968        final Monitor onPlayerStateChangedCalled = new Monitor();
1969        final AtomicInteger playerState = new AtomicInteger();
1970        final Monitor onBufferingStateChangedCalled = new Monitor();
1971        final AtomicInteger bufferingState = new AtomicInteger();
1972        final Monitor onPlaybackSpeedChanged = new Monitor();
1973        final AtomicReference<Float> playbackSpeed = new AtomicReference<>();
1974
1975        PlayerEventCallback callback = new PlayerEventCallback() {
1976            @Override
1977            public void onCurrentDataSourceChanged(MediaPlayerInterface mpb, DataSourceDesc dsd) {
1978                onDsdChangedCalled.signal();
1979            }
1980
1981            @Override
1982            public void onMediaPrepared(MediaPlayerInterface mpb, DataSourceDesc dsd) {
1983                onPrepareCalled.signal();
1984            }
1985
1986            @Override
1987            public void onPlayerStateChanged(MediaPlayerInterface mpb, int state) {
1988                playerState.set(state);
1989                onPlayerStateChangedCalled.signal();
1990            }
1991
1992            @Override
1993            public void onBufferingStateChanged(MediaPlayerInterface mpb, DataSourceDesc dsd,
1994                    int state) {
1995                bufferingState.set(state);
1996                onBufferingStateChangedCalled.signal();
1997            }
1998
1999            @Override
2000            public void onPlaybackSpeedChanged(MediaPlayerInterface mpb, float speed) {
2001                playbackSpeed.set(speed);
2002                onPlaybackSpeedChanged.signal();
2003            }
2004
2005            @Override
2006            public void onSeekCompleted(MediaPlayerInterface mpb, long position) {
2007                onSeekCompleteCalled.signal();
2008            }
2009        };
2010        MediaPlayerInterface basePlayer = mPlayer.getMediaPlayerInterface();
2011        ExecutorService executor = Executors.newFixedThreadPool(1);
2012        basePlayer.registerPlayerEventCallback(executor, callback);
2013
2014        onPrepareCalled.reset();
2015        onPlayerStateChangedCalled.reset();
2016        onBufferingStateChangedCalled.reset();
2017        basePlayer.prepare();
2018        do {
2019            assertTrue(onBufferingStateChangedCalled.waitForSignal(1000));
2020        } while (bufferingState.get()
2021                != MediaPlayerInterface.BUFFERING_STATE_BUFFERING_AND_STARVED);
2022
2023        assertTrue(onPrepareCalled.waitForSignal(1000));
2024        do {
2025            assertTrue(onPlayerStateChangedCalled.waitForSignal(1000));
2026        } while (playerState.get() != MediaPlayerInterface.PLAYER_STATE_PAUSED);
2027        do {
2028            assertTrue(onBufferingStateChangedCalled.waitForSignal(1000));
2029        } while (bufferingState.get()
2030                != MediaPlayerInterface.BUFFERING_STATE_BUFFERING_AND_PLAYABLE);
2031
2032        onSeekCompleteCalled.reset();
2033        basePlayer.seekTo(mp4Duration >> 1);
2034        onSeekCompleteCalled.waitForSignal();
2035
2036        onPlaybackSpeedChanged.reset();
2037        basePlayer.setPlaybackSpeed(0.5f);
2038        do {
2039            assertTrue(onPlaybackSpeedChanged.waitForSignal(1000));
2040        } while (Math.abs(playbackSpeed.get() - 0.5f) > FLOAT_TOLERANCE);
2041
2042        basePlayer.skipToNext();
2043        assertTrue(onDsdChangedCalled.waitForSignal(1000));
2044
2045        basePlayer.reset();
2046
2047        basePlayer.unregisterPlayerEventCallback(callback);
2048        executor.shutdown();
2049    }
2050
2051    public void testRecordAndPlay() throws Exception {
2052        if (!hasMicrophone()) {
2053            return;
2054        }
2055        /* FIXME: check the codec exists.
2056        if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_AUDIO_AMR_NB)
2057                || !MediaUtils.checkEncoder(MediaFormat.MIMETYPE_AUDIO_AMR_NB)) {
2058            return; // skip
2059        }
2060        */
2061        File outputFile = new File(Environment.getExternalStorageDirectory(),
2062                "record_and_play.3gp");
2063        String outputFileLocation = outputFile.getAbsolutePath();
2064        try {
2065            recordMedia(outputFileLocation);
2066
2067            Uri uri = Uri.parse(outputFileLocation);
2068            MediaPlayer2 mp = MediaPlayer2.create();
2069            try {
2070                mp.setDataSource(new DataSourceDesc.Builder()
2071                        .setDataSource(mContext, uri)
2072                        .build());
2073                mp.prepare();
2074                Thread.sleep(SLEEP_TIME);
2075                playAndStop(mp);
2076            } finally {
2077                mp.close();
2078            }
2079
2080            try {
2081                mp = createMediaPlayer2(mContext, uri);
2082                playAndStop(mp);
2083            } finally {
2084                if (mp != null) {
2085                    mp.close();
2086                }
2087            }
2088
2089            try {
2090                mp = createMediaPlayer2(mContext, uri, mActivity.getSurfaceHolder());
2091                playAndStop(mp);
2092            } finally {
2093                if (mp != null) {
2094                    mp.close();
2095                }
2096            }
2097        } finally {
2098            outputFile.delete();
2099        }
2100    }
2101
2102    private void playAndStop(MediaPlayer2 mp) throws Exception {
2103        mp.play();
2104        Thread.sleep(SLEEP_TIME);
2105        mp.reset();
2106    }
2107
2108    private void recordMedia(String outputFile) throws Exception {
2109        MediaRecorder mr = new MediaRecorder();
2110        try {
2111            mr.setAudioSource(MediaRecorder.AudioSource.MIC);
2112            mr.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
2113            mr.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
2114            mr.setOutputFile(outputFile);
2115
2116            mr.prepare();
2117            mr.start();
2118            Thread.sleep(SLEEP_TIME);
2119            mr.stop();
2120        } finally {
2121            mr.release();
2122        }
2123    }
2124
2125    private boolean hasMicrophone() {
2126        return mActivity.getPackageManager().hasSystemFeature(
2127                PackageManager.FEATURE_MICROPHONE);
2128    }
2129
2130    // Smoke test playback from a Media2DataSource.
2131    @Test
2132    @LargeTest
2133    public void testPlaybackFromAMedia2DataSource() throws Exception {
2134        final int resid = R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
2135        final int duration = 10000;
2136
2137        /* FIXME: check the codec exists.
2138        if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
2139            return;
2140        }
2141        */
2142
2143        TestMedia2DataSource dataSource =
2144                TestMedia2DataSource.fromAssetFd(mResources.openRawResourceFd(resid));
2145        // Test returning -1 from getSize() to indicate unknown size.
2146        dataSource.returnFromGetSize(-1);
2147        mPlayer.setDataSource(new DataSourceDesc.Builder()
2148                .setDataSource(dataSource)
2149                .build());
2150        playLoadedVideo(null, null, -1);
2151        assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
2152
2153        // Test pause and restart.
2154        mPlayer.pause();
2155        Thread.sleep(SLEEP_TIME);
2156        assertFalse(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
2157
2158        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
2159            @Override
2160            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
2161                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
2162                    mOnPrepareCalled.signal();
2163                }
2164            }
2165
2166            @Override
2167            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
2168                if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
2169                    mOnPlayCalled.signal();
2170                }
2171            }
2172        };
2173        mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
2174
2175        mOnPlayCalled.reset();
2176        mPlayer.play();
2177        mOnPlayCalled.waitForSignal();
2178        assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
2179
2180        // Test reset.
2181        mPlayer.reset();
2182        mPlayer.setDataSource(new DataSourceDesc.Builder()
2183                .setDataSource(dataSource)
2184                .build());
2185
2186        mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
2187
2188        mOnPrepareCalled.reset();
2189        mPlayer.prepare();
2190        mOnPrepareCalled.waitForSignal();
2191
2192        mOnPlayCalled.reset();
2193        mPlayer.play();
2194        mOnPlayCalled.waitForSignal();
2195        assertTrue(mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING);
2196
2197        // Test seek. Note: the seek position is cached and returned as the
2198        // current position so there's no point in comparing them.
2199        mPlayer.seekTo(duration - SLEEP_TIME, MediaPlayer2.SEEK_PREVIOUS_SYNC);
2200        while (mPlayer.getMediaPlayer2State() == MediaPlayer2.MEDIAPLAYER2_STATE_PLAYING) {
2201            Thread.sleep(SLEEP_TIME);
2202        }
2203    }
2204
2205    @Test
2206    @LargeTest
2207    public void testNullMedia2DataSourceIsRejected() throws Exception {
2208        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
2209            @Override
2210            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
2211                if (what == MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE) {
2212                    mCallStatus = status;
2213                    mOnPlayCalled.signal();
2214                }
2215            }
2216        };
2217        mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
2218
2219        mCallStatus = MediaPlayer2.CALL_STATUS_NO_ERROR;
2220        mPlayer.setDataSource((DataSourceDesc) null);
2221        mOnPlayCalled.waitForSignal();
2222        assertTrue(mCallStatus != MediaPlayer2.CALL_STATUS_NO_ERROR);
2223    }
2224
2225    @Test
2226    @LargeTest
2227    public void testMedia2DataSourceIsClosedOnReset() throws Exception {
2228        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
2229            @Override
2230            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
2231                if (what == MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE) {
2232                    mCallStatus = status;
2233                    mOnPlayCalled.signal();
2234                }
2235            }
2236        };
2237        mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
2238
2239        TestMedia2DataSource dataSource = new TestMedia2DataSource(new byte[0]);
2240        mPlayer.setDataSource(new DataSourceDesc.Builder()
2241                .setDataSource(dataSource)
2242                .build());
2243        mOnPlayCalled.waitForSignal();
2244        mPlayer.reset();
2245        assertTrue(dataSource.isClosed());
2246    }
2247
2248    @Test
2249    @LargeTest
2250    public void testPlaybackFailsIfMedia2DataSourceThrows() throws Exception {
2251        final int resid = R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
2252        /* FIXME: check the codec exists.
2253        if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
2254            return;
2255        }
2256        */
2257
2258        setOnErrorListener();
2259        TestMedia2DataSource dataSource =
2260                TestMedia2DataSource.fromAssetFd(mResources.openRawResourceFd(resid));
2261        mPlayer.setDataSource(new DataSourceDesc.Builder()
2262                .setDataSource(dataSource)
2263                .build());
2264
2265        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
2266            @Override
2267            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
2268                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
2269                    mOnPrepareCalled.signal();
2270                }
2271            }
2272        };
2273        synchronized (mEventCbLock) {
2274            mEventCallbacks.add(ecb);
2275        }
2276
2277        mOnPrepareCalled.reset();
2278        mPlayer.prepare();
2279        mOnPrepareCalled.waitForSignal();
2280
2281        dataSource.throwFromReadAt();
2282        mPlayer.play();
2283        assertTrue(mOnErrorCalled.waitForSignal());
2284    }
2285
2286    @Test
2287    @LargeTest
2288    public void testPlaybackFailsIfMedia2DataSourceReturnsAnError() throws Exception {
2289        final int resid = R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
2290        /* FIXME: check the codec exists.
2291        if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
2292            return;
2293        }
2294        */
2295
2296        TestMedia2DataSource dataSource =
2297                TestMedia2DataSource.fromAssetFd(mResources.openRawResourceFd(resid));
2298        mPlayer.setDataSource(new DataSourceDesc.Builder()
2299                .setDataSource(dataSource)
2300                .build());
2301
2302        setOnErrorListener();
2303        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
2304            @Override
2305            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
2306                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
2307                    mOnPrepareCalled.signal();
2308                }
2309            }
2310        };
2311        synchronized (mEventCbLock) {
2312            mEventCallbacks.add(ecb);
2313        }
2314
2315        mOnPrepareCalled.reset();
2316        mPlayer.prepare();
2317        mOnPrepareCalled.waitForSignal();
2318
2319        dataSource.returnFromReadAt(-2);
2320        mPlayer.play();
2321        assertTrue(mOnErrorCalled.waitForSignal());
2322    }
2323
2324    @Test
2325    @SmallTest
2326    public void testClearPendingCommands() throws Exception {
2327        final Monitor readAllowed = new Monitor();
2328        Media2DataSource dataSource = new Media2DataSource() {
2329            @Override
2330            public int readAt(long position, byte[] buffer, int offset, int size)
2331                    throws IOException {
2332                try {
2333                    readAllowed.waitForSignal();
2334                } catch (InterruptedException e) {
2335                    fail();
2336                }
2337                return -1;
2338            }
2339
2340            @Override
2341            public long getSize() throws IOException {
2342                return -1;  // Unknown size
2343            }
2344
2345            @Override
2346            public void close() throws IOException {}
2347        };
2348        final ArrayDeque<Integer> commandsCompleted = new ArrayDeque<>();
2349        setOnErrorListener();
2350        MediaPlayer2.MediaPlayer2EventCallback ecb = new MediaPlayer2.MediaPlayer2EventCallback() {
2351            @Override
2352            public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
2353                if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
2354                    mOnPrepareCalled.signal();
2355                }
2356            }
2357
2358            @Override
2359            public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd, int what, int status) {
2360                commandsCompleted.add(what);
2361            }
2362
2363            @Override
2364            public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
2365                mOnErrorCalled.signal();
2366            }
2367        };
2368        synchronized (mEventCbLock) {
2369            mEventCallbacks.add(ecb);
2370        }
2371
2372        mOnPrepareCalled.reset();
2373        mOnErrorCalled.reset();
2374
2375        mPlayer.setDataSource(new DataSourceDesc.Builder()
2376                .setDataSource(dataSource)
2377                .build());
2378
2379        // prepare() will be pending until readAllowed is signaled.
2380        mPlayer.prepare();
2381
2382        mPlayer.play();
2383        mPlayer.pause();
2384        mPlayer.play();
2385        mPlayer.pause();
2386        mPlayer.play();
2387        mPlayer.seekTo(1000);
2388
2389        // Cause a failure on the pending prepare operation.
2390        readAllowed.signal();
2391        mOnErrorCalled.waitForSignal();
2392        assertEquals(0, mOnPrepareCalled.getNumSignal());
2393        assertEquals(1, commandsCompleted.size());
2394        assertEquals(MediaPlayer2.CALL_COMPLETED_SET_DATA_SOURCE,
2395                (int) commandsCompleted.peekFirst());
2396    }
2397}
2398