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