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