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