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