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