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