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